mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-29 06:27:05 +00:00
Merge pull request #47414 from karataliu/wincri5.devwin
Automatic merge from submit-queue (batch tested with PRs 47227, 47119, 46280, 47414, 46696) Move seccomp helper methods and tests to platform-specific files. **What this PR does / why we need it**: Seccomp helper methods are for linux only, move them to linux-specific helper file. As discussed in https://github.com/kubernetes/kubernetes/pull/46744 **Which issue this PR fixes** **Special notes for your reviewer**: **Release note**:
This commit is contained in:
commit
3adb6c630b
@ -82,6 +82,7 @@ go_test(
|
||||
"docker_legacy_test.go",
|
||||
"docker_sandbox_test.go",
|
||||
"docker_service_test.go",
|
||||
"helpers_linux_test.go",
|
||||
"helpers_test.go",
|
||||
"naming_test.go",
|
||||
"security_context_test.go",
|
||||
|
@ -17,11 +17,7 @@ limitations under the License.
|
||||
package dockershim
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/md5"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
@ -34,7 +30,6 @@ import (
|
||||
dockernat "github.com/docker/go-connections/nat"
|
||||
"github.com/golang/glog"
|
||||
|
||||
"k8s.io/api/core/v1"
|
||||
"k8s.io/kubernetes/pkg/credentialprovider"
|
||||
runtimeapi "k8s.io/kubernetes/pkg/kubelet/apis/cri/v1alpha1/runtime"
|
||||
"k8s.io/kubernetes/pkg/kubelet/types"
|
||||
@ -202,59 +197,6 @@ func makePortsAndBindings(pm []*runtimeapi.PortMapping) (map[dockernat.Port]stru
|
||||
return exposedPorts, portBindings
|
||||
}
|
||||
|
||||
func getSeccompDockerOpts(annotations map[string]string, ctrName, profileRoot string) ([]dockerOpt, error) {
|
||||
profile, profileOK := annotations[v1.SeccompContainerAnnotationKeyPrefix+ctrName]
|
||||
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 defaultSeccompOpt, nil
|
||||
}
|
||||
|
||||
if profile == "docker/default" {
|
||||
// return nil so docker will load the default seccomp profile
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
if !strings.HasPrefix(profile, "localhost/") {
|
||||
return nil, fmt.Errorf("unknown seccomp profile option: %s", profile)
|
||||
}
|
||||
|
||||
name := strings.TrimPrefix(profile, "localhost/") // by pod annotation validation, name is a valid subpath
|
||||
fname := filepath.Join(profileRoot, filepath.FromSlash(name))
|
||||
file, err := ioutil.ReadFile(fname)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot load seccomp profile %q: %v", name, err)
|
||||
}
|
||||
|
||||
b := bytes.NewBuffer(nil)
|
||||
if err := json.Compact(b, file); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Rather than the full profile, just put the filename & md5sum in the event log.
|
||||
msg := fmt.Sprintf("%s(md5:%x)", name, md5.Sum(file))
|
||||
|
||||
return []dockerOpt{{"seccomp", b.String(), msg}}, nil
|
||||
}
|
||||
|
||||
// getSeccompSecurityOpts gets container seccomp options from container and sandbox
|
||||
// config, currently from sandbox annotations.
|
||||
// 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) {
|
||||
seccompOpts, err := getSeccompDockerOpts(sandboxConfig.GetAnnotations(), containerName, seccompProfileRoot)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return fmtDockerOpts(seccompOpts, separator), nil
|
||||
}
|
||||
|
||||
// getApparmorSecurityOpts gets apparmor options from container config.
|
||||
func getApparmorSecurityOpts(sc *runtimeapi.LinuxContainerSecurityContext, separator rune) ([]string, error) {
|
||||
if sc == nil || sc.ApparmorProfile == "" {
|
||||
|
@ -19,11 +19,18 @@ limitations under the License.
|
||||
package dockershim
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/md5"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/blang/semver"
|
||||
dockertypes "github.com/docker/engine-api/types"
|
||||
dockercontainer "github.com/docker/engine-api/types/container"
|
||||
"k8s.io/api/core/v1"
|
||||
runtimeapi "k8s.io/kubernetes/pkg/kubelet/apis/cri/v1alpha1/runtime"
|
||||
)
|
||||
|
||||
@ -41,6 +48,59 @@ func (ds *dockerService) getSecurityOpts(containerName string, sandboxConfig *ru
|
||||
return seccompSecurityOpts, nil
|
||||
}
|
||||
|
||||
func getSeccompDockerOpts(annotations map[string]string, ctrName, profileRoot string) ([]dockerOpt, error) {
|
||||
profile, profileOK := annotations[v1.SeccompContainerAnnotationKeyPrefix+ctrName]
|
||||
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 defaultSeccompOpt, nil
|
||||
}
|
||||
|
||||
if profile == "docker/default" {
|
||||
// return nil so docker will load the default seccomp profile
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
if !strings.HasPrefix(profile, "localhost/") {
|
||||
return nil, fmt.Errorf("unknown seccomp profile option: %s", profile)
|
||||
}
|
||||
|
||||
name := strings.TrimPrefix(profile, "localhost/") // by pod annotation validation, name is a valid subpath
|
||||
fname := filepath.Join(profileRoot, filepath.FromSlash(name))
|
||||
file, err := ioutil.ReadFile(fname)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot load seccomp profile %q: %v", name, err)
|
||||
}
|
||||
|
||||
b := bytes.NewBuffer(nil)
|
||||
if err := json.Compact(b, file); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Rather than the full profile, just put the filename & md5sum in the event log.
|
||||
msg := fmt.Sprintf("%s(md5:%x)", name, md5.Sum(file))
|
||||
|
||||
return []dockerOpt{{"seccomp", b.String(), msg}}, nil
|
||||
}
|
||||
|
||||
// getSeccompSecurityOpts gets container seccomp options from container and sandbox
|
||||
// config, currently from sandbox annotations.
|
||||
// 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) {
|
||||
seccompOpts, err := getSeccompDockerOpts(sandboxConfig.GetAnnotations(), containerName, seccompProfileRoot)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return fmtDockerOpts(seccompOpts, separator), nil
|
||||
}
|
||||
|
||||
func (ds *dockerService) updateCreateConfig(
|
||||
createConfig *dockertypes.ContainerCreateConfig,
|
||||
config *runtimeapi.ContainerConfig,
|
||||
|
123
pkg/kubelet/dockershim/helpers_linux_test.go
Normal file
123
pkg/kubelet/dockershim/helpers_linux_test.go
Normal file
@ -0,0 +1,123 @@
|
||||
// +build linux
|
||||
|
||||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package dockershim
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"path"
|
||||
"testing"
|
||||
|
||||
"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) {
|
||||
containerName := "bar"
|
||||
makeConfig := func(annotations map[string]string) *runtimeapi.PodSandboxConfig {
|
||||
return makeSandboxConfigWithLabelsAndAnnotations("pod", "ns", "1234", 1, nil, annotations)
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
msg string
|
||||
config *runtimeapi.PodSandboxConfig
|
||||
expectedOpts []string
|
||||
}{{
|
||||
msg: "No security annotations",
|
||||
config: makeConfig(nil),
|
||||
expectedOpts: []string{"seccomp=unconfined"},
|
||||
}, {
|
||||
msg: "Seccomp unconfined",
|
||||
config: makeConfig(map[string]string{
|
||||
v1.SeccompContainerAnnotationKeyPrefix + containerName: "unconfined",
|
||||
}),
|
||||
expectedOpts: []string{"seccomp=unconfined"},
|
||||
}, {
|
||||
msg: "Seccomp default",
|
||||
config: makeConfig(map[string]string{
|
||||
v1.SeccompContainerAnnotationKeyPrefix + containerName: "docker/default",
|
||||
}),
|
||||
expectedOpts: nil,
|
||||
}, {
|
||||
msg: "Seccomp pod default",
|
||||
config: makeConfig(map[string]string{
|
||||
v1.SeccompPodAnnotationKey: "docker/default",
|
||||
}),
|
||||
expectedOpts: nil,
|
||||
}}
|
||||
|
||||
for i, test := range tests {
|
||||
opts, err := getSeccompSecurityOpts(containerName, test.config, "test/seccomp/profile/root", '=')
|
||||
assert.NoError(t, err, "TestCase[%d]: %s", i, test.msg)
|
||||
assert.Len(t, opts, len(test.expectedOpts), "TestCase[%d]: %s", i, test.msg)
|
||||
for _, opt := range test.expectedOpts {
|
||||
assert.Contains(t, opts, opt, "TestCase[%d]: %s", i, test.msg)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
msg string
|
||||
config *runtimeapi.PodSandboxConfig
|
||||
expectedOpts []string
|
||||
expectErr bool
|
||||
}{{
|
||||
msg: "Seccomp localhost/test profile",
|
||||
config: makeConfig(map[string]string{
|
||||
v1.SeccompPodAnnotationKey: "localhost/test",
|
||||
}),
|
||||
expectedOpts: []string{`seccomp={"foo":"bar"}`},
|
||||
expectErr: false,
|
||||
}, {
|
||||
msg: "Seccomp localhost/sub/subtest profile",
|
||||
config: makeConfig(map[string]string{
|
||||
v1.SeccompPodAnnotationKey: "localhost/sub/subtest",
|
||||
}),
|
||||
expectedOpts: []string{`seccomp={"abc":"def"}`},
|
||||
expectErr: false,
|
||||
}, {
|
||||
msg: "Seccomp non-existent",
|
||||
config: makeConfig(map[string]string{
|
||||
v1.SeccompPodAnnotationKey: "localhost/non-existent",
|
||||
}),
|
||||
expectedOpts: nil,
|
||||
expectErr: true,
|
||||
}}
|
||||
|
||||
profileRoot := path.Join("fixtures", "seccomp")
|
||||
for i, test := range tests {
|
||||
opts, err := getSeccompSecurityOpts(containerName, test.config, profileRoot, '=')
|
||||
if test.expectErr {
|
||||
assert.Error(t, err, fmt.Sprintf("TestCase[%d]: %s", i, test.msg))
|
||||
continue
|
||||
}
|
||||
assert.NoError(t, err, "TestCase[%d]: %s", i, test.msg)
|
||||
assert.Len(t, opts, len(test.expectedOpts), "TestCase[%d]: %s", i, test.msg)
|
||||
for _, opt := range test.expectedOpts {
|
||||
assert.Contains(t, opts, opt, "TestCase[%d]: %s", i, test.msg)
|
||||
}
|
||||
}
|
||||
}
|
@ -21,7 +21,6 @@ import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
@ -31,8 +30,6 @@ import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"k8s.io/api/core/v1"
|
||||
|
||||
runtimeapi "k8s.io/kubernetes/pkg/kubelet/apis/cri/v1alpha1/runtime"
|
||||
"k8s.io/kubernetes/pkg/security/apparmor"
|
||||
|
||||
@ -50,99 +47,6 @@ func TestLabelsAndAnnotationsRoundTrip(t *testing.T) {
|
||||
assert.Equal(t, expectedAnnotations, actualAnnotations)
|
||||
}
|
||||
|
||||
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 {
|
||||
msg string
|
||||
config *runtimeapi.PodSandboxConfig
|
||||
expectedOpts []string
|
||||
}{{
|
||||
msg: "No security annotations",
|
||||
config: makeConfig(nil),
|
||||
expectedOpts: []string{"seccomp=unconfined"},
|
||||
}, {
|
||||
msg: "Seccomp unconfined",
|
||||
config: makeConfig(map[string]string{
|
||||
v1.SeccompContainerAnnotationKeyPrefix + containerName: "unconfined",
|
||||
}),
|
||||
expectedOpts: []string{"seccomp=unconfined"},
|
||||
}, {
|
||||
msg: "Seccomp default",
|
||||
config: makeConfig(map[string]string{
|
||||
v1.SeccompContainerAnnotationKeyPrefix + containerName: "docker/default",
|
||||
}),
|
||||
expectedOpts: nil,
|
||||
}, {
|
||||
msg: "Seccomp pod default",
|
||||
config: makeConfig(map[string]string{
|
||||
v1.SeccompPodAnnotationKey: "docker/default",
|
||||
}),
|
||||
expectedOpts: nil,
|
||||
}}
|
||||
|
||||
for i, test := range tests {
|
||||
opts, err := getSeccompSecurityOpts(containerName, test.config, "test/seccomp/profile/root", '=')
|
||||
assert.NoError(t, err, "TestCase[%d]: %s", i, test.msg)
|
||||
assert.Len(t, opts, len(test.expectedOpts), "TestCase[%d]: %s", i, test.msg)
|
||||
for _, opt := range test.expectedOpts {
|
||||
assert.Contains(t, opts, opt, "TestCase[%d]: %s", i, test.msg)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
msg string
|
||||
config *runtimeapi.PodSandboxConfig
|
||||
expectedOpts []string
|
||||
expectErr bool
|
||||
}{{
|
||||
msg: "Seccomp localhost/test profile",
|
||||
config: makeConfig(map[string]string{
|
||||
v1.SeccompPodAnnotationKey: "localhost/test",
|
||||
}),
|
||||
expectedOpts: []string{`seccomp={"foo":"bar"}`},
|
||||
expectErr: false,
|
||||
}, {
|
||||
msg: "Seccomp localhost/sub/subtest profile",
|
||||
config: makeConfig(map[string]string{
|
||||
v1.SeccompPodAnnotationKey: "localhost/sub/subtest",
|
||||
}),
|
||||
expectedOpts: []string{`seccomp={"abc":"def"}`},
|
||||
expectErr: false,
|
||||
}, {
|
||||
msg: "Seccomp non-existent",
|
||||
config: makeConfig(map[string]string{
|
||||
v1.SeccompPodAnnotationKey: "localhost/non-existent",
|
||||
}),
|
||||
expectedOpts: nil,
|
||||
expectErr: true,
|
||||
}}
|
||||
|
||||
profileRoot := path.Join("fixtures", "seccomp")
|
||||
for i, test := range tests {
|
||||
opts, err := getSeccompSecurityOpts(containerName, test.config, profileRoot, '=')
|
||||
if test.expectErr {
|
||||
assert.Error(t, err, fmt.Sprintf("TestCase[%d]: %s", i, test.msg))
|
||||
continue
|
||||
}
|
||||
assert.NoError(t, err, "TestCase[%d]: %s", i, test.msg)
|
||||
assert.Len(t, opts, len(test.expectedOpts), "TestCase[%d]: %s", i, test.msg)
|
||||
for _, opt := range test.expectedOpts {
|
||||
assert.Contains(t, opts, opt, "TestCase[%d]: %s", i, test.msg)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestGetApparmorSecurityOpts tests the logic of generating container apparmor options from sandbox annotations.
|
||||
func TestGetApparmorSecurityOpts(t *testing.T) {
|
||||
makeConfig := func(profile string) *runtimeapi.LinuxContainerSecurityContext {
|
||||
|
Loading…
Reference in New Issue
Block a user