Use seccomp from security context

This commit is contained in:
Pengfei Ni 2017-07-19 16:30:27 +08:00
parent f3150c9c8c
commit bf01fa2f00
6 changed files with 57 additions and 103 deletions

View File

@ -150,7 +150,7 @@ func (ds *dockerService) CreateContainer(podSandboxID string, config *runtimeapi
} }
hc.Resources.Devices = devices hc.Resources.Devices = devices
securityOpts, err := ds.getSecurityOpts(config.Metadata.Name, sandboxConfig, securityOptSep) securityOpts, err := ds.getSecurityOpts(config.GetLinux().GetSecurityContext().GetSeccompProfilePath(), securityOptSep)
if err != nil { if err != nil {
return "", fmt.Errorf("failed to generate security options for container %q: %v", config.Metadata.Name, err) return "", fmt.Errorf("failed to generate security options for container %q: %v", config.Metadata.Name, err)
} }

View File

@ -588,7 +588,7 @@ func (ds *dockerService) makeSandboxDockerConfig(c *runtimeapi.PodSandboxConfig,
} }
// Set security options. // Set security options.
securityOpts, err := ds.getSecurityOpts(sandboxContainerName, c, securityOptSep) securityOpts, err := ds.getSecurityOpts(c.GetLinux().GetSecurityContext().GetSeccompProfilePath(), securityOptSep)
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to generate sandbox security options for sandbox %q: %v", c.Metadata.Name, err) return nil, fmt.Errorf("failed to generate sandbox security options for sandbox %q: %v", c.Metadata.Name, err)
} }

View File

@ -146,7 +146,7 @@ type dockerNetworkHost struct {
var internalLabelKeys []string = []string{containerTypeLabelKey, containerLogPathLabelKey, sandboxIDLabelKey} var internalLabelKeys []string = []string{containerTypeLabelKey, containerLogPathLabelKey, sandboxIDLabelKey}
// NOTE: Anything passed to DockerService should be eventually handled in another way when we switch to running the shim as a different process. // NOTE: Anything passed to DockerService should be eventually handled in another way when we switch to running the shim as a different process.
func NewDockerService(client libdocker.Interface, seccompProfileRoot string, podSandboxImage string, streamingConfig *streaming.Config, func NewDockerService(client libdocker.Interface, podSandboxImage string, streamingConfig *streaming.Config,
pluginSettings *NetworkPluginSettings, cgroupsName string, kubeCgroupDriver string, execHandlerName, dockershimRootDir string, disableSharedPID bool) (DockerService, error) { pluginSettings *NetworkPluginSettings, cgroupsName string, kubeCgroupDriver string, execHandlerName, dockershimRootDir string, disableSharedPID bool) (DockerService, error) {
c := libdocker.NewInstrumentedInterface(client) c := libdocker.NewInstrumentedInterface(client)
checkpointHandler, err := NewPersistentCheckpointHandler(dockershimRootDir) checkpointHandler, err := NewPersistentCheckpointHandler(dockershimRootDir)
@ -165,10 +165,9 @@ func NewDockerService(client libdocker.Interface, seccompProfileRoot string, pod
} }
ds := &dockerService{ ds := &dockerService{
seccompProfileRoot: seccompProfileRoot, client: c,
client: c, os: kubecontainer.RealOS{},
os: kubecontainer.RealOS{}, podSandboxImage: podSandboxImage,
podSandboxImage: podSandboxImage,
streamingRuntime: &streamingRuntime{ streamingRuntime: &streamingRuntime{
client: client, client: client,
execHandler: execHandler, execHandler: execHandler,
@ -244,12 +243,11 @@ type DockerService interface {
} }
type dockerService struct { type dockerService struct {
seccompProfileRoot string client libdocker.Interface
client libdocker.Interface os kubecontainer.OSInterface
os kubecontainer.OSInterface podSandboxImage string
podSandboxImage string streamingRuntime *streamingRuntime
streamingRuntime *streamingRuntime streamingServer streaming.Server
streamingServer streaming.Server
network *network.PluginManager network *network.PluginManager
// Map of podSandboxID :: network-is-ready // Map of podSandboxID :: network-is-ready

View File

@ -30,7 +30,6 @@ import (
"github.com/blang/semver" "github.com/blang/semver"
dockertypes "github.com/docker/docker/api/types" dockertypes "github.com/docker/docker/api/types"
dockercontainer "github.com/docker/docker/api/types/container" dockercontainer "github.com/docker/docker/api/types/container"
"k8s.io/api/core/v1"
runtimeapi "k8s.io/kubernetes/pkg/kubelet/apis/cri/v1alpha1/runtime" runtimeapi "k8s.io/kubernetes/pkg/kubelet/apis/cri/v1alpha1/runtime"
) )
@ -38,46 +37,35 @@ func DefaultMemorySwap() int64 {
return 0 return 0
} }
func (ds *dockerService) getSecurityOpts(containerName string, sandboxConfig *runtimeapi.PodSandboxConfig, separator rune) ([]string, error) { func (ds *dockerService) getSecurityOpts(seccompProfile string, separator rune) ([]string, error) {
// Apply seccomp options. // Apply seccomp options.
seccompSecurityOpts, err := getSeccompSecurityOpts(containerName, sandboxConfig, ds.seccompProfileRoot, separator) seccompSecurityOpts, err := getSeccompSecurityOpts(seccompProfile, separator)
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to generate seccomp security options for container %q: %v", containerName, err) return nil, fmt.Errorf("failed to generate seccomp security options for container: %v", err)
} }
return seccompSecurityOpts, nil return seccompSecurityOpts, nil
} }
func getSeccompDockerOpts(annotations map[string]string, ctrName, profileRoot string) ([]dockerOpt, error) { func getSeccompDockerOpts(seccompProfile string) ([]dockerOpt, error) {
profile, profileOK := annotations[v1.SeccompContainerAnnotationKeyPrefix+ctrName] if seccompProfile == "" || seccompProfile == "unconfined" {
if !profileOK {
// try the pod profile
profile, profileOK = annotations[v1.SeccompPodAnnotationKey]
if !profileOK {
// return early the default
return defaultSeccompOpt, nil
}
}
if profile == "unconfined" {
// return early the default // return early the default
return defaultSeccompOpt, nil return defaultSeccompOpt, nil
} }
if profile == "docker/default" { if seccompProfile == "docker/default" {
// return nil so docker will load the default seccomp profile // return nil so docker will load the default seccomp profile
return nil, nil return nil, nil
} }
if !strings.HasPrefix(profile, "localhost/") { if !strings.HasPrefix(seccompProfile, "localhost/") {
return nil, fmt.Errorf("unknown seccomp profile option: %s", profile) return nil, fmt.Errorf("unknown seccomp profile option: %s", seccompProfile)
} }
name := strings.TrimPrefix(profile, "localhost/") // by pod annotation validation, name is a valid subpath fname := strings.TrimPrefix(seccompProfile, "localhost/") // by pod annotation validation, name is a valid subpath
fname := filepath.Join(profileRoot, filepath.FromSlash(name)) file, err := ioutil.ReadFile(filepath.FromSlash(fname))
file, err := ioutil.ReadFile(fname)
if err != nil { if err != nil {
return nil, fmt.Errorf("cannot load seccomp profile %q: %v", name, err) return nil, fmt.Errorf("cannot load seccomp profile %q: %v", fname, err)
} }
b := bytes.NewBuffer(nil) b := bytes.NewBuffer(nil)
@ -85,16 +73,15 @@ func getSeccompDockerOpts(annotations map[string]string, ctrName, profileRoot st
return nil, err return nil, err
} }
// Rather than the full profile, just put the filename & md5sum in the event log. // Rather than the full profile, just put the filename & md5sum in the event log.
msg := fmt.Sprintf("%s(md5:%x)", name, md5.Sum(file)) msg := fmt.Sprintf("%s(md5:%x)", fname, md5.Sum(file))
return []dockerOpt{{"seccomp", b.String(), msg}}, nil return []dockerOpt{{"seccomp", b.String(), msg}}, nil
} }
// getSeccompSecurityOpts gets container seccomp options from container and sandbox // getSeccompSecurityOpts gets container seccomp options from container security context.
// config, currently from sandbox annotations.
// It is an experimental feature and may be promoted to official runtime api in the future. // It is an experimental feature and may be promoted to official runtime api in the future.
func getSeccompSecurityOpts(containerName string, sandboxConfig *runtimeapi.PodSandboxConfig, seccompProfileRoot string, separator rune) ([]string, error) { func getSeccompSecurityOpts(seccompProfile string, separator rune) ([]string, error) {
seccompOpts, err := getSeccompDockerOpts(sandboxConfig.GetAnnotations(), containerName, seccompProfileRoot) seccompOpts, err := getSeccompDockerOpts(seccompProfile)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -20,51 +20,32 @@ package dockershim
import ( import (
"fmt" "fmt"
"path"
"testing" "testing"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"k8s.io/api/core/v1"
runtimeapi "k8s.io/kubernetes/pkg/kubelet/apis/cri/v1alpha1/runtime"
) )
func TestGetSeccompSecurityOpts(t *testing.T) { func TestGetSeccompSecurityOpts(t *testing.T) {
containerName := "bar"
makeConfig := func(annotations map[string]string) *runtimeapi.PodSandboxConfig {
return makeSandboxConfigWithLabelsAndAnnotations("pod", "ns", "1234", 1, nil, annotations)
}
tests := []struct { tests := []struct {
msg string msg string
config *runtimeapi.PodSandboxConfig seccompProfile string
expectedOpts []string expectedOpts []string
}{{ }{{
msg: "No security annotations", msg: "No security annotations",
config: makeConfig(nil), seccompProfile: "",
expectedOpts: []string{"seccomp=unconfined"}, expectedOpts: []string{"seccomp=unconfined"},
}, { }, {
msg: "Seccomp unconfined", msg: "Seccomp unconfined",
config: makeConfig(map[string]string{ seccompProfile: "unconfined",
v1.SeccompContainerAnnotationKeyPrefix + containerName: "unconfined", expectedOpts: []string{"seccomp=unconfined"},
}),
expectedOpts: []string{"seccomp=unconfined"},
}, { }, {
msg: "Seccomp default", msg: "Seccomp default",
config: makeConfig(map[string]string{ seccompProfile: "docker/default",
v1.SeccompContainerAnnotationKeyPrefix + containerName: "docker/default", expectedOpts: nil,
}),
expectedOpts: nil,
}, {
msg: "Seccomp pod default",
config: makeConfig(map[string]string{
v1.SeccompPodAnnotationKey: "docker/default",
}),
expectedOpts: nil,
}} }}
for i, test := range tests { for i, test := range tests {
opts, err := getSeccompSecurityOpts(containerName, test.config, "test/seccomp/profile/root", '=') opts, err := getSeccompSecurityOpts(test.seccompProfile, '=')
assert.NoError(t, err, "TestCase[%d]: %s", i, test.msg) assert.NoError(t, err, "TestCase[%d]: %s", i, test.msg)
assert.Len(t, opts, len(test.expectedOpts), "TestCase[%d]: %s", i, test.msg) assert.Len(t, opts, len(test.expectedOpts), "TestCase[%d]: %s", i, test.msg)
for _, opt := range test.expectedOpts { for _, opt := range test.expectedOpts {
@ -74,42 +55,30 @@ func TestGetSeccompSecurityOpts(t *testing.T) {
} }
func TestLoadSeccompLocalhostProfiles(t *testing.T) { func TestLoadSeccompLocalhostProfiles(t *testing.T) {
containerName := "bar"
makeConfig := func(annotations map[string]string) *runtimeapi.PodSandboxConfig {
return makeSandboxConfigWithLabelsAndAnnotations("pod", "ns", "1234", 1, nil, annotations)
}
tests := []struct { tests := []struct {
msg string msg string
config *runtimeapi.PodSandboxConfig seccompProfile string
expectedOpts []string expectedOpts []string
expectErr bool expectErr bool
}{{ }{{
msg: "Seccomp localhost/test profile", msg: "Seccomp localhost/test profile",
config: makeConfig(map[string]string{ seccompProfile: "localhost/fixtures/seccomp/test",
v1.SeccompPodAnnotationKey: "localhost/test", expectedOpts: []string{`seccomp={"foo":"bar"}`},
}), expectErr: false,
expectedOpts: []string{`seccomp={"foo":"bar"}`},
expectErr: false,
}, { }, {
msg: "Seccomp localhost/sub/subtest profile", msg: "Seccomp localhost/sub/subtest profile",
config: makeConfig(map[string]string{ seccompProfile: "localhost/fixtures/seccomp/sub/subtest",
v1.SeccompPodAnnotationKey: "localhost/sub/subtest", expectedOpts: []string{`seccomp={"abc":"def"}`},
}), expectErr: false,
expectedOpts: []string{`seccomp={"abc":"def"}`},
expectErr: false,
}, { }, {
msg: "Seccomp non-existent", msg: "Seccomp non-existent",
config: makeConfig(map[string]string{ seccompProfile: "localhost/fixtures/seccomp/non-existent",
v1.SeccompPodAnnotationKey: "localhost/non-existent", expectedOpts: nil,
}), expectErr: true,
expectedOpts: nil,
expectErr: true,
}} }}
profileRoot := path.Join("fixtures", "seccomp")
for i, test := range tests { for i, test := range tests {
opts, err := getSeccompSecurityOpts(containerName, test.config, profileRoot, '=') opts, err := getSeccompSecurityOpts(test.seccompProfile, '=')
if test.expectErr { if test.expectErr {
assert.Error(t, err, fmt.Sprintf("TestCase[%d]: %s", i, test.msg)) assert.Error(t, err, fmt.Sprintf("TestCase[%d]: %s", i, test.msg))
continue continue

View File

@ -29,7 +29,7 @@ func DefaultMemorySwap() int64 {
return -1 return -1
} }
func (ds *dockerService) getSecurityOpts(containerName string, sandboxConfig *runtimeapi.PodSandboxConfig, separator rune) ([]string, error) { func (ds *dockerService) getSecurityOpts(seccompProfile string, separator rune) ([]string, error) {
glog.Warningf("getSecurityOpts is unsupported in this build") glog.Warningf("getSecurityOpts is unsupported in this build")
return nil, nil return nil, nil
} }