From b48f27df6bc01be83d5157ca50853e09d0cc5c80 Mon Sep 17 00:00:00 2001 From: Thomas Hartland Date: Thu, 11 Mar 2021 16:43:28 +0000 Subject: [PATCH] Support PID NamespaceMode_TARGET This commit adds support for the PID namespace mode TARGET when generating a container spec. The container that is created will be sharing its PID namespace with the target container that was specified by ID in the namespace options. Signed-off-by: Thomas Hartland --- pkg/cri/opts/spec_linux.go | 10 +++++----- pkg/cri/server/container_create_linux.go | 17 ++++++++++++++++- pkg/cri/server/container_start.go | 11 +++++++++++ pkg/cri/server/helpers.go | 24 ++++++++++++++++++++++++ 4 files changed, 56 insertions(+), 6 deletions(-) diff --git a/pkg/cri/opts/spec_linux.go b/pkg/cri/opts/spec_linux.go index 67a652e91..c5ec3dfdd 100644 --- a/pkg/cri/opts/spec_linux.go +++ b/pkg/cri/opts/spec_linux.go @@ -593,16 +593,16 @@ func WithSupplementalGroups(groups []int64) oci.SpecOpts { } // WithPodNamespaces sets the pod namespaces for the container -func WithPodNamespaces(config *runtime.LinuxContainerSecurityContext, pid uint32) oci.SpecOpts { +func WithPodNamespaces(config *runtime.LinuxContainerSecurityContext, sandboxPid uint32, targetPid uint32) oci.SpecOpts { namespaces := config.GetNamespaceOptions() opts := []oci.SpecOpts{ - oci.WithLinuxNamespace(runtimespec.LinuxNamespace{Type: runtimespec.NetworkNamespace, Path: GetNetworkNamespace(pid)}), - oci.WithLinuxNamespace(runtimespec.LinuxNamespace{Type: runtimespec.IPCNamespace, Path: GetIPCNamespace(pid)}), - oci.WithLinuxNamespace(runtimespec.LinuxNamespace{Type: runtimespec.UTSNamespace, Path: GetUTSNamespace(pid)}), + oci.WithLinuxNamespace(runtimespec.LinuxNamespace{Type: runtimespec.NetworkNamespace, Path: GetNetworkNamespace(sandboxPid)}), + oci.WithLinuxNamespace(runtimespec.LinuxNamespace{Type: runtimespec.IPCNamespace, Path: GetIPCNamespace(sandboxPid)}), + oci.WithLinuxNamespace(runtimespec.LinuxNamespace{Type: runtimespec.UTSNamespace, Path: GetUTSNamespace(sandboxPid)}), } if namespaces.GetPid() != runtime.NamespaceMode_CONTAINER { - opts = append(opts, oci.WithLinuxNamespace(runtimespec.LinuxNamespace{Type: runtimespec.PIDNamespace, Path: GetPIDNamespace(pid)})) + opts = append(opts, oci.WithLinuxNamespace(runtimespec.LinuxNamespace{Type: runtimespec.PIDNamespace, Path: GetPIDNamespace(targetPid)})) } return oci.Compose(opts...) } diff --git a/pkg/cri/server/container_create_linux.go b/pkg/cri/server/container_create_linux.go index 7d9c4b536..26386e991 100644 --- a/pkg/cri/server/container_create_linux.go +++ b/pkg/cri/server/container_create_linux.go @@ -270,9 +270,24 @@ func (c *criService) containerSpec( specOpts = append(specOpts, customopts.WithAnnotation(pKey, pValue)) } + // Default target PID namespace is the sandbox PID. + targetPid := sandboxPid + // If the container targets another container's PID namespace, + // set targetPid to the PID of that container. + nsOpts := securityContext.GetNamespaceOptions() + if nsOpts.GetPid() == runtime.NamespaceMode_TARGET { + targetContainer, err := c.validateTargetContainer(sandboxID, nsOpts.TargetId) + if err != nil { + return nil, errors.Wrapf(err, "invalid target container") + } + + status := targetContainer.Status.Get() + targetPid = status.Pid + } + specOpts = append(specOpts, customopts.WithOOMScoreAdj(config, c.config.RestrictOOMScoreAdj), - customopts.WithPodNamespaces(securityContext, sandboxPid), + customopts.WithPodNamespaces(securityContext, sandboxPid, targetPid), customopts.WithSupplementalGroups(supplementalGroups), customopts.WithAnnotation(annotations.ContainerType, annotations.ContainerTypeContainer), customopts.WithAnnotation(annotations.SandboxID, sandboxID), diff --git a/pkg/cri/server/container_start.go b/pkg/cri/server/container_start.go index 90c807d7f..11390f1f6 100644 --- a/pkg/cri/server/container_start.go +++ b/pkg/cri/server/container_start.go @@ -84,6 +84,17 @@ func (c *criService) StartContainer(ctx context.Context, r *runtime.StartContain return nil, errors.Errorf("sandbox container %q is not running", sandboxID) } + // Recheck target container validity in Linux namespace options. + if linux := config.GetLinux(); linux != nil { + nsOpts := linux.GetSecurityContext().GetNamespaceOptions() + if nsOpts.GetPid() == runtime.NamespaceMode_TARGET { + _, err := c.validateTargetContainer(sandboxID, nsOpts.TargetId) + if err != nil { + return nil, errors.Wrap(err, "invalid target container") + } + } + } + ioCreation := func(id string) (_ containerdio.IO, err error) { stdoutWC, stderrWC, err := c.createContainerLoggers(meta.LogPath, config.GetTty()) if err != nil { diff --git a/pkg/cri/server/helpers.go b/pkg/cri/server/helpers.go index 1d5f49f11..8c9a4ac41 100644 --- a/pkg/cri/server/helpers.go +++ b/pkg/cri/server/helpers.go @@ -242,6 +242,30 @@ func (c *criService) ensureImageExists(ctx context.Context, ref string, config * return &newImage, nil } +// validateTargetContainer checks that a container is a valid +// target for a container using PID NamespaceMode_TARGET. +// The target container must be in the same sandbox and must be running. +// Returns the target container for convenience. +func (c *criService) validateTargetContainer(sandboxID, targetContainerID string) (containerstore.Container, error) { + targetContainer, err := c.containerStore.Get(targetContainerID) + if err != nil { + return containerstore.Container{}, errors.Wrapf(err, "container %q does not exist", targetContainerID) + } + + targetSandboxID := targetContainer.Metadata.SandboxID + if targetSandboxID != sandboxID { + return containerstore.Container{}, + errors.Errorf("container %q (sandbox %s) does not belong to sandbox %s", targetContainerID, targetSandboxID, sandboxID) + } + + status := targetContainer.Status.Get() + if state := status.State(); state != runtime.ContainerState_CONTAINER_RUNNING { + return containerstore.Container{}, errors.Errorf("container %q is not running - in state %s", targetContainerID, state) + } + + return targetContainer, nil +} + // isInCRIMounts checks whether a destination is in CRI mount list. func isInCRIMounts(dst string, mounts []*runtime.Mount) bool { for _, m := range mounts {