From 696bcd2773cf6c255855db9cf2ef724547626438 Mon Sep 17 00:00:00 2001 From: Paul Holzinger Date: Tue, 19 Apr 2022 13:58:35 +0200 Subject: use etchosts package from c/common Use the new logic from c/common to create the hosts file. This will help to better allign the hosts files between buildah and podman. Also this fixes several bugs: - remove host entries when container is stopped and has a netNsCtr - add entries for containers in a pod - do not duplicate entries in the hosts file - use the correct slirp ip when an userns is used Features: - configure host.containers.internal entry in containers.conf - configure base hosts file in containers.conf Fixes #12003 Fixes #13224 Signed-off-by: Paul Holzinger --- test/system/030-run.bats | 2 +- test/system/200-pod.bats | 2 +- test/system/500-networking.bats | 34 +++++++++++++++++++++++++++++++++- 3 files changed, 35 insertions(+), 3 deletions(-) (limited to 'test/system') diff --git a/test/system/030-run.bats b/test/system/030-run.bats index 526003c2d..31db5f1c4 100644 --- a/test/system/030-run.bats +++ b/test/system/030-run.bats @@ -782,7 +782,7 @@ EOF mv $hosts_tmp /etc/hosts assert "$status" = 0 \ "podman run without /etc/hosts file should work" - assert "$output" =~ "^1\.2\.3\.4 foo.com.*" \ + assert "$output" =~ "^1\.2\.3\.4[[:blank:]]foo\.com.*" \ "users can add hosts even without /etc/hosts" } diff --git a/test/system/200-pod.bats b/test/system/200-pod.bats index 56449dcad..ef4bf1a6c 100644 --- a/test/system/200-pod.bats +++ b/test/system/200-pod.bats @@ -250,7 +250,7 @@ EOF is "$output" ".*invalid config provided: cannot set hostname when joining the pod UTS namespace: invalid configuration" "--hostname should not be allowed in share UTS pod" run_podman run --rm --pod $pod_id $IMAGE cat /etc/hosts - is "$output" ".*$add_host_ip $add_host_n" "--add-host was added" + is "$output" ".*$add_host_ip[[:blank:]]$add_host_n" "--add-host was added" is "$output" ".* $hostname" "--hostname is in /etc/hosts" # ^^^^ this must be a tab, not a space diff --git a/test/system/500-networking.bats b/test/system/500-networking.bats index 3bfc58a07..f5bb76d9a 100644 --- a/test/system/500-networking.bats +++ b/test/system/500-networking.bats @@ -133,14 +133,46 @@ load helpers done } +@test "podman pod manages /etc/hosts correctly" { + local pod_name=pod-$(random_string 10) + local infra_name=infra-$(random_string 10) + local con1_name=con1-$(random_string 10) + local con2_name=con2-$(random_string 10) + run_podman pod create --name $pod_name --infra-name $infra_name + pid="$output" + run_podman run --pod $pod_name --name $con1_name $IMAGE cat /etc/hosts + is "$output" ".*\s$pod_name $infra_name.*" "Pod hostname in /etc/hosts" + is "$output" ".*127.0.0.1\s.*$con1_name.*" "Container1 name in /etc/hosts" + # get the length of the hosts file + old_lines=${#lines[@]} + + # since the first container should be cleaned up now we should only see the + # new host entry and the old one should be removed (lines check) + run_podman run --pod $pod_name --name $con2_name $IMAGE cat /etc/hosts + is "$output" ".*\s$pod_name $infra_name.*" "Pod hostname in /etc/hosts" + is "$output" ".*127.0.0.1\s.*$con2_name.*" "Container2 name in /etc/hosts" + is "${#lines[@]}" "$old_lines" "Number of hosts lines is equal" + + run_podman pod rm $pod_name + is "$output" "$pid" "Only ID in output (no extra errors)" +} + @test "podman run with slirp4ns assigns correct addresses to /etc/hosts" { CIDR="$(random_rfc1918_subnet)" IP=$(hostname -I | cut -f 1 -d " ") local conname=con-$(random_string 10) run_podman run --rm --network slirp4netns:cidr="${CIDR}.0/24" \ --name $conname --hostname $conname $IMAGE cat /etc/hosts - is "$output" ".*${IP} host.containers.internal" "host.containers.internal should be the cidr+2 address" + is "$output" ".*${IP} host.containers.internal" "host.containers.internal should be host address" is "$output" ".*${CIDR}.100 $conname $conname" "$conname should be the cidr+100 address" + + if is_rootless; then + # check the slirp ip also works correct with userns + run_podman run --rm --userns keep-id --network slirp4netns:cidr="${CIDR}.0/24" \ + --name $conname --hostname $conname $IMAGE cat /etc/hosts + is "$output" ".*${IP} host.containers.internal" "host.containers.internal should be host address" + is "$output" ".*${CIDR}.100 $conname $conname" "$conname should be the cidr+100 address" + fi } @test "podman run with slirp4ns adds correct dns address to resolv.conf" { -- cgit v1.2.3-54-g00ecf From 128086639c6317c324c32423765c72f38c115f74 Mon Sep 17 00:00:00 2001 From: Paul Holzinger Date: Tue, 19 Apr 2022 16:22:39 +0200 Subject: libpod: fix c.Hostname() to respect the utsNsCtr When we lookup the hostname for a given container we have to check if the container is joined to another utsns and use this hostname then instead. This fixes a problem where the `hostname` command would use the correct name but /etc/hostname would contain a different name. Signed-off-by: Paul Holzinger --- libpod/container.go | 9 +++++++++ test/system/500-networking.bats | 8 ++++++-- 2 files changed, 15 insertions(+), 2 deletions(-) (limited to 'test/system') diff --git a/libpod/container.go b/libpod/container.go index bc3cab439..6e2b7f528 100644 --- a/libpod/container.go +++ b/libpod/container.go @@ -628,6 +628,15 @@ func (c *Container) RuntimeName() string { // Hostname gets the container's hostname func (c *Container) Hostname() string { + if c.config.UTSNsCtr != "" { + utsNsCtr, err := c.runtime.GetContainer(c.config.UTSNsCtr) + if err != nil { + // should we return an error here? + logrus.Errorf("unable to lookup uts namespace for container %s: %v", c.ID(), err) + return "" + } + return utsNsCtr.Hostname() + } if c.config.Spec.Hostname != "" { return c.config.Spec.Hostname } diff --git a/test/system/500-networking.bats b/test/system/500-networking.bats index f5bb76d9a..f57c3dbd7 100644 --- a/test/system/500-networking.bats +++ b/test/system/500-networking.bats @@ -142,7 +142,7 @@ load helpers pid="$output" run_podman run --pod $pod_name --name $con1_name $IMAGE cat /etc/hosts is "$output" ".*\s$pod_name $infra_name.*" "Pod hostname in /etc/hosts" - is "$output" ".*127.0.0.1\s.*$con1_name.*" "Container1 name in /etc/hosts" + is "$output" ".*127.0.0.1\s$con1_name.*" "Container1 name in /etc/hosts" # get the length of the hosts file old_lines=${#lines[@]} @@ -150,9 +150,13 @@ load helpers # new host entry and the old one should be removed (lines check) run_podman run --pod $pod_name --name $con2_name $IMAGE cat /etc/hosts is "$output" ".*\s$pod_name $infra_name.*" "Pod hostname in /etc/hosts" - is "$output" ".*127.0.0.1\s.*$con2_name.*" "Container2 name in /etc/hosts" + is "$output" ".*127.0.0.1\s$con2_name.*" "Container2 name in /etc/hosts" is "${#lines[@]}" "$old_lines" "Number of hosts lines is equal" + run_podman run --pod $pod_name $IMAGE sh -c "hostname && cat /etc/hostname" + is "${lines[0]}" "$pod_name" "hostname is the pod hostname" + is "${lines[1]}" "$pod_name" "/etc/hostname contains correct pod hostname" + run_podman pod rm $pod_name is "$output" "$pid" "Only ID in output (no extra errors)" } -- cgit v1.2.3-54-g00ecf From cf1b0c1965c9cc7f3b6d870720ba78865c8602e4 Mon Sep 17 00:00:00 2001 From: Paul Holzinger Date: Tue, 19 Apr 2022 18:47:32 +0200 Subject: network dis-/connect: update /etc/hosts When we connect or disconnect from a network we also have to update /etc/hosts to ensure we only have valid entries in there. This also fixes problems with docker-compose since this makes use of network connect/disconnect. Fixes #12533 Signed-off-by: Paul Holzinger --- libpod/networking_linux.go | 70 ++++++++++++++++++++++++++++++++++------- test/system/500-networking.bats | 15 ++++++++- 2 files changed, 73 insertions(+), 12 deletions(-) (limited to 'test/system') diff --git a/libpod/networking_linux.go b/libpod/networking_linux.go index 71e29f18f..41beaf41d 100644 --- a/libpod/networking_linux.go +++ b/libpod/networking_linux.go @@ -20,7 +20,9 @@ import ( "time" "github.com/containernetworking/plugins/pkg/ns" + "github.com/containers/common/libnetwork/etchosts" "github.com/containers/common/libnetwork/types" + "github.com/containers/common/pkg/config" "github.com/containers/common/pkg/netns" "github.com/containers/podman/v4/libpod/define" "github.com/containers/podman/v4/libpod/events" @@ -1282,15 +1284,35 @@ func (c *Container) NetworkDisconnect(nameOrID, netName string, force bool) erro for _, ip := range oldStatus.DNSServerIPs { stringIPs = append(stringIPs, ip.String()) } - if len(stringIPs) == 0 { - return nil + if len(stringIPs) > 0 { + logrus.Debugf("Removing DNS Servers %v from resolv.conf", stringIPs) + if err := c.removeNameserver(stringIPs); err != nil { + return err + } } - logrus.Debugf("Removing DNS Servers %v from resolv.conf", stringIPs) - if err := c.removeNameserver(stringIPs); err != nil { - return err + + // update /etc/hosts file + if file, ok := c.state.BindMounts[config.DefaultHostsFile]; ok { + // sync the names with c.getHostsEntries() + names := []string{c.Hostname(), c.config.Name} + rm := etchosts.GetNetworkHostEntries(map[string]types.StatusBlock{netName: oldStatus}, names...) + if len(rm) > 0 { + // make sure to lock this file to prevent concurrent writes when + // this is used a net dependency container + lock, err := lockfile.GetLockfile(file) + if err != nil { + return fmt.Errorf("failed to lock hosts file: %w", err) + } + logrus.Debugf("Remove /etc/hosts entries %v", rm) + lock.Lock() + err = etchosts.Remove(file, rm) + lock.Unlock() + if err != nil { + return err + } + } } } - return nil } @@ -1361,6 +1383,13 @@ func (c *Container) NetworkConnect(nameOrID, netName string, netOpts types.PerNe return errors.New("when adding aliases, results must be of length 1") } + // we need to get the old host entries before we add the new one to the status + // if we do not add do it here we will get the wrong existing entries which will throw of the logic + // we could also copy the map but this does not seem worth it + // sync the hostNames with c.getHostsEntries() + hostNames := []string{c.Hostname(), c.config.Name} + oldHostEntries := etchosts.GetNetworkHostEntries(networkStatus, hostNames...) + // update network status if networkStatus == nil { networkStatus = make(map[string]types.StatusBlock, 1) @@ -1394,12 +1423,31 @@ func (c *Container) NetworkConnect(nameOrID, netName string, netOpts types.PerNe } stringIPs = append(stringIPs, ip.String()) } - if len(stringIPs) == 0 { - return nil + if len(stringIPs) > 0 { + logrus.Debugf("Adding DNS Servers %v to resolv.conf", stringIPs) + if err := c.addNameserver(stringIPs); err != nil { + return err + } } - logrus.Debugf("Adding DNS Servers %v to resolv.conf", stringIPs) - if err := c.addNameserver(stringIPs); err != nil { - return err + + // update /etc/hosts file + if file, ok := c.state.BindMounts[config.DefaultHostsFile]; ok { + // make sure to lock this file to prevent concurrent writes when + // this is used a net dependency container + lock, err := lockfile.GetLockfile(file) + if err != nil { + return fmt.Errorf("failed to lock hosts file: %w", err) + } + new := etchosts.GetNetworkHostEntries(results, hostNames...) + logrus.Debugf("Add /etc/hosts entries %v", new) + // use special AddIfExists API to make sure we only add new entries if an old one exists + // see the AddIfExists() comment for more information + lock.Lock() + err = etchosts.AddIfExists(file, oldHostEntries, new) + lock.Unlock() + if err != nil { + return err + } } return nil diff --git a/test/system/500-networking.bats b/test/system/500-networking.bats index f57c3dbd7..274af7ec5 100644 --- a/test/system/500-networking.bats +++ b/test/system/500-networking.bats @@ -469,9 +469,17 @@ load helpers run_podman inspect $cid --format "{{(index .NetworkSettings.Networks \"$netname\").Aliases}}" is "$output" "[${cid:0:12}]" "short container id in network aliases" + # check /etc/hosts for our entry + run_podman exec $cid cat /etc/hosts + is "$output" ".*$ip.*" "hosts contain expected ip" + run_podman network disconnect $netname $cid is "$output" "" "Output should be empty (no errors)" + # check /etc/hosts again, the entry should be gone now + run_podman exec $cid cat /etc/hosts + assert "$output" !~ "$ip" "IP ($ip) should no longer be in /etc/hosts" + # check that we cannot curl (timeout after 3 sec) run curl --max-time 3 -s $SERVER/index.txt assert $status -ne 0 \ @@ -487,13 +495,18 @@ load helpers # check that we have a new ip and mac # if the ip is still the same this whole test turns into a nop run_podman inspect $cid --format "{{(index .NetworkSettings.Networks \"$netname\").IPAddress}}" - assert "$output" != "$ip" \ + new_ip="$output" + assert "$new_ip" != "$ip" \ "IP address did not change after podman network disconnect/connect" run_podman inspect $cid --format "{{(index .NetworkSettings.Networks \"$netname\").MacAddress}}" assert "$output" != "$mac" \ "MAC address did not change after podman network disconnect/connect" + # check /etc/hosts for the new entry + run_podman exec $cid cat /etc/hosts + is "$output" ".*$new_ip.*" "hosts contain expected new ip" + # Disconnect/reconnect of a container *with no ports* should succeed quietly run_podman network disconnect $netname $background_cid is "$output" "" "disconnect of container with no open ports" -- cgit v1.2.3-54-g00ecf From e0f5bf279bd6aba1b018c2a2d7d31ee5a5f7b70b Mon Sep 17 00:00:00 2001 From: Paul Holzinger Date: Thu, 21 Apr 2022 19:19:09 +0200 Subject: test/system: add containers.conf test for new /etc/hosts options Signed-off-by: Paul Holzinger --- test/system/500-networking.bats | 44 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) (limited to 'test/system') diff --git a/test/system/500-networking.bats b/test/system/500-networking.bats index 274af7ec5..880bf63ac 100644 --- a/test/system/500-networking.bats +++ b/test/system/500-networking.bats @@ -677,4 +677,48 @@ EOF done } +@test "podman run CONTAINERS_CONF /etc/hosts options" { + skip_if_remote "CONTAINERS_CONF redirect does not work on remote" + + containersconf=$PODMAN_TMPDIR/containers.conf + basehost=$PODMAN_TMPDIR/host + + ip1="$(random_rfc1918_subnet).$((RANDOM % 256))" + name1=host1$(random_string) + ip2="$(random_rfc1918_subnet).$((RANDOM % 256))" + name2=host2$(random_string) + + cat >$basehost <$containersconf <