Skip to content

Commit

Permalink
libpod: write /etc/{hosts,resolv.conf} once
Browse files Browse the repository at this point in the history
My PR[1] to remove PostConfigureNetNS is blocked on other things I want
to split this change out. It reduces the complexity when generating
/etc/hosts and /etc/resolv.conf as now we always write this file after
we setup the network. this means we can get the actual ip from the netns
which is important.

[NO NEW TESTS NEEDED] This is just a rework.

[1] containers#18468

Signed-off-by: Paul Holzinger <pholzing@redhat.com>
  • Loading branch information
Luap99 authored and cgiradkar committed Jul 17, 2023
1 parent 8ac6fe3 commit fa24b3b
Show file tree
Hide file tree
Showing 2 changed files with 70 additions and 75 deletions.
48 changes: 15 additions & 33 deletions libpod/container_internal.go
Expand Up @@ -18,7 +18,6 @@ import (
"github.com/containers/buildah/pkg/overlay"
butil "github.com/containers/buildah/util"
"github.com/containers/common/libnetwork/etchosts"
"github.com/containers/common/libnetwork/resolvconf"
"github.com/containers/common/pkg/cgroups"
"github.com/containers/common/pkg/chown"
"github.com/containers/common/pkg/config"
Expand Down Expand Up @@ -961,51 +960,34 @@ func (c *Container) checkDependenciesRunning() ([]string, error) {
}

func (c *Container) completeNetworkSetup() error {
var nameservers []string
netDisabled, err := c.NetworkDisabled()
if err != nil {
return err
}
if !c.config.PostConfigureNetNS || netDisabled {
return nil
}
if err := c.syncContainer(); err != nil {
return err
}
if err := c.runtime.setupNetNS(c); err != nil {
return err
if netDisabled {
// with net=none we still want to set up /etc/hosts
return c.addHosts()
}
if err := c.save(); err != nil {
return err
if c.config.NetNsCtr != "" {
return nil
}
state := c.state
// collect any dns servers that the network backend tells us to use
for _, status := range c.getNetworkStatus() {
for _, server := range status.DNSServerIPs {
nameservers = append(nameservers, server.String())
if c.config.PostConfigureNetNS {
if err := c.syncContainer(); err != nil {
return err
}
}
nameservers = c.addSlirp4netnsDNS(nameservers)

// check if we have a bindmount for /etc/hosts
if hostsBindMount, ok := state.BindMounts[config.DefaultHostsFile]; ok {
entries, err := c.getHostsEntries()
if err != nil {
if err := c.runtime.setupNetNS(c); err != nil {
return err
}
// add new container ips to the hosts file
if err := etchosts.Add(hostsBindMount, entries); err != nil {
if err := c.save(); err != nil {
return err
}
}

// check if we have a bindmount for resolv.conf
resolvBindMount := state.BindMounts[resolvconf.DefaultResolvConf]
if len(nameservers) < 1 || resolvBindMount == "" || len(c.config.NetNsCtr) > 0 {
return nil
// add /etc/hosts entries
if err := c.addHosts(); err != nil {
return err
}
// write and return
return resolvconf.Add(resolvBindMount, nameservers)

return c.addResolvConf()
}

// Initialize a container, creating it in the runtime
Expand Down
97 changes: 55 additions & 42 deletions libpod/container_internal_common.go
Expand Up @@ -1819,17 +1819,17 @@ func (c *Container) makeBindMounts() error {
// will recreate. Only do this if we aren't sharing them with
// another container.
if c.config.NetNsCtr == "" {
if resolvePath, ok := c.state.BindMounts["/etc/resolv.conf"]; ok {
if resolvePath, ok := c.state.BindMounts[resolvconf.DefaultResolvConf]; ok {
if err := os.Remove(resolvePath); err != nil && !os.IsNotExist(err) {
return fmt.Errorf("container %s: %w", c.ID(), err)
}
delete(c.state.BindMounts, "/etc/resolv.conf")
delete(c.state.BindMounts, resolvconf.DefaultResolvConf)
}
if hostsPath, ok := c.state.BindMounts["/etc/hosts"]; ok {
if hostsPath, ok := c.state.BindMounts[config.DefaultHostsFile]; ok {
if err := os.Remove(hostsPath); err != nil && !os.IsNotExist(err) {
return fmt.Errorf("container %s: %w", c.ID(), err)
}
delete(c.state.BindMounts, "/etc/hosts")
delete(c.state.BindMounts, config.DefaultHostsFile)
}
}

Expand All @@ -1851,9 +1851,9 @@ func (c *Container) makeBindMounts() error {

// The other container may not have a resolv.conf or /etc/hosts
// If it doesn't, don't copy them
resolvPath, exists := bindMounts["/etc/resolv.conf"]
resolvPath, exists := bindMounts[resolvconf.DefaultResolvConf]
if !c.config.UseImageResolvConf && exists {
err := c.mountIntoRootDirs("/etc/resolv.conf", resolvPath)
err := c.mountIntoRootDirs(resolvconf.DefaultResolvConf, resolvPath)

if err != nil {
return fmt.Errorf("assigning mounts to container %s: %w", c.ID(), err)
Expand Down Expand Up @@ -1896,31 +1896,31 @@ func (c *Container) makeBindMounts() error {
}
} else {
if !c.config.UseImageResolvConf {
if err := c.generateResolvConf(); err != nil {
if err := c.createResolvConf(); err != nil {
return fmt.Errorf("creating resolv.conf for container %s: %w", c.ID(), err)
}
}

if !c.config.UseImageHosts {
if err := c.createHosts(); err != nil {
if err := c.createHostsFile(); err != nil {
return fmt.Errorf("creating hosts file for container %s: %w", c.ID(), err)
}
}
}

if c.state.BindMounts["/etc/hosts"] != "" {
if err := c.relabel(c.state.BindMounts["/etc/hosts"], c.config.MountLabel, true); err != nil {
if c.state.BindMounts[config.DefaultHostsFile] != "" {
if err := c.relabel(c.state.BindMounts[config.DefaultHostsFile], c.config.MountLabel, true); err != nil {
return err
}
}

if c.state.BindMounts["/etc/resolv.conf"] != "" {
if err := c.relabel(c.state.BindMounts["/etc/resolv.conf"], c.config.MountLabel, true); err != nil {
if c.state.BindMounts[resolvconf.DefaultResolvConf] != "" {
if err := c.relabel(c.state.BindMounts[resolvconf.DefaultResolvConf], c.config.MountLabel, true); err != nil {
return err
}
}
} else if !c.config.UseImageHosts && c.state.BindMounts["/etc/hosts"] == "" {
if err := c.createHosts(); err != nil {
} else if !c.config.UseImageHosts && c.state.BindMounts[config.DefaultHostsFile] == "" {
if err := c.createHostsFile(); err != nil {
return fmt.Errorf("creating hosts file for container %s: %w", c.ID(), err)
}
}
Expand Down Expand Up @@ -2028,8 +2028,25 @@ rootless=%d
return c.makePlatformBindMounts()
}

// generateResolvConf generates a containers resolv.conf
func (c *Container) generateResolvConf() error {
// createResolvConf create the resolv.conf file and bind mount it
func (c *Container) createResolvConf() error {
destPath := filepath.Join(c.state.RunDir, "resolv.conf")
f, err := os.Create(destPath)
if err != nil {
return err
}
f.Close()
return c.bindMountRootFile(destPath, resolvconf.DefaultResolvConf)
}

// addResolvConf add resolv.conf entries
func (c *Container) addResolvConf() error {
destPath, ok := c.state.BindMounts[resolvconf.DefaultResolvConf]
if !ok {
// no resolv.conf mount, do nothing
return nil
}

var (
networkNameServers []string
networkSearchDomains []string
Expand Down Expand Up @@ -2082,11 +2099,7 @@ func (c *Container) generateResolvConf() error {
nameservers = networkNameServers

// slirp4netns has a built in DNS forwarder.
// If in userns the network is not setup here, instead we need to do that in
// c.completeNetworkSetup() which knows the actual slirp dns ip only at that point
if !c.config.PostConfigureNetNS {
nameservers = c.addSlirp4netnsDNS(nameservers)
}
nameservers = c.addSlirp4netnsDNS(nameservers)
}

// Set DNS search domains
Expand All @@ -2103,8 +2116,6 @@ func (c *Container) generateResolvConf() error {
options = append(options, c.runtime.config.Containers.DNSOptions...)
options = append(options, c.config.DNSOption...)

destPath := filepath.Join(c.state.RunDir, "resolv.conf")

var namespaces []spec.LinuxNamespace
if c.config.Spec.Linux != nil {
namespaces = c.config.Spec.Linux.Namespaces
Expand All @@ -2122,7 +2133,7 @@ func (c *Container) generateResolvConf() error {
return fmt.Errorf("building resolv.conf for container %s: %w", c.ID(), err)
}

return c.bindMountRootFile(destPath, resolvconf.DefaultResolvConf)
return nil
}

// Check if a container uses IPv6.
Expand Down Expand Up @@ -2209,36 +2220,38 @@ func (c *Container) getHostsEntries() (etchosts.HostEntries, error) {
return entries, nil
}

func (c *Container) createHosts() error {
var containerIPsEntries etchosts.HostEntries
var err error
// if we configure the netns after the container create we should not add
// the hosts here since we have no information about the actual ips
// instead we will add them in c.completeNetworkSetup()
if !c.config.PostConfigureNetNS {
containerIPsEntries, err = c.getHostsEntries()
if err != nil {
return fmt.Errorf("failed to get container ip host entries: %w", err)
}
func (c *Container) createHostsFile() error {
targetFile := filepath.Join(c.state.RunDir, "hosts")
f, err := os.Create(targetFile)
if err != nil {
return err
}
f.Close()
return c.bindMountRootFile(targetFile, config.DefaultHostsFile)
}

func (c *Container) addHosts() error {
targetFile, ok := c.state.BindMounts[config.DefaultHostsFile]
if !ok {
// no host file nothing to do
return nil
}
containerIPsEntries, err := c.getHostsEntries()
if err != nil {
return fmt.Errorf("failed to get container ip host entries: %w", err)
}
baseHostFile, err := etchosts.GetBaseHostFile(c.runtime.config.Containers.BaseHostsFile, c.state.Mountpoint)
if err != nil {
return err
}

targetFile := filepath.Join(c.state.RunDir, "hosts")
err = etchosts.New(&etchosts.Params{
return etchosts.New(&etchosts.Params{
BaseFile: baseHostFile,
ExtraHosts: c.config.HostAdd,
ContainerIPs: containerIPsEntries,
HostContainersInternalIP: etchosts.GetHostContainersInternalIP(c.runtime.config, c.state.NetworkStatus, c.runtime.network),
TargetFile: targetFile,
})
if err != nil {
return err
}

return c.bindMountRootFile(targetFile, config.DefaultHostsFile)
}

// bindMountRootFile will chown and relabel the source file to make it usable in the container.
Expand Down

0 comments on commit fa24b3b

Please sign in to comment.