From aa649e71230765c1281109169946cd20d2cddff0 Mon Sep 17 00:00:00 2001 From: George Joseph Date: Sun, 24 Nov 2024 15:31:58 -0700 Subject: [PATCH] Pass container hostname to netavark Passing the hostname allows netavark to include it in DHCP lease requests which, in an environment where DDNS is used, can cause DNS entries to be created automatically. * The current Hostname() function in container.go was updated to check the new `use_container_name_as_hostname` option in the CONTAINERS table of containers.conf. If set and no hostname was configured for the container, it causes the hostname to be set to a version of the container's name with the characters not valid for a hostname removed. If not set (the default), the original behavior of setting the hostname to the short container ID is preserved. * Because the Hostname() function can return the host's hostname if the container isn't running in a private UTS namespace, and we'd NEVER want to send _that_ in a DHCP request for a container, a new function NetworkHostname() was added which functions like Hostname() except that it will return an empty string instead of the host's hostname if the container is not running in a private UTS namespace. * networking_common.getNetworkOptions() now uses NetworkHostname() to set the ContainerHostname member of the NetworkOptions structure. That member was added to the structure in a corresponding commit in common/libnetwork/types/network.go. Signed-off-by: George Joseph --- libpod/container.go | 60 ++++++++++++++++++++++++++++++++++++- libpod/networking_common.go | 7 +++-- 2 files changed, 63 insertions(+), 4 deletions(-) diff --git a/libpod/container.go b/libpod/container.go index f54b8f35fd..51c17186ca 100644 --- a/libpod/container.go +++ b/libpod/container.go @@ -20,6 +20,7 @@ import ( "github.com/containers/podman/v5/libpod/define" "github.com/containers/podman/v5/libpod/lock" "github.com/containers/storage" + "github.com/containers/storage/pkg/regexp" spec "github.com/opencontainers/runtime-spec/specs-go" "github.com/sirupsen/logrus" "golang.org/x/sys/unix" @@ -667,7 +668,7 @@ func (c *Container) RuntimeName() string { // Unlocked // Hostname gets the container's hostname -func (c *Container) Hostname() string { +func (c *Container) ConfiguredHostname() string { if c.config.UTSNsCtr != "" { utsNsCtr, err := c.runtime.GetContainer(c.config.UTSNsCtr) if err != nil { @@ -680,6 +681,18 @@ func (c *Container) Hostname() string { if c.config.Spec.Hostname != "" { return c.config.Spec.Hostname } + return "" +} + +// Characters valid in a hostname are in the set [0-9a-zA-Z.-] +var invalidHostnameCharacters = regexp.Delayed("[^0-9a-zA-Z.-]") + +// Hostname gets the container's hostname +func (c *Container) Hostname() string { + hostname := c.ConfiguredHostname() + if hostname != "" { + return hostname + } // if the container is not running in a private UTS namespace, // return the host's hostname. @@ -690,6 +703,51 @@ func (c *Container) Hostname() string { return hostname } } + + // If use_container_name_as_hostname is set in the CONTAINERS table in containers.conf + // use a sanitized version of the container's name as the hostname. + if c.runtime.config.Containers.UseContainerNameAsHostName { + sanitizedHostname := invalidHostnameCharacters.ReplaceAllString(c.Name(), "") + return sanitizedHostname + } + + // Otherwise use the container's short ID as the hostname. + if len(c.ID()) < 11 { + return c.ID() + } + return c.ID()[:12] +} + +// If the container isn't running in a private UTS namespace, Hostname() +// will return the host's hostname as the container's hostname. If netavark +// were to try and obtain a DHCP lease with the host's hostname in an environment +// where DDNS was active, bad things could happen. NetworkHostname() on the +// other hand, will return an empty string if the container isn't running +// in a private UTS namespace. +// +// This function should only be used to populate the ContainerHostname member +// of the common.libnetwork.types.NetworkOptions struct. +func (c *Container) NetworkHostname() string { + hostname := c.ConfiguredHostname() + if hostname != "" { + return hostname + } + + // if the container is not running in a private UTS namespace, + // return an empty string. + privateUTS := c.hasPrivateUTS() + if !privateUTS { + return "" + } + + // If use_container_name_as_hostname is set in the CONTAINERS table in containers.conf + // use a sanitized version of the container's name as the hostname. + if c.runtime.config.Containers.UseContainerNameAsHostName { + sanitizedHostname := invalidHostnameCharacters.ReplaceAllString(c.Name(), "") + return sanitizedHostname + } + + // Otherwise use the container's short ID as the hostname. if len(c.ID()) < 11 { return c.ID() } diff --git a/libpod/networking_common.go b/libpod/networking_common.go index bac0d48622..b23308fa7c 100644 --- a/libpod/networking_common.go +++ b/libpod/networking_common.go @@ -55,9 +55,10 @@ func (c *Container) getNetworkOptions(networkOpts map[string]types.PerNetworkOpt nameservers = append(nameservers, ip.String()) } opts := types.NetworkOptions{ - ContainerID: c.config.ID, - ContainerName: getNetworkPodName(c), - DNSServers: nameservers, + ContainerID: c.config.ID, + ContainerName: getNetworkPodName(c), + DNSServers: nameservers, + ContainerHostname: c.NetworkHostname(), } opts.PortMappings = c.convertPortMappings()