mirror of
https://github.com/containers/podman.git
synced 2025-12-06 05:37:49 +08:00
Pull in updates made to the filters code for images. Filters now perform an AND operation except for th reference filter which does an OR operation for positive case but an AND operation for negative cases. Signed-off-by: Urvashi Mohnani <umohnani@redhat.com>
183 lines
5.8 KiB
Go
183 lines
5.8 KiB
Go
package resolvconf
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
|
|
"github.com/opencontainers/runtime-spec/specs-go"
|
|
"github.com/sirupsen/logrus"
|
|
"golang.org/x/exp/slices"
|
|
)
|
|
|
|
const (
|
|
localhost = "127.0.0.1"
|
|
systemdResolvedIP = "127.0.0.53"
|
|
)
|
|
|
|
// Params for the New() function.
|
|
type Params struct {
|
|
// Path is the path to new resolv.conf file which should be created.
|
|
Path string
|
|
// Namespaces is the list of container namespaces.
|
|
// This is required to fist check for a resolv.conf under /etc/netns,
|
|
// created by "ip netns". Also used to check if the container has a
|
|
// netns in which case localhost nameserver must be filtered.
|
|
Namespaces []specs.LinuxNamespace
|
|
// IPv6Enabled will filter ipv6 nameservers when not set to true.
|
|
IPv6Enabled bool
|
|
// KeepHostServers can be set when it is required to still keep the
|
|
// original resolv.conf content even when custom Nameserver/Searches/Options
|
|
// are set. In this case they will be appended to the given values.
|
|
KeepHostServers bool
|
|
// Nameservers is a list of nameservers the container should use,
|
|
// instead of the default ones from the host.
|
|
Nameservers []string
|
|
// Searches is a list of dns search domains the container should use,
|
|
// instead of the default ones from the host.
|
|
Searches []string
|
|
// Options is a list of dns options the container should use,
|
|
// instead of the default ones from the host.
|
|
Options []string
|
|
|
|
// resolvConfPath is the path which should be used as base to get the dns
|
|
// options. This should only be used for testing purposes. For all other
|
|
// callers this defaults to /etc/resolv.conf.
|
|
resolvConfPath string
|
|
}
|
|
|
|
func getDefaultResolvConf(params *Params) ([]byte, bool, error) {
|
|
resolveConf := DefaultResolvConf
|
|
// this is only used by testing
|
|
if params.resolvConfPath != "" {
|
|
resolveConf = params.resolvConfPath
|
|
}
|
|
hostNS := true
|
|
for _, ns := range params.Namespaces {
|
|
if ns.Type == specs.NetworkNamespace {
|
|
hostNS = false
|
|
if ns.Path != "" && !strings.HasPrefix(ns.Path, "/proc/") {
|
|
// check for netns created by "ip netns"
|
|
path := filepath.Join("/etc/netns", filepath.Base(ns.Path), "resolv.conf")
|
|
_, err := os.Stat(path)
|
|
if err == nil {
|
|
resolveConf = path
|
|
}
|
|
}
|
|
break
|
|
}
|
|
}
|
|
|
|
contents, err := os.ReadFile(resolveConf)
|
|
if err != nil && !errors.Is(err, os.ErrNotExist) {
|
|
return nil, false, err
|
|
}
|
|
if hostNS {
|
|
return contents, hostNS, nil
|
|
}
|
|
|
|
ns := getNameservers(contents)
|
|
// Check for local only resolver, in this case we want to get the real nameservers
|
|
// since localhost is not reachable from the netns.
|
|
if len(ns) == 1 {
|
|
var path string
|
|
switch ns[0] {
|
|
case systemdResolvedIP:
|
|
// used by systemd-resolved
|
|
path = "/run/systemd/resolve/resolv.conf"
|
|
case localhost:
|
|
// used by NetworkManager https://github.com/containers/podman/issues/13599
|
|
path = "/run/NetworkManager/no-stub-resolv.conf"
|
|
}
|
|
if path != "" {
|
|
// read the actual resolv.conf file for
|
|
resolvedContents, err := os.ReadFile(path)
|
|
if err != nil {
|
|
// do not error when the file does not exists, the detection logic is not perfect
|
|
if !errors.Is(err, os.ErrNotExist) {
|
|
return nil, false, fmt.Errorf("local resolver detected, but could not read real resolv.conf at %q: %w", path, err)
|
|
}
|
|
} else {
|
|
logrus.Debugf("found local resolver, using %q to get the nameservers", path)
|
|
contents = resolvedContents
|
|
}
|
|
}
|
|
}
|
|
|
|
return contents, hostNS, nil
|
|
}
|
|
|
|
// unsetSearchDomainsIfNeeded removes the search domain when they contain a single dot as element.
|
|
func unsetSearchDomainsIfNeeded(searches []string) []string {
|
|
if slices.Contains(searches, ".") {
|
|
return nil
|
|
}
|
|
return searches
|
|
}
|
|
|
|
// New creates a new resolv.conf file with the given params.
|
|
func New(params *Params) error {
|
|
// short path, if everything is given there is no need to actually read the hosts /etc/resolv.conf
|
|
if len(params.Nameservers) > 0 && len(params.Options) > 0 && len(params.Searches) > 0 && !params.KeepHostServers {
|
|
return build(params.Path, params.Nameservers, unsetSearchDomainsIfNeeded(params.Searches), params.Options)
|
|
}
|
|
|
|
content, hostNS, err := getDefaultResolvConf(params)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to get the default /etc/resolv.conf content: %w", err)
|
|
}
|
|
|
|
content = filterResolvDNS(content, params.IPv6Enabled, !hostNS)
|
|
|
|
nameservers := params.Nameservers
|
|
if len(nameservers) == 0 || params.KeepHostServers {
|
|
nameservers = append(nameservers, getNameservers(content)...)
|
|
}
|
|
|
|
searches := unsetSearchDomainsIfNeeded(params.Searches)
|
|
// if no params.Searches then use host ones
|
|
// otherwise make sure that they were no explicitly unset before adding host ones
|
|
if len(params.Searches) == 0 || (params.KeepHostServers && len(searches) > 0) {
|
|
searches = append(searches, getSearchDomains(content)...)
|
|
}
|
|
|
|
options := params.Options
|
|
if len(options) == 0 || params.KeepHostServers {
|
|
options = append(options, getOptions(content)...)
|
|
}
|
|
|
|
return build(params.Path, nameservers, searches, options)
|
|
}
|
|
|
|
// Add will add the given nameservers to the given resolv.conf file.
|
|
// It will add the nameserver in front of the existing ones.
|
|
func Add(path string, nameservers []string) error {
|
|
contents, err := os.ReadFile(path)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
nameservers = append(nameservers, getNameservers(contents)...)
|
|
return build(path, nameservers, getSearchDomains(contents), getOptions(contents))
|
|
}
|
|
|
|
// Remove the given nameserver from the given resolv.conf file.
|
|
func Remove(path string, nameservers []string) error {
|
|
contents, err := os.ReadFile(path)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
oldNameservers := getNameservers(contents)
|
|
newNameserver := make([]string, 0, len(oldNameservers))
|
|
for _, ns := range oldNameservers {
|
|
if !slices.Contains(nameservers, ns) {
|
|
newNameserver = append(newNameserver, ns)
|
|
}
|
|
}
|
|
|
|
return build(path, newNameserver, getSearchDomains(contents), getOptions(contents))
|
|
}
|