From db68764d8bfa9380a208c121f2c49c2ac90d1a82 Mon Sep 17 00:00:00 2001 From: Gavin Lam Date: Mon, 11 Dec 2023 23:25:51 -0500 Subject: [PATCH] Fix Docker API compatibility with network alias (#17167) * Add BaseHostsFile to container configuration * Do not copy /etc/hosts file from host when creating a container using Docker API Signed-off-by: Gavin Lam --- libpod/container_config.go | 6 ++++++ libpod/container_internal_common.go | 9 ++++++++- libpod/options.go | 13 +++++++++++++ pkg/api/handlers/compat/containers_create.go | 2 ++ pkg/specgen/generate/container_create.go | 3 +++ pkg/specgen/specgen.go | 7 +++++++ test/compose/etc_hosts/README.md | 10 ++++++++++ test/compose/etc_hosts/docker-compose.yml | 19 +++++++++++++++++++ test/compose/etc_hosts/hosts | 2 ++ test/compose/etc_hosts/setup.sh | 5 +++++ test/compose/etc_hosts/teardown.sh | 5 +++++ test/compose/etc_hosts/tests.sh | 12 ++++++++++++ test/compose/test-compose | 2 +- 13 files changed, 93 insertions(+), 2 deletions(-) create mode 100644 test/compose/etc_hosts/README.md create mode 100644 test/compose/etc_hosts/docker-compose.yml create mode 100644 test/compose/etc_hosts/hosts create mode 100644 test/compose/etc_hosts/setup.sh create mode 100644 test/compose/etc_hosts/teardown.sh create mode 100644 test/compose/etc_hosts/tests.sh diff --git a/libpod/container_config.go b/libpod/container_config.go index 7df0c2c396..d252d95a83 100644 --- a/libpod/container_config.go +++ b/libpod/container_config.go @@ -291,6 +291,12 @@ type ContainerNetworkConfig struct { // bind-mounted inside the container. // Conflicts with HostAdd. UseImageHosts bool + // BaseHostsFile is the path to a hosts file, the entries from this file + // are added to the containers hosts file. As special value "image" is + // allowed which uses the /etc/hosts file from within the image and "none" + // which uses no base file at all. If it is empty we should default + // to the base_hosts_file configuration in containers.conf. + BaseHostsFile string `json:"baseHostsFile,omitempty"` // Hosts to add in container // Will be appended to host's host file HostAdd []string `json:"hostsAdd,omitempty"` diff --git a/libpod/container_internal_common.go b/libpod/container_internal_common.go index b66268765b..7fc77a09f5 100644 --- a/libpod/container_internal_common.go +++ b/libpod/container_internal_common.go @@ -2267,7 +2267,14 @@ func (c *Container) addHosts() error { 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) + + // Consider container level BaseHostsFile configuration first. + // If it is empty, fallback to containers.conf level configuration. + baseHostsFileConf := c.config.BaseHostsFile + if baseHostsFileConf == "" { + baseHostsFileConf = c.runtime.config.Containers.BaseHostsFile + } + baseHostFile, err := etchosts.GetBaseHostFile(baseHostsFileConf, c.state.Mountpoint) if err != nil { return err } diff --git a/libpod/options.go b/libpod/options.go index 09a833a4a7..6bc1046df9 100644 --- a/libpod/options.go +++ b/libpod/options.go @@ -2373,6 +2373,19 @@ func WithGroupEntry(groupEntry string) CtrCreateOption { } } +// WithBaseHostsFile sets the option to copy /etc/hosts file. +func WithBaseHostsFile(baseHostsFile string) CtrCreateOption { + return func(ctr *Container) error { + if ctr.valid { + return define.ErrCtrFinalized + } + + ctr.config.BaseHostsFile = baseHostsFile + + return nil + } +} + // WithMountAllDevices sets the option to mount all of a privileged container's // host devices func WithMountAllDevices() CtrCreateOption { diff --git a/pkg/api/handlers/compat/containers_create.go b/pkg/api/handlers/compat/containers_create.go index 80d7d7f316..0a61574c2e 100644 --- a/pkg/api/handlers/compat/containers_create.go +++ b/pkg/api/handlers/compat/containers_create.go @@ -116,6 +116,8 @@ func CreateContainer(w http.ResponseWriter, r *http.Request) { } // moby always create the working directory sg.CreateWorkingDir = true + // moby doesn't inherit /etc/hosts from host + sg.BaseHostsFile = "none" ic := abi.ContainerEngine{Libpod: runtime} report, err := ic.ContainerCreate(r.Context(), sg) diff --git a/pkg/specgen/generate/container_create.go b/pkg/specgen/generate/container_create.go index ea5fa1e7c3..5a36878425 100644 --- a/pkg/specgen/generate/container_create.go +++ b/pkg/specgen/generate/container_create.go @@ -378,6 +378,9 @@ func createContainerOptions(rt *libpod.Runtime, s *specgen.SpecGenerator, pod *l if s.GroupEntry != "" { options = append(options, libpod.WithGroupEntry(s.GroupEntry)) } + if s.BaseHostsFile != "" { + options = append(options, libpod.WithBaseHostsFile(s.BaseHostsFile)) + } if s.Privileged { options = append(options, libpod.WithMountAllDevices()) diff --git a/pkg/specgen/specgen.go b/pkg/specgen/specgen.go index 80b6899902..b65e1c0836 100644 --- a/pkg/specgen/specgen.go +++ b/pkg/specgen/specgen.go @@ -507,6 +507,13 @@ type ContainerNetworkConfig struct { // specgen is stable so we can not change this right now. // TODO (5.0): change to pointer UseImageHosts bool `json:"use_image_hosts"` + // BaseHostsFile is the path to a hosts file, the entries from this file + // are added to the containers hosts file. As special value "image" is + // allowed which uses the /etc/hosts file from within the image and "none" + // which uses no base file at all. If it is empty we should default + // to the base_hosts_file configuration in containers.conf. + // Optional. + BaseHostsFile string `json:"base_hosts_file,omitempty"` // HostAdd is a set of hosts which will be added to the container's // /etc/hosts file. // Conflicts with UseImageHosts. diff --git a/test/compose/etc_hosts/README.md b/test/compose/etc_hosts/README.md new file mode 100644 index 0000000000..ed6216fa0c --- /dev/null +++ b/test/compose/etc_hosts/README.md @@ -0,0 +1,10 @@ +etc hosts +=========== + +This test mounts a /etc/hosts file in the host containing an entry `foobar`, then create a container with an alias of the same hostname. + +Validation +------------ + +* No /etc/hosts entries are copied from the host. There should be only one entry of the hostname, which is IP address of the alias. +* The hostname is resolved to IP address of the alias. diff --git a/test/compose/etc_hosts/docker-compose.yml b/test/compose/etc_hosts/docker-compose.yml new file mode 100644 index 0000000000..b43e780f89 --- /dev/null +++ b/test/compose/etc_hosts/docker-compose.yml @@ -0,0 +1,19 @@ +version: '3.3' + +services: + test: + image: alpine + command: ["top"] + hostname: foobar + networks: + net1: + aliases: + - foobar + +networks: + net1: + driver: bridge + ipam: + driver: default + config: + - subnet: 10.123.0.0/24 diff --git a/test/compose/etc_hosts/hosts b/test/compose/etc_hosts/hosts new file mode 100644 index 0000000000..34ee5931c7 --- /dev/null +++ b/test/compose/etc_hosts/hosts @@ -0,0 +1,2 @@ +127.0.0.1 localhost +127.0.0.1 foobar diff --git a/test/compose/etc_hosts/setup.sh b/test/compose/etc_hosts/setup.sh new file mode 100644 index 0000000000..378aa1af41 --- /dev/null +++ b/test/compose/etc_hosts/setup.sh @@ -0,0 +1,5 @@ +if ! is_rootless; then + mount --bind $TEST_ROOTDIR/etc_hosts/hosts /etc/hosts +else + $PODMAN_BIN unshare mount --bind $TEST_ROOTDIR/etc_hosts/hosts /etc/hosts +fi diff --git a/test/compose/etc_hosts/teardown.sh b/test/compose/etc_hosts/teardown.sh new file mode 100644 index 0000000000..3af7d02e26 --- /dev/null +++ b/test/compose/etc_hosts/teardown.sh @@ -0,0 +1,5 @@ +if ! is_rootless; then + umount /etc/hosts +else + $PODMAN_BIN unshare umount /etc/hosts +fi diff --git a/test/compose/etc_hosts/tests.sh b/test/compose/etc_hosts/tests.sh new file mode 100644 index 0000000000..16ed314bf7 --- /dev/null +++ b/test/compose/etc_hosts/tests.sh @@ -0,0 +1,12 @@ +# -*- bash -*- + +ctr_name="etc_hosts_test_1" +if [ "$TEST_FLAVOR" = "compose_v2" ]; then + ctr_name="etc_hosts-test-1" +fi + +podman exec "$ctr_name" sh -c 'grep "foobar" /etc/hosts' +like "$output" "10\.123\.0\." "$testname : no entries are copied from the host" + +podman exec "$ctr_name" sh -c 'getent hosts foobar | awk "{print \$1}"' +like "$output" "10\.123\.0\." "$testname : hostname is resolved to IP address of the alias" diff --git a/test/compose/test-compose b/test/compose/test-compose index 83a033dafc..378548f440 100755 --- a/test/compose/test-compose +++ b/test/compose/test-compose @@ -207,7 +207,7 @@ function start_service() { $PODMAN_BIN \ --log-level debug \ - --storage-driver=vfs \ + --storage-driver=vfs \ --root $WORKDIR/root \ --runroot $WORKDIR/runroot \ --cgroup-manager=systemd \