diff options
Diffstat (limited to 'libpod')
-rw-r--r-- | libpod/container_internal_linux.go | 156 | ||||
-rw-r--r-- | libpod/container_log_linux.go | 2 | ||||
-rw-r--r-- | libpod/healthcheck.go | 36 | ||||
-rw-r--r-- | libpod/networking_linux.go | 55 | ||||
-rw-r--r-- | libpod/oci_conmon_exec_linux.go | 2 | ||||
-rw-r--r-- | libpod/options.go | 2 |
6 files changed, 197 insertions, 56 deletions
diff --git a/libpod/container_internal_linux.go b/libpod/container_internal_linux.go index 86d8586d0..afa351c17 100644 --- a/libpod/container_internal_linux.go +++ b/libpod/container_internal_linux.go @@ -2045,19 +2045,8 @@ func (c *Container) generateResolvConf() (string, error) { } } - ipv6 := false - // If network status is set check for ipv6 and dns namesevers netStatus := c.getNetworkStatus() for _, status := range netStatus { - for _, netInt := range status.Interfaces { - for _, netAddress := range netInt.Subnets { - // Note: only using To16() does not work since it also returns a valid ip for ipv4 - if netAddress.IPNet.IP.To4() == nil && netAddress.IPNet.IP.To16() != nil { - ipv6 = true - } - } - } - if status.DNSServerIPs != nil { for _, nsIP := range status.DNSServerIPs { networkNameServers = append(networkNameServers, nsIP.String()) @@ -2070,16 +2059,9 @@ func (c *Container) generateResolvConf() (string, error) { } } - if c.config.NetMode.IsSlirp4netns() { - ctrNetworkSlipOpts := []string{} - if c.config.NetworkOptions != nil { - ctrNetworkSlipOpts = append(ctrNetworkSlipOpts, c.config.NetworkOptions["slirp4netns"]...) - } - slirpOpts, err := parseSlirp4netnsNetworkOptions(c.runtime, ctrNetworkSlipOpts) - if err != nil { - return "", err - } - ipv6 = slirpOpts.enableIPv6 + ipv6, err := c.checkForIPv6(netStatus) + if err != nil { + return "", err } // Ensure that the container's /etc/resolv.conf is compatible with its @@ -2099,38 +2081,38 @@ func (c *Container) generateResolvConf() (string, error) { } dnsServers := append(dns, c.config.DNSServer...) // If the user provided dns, it trumps all; then dns masq; then resolv.conf + var search []string switch { case len(dnsServers) > 0: - // We store DNS servers as net.IP, so need to convert to string for _, server := range dnsServers { nameservers = append(nameservers, server.String()) } - case len(networkNameServers) > 0: - nameservers = append(nameservers, networkNameServers...) default: // Make a new resolv.conf - nameservers = resolvconf.GetNameservers(resolv.Content) - // slirp4netns has a built in DNS server. + // first add the nameservers from the networks status + nameservers = append(nameservers, networkNameServers...) + // when we add network dns server we also have to add the search domains + search = networkSearchDomains + // slirp4netns has a built in DNS forwarder. if c.config.NetMode.IsSlirp4netns() { slirp4netnsDNS, err := GetSlirp4netnsDNS(c.slirp4netnsSubnet) if err != nil { logrus.Warn("Failed to determine Slirp4netns DNS: ", err.Error()) } else { - nameservers = append([]string{slirp4netnsDNS.String()}, nameservers...) + nameservers = append(nameservers, slirp4netnsDNS.String()) } } + nameservers = append(nameservers, resolvconf.GetNameservers(resolv.Content)...) } - var search []string - if len(c.config.DNSSearch) > 0 || len(c.runtime.config.Containers.DNSSearches) > 0 || len(networkSearchDomains) > 0 { + if len(c.config.DNSSearch) > 0 || len(c.runtime.config.Containers.DNSSearches) > 0 { if !util.StringInSlice(".", c.config.DNSSearch) { - search = c.runtime.config.Containers.DNSSearches + search = append(search, c.runtime.config.Containers.DNSSearches...) search = append(search, c.config.DNSSearch...) - search = append(search, networkSearchDomains...) } } else { - search = resolvconf.GetSearchDomains(resolv.Content) + search = append(search, resolvconf.GetSearchDomains(resolv.Content)...) } var options []string @@ -2160,6 +2142,116 @@ func (c *Container) generateResolvConf() (string, error) { return destPath, nil } +// Check if a container uses IPv6. +func (c *Container) checkForIPv6(netStatus map[string]types.StatusBlock) (bool, error) { + for _, status := range netStatus { + for _, netInt := range status.Interfaces { + for _, netAddress := range netInt.Subnets { + // Note: only using To16() does not work since it also returns a valid ip for ipv4 + if netAddress.IPNet.IP.To4() == nil && netAddress.IPNet.IP.To16() != nil { + return true, nil + } + } + } + } + + if c.config.NetMode.IsSlirp4netns() { + ctrNetworkSlipOpts := []string{} + if c.config.NetworkOptions != nil { + ctrNetworkSlipOpts = append(ctrNetworkSlipOpts, c.config.NetworkOptions["slirp4netns"]...) + } + slirpOpts, err := parseSlirp4netnsNetworkOptions(c.runtime, ctrNetworkSlipOpts) + if err != nil { + return false, err + } + return slirpOpts.enableIPv6, nil + } + + return false, nil +} + +// Add a new nameserver to the container's resolv.conf, ensuring that it is the +// first nameserver present. +// Usable only with running containers. +func (c *Container) addNameserver(ips []string) error { + // Take no action if container is not running. + if !c.ensureState(define.ContainerStateRunning, define.ContainerStateCreated) { + return nil + } + + // Do we have a resolv.conf at all? + path, ok := c.state.BindMounts["/etc/resolv.conf"] + if !ok { + return nil + } + + // Read in full contents, parse out existing nameservers + contents, err := ioutil.ReadFile(path) + if err != nil { + return err + } + ns := resolvconf.GetNameservers(contents) + options := resolvconf.GetOptions(contents) + search := resolvconf.GetSearchDomains(contents) + + // We could verify that it doesn't already exist + // but extra nameservers shouldn't harm anything. + // Ensure we are the first entry in resolv.conf though, otherwise we + // might be after user-added servers. + ns = append(ips, ns...) + + // We're rewriting the container's resolv.conf as part of this, but we + // hold the container lock, so there should be no risk of parallel + // modification. + if _, err := resolvconf.Build(path, ns, search, options); err != nil { + return errors.Wrapf(err, "error adding new nameserver to container %s resolv.conf", c.ID()) + } + + return nil +} + +// Remove an entry from the existing resolv.conf of the container. +// Usable only with running containers. +func (c *Container) removeNameserver(ips []string) error { + // Take no action if container is not running. + if !c.ensureState(define.ContainerStateRunning, define.ContainerStateCreated) { + return nil + } + + // Do we have a resolv.conf at all? + path, ok := c.state.BindMounts["/etc/resolv.conf"] + if !ok { + return nil + } + + // Read in full contents, parse out existing nameservers + contents, err := ioutil.ReadFile(path) + if err != nil { + return err + } + ns := resolvconf.GetNameservers(contents) + options := resolvconf.GetOptions(contents) + search := resolvconf.GetSearchDomains(contents) + + toRemove := make(map[string]bool) + for _, ip := range ips { + toRemove[ip] = true + } + + newNS := make([]string, 0, len(ns)) + for _, server := range ns { + if !toRemove[server] { + newNS = append(newNS, server) + } + } + + if _, err := resolvconf.Build(path, newNS, search, options); err != nil { + return errors.Wrapf(err, "error removing nameservers from container %s resolv.conf", c.ID()) + } + + return nil +} + // updateHosts updates the container's hosts file func (c *Container) updateHosts(path string) error { var hosts string diff --git a/libpod/container_log_linux.go b/libpod/container_log_linux.go index 6b7cb4aa6..6150973ca 100644 --- a/libpod/container_log_linux.go +++ b/libpod/container_log_linux.go @@ -226,7 +226,7 @@ func (c *Container) readFromJournal(ctx context.Context, options *logs.LogOption } if formatError != nil { - logrus.Errorf("Failed to parse journald log entry: %v", err) + logrus.Errorf("Failed to parse journald log entry: %v", formatError) return } diff --git a/libpod/healthcheck.go b/libpod/healthcheck.go index 53bad47b4..40af9aec3 100644 --- a/libpod/healthcheck.go +++ b/libpod/healthcheck.go @@ -2,7 +2,6 @@ package libpod import ( "bufio" - "bytes" "io/ioutil" "os" "path/filepath" @@ -22,16 +21,6 @@ const ( MaxHealthCheckLogLength = 500 ) -// hcWriteCloser allows us to use bufio as a WriteCloser -type hcWriteCloser struct { - *bufio.Writer -} - -// Used to add a closer to bufio -func (hcwc hcWriteCloser) Close() error { - return nil -} - // HealthCheck verifies the state and validity of the healthcheck configuration // on the container and then executes the healthcheck func (r *Runtime) HealthCheck(name string) (define.HealthCheckStatus, error) { @@ -51,7 +40,6 @@ func (c *Container) runHealthCheck() (define.HealthCheckStatus, error) { var ( newCommand []string returnCode int - capture bytes.Buffer inStartPeriod bool ) hcCommand := c.HealthCheckConfig().Test @@ -73,20 +61,30 @@ func (c *Container) runHealthCheck() (define.HealthCheckStatus, error) { if len(newCommand) < 1 || newCommand[0] == "" { return define.HealthCheckNotDefined, errors.Errorf("container %s has no defined healthcheck", c.ID()) } - captureBuffer := bufio.NewWriter(&capture) - hcw := hcWriteCloser{ - captureBuffer, + rPipe, wPipe, err := os.Pipe() + if err != nil { + return define.HealthCheckInternalError, errors.Wrapf(err, "unable to create pipe for healthcheck session") } + defer wPipe.Close() + defer rPipe.Close() + streams := new(define.AttachStreams) - streams.OutputStream = hcw - streams.ErrorStream = hcw streams.InputStream = bufio.NewReader(os.Stdin) - + streams.OutputStream = wPipe + streams.ErrorStream = wPipe streams.AttachOutput = true streams.AttachError = true streams.AttachInput = true + stdout := []string{} + go func() { + scanner := bufio.NewScanner(rPipe) + for scanner.Scan() { + stdout = append(stdout, scanner.Text()) + } + }() + logrus.Debugf("executing health check command %s for %s", strings.Join(newCommand, " "), c.ID()) timeStart := time.Now() hcResult := define.HealthCheckSuccess @@ -119,7 +117,7 @@ func (c *Container) runHealthCheck() (define.HealthCheckStatus, error) { } } - eventLog := capture.String() + eventLog := strings.Join(stdout, "\n") if len(eventLog) > MaxHealthCheckLogLength { eventLog = eventLog[:MaxHealthCheckLogLength] } diff --git a/libpod/networking_linux.go b/libpod/networking_linux.go index f490ac626..19d5c7f76 100644 --- a/libpod/networking_linux.go +++ b/libpod/networking_linux.go @@ -28,6 +28,7 @@ import ( "github.com/containers/podman/v4/pkg/resolvconf" "github.com/containers/podman/v4/pkg/rootless" "github.com/containers/podman/v4/pkg/util" + "github.com/containers/podman/v4/utils" "github.com/containers/storage/pkg/lockfile" "github.com/opencontainers/selinux/go-selinux/label" "github.com/pkg/errors" @@ -495,6 +496,12 @@ func (r *Runtime) GetRootlessNetNs(new bool) (*RootlessNetNS, error) { return nil, err } + // move to systemd scope to prevent systemd from killing it + err = utils.MoveRootlessNetnsSlirpProcessToUserSlice(cmd.Process.Pid) + if err != nil { + logrus.Errorf("failed to move the rootless netns slirp4netns process to the systemd user.slice: %v", err) + } + // build a new resolv.conf file which uses the slirp4netns dns server address resolveIP, err := GetSlirp4netnsDNS(nil) if err != nil { @@ -1163,6 +1170,7 @@ func (c *Container) NetworkDisconnect(nameOrID, netName string, force bool) erro } // update network status if container is running + oldStatus, statusExist := networkStatus[netName] delete(networkStatus, netName) c.state.NetworkStatus = networkStatus err = c.save() @@ -1173,8 +1181,26 @@ func (c *Container) NetworkDisconnect(nameOrID, netName string, force bool) erro // Reload ports when there are still connected networks, maybe we removed the network interface with the child ip. // Reloading without connected networks does not make sense, so we can skip this step. if rootless.IsRootless() && len(networkStatus) > 0 { - return c.reloadRootlessRLKPortMapping() + if err := c.reloadRootlessRLKPortMapping(); err != nil { + return err + } + } + + // Update resolv.conf if required + if statusExist { + stringIPs := make([]string, 0, len(oldStatus.DNSServerIPs)) + for _, ip := range oldStatus.DNSServerIPs { + stringIPs = append(stringIPs, ip.String()) + } + if len(stringIPs) == 0 { + return nil + } + logrus.Debugf("Removing DNS Servers %v from resolv.conf", stringIPs) + if err := c.removeNameserver(stringIPs); err != nil { + return err + } } + return nil } @@ -1256,11 +1282,36 @@ func (c *Container) NetworkConnect(nameOrID, netName string, netOpts types.PerNe if err != nil { return err } + // The first network needs a port reload to set the correct child ip for the rootlessport process. // Adding a second network does not require a port reload because the child ip is still valid. if rootless.IsRootless() && len(networks) == 0 { - return c.reloadRootlessRLKPortMapping() + if err := c.reloadRootlessRLKPortMapping(); err != nil { + return err + } } + + ipv6, err := c.checkForIPv6(networkStatus) + if err != nil { + return err + } + + // Update resolv.conf if required + stringIPs := make([]string, 0, len(results[netName].DNSServerIPs)) + for _, ip := range results[netName].DNSServerIPs { + if (ip.To4() == nil) && !ipv6 { + continue + } + stringIPs = append(stringIPs, ip.String()) + } + if len(stringIPs) == 0 { + return nil + } + logrus.Debugf("Adding DNS Servers %v to resolv.conf", stringIPs) + if err := c.addNameserver(stringIPs); err != nil { + return err + } + return nil } diff --git a/libpod/oci_conmon_exec_linux.go b/libpod/oci_conmon_exec_linux.go index 04deaac83..aa970bbde 100644 --- a/libpod/oci_conmon_exec_linux.go +++ b/libpod/oci_conmon_exec_linux.go @@ -257,7 +257,7 @@ func (r *ConmonOCIRuntime) ExecStopContainer(ctr *Container, sessionID string, t } // Wait for the PID to stop - if err := waitPidStop(pid, killContainerTimeout*time.Second); err != nil { + if err := waitPidStop(pid, killContainerTimeout); err != nil { return errors.Wrapf(err, "timed out waiting for container %s exec session %s PID %d to stop after SIGKILL", ctr.ID(), sessionID, pid) } diff --git a/libpod/options.go b/libpod/options.go index 4f9e49d0f..e0502a72d 100644 --- a/libpod/options.go +++ b/libpod/options.go @@ -1865,7 +1865,7 @@ func WithPodCgroupParent(path string) PodCreateOption { // this pod. // This can still be overridden at the container level by explicitly specifying // a Cgroup parent. -func WithPodCgroups() PodCreateOption { +func WithPodParent() PodCreateOption { return func(pod *Pod) error { if pod.valid { return define.ErrPodFinalized |