Address comments for privileged runtime code.

Signed-off-by: Lantao Liu <lantaol@google.com>
This commit is contained in:
Lantao Liu 2018-03-21 06:51:09 +00:00
parent 246ffa325d
commit ca67f94ee0
10 changed files with 284 additions and 177 deletions

View File

@ -18,12 +18,13 @@ source $(dirname "${BASH_SOURCE[0]}")/utils.sh
# RESTART_WAIT_PERIOD is the period to wait before restarting containerd. # RESTART_WAIT_PERIOD is the period to wait before restarting containerd.
RESTART_WAIT_PERIOD=${RESTART_WAIT_PERIOD:-10} RESTART_WAIT_PERIOD=${RESTART_WAIT_PERIOD:-10}
CONTAINERD_CONFIG="--log-level=debug " # CONTAINERD_FLAGS contains all containerd flags.
CONTAINERD_FLAGS="--log-level=debug "
# Use a configuration file for containerd. # Use a configuration file for containerd.
CONTAINERD_CONFIG_FILE=${CONTAINERD_CONFIG_FILE:-""} CONTAINERD_CONFIG_FILE=${CONTAINERD_CONFIG_FILE:-""}
if [ -f "${CONTAINERD_CONFIG_FILE}" ]; then if [ -f "${CONTAINERD_CONFIG_FILE}" ]; then
CONTAINERD_CONFIG+="--config $CONTAINERD_CONFIG_FILE" CONTAINERD_FLAGS+="--config ${CONTAINERD_CONFIG_FILE} "
fi fi
CONTAINERD_SOCK=/run/containerd/containerd.sock CONTAINERD_SOCK=/run/containerd/containerd.sock
@ -39,7 +40,7 @@ test_setup() {
exit 1 exit 1
fi fi
sudo pkill -x containerd sudo pkill -x containerd
keepalive "sudo ${ROOT}/_output/containerd ${CONTAINERD_CONFIG}" \ keepalive "sudo ${ROOT}/_output/containerd ${CONTAINERD_FLAGS}" \
${RESTART_WAIT_PERIOD} &> ${report_dir}/containerd.log & ${RESTART_WAIT_PERIOD} &> ${report_dir}/containerd.log &
containerd_pid=$! containerd_pid=$!
# Wait for containerd to be running by using the containerd client ctr to check the version # Wait for containerd to be running by using the containerd client ctr to check the version

View File

@ -32,6 +32,7 @@ const (
// SandboxID is the sandbox ID annotation // SandboxID is the sandbox ID annotation
SandboxID = "io.kubernetes.cri.sandbox-id" SandboxID = "io.kubernetes.cri.sandbox-id"
// PrivilegedSandbox is the privileged annotation // UntrustedWorkload is the sandbox annotation for untrusted workload. Untrusted
PrivilegedSandbox = "io.kubernetes.cri.privileged-sandbox" // workload can only run on dedicated runtime for untrusted workload.
UntrustedWorkload = "io.kubernetes.cri.untrusted-workload"
) )

View File

@ -18,9 +18,10 @@ package config
import "github.com/containerd/containerd" import "github.com/containerd/containerd"
// Runtime struct to contain the type(ID), engine, and root variables for a default and a privileged runtime // Runtime struct to contain the type(ID), engine, and root variables for a default runtime
// and a runtime for untrusted worload.
type Runtime struct { type Runtime struct {
//Type is the runtime type to use in containerd e.g. io.containerd.runtime.v1.linux // Type is the runtime type to use in containerd e.g. io.containerd.runtime.v1.linux
Type string `toml:"runtime_type" json:"runtimeType"` Type string `toml:"runtime_type" json:"runtimeType"`
// Engine is the name of the runtime engine used by containerd. // Engine is the name of the runtime engine used by containerd.
Engine string `toml:"runtime_engine" json:"runtimeEngine"` Engine string `toml:"runtime_engine" json:"runtimeEngine"`
@ -34,8 +35,8 @@ type ContainerdConfig struct {
Snapshotter string `toml:"snapshotter" json:"snapshotter"` Snapshotter string `toml:"snapshotter" json:"snapshotter"`
// DefaultRuntime is the runtime to use in containerd. // DefaultRuntime is the runtime to use in containerd.
DefaultRuntime Runtime `toml:"default_runtime" json:"defaultRuntime"` DefaultRuntime Runtime `toml:"default_runtime" json:"defaultRuntime"`
// PrivilegedRuntime is a non-secure runtime used only to run trusted workloads on it // UntrustedWorkloadRuntime is a runtime to run untrusted workloads on it.
PrivilegedRuntime Runtime `toml:"privileged_runtime" json:"privilegedRuntime"` UntrustedWorkloadRuntime Runtime `toml:"untrusted_workload_runtime" json:"untrustedWorkloadRuntime"`
} }
// CniConfig contains toml config related to cni // CniConfig contains toml config related to cni
@ -111,11 +112,6 @@ func DefaultConfig() PluginConfig {
Engine: "", Engine: "",
Root: "", Root: "",
}, },
PrivilegedRuntime: Runtime{
Type: "io.containerd.runtime.v1.linux",
Engine: "",
Root: "",
},
}, },
StreamServerAddress: "", StreamServerAddress: "",
StreamServerPort: "10010", StreamServerPort: "10010",

View File

@ -87,9 +87,6 @@ func (c *criService) CreateContainer(ctx context.Context, r *runtime.CreateConta
} }
sandboxPid := s.Pid() sandboxPid := s.Pid()
trusted := sandbox.Config.Annotations[annotations.PrivilegedSandbox] == "true"
containerRuntime := c.getRuntime(trusted)
// Generate unique id and name for the container and reserve the name. // Generate unique id and name for the container and reserve the name.
// Reserve the container name to avoid concurrent `CreateContainer` request creating // Reserve the container name to avoid concurrent `CreateContainer` request creating
// the same container. // the same container.
@ -125,6 +122,17 @@ func (c *criService) CreateContainer(ctx context.Context, r *runtime.CreateConta
return nil, errors.Errorf("image %q not found", imageRef) return nil, errors.Errorf("image %q not found", imageRef)
} }
// Run container using the same runtime with sandbox.
sandboxInfo, err := sandbox.Container.Info(ctx)
if err != nil {
return nil, errors.Wrapf(err, "failed to get sandbox %q info", sandboxID)
}
ociRuntime, err := getRuntimeConfigFromContainerInfo(sandboxInfo)
if err != nil {
return nil, errors.Wrap(err, "failed to get OCI runtime")
}
logrus.Debugf("Use OCI %+v for container %q", ociRuntime, id)
// Create container root directory. // Create container root directory.
containerRootDir := getContainerRootDir(c.config.RootDir, id) containerRootDir := getContainerRootDir(c.config.RootDir, id)
if err = c.os.MkdirAll(containerRootDir, 0755); err != nil { if err = c.os.MkdirAll(containerRootDir, 0755); err != nil {
@ -230,10 +238,10 @@ func (c *criService) CreateContainer(ctx context.Context, r *runtime.CreateConta
opts = append(opts, opts = append(opts,
containerd.WithSpec(spec, specOpts...), containerd.WithSpec(spec, specOpts...),
containerd.WithRuntime( containerd.WithRuntime(
containerRuntime.Type, ociRuntime.Type,
&runctypes.RuncOptions{ &runctypes.RuncOptions{
Runtime: containerRuntime.Engine, Runtime: ociRuntime.Engine,
RuntimeRoot: containerRuntime.Root, RuntimeRoot: ociRuntime.Root,
SystemdCgroup: c.config.SystemdCgroup}), // TODO (mikebrow): add CriuPath when we add support for pause SystemdCgroup: c.config.SystemdCgroup}), // TODO (mikebrow): add CriuPath when we add support for pause
containerd.WithContainerLabels(containerLabels), containerd.WithContainerLabels(containerLabels),
containerd.WithContainerExtension(containerMetadataExtension, &meta)) containerd.WithContainerExtension(containerMetadataExtension, &meta))

View File

@ -21,10 +21,10 @@ import (
runtimespec "github.com/opencontainers/runtime-spec/specs-go" runtimespec "github.com/opencontainers/runtime-spec/specs-go"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/sirupsen/logrus"
"golang.org/x/net/context" "golang.org/x/net/context"
runtime "k8s.io/kubernetes/pkg/kubelet/apis/cri/runtime/v1alpha2" runtime "k8s.io/kubernetes/pkg/kubelet/apis/cri/runtime/v1alpha2"
criconfig "github.com/containerd/cri/pkg/config"
containerstore "github.com/containerd/cri/pkg/store/container" containerstore "github.com/containerd/cri/pkg/store/container"
) )
@ -106,6 +106,7 @@ type containerInfo struct {
Removing bool `json:"removing"` Removing bool `json:"removing"`
SnapshotKey string `json:"snapshotKey"` SnapshotKey string `json:"snapshotKey"`
Snapshotter string `json:"snapshotter"` Snapshotter string `json:"snapshotter"`
Runtime *criconfig.Runtime `json:"runtime"`
Config *runtime.ContainerConfig `json:"config"` Config *runtime.ContainerConfig `json:"config"`
RuntimeSpec *runtimespec.Spec `json:"runtimeSpec"` RuntimeSpec *runtimespec.Spec `json:"runtimeSpec"`
} }
@ -128,20 +129,24 @@ func toCRIContainerInfo(ctx context.Context, container containerstore.Container,
Config: meta.Config, Config: meta.Config,
} }
spec, err := container.Container.Spec(ctx) var err error
if err == nil { ci.RuntimeSpec, err = container.Container.Spec(ctx)
ci.RuntimeSpec = spec if err != nil {
} else { return nil, errors.Wrap(err, "failed to get container runtime spec")
logrus.WithError(err).Errorf("Failed to get container %q spec", container.ID)
} }
ctrInfo, err := container.Container.Info(ctx) ctrInfo, err := container.Container.Info(ctx)
if err == nil { if err != nil {
ci.SnapshotKey = ctrInfo.SnapshotKey return nil, errors.Wrap(err, "failed to get container info")
ci.Snapshotter = ctrInfo.Snapshotter
} else {
logrus.WithError(err).Errorf("Failed to get container %q info", container.ID)
} }
ci.SnapshotKey = ctrInfo.SnapshotKey
ci.Snapshotter = ctrInfo.Snapshotter
ociRuntime, err := getRuntimeConfigFromContainerInfo(ctrInfo)
if err != nil {
return nil, errors.Wrap(err, "failed to get container runtime config")
}
ci.Runtime = &ociRuntime
infoBytes, err := json.Marshal(ci) infoBytes, err := json.Marshal(ci)
if err != nil { if err != nil {

View File

@ -25,7 +25,10 @@ import (
"strings" "strings"
"github.com/containerd/containerd" "github.com/containerd/containerd"
"github.com/containerd/containerd/containers"
"github.com/containerd/containerd/content" "github.com/containerd/containerd/content"
"github.com/containerd/containerd/linux/runctypes"
"github.com/containerd/typeurl"
"github.com/docker/distribution/reference" "github.com/docker/distribution/reference"
imagedigest "github.com/opencontainers/go-digest" imagedigest "github.com/opencontainers/go-digest"
"github.com/opencontainers/image-spec/identity" "github.com/opencontainers/image-spec/identity"
@ -35,7 +38,6 @@ import (
"github.com/opencontainers/selinux/go-selinux" "github.com/opencontainers/selinux/go-selinux"
"github.com/opencontainers/selinux/go-selinux/label" "github.com/opencontainers/selinux/go-selinux/label"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/sirupsen/logrus"
"golang.org/x/net/context" "golang.org/x/net/context"
runtime "k8s.io/kubernetes/pkg/kubelet/apis/cri/runtime/v1alpha2" runtime "k8s.io/kubernetes/pkg/kubelet/apis/cri/runtime/v1alpha2"
@ -410,17 +412,22 @@ func getPodCNILabels(id string, config *runtime.PodSandboxConfig) map[string]str
} }
} }
// getRuntime returns the runtime configuration // getRuntimeConfigFromContainerInfo gets runtime configuration from containerd
// If the container is privileged, it will return // container info.
// the privileged runtime else not. func getRuntimeConfigFromContainerInfo(c containers.Container) (criconfig.Runtime, error) {
func (c *criService) getRuntime(privileged bool) (runtime criconfig.Runtime) { r := criconfig.Runtime{
runtime = c.config.ContainerdConfig.DefaultRuntime Type: c.Runtime.Name,
if privileged && c.config.ContainerdConfig.PrivilegedRuntime.Engine != "" {
runtime = c.config.ContainerdConfig.PrivilegedRuntime
} }
if c.Runtime.Options == nil {
logrus.Debugf("runtime=%s(%s), runtime root='%s', privileged='%v'", runtime.Type, runtime.Engine, runtime.Root, privileged) // CRI plugin makes sure that runtime option is always set.
return criconfig.Runtime{}, errors.New("runtime options is nil")
return runtime }
data, err := typeurl.UnmarshalAny(c.Runtime.Options)
if err != nil {
return criconfig.Runtime{}, errors.Wrap(err, "failed to unmarshal runtime options")
}
runtimeOpts := data.(*runctypes.RuncOptions)
r.Engine = runtimeOpts.Runtime
r.Root = runtimeOpts.RuntimeRoot
return r, nil
} }

View File

@ -19,10 +19,15 @@ package server
import ( import (
"testing" "testing"
criconfig "github.com/containerd/cri/pkg/config" "github.com/containerd/containerd"
"github.com/containerd/cri/pkg/util" "github.com/containerd/containerd/containers"
"github.com/containerd/containerd/linux/runctypes"
imagedigest "github.com/opencontainers/go-digest" imagedigest "github.com/opencontainers/go-digest"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"golang.org/x/net/context"
criconfig "github.com/containerd/cri/pkg/config"
"github.com/containerd/cri/pkg/util"
) )
// TestGetUserFromImage tests the logic of getting image uid or user name of image user. // TestGetUserFromImage tests the logic of getting image uid or user name of image user.
@ -143,63 +148,45 @@ func TestBuildLabels(t *testing.T) {
assert.Equal(t, "b", configLabels["a"], "change in new labels should not affect original label") assert.Equal(t, "b", configLabels["a"], "change in new labels should not affect original label")
} }
func Test_criService_getRuntime(t *testing.T) { func TestGetRuntimeConfigFromContainerInfo(t *testing.T) {
for desc, test := range map[string]struct {
const ( typ string
privilegedWorkload = true engine string
nonPrivilegedWorkload = false root string
) expectErr bool
expectedRuntime criconfig.Runtime
nonPrivilegedRuntime := criconfig.Runtime{
Type: "io.containerd.runtime.v1.linux",
Engine: "kata-runtime",
Root: "",
}
privilegedRuntime := criconfig.Runtime{
Type: "io.containerd.runtime.v1.linux",
Engine: "runc",
Root: "",
}
// Crate a configuration that does not specify a privileged runtime
// Both privileged and non-privileged workloads are created with the
// defaultRuntime (nonPrivilegedRuntime).
nonPrivilegedConfig := criService{
config: criconfig.Config{
PluginConfig: criconfig.DefaultConfig(),
},
}
nonPrivilegedConfig.config.ContainerdConfig.DefaultRuntime = nonPrivilegedRuntime
// Crate a configuration that specifies a privileged runtime
// The privileged workloads are created with the privilegedRuntime
// The non-privileged workloads be created with the
// defaultRuntime(nonPrivilegedRuntime)
privilegedConfig := criService{
config: criconfig.Config{
PluginConfig: criconfig.DefaultConfig(),
},
}
privilegedConfig.config.ContainerdConfig.DefaultRuntime = nonPrivilegedRuntime
privilegedConfig.config.ContainerdConfig.PrivilegedRuntime = privilegedRuntime
tests := []struct {
name string
cri criService
privileged bool
wantRuntime criconfig.Runtime
}{ }{
{"nonPrivilegedConfig/PrivilegedWorkload", nonPrivilegedConfig, privilegedWorkload, nonPrivilegedRuntime}, "should return error if there is no runc options": {
{"nonPrivilegedConfig/PrivilegedWorkload", nonPrivilegedConfig, nonPrivilegedWorkload, nonPrivilegedRuntime}, typ: "test.type",
{"PrivilegedConfig/nonPrivilegedWorkload", privilegedConfig, privilegedWorkload, privilegedRuntime}, expectErr: true,
{"PrivilegedConfig/nonPrivilegedWorkload", privilegedConfig, nonPrivilegedWorkload, nonPrivilegedRuntime}, },
} "should retrieve runtime from container info": {
typ: "test.type",
for _, tt := range tests { engine: "test-engine",
t.Run(tt.name, func(t *testing.T) { root: "/test/root",
gotRuntime := tt.cri.getRuntime(tt.privileged) expectedRuntime: criconfig.Runtime{
assert.Equal(t, tt.wantRuntime, gotRuntime) Type: "test.type",
Engine: "test-engine",
Root: "/test/root",
},
},
} {
t.Run(desc, func(t *testing.T) {
var opts interface{}
if test.engine != "" || test.root != "" {
opts = &runctypes.RuncOptions{
Runtime: test.engine,
RuntimeRoot: test.root,
}
}
c := containers.Container{}
assert.NoError(t, containerd.WithRuntime(
test.typ,
opts,
)(context.Background(), nil, &c))
r, err := getRuntimeConfigFromContainerInfo(c)
assert.Equal(t, test.expectErr, err != nil)
assert.Equal(t, test.expectedRuntime, r)
}) })
} }
} }

View File

@ -37,6 +37,7 @@ import (
runtime "k8s.io/kubernetes/pkg/kubelet/apis/cri/runtime/v1alpha2" runtime "k8s.io/kubernetes/pkg/kubelet/apis/cri/runtime/v1alpha2"
"github.com/containerd/cri/pkg/annotations" "github.com/containerd/cri/pkg/annotations"
criconfig "github.com/containerd/cri/pkg/config"
customopts "github.com/containerd/cri/pkg/containerd/opts" customopts "github.com/containerd/cri/pkg/containerd/opts"
ctrdutil "github.com/containerd/cri/pkg/containerd/util" ctrdutil "github.com/containerd/cri/pkg/containerd/util"
"github.com/containerd/cri/pkg/log" "github.com/containerd/cri/pkg/log"
@ -49,32 +50,6 @@ func init() {
"github.com/containerd/cri/pkg/store/sandbox", "Metadata") "github.com/containerd/cri/pkg/store/sandbox", "Metadata")
} }
// privilegedSandbox returns true if the sandbox configuration
// requires additional host privileges for the sandbox.
func privilegedSandbox(req *runtime.RunPodSandboxRequest) bool {
securityContext := req.GetConfig().GetLinux().GetSecurityContext()
if securityContext == nil {
return false
}
if securityContext.Privileged {
return true
}
namespaceOptions := securityContext.GetNamespaceOptions()
if namespaceOptions == nil {
return false
}
if namespaceOptions.Network == runtime.NamespaceMode_NODE ||
namespaceOptions.Pid == runtime.NamespaceMode_NODE ||
namespaceOptions.Ipc == runtime.NamespaceMode_NODE {
return true
}
return false
}
// RunPodSandbox creates and starts a pod-level sandbox. Runtimes should ensure // RunPodSandbox creates and starts a pod-level sandbox. Runtimes should ensure
// the sandbox is in ready state. // the sandbox is in ready state.
func (c *criService) RunPodSandbox(ctx context.Context, r *runtime.RunPodSandboxRequest) (_ *runtime.RunPodSandboxResponse, retErr error) { func (c *criService) RunPodSandbox(ctx context.Context, r *runtime.RunPodSandboxRequest) (_ *runtime.RunPodSandboxResponse, retErr error) {
@ -156,14 +131,11 @@ func (c *criService) RunPodSandbox(ctx context.Context, r *runtime.RunPodSandbox
}() }()
} }
privileged := privilegedSandbox(r) ociRuntime, err := c.getSandboxRuntime(config)
containerRuntime := c.getRuntime(privileged) if err != nil {
return nil, errors.Wrap(err, "failed to get sandbox runtime")
if sandbox.Config.Annotations == nil {
sandbox.Config.Annotations = make(map[string]string)
} }
logrus.Debugf("Use OCI %+v for sandbox %q", ociRuntime, id)
sandbox.Config.Annotations[annotations.PrivilegedSandbox] = fmt.Sprintf("%v", privileged)
// Create sandbox container. // Create sandbox container.
spec, err := c.generateSandboxContainerSpec(id, config, &image.ImageSpec.Config, sandbox.NetNSPath) spec, err := c.generateSandboxContainerSpec(id, config, &image.ImageSpec.Config, sandbox.NetNSPath)
@ -197,10 +169,10 @@ func (c *criService) RunPodSandbox(ctx context.Context, r *runtime.RunPodSandbox
containerd.WithContainerLabels(sandboxLabels), containerd.WithContainerLabels(sandboxLabels),
containerd.WithContainerExtension(sandboxMetadataExtension, &sandbox.Metadata), containerd.WithContainerExtension(sandboxMetadataExtension, &sandbox.Metadata),
containerd.WithRuntime( containerd.WithRuntime(
containerRuntime.Type, ociRuntime.Type,
&runctypes.RuncOptions{ &runctypes.RuncOptions{
Runtime: containerRuntime.Engine, Runtime: ociRuntime.Engine,
RuntimeRoot: containerRuntime.Root, RuntimeRoot: ociRuntime.Root,
SystemdCgroup: c.config.SystemdCgroup})} // TODO (mikebrow): add CriuPath when we add support for pause SystemdCgroup: c.config.SystemdCgroup})} // TODO (mikebrow): add CriuPath when we add support for pause
container, err := c.client.NewContainer(ctx, id, opts...) container, err := c.client.NewContainer(ctx, id, opts...)
@ -553,3 +525,48 @@ func toCNIPortMappings(criPortMappings []*runtime.PortMapping) []cni.PortMapping
} }
return portMappings return portMappings
} }
// untrustedWorkload returns true if the sandbox contains untrusted workload.
func untrustedWorkload(config *runtime.PodSandboxConfig) bool {
return config.GetAnnotations()[annotations.UntrustedWorkload] == "true"
}
// hostPrivilegedSandbox returns true if the sandbox configuration
// requires additional host privileges for the sandbox.
func hostPrivilegedSandbox(config *runtime.PodSandboxConfig) bool {
securityContext := config.GetLinux().GetSecurityContext()
if securityContext.GetPrivileged() {
return true
}
namespaceOptions := securityContext.GetNamespaceOptions()
if namespaceOptions.GetNetwork() == runtime.NamespaceMode_NODE ||
namespaceOptions.GetPid() == runtime.NamespaceMode_NODE ||
namespaceOptions.GetIpc() == runtime.NamespaceMode_NODE {
return true
}
return false
}
// getSandboxRuntime returns the runtime configuration for sandbox.
// If the sandbox contains untrusted workload, runtime for untrusted workload will be returned,
// or else default runtime will be returned.
func (c *criService) getSandboxRuntime(config *runtime.PodSandboxConfig) (criconfig.Runtime, error) {
untrusted := false
if untrustedWorkload(config) {
// TODO(random-liu): Figure out we should return error or not.
if hostPrivilegedSandbox(config) {
return criconfig.Runtime{}, errors.New("untrusted workload with host privilege is not allowed")
}
untrusted = true
}
if untrusted {
if c.config.ContainerdConfig.UntrustedWorkloadRuntime.Type == "" {
return criconfig.Runtime{}, errors.New("no runtime for untrusted workload is configured")
}
return c.config.ContainerdConfig.UntrustedWorkloadRuntime, nil
}
return c.config.ContainerdConfig.DefaultRuntime, nil
}

View File

@ -20,7 +20,6 @@ import (
"os" "os"
"testing" "testing"
"github.com/containerd/cri/pkg/annotations"
cni "github.com/containerd/go-cni" cni "github.com/containerd/go-cni"
"github.com/containerd/typeurl" "github.com/containerd/typeurl"
imagespec "github.com/opencontainers/image-spec/specs-go/v1" imagespec "github.com/opencontainers/image-spec/specs-go/v1"
@ -29,6 +28,8 @@ import (
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
runtime "k8s.io/kubernetes/pkg/kubelet/apis/cri/runtime/v1alpha2" runtime "k8s.io/kubernetes/pkg/kubelet/apis/cri/runtime/v1alpha2"
"github.com/containerd/cri/pkg/annotations"
criconfig "github.com/containerd/cri/pkg/config"
ostesting "github.com/containerd/cri/pkg/os/testing" ostesting "github.com/containerd/cri/pkg/os/testing"
sandboxstore "github.com/containerd/cri/pkg/store/sandbox" sandboxstore "github.com/containerd/cri/pkg/store/sandbox"
) )
@ -433,60 +434,136 @@ func TestTypeurlMarshalUnmarshalSandboxMeta(t *testing.T) {
} }
} }
// TODO(random-liu): [P1] Add unit test for different error cases to make sure func TestHostPrivilegedSandbox(t *testing.T) {
// the function cleans up on error properly. privilegedContext := &runtime.PodSandboxConfig{
Linux: &runtime.LinuxPodSandboxConfig{
func TestPrivilegedSandbox(t *testing.T) { SecurityContext: &runtime.LinuxSandboxSecurityContext{
privilegedContext := runtime.RunPodSandboxRequest{ Privileged: true,
Config: &runtime.PodSandboxConfig{
Linux: &runtime.LinuxPodSandboxConfig{
SecurityContext: &runtime.LinuxSandboxSecurityContext{
Privileged: true,
},
}, },
}, },
} }
nonPrivilegedContext := runtime.RunPodSandboxRequest{ nonPrivilegedContext := &runtime.PodSandboxConfig{
Config: &runtime.PodSandboxConfig{ Linux: &runtime.LinuxPodSandboxConfig{
Linux: &runtime.LinuxPodSandboxConfig{ SecurityContext: &runtime.LinuxSandboxSecurityContext{
SecurityContext: &runtime.LinuxSandboxSecurityContext{ Privileged: false,
Privileged: false,
},
}, },
}, },
} }
hostNamespace := runtime.RunPodSandboxRequest{ hostNamespace := &runtime.PodSandboxConfig{
Config: &runtime.PodSandboxConfig{ Linux: &runtime.LinuxPodSandboxConfig{
Linux: &runtime.LinuxPodSandboxConfig{ SecurityContext: &runtime.LinuxSandboxSecurityContext{
SecurityContext: &runtime.LinuxSandboxSecurityContext{ Privileged: false,
Privileged: false, NamespaceOptions: &runtime.NamespaceOption{
NamespaceOptions: &runtime.NamespaceOption{ Network: runtime.NamespaceMode_NODE,
Network: runtime.NamespaceMode_NODE, Pid: runtime.NamespaceMode_NODE,
Pid: runtime.NamespaceMode_NODE, Ipc: runtime.NamespaceMode_NODE,
Ipc: runtime.NamespaceMode_NODE,
},
}, },
}, },
}, },
} }
type args struct {
req *runtime.RunPodSandboxRequest
}
tests := []struct { tests := []struct {
name string name string
args args config *runtime.PodSandboxConfig
want bool want bool
}{ }{
{"Security Context is nil", args{&runtime.RunPodSandboxRequest{}}, false}, {"Security Context is nil", nil, false},
{"Security Context is privileged", args{&privilegedContext}, true}, {"Security Context is privileged", privilegedContext, true},
{"Security Context is not privileged", args{&nonPrivilegedContext}, false}, {"Security Context is not privileged", nonPrivilegedContext, false},
{"Security Context namespace host access", args{&hostNamespace}, true}, {"Security Context namespace host access", hostNamespace, true},
} }
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
if got := privilegedSandbox(tt.args.req); got != tt.want { if got := hostPrivilegedSandbox(tt.config); got != tt.want {
t.Errorf("privilegedSandbox() = %v, want %v", got, tt.want) t.Errorf("hostPrivilegedSandbox() = %v, want %v", got, tt.want)
} }
}) })
} }
} }
func TestGetSandboxRuntime(t *testing.T) {
untrustedWorkloadRuntime := criconfig.Runtime{
Type: "io.containerd.runtime.v1.linux",
Engine: "untursted-workload-runtime",
Root: "",
}
defaultRuntime := criconfig.Runtime{
Type: "io.containerd.runtime.v1.linux",
Engine: "default-runtime",
Root: "",
}
for desc, test := range map[string]struct {
sandboxConfig *runtime.PodSandboxConfig
defaultRuntime criconfig.Runtime
untrustedWorkloadRuntime criconfig.Runtime
expectErr bool
expectedRuntime criconfig.Runtime
}{
"should return error if untrusted workload requires host privilege": {
sandboxConfig: &runtime.PodSandboxConfig{
Linux: &runtime.LinuxPodSandboxConfig{
SecurityContext: &runtime.LinuxSandboxSecurityContext{
Privileged: true,
},
},
Annotations: map[string]string{
annotations.UntrustedWorkload: "true",
},
},
defaultRuntime: defaultRuntime,
untrustedWorkloadRuntime: untrustedWorkloadRuntime,
expectErr: true,
},
"should use untrusted workload runtime for untrusted workload": {
sandboxConfig: &runtime.PodSandboxConfig{
Annotations: map[string]string{
annotations.UntrustedWorkload: "true",
},
},
defaultRuntime: defaultRuntime,
untrustedWorkloadRuntime: untrustedWorkloadRuntime,
expectedRuntime: untrustedWorkloadRuntime,
},
"should use default runtime for regular workload": {
sandboxConfig: &runtime.PodSandboxConfig{},
defaultRuntime: defaultRuntime,
untrustedWorkloadRuntime: untrustedWorkloadRuntime,
expectedRuntime: defaultRuntime,
},
"should use default runtime for trusted workload": {
sandboxConfig: &runtime.PodSandboxConfig{
Annotations: map[string]string{
annotations.UntrustedWorkload: "false",
},
},
defaultRuntime: defaultRuntime,
untrustedWorkloadRuntime: untrustedWorkloadRuntime,
expectedRuntime: defaultRuntime,
},
"should return error if untrusted workload runtime is required but not configured": {
sandboxConfig: &runtime.PodSandboxConfig{
Annotations: map[string]string{
annotations.UntrustedWorkload: "true",
},
},
defaultRuntime: defaultRuntime,
expectErr: true,
},
} {
t.Run(desc, func(t *testing.T) {
cri := newTestCRIService()
cri.config = criconfig.Config{
PluginConfig: criconfig.DefaultConfig(),
}
cri.config.ContainerdConfig.DefaultRuntime = test.defaultRuntime
cri.config.ContainerdConfig.UntrustedWorkloadRuntime = test.untrustedWorkloadRuntime
r, err := cri.getSandboxRuntime(test.sandboxConfig)
assert.Equal(t, test.expectErr, err != nil)
assert.Equal(t, test.expectedRuntime, r)
})
}
}
// TODO(random-liu): [P1] Add unit test for different error cases to make sure
// the function cleans up on error properly.

View File

@ -26,6 +26,7 @@ import (
"golang.org/x/net/context" "golang.org/x/net/context"
runtime "k8s.io/kubernetes/pkg/kubelet/apis/cri/runtime/v1alpha2" runtime "k8s.io/kubernetes/pkg/kubelet/apis/cri/runtime/v1alpha2"
criconfig "github.com/containerd/cri/pkg/config"
sandboxstore "github.com/containerd/cri/pkg/store/sandbox" sandboxstore "github.com/containerd/cri/pkg/store/sandbox"
) )
@ -107,6 +108,7 @@ type sandboxInfo struct {
Image string `json:"image"` Image string `json:"image"`
SnapshotKey string `json:"snapshotKey"` SnapshotKey string `json:"snapshotKey"`
Snapshotter string `json:"snapshotter"` Snapshotter string `json:"snapshotter"`
Runtime *criconfig.Runtime `json:"runtime"`
Config *runtime.PodSandboxConfig `json:"config"` Config *runtime.PodSandboxConfig `json:"config"`
RuntimeSpec *runtimespec.Spec `json:"runtimeSpec"` RuntimeSpec *runtimespec.Spec `json:"runtimeSpec"`
} }
@ -163,6 +165,12 @@ func toCRISandboxInfo(ctx context.Context, sandbox sandboxstore.Sandbox) (map[st
si.SnapshotKey = ctrInfo.SnapshotKey si.SnapshotKey = ctrInfo.SnapshotKey
si.Snapshotter = ctrInfo.Snapshotter si.Snapshotter = ctrInfo.Snapshotter
ociRuntime, err := getRuntimeConfigFromContainerInfo(ctrInfo)
if err != nil {
return nil, errors.Wrap(err, "failed to get sandbox container runtime config")
}
si.Runtime = &ociRuntime
infoBytes, err := json.Marshal(si) infoBytes, err := json.Marshal(si)
if err != nil { if err != nil {
return nil, errors.Wrapf(err, "failed to marshal info %v", si) return nil, errors.Wrapf(err, "failed to marshal info %v", si)