mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-29 22:46:12 +00:00
Merge pull request #101943 from saschagrunert/seccomp-default
Add kubelet `SeccompDefault` alpha feature
This commit is contained in:
commit
2e93b3924a
@ -166,6 +166,9 @@ type KubeletFlags struct {
|
||||
// This flag, if set, instructs the kubelet to keep volumes from terminated pods mounted to the node.
|
||||
// This can be useful for debugging volume related issues.
|
||||
KeepTerminatedPodVolumes bool
|
||||
// SeccompDefault enables the use of `RuntimeDefault` as the default seccomp profile for all workloads on the node.
|
||||
// To use this flag, the corresponding SeccompDefault feature gate must be enabled.
|
||||
SeccompDefault bool
|
||||
}
|
||||
|
||||
// NewKubeletFlags will create a new KubeletFlags with default values
|
||||
@ -211,6 +214,10 @@ func ValidateKubeletFlags(f *KubeletFlags) error {
|
||||
return fmt.Errorf("unknown 'kubernetes.io' or 'k8s.io' labels specified with --node-labels: %v\n--node-labels in the 'kubernetes.io' namespace must begin with an allowed prefix (%s) or be in the specifically allowed set (%s)", unknownLabels.List(), strings.Join(kubeletapis.KubeletLabelNamespaces(), ", "), strings.Join(kubeletapis.KubeletLabels(), ", "))
|
||||
}
|
||||
|
||||
if f.SeccompDefault && !utilfeature.DefaultFeatureGate.Enabled(features.SeccompDefault) {
|
||||
return fmt.Errorf("the SeccompDefault feature gate must be enabled in order to use the --seccomp-default flag")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -346,6 +353,7 @@ func (f *KubeletFlags) AddFlags(mainfs *pflag.FlagSet) {
|
||||
fs.Var(&bindableNodeLabels, "node-labels", fmt.Sprintf("<Warning: Alpha feature> Labels to add when registering the node in the cluster. Labels must be key=value pairs separated by ','. Labels in the 'kubernetes.io' namespace must begin with an allowed prefix (%s) or be in the specifically allowed set (%s)", strings.Join(kubeletapis.KubeletLabelNamespaces(), ", "), strings.Join(kubeletapis.KubeletLabels(), ", ")))
|
||||
fs.StringVar(&f.LockFilePath, "lock-file", f.LockFilePath, "<Warning: Alpha feature> The path to file for kubelet to use as a lock file.")
|
||||
fs.BoolVar(&f.ExitOnLockContention, "exit-on-lock-contention", f.ExitOnLockContention, "Whether kubelet should exit upon lock-file contention.")
|
||||
fs.BoolVar(&f.SeccompDefault, "seccomp-default", f.SeccompDefault, "<Warning: Alpha feature> Enable the use of `RuntimeDefault` as the default seccomp profile for all workloads. The SeccompDefault feature gate must be enabled to allow this flag, which is disabled per default.")
|
||||
|
||||
// DEPRECATED FLAGS
|
||||
fs.StringVar(&f.BootstrapKubeconfig, "experimental-bootstrap-kubeconfig", f.BootstrapKubeconfig, "")
|
||||
|
@ -1135,6 +1135,10 @@ func RunKubelet(kubeServer *options.KubeletServer, kubeDeps *kubelet.Dependencie
|
||||
kubeDeps.OSInterface = kubecontainer.RealOS{}
|
||||
}
|
||||
|
||||
if kubeServer.KubeletConfiguration.SeccompDefault && !utilfeature.DefaultFeatureGate.Enabled(features.SeccompDefault) {
|
||||
return fmt.Errorf("the SeccompDefault feature gate must be enabled in order to use the SeccompDefault configuration")
|
||||
}
|
||||
|
||||
k, err := createAndInitKubelet(&kubeServer.KubeletConfiguration,
|
||||
kubeDeps,
|
||||
&kubeServer.ContainerRuntimeOptions,
|
||||
@ -1164,7 +1168,9 @@ func RunKubelet(kubeServer *options.KubeletServer, kubeDeps *kubelet.Dependencie
|
||||
kubeServer.KeepTerminatedPodVolumes,
|
||||
kubeServer.NodeLabels,
|
||||
kubeServer.SeccompProfileRoot,
|
||||
kubeServer.NodeStatusMaxImages)
|
||||
kubeServer.NodeStatusMaxImages,
|
||||
kubeServer.KubeletFlags.SeccompDefault || kubeServer.KubeletConfiguration.SeccompDefault,
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create kubelet: %w", err)
|
||||
}
|
||||
@ -1238,7 +1244,9 @@ func createAndInitKubelet(kubeCfg *kubeletconfiginternal.KubeletConfiguration,
|
||||
keepTerminatedPodVolumes bool,
|
||||
nodeLabels map[string]string,
|
||||
seccompProfileRoot string,
|
||||
nodeStatusMaxImages int32) (k kubelet.Bootstrap, err error) {
|
||||
nodeStatusMaxImages int32,
|
||||
seccompDefault bool,
|
||||
) (k kubelet.Bootstrap, err error) {
|
||||
// TODO: block until all sources have delivered at least one update to the channel, or break the sync loop
|
||||
// up into "per source" synchronizations
|
||||
|
||||
@ -1271,7 +1279,9 @@ func createAndInitKubelet(kubeCfg *kubeletconfiginternal.KubeletConfiguration,
|
||||
keepTerminatedPodVolumes,
|
||||
nodeLabels,
|
||||
seccompProfileRoot,
|
||||
nodeStatusMaxImages)
|
||||
nodeStatusMaxImages,
|
||||
seccompDefault,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -714,6 +714,12 @@ const (
|
||||
//
|
||||
// Enables apiserver and kubelet to allow up to 32 DNSSearchPaths and up to 2048 DNSSearchListChars.
|
||||
ExpandedDNSConfig featuregate.Feature = "ExpandedDNSConfig"
|
||||
|
||||
// owner: @saschagrunert
|
||||
// alpha: v1.22
|
||||
//
|
||||
// Enables the use of `RuntimeDefault` as the default seccomp profile for all workloads.
|
||||
SeccompDefault featuregate.Feature = "SeccompDefault"
|
||||
)
|
||||
|
||||
func init() {
|
||||
@ -821,6 +827,7 @@ var defaultKubernetesFeatureGates = map[featuregate.Feature]featuregate.FeatureS
|
||||
DisableCloudProviders: {Default: false, PreRelease: featuregate.Alpha},
|
||||
StatefulSetMinReadySeconds: {Default: false, PreRelease: featuregate.Alpha},
|
||||
ExpandedDNSConfig: {Default: false, PreRelease: featuregate.Alpha},
|
||||
SeccompDefault: {Default: false, PreRelease: featuregate.Alpha},
|
||||
|
||||
// inherited features from generic apiserver, relisted here to get a conflict if it is changed
|
||||
// unintentionally on either side:
|
||||
|
@ -234,6 +234,7 @@ var (
|
||||
"ReservedSystemCPUs",
|
||||
"RuntimeRequestTimeout.Duration",
|
||||
"RunOnce",
|
||||
"SeccompDefault",
|
||||
"SerializeImagePulls",
|
||||
"ShowHiddenMetricsForVersion",
|
||||
"StreamingConnectionIdleTimeout.Duration",
|
||||
|
@ -69,6 +69,7 @@ registryBurst: 10
|
||||
registryPullQPS: 5
|
||||
resolvConf: /etc/resolv.conf
|
||||
runtimeRequestTimeout: 2m0s
|
||||
seccompDefault: false
|
||||
serializeImagePulls: true
|
||||
shutdownGracePeriod: 0s
|
||||
shutdownGracePeriodCriticalPods: 0s
|
||||
|
@ -69,6 +69,7 @@ registryBurst: 10
|
||||
registryPullQPS: 5
|
||||
resolvConf: /etc/resolv.conf
|
||||
runtimeRequestTimeout: 2m0s
|
||||
seccompDefault: false
|
||||
serializeImagePulls: true
|
||||
shutdownGracePeriod: 0s
|
||||
shutdownGracePeriodCriticalPods: 0s
|
||||
|
@ -407,6 +407,8 @@ type KubeletConfiguration struct {
|
||||
EnableProfilingHandler bool
|
||||
// EnableDebugFlagsHandler enables/debug/flags/v handler.
|
||||
EnableDebugFlagsHandler bool
|
||||
// SeccompDefault enables the use of `RuntimeDefault` as the default seccomp profile for all workloads.
|
||||
SeccompDefault bool
|
||||
}
|
||||
|
||||
// KubeletAuthorizationMode denotes the authorization mode for the kubelet
|
||||
|
@ -252,4 +252,7 @@ func SetDefaults_KubeletConfiguration(obj *kubeletconfigv1beta1.KubeletConfigura
|
||||
if obj.EnableDebugFlagsHandler == nil {
|
||||
obj.EnableDebugFlagsHandler = utilpointer.BoolPtr(true)
|
||||
}
|
||||
if obj.SeccompDefault == nil {
|
||||
obj.SeccompDefault = utilpointer.BoolPtr(false)
|
||||
}
|
||||
}
|
||||
|
@ -371,6 +371,9 @@ func autoConvert_v1beta1_KubeletConfiguration_To_config_KubeletConfiguration(in
|
||||
if err := v1.Convert_Pointer_bool_To_bool(&in.EnableDebugFlagsHandler, &out.EnableDebugFlagsHandler, s); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := v1.Convert_Pointer_bool_To_bool(&in.SeccompDefault, &out.SeccompDefault, s); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -532,6 +535,9 @@ func autoConvert_config_KubeletConfiguration_To_v1beta1_KubeletConfiguration(in
|
||||
if err := v1.Convert_bool_To_Pointer_bool(&in.EnableDebugFlagsHandler, &out.EnableDebugFlagsHandler, s); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := v1.Convert_bool_To_Pointer_bool(&in.SeccompDefault, &out.SeccompDefault, s); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -367,7 +367,9 @@ func NewMainKubelet(kubeCfg *kubeletconfiginternal.KubeletConfiguration,
|
||||
keepTerminatedPodVolumes bool,
|
||||
nodeLabels map[string]string,
|
||||
seccompProfileRoot string,
|
||||
nodeStatusMaxImages int32) (*Kubelet, error) {
|
||||
nodeStatusMaxImages int32,
|
||||
seccompDefault bool,
|
||||
) (*Kubelet, error) {
|
||||
if rootDirectory == "" {
|
||||
return nil, fmt.Errorf("invalid root directory %q", rootDirectory)
|
||||
}
|
||||
@ -649,6 +651,7 @@ func NewMainKubelet(kubeCfg *kubeletconfiginternal.KubeletConfiguration,
|
||||
kubeDeps.dockerLegacyService,
|
||||
klet.containerLogManager,
|
||||
klet.runtimeClassManager,
|
||||
seccompDefault,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -202,8 +202,11 @@ func toKubeRuntimeStatus(status *runtimeapi.RuntimeStatus) *kubecontainer.Runtim
|
||||
return &kubecontainer.RuntimeStatus{Conditions: conditions}
|
||||
}
|
||||
|
||||
func fieldProfile(scmp *v1.SeccompProfile, profileRootPath string) string {
|
||||
func fieldProfile(scmp *v1.SeccompProfile, profileRootPath string, fallbackToRuntimeDefault bool) string {
|
||||
if scmp == nil {
|
||||
if fallbackToRuntimeDefault {
|
||||
return v1.SeccompProfileRuntimeDefault
|
||||
}
|
||||
return ""
|
||||
}
|
||||
if scmp.Type == v1.SeccompProfileTypeRuntimeDefault {
|
||||
@ -216,6 +219,10 @@ func fieldProfile(scmp *v1.SeccompProfile, profileRootPath string) string {
|
||||
if scmp.Type == v1.SeccompProfileTypeUnconfined {
|
||||
return v1.SeccompProfileNameUnconfined
|
||||
}
|
||||
|
||||
if fallbackToRuntimeDefault {
|
||||
return v1.SeccompProfileRuntimeDefault
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
@ -229,10 +236,10 @@ func annotationProfile(profile, profileRootPath string) string {
|
||||
}
|
||||
|
||||
func (m *kubeGenericRuntimeManager) getSeccompProfilePath(annotations map[string]string, containerName string,
|
||||
podSecContext *v1.PodSecurityContext, containerSecContext *v1.SecurityContext) string {
|
||||
podSecContext *v1.PodSecurityContext, containerSecContext *v1.SecurityContext, fallbackToRuntimeDefault bool) string {
|
||||
// container fields are applied first
|
||||
if containerSecContext != nil && containerSecContext.SeccompProfile != nil {
|
||||
return fieldProfile(containerSecContext.SeccompProfile, m.seccompProfileRoot)
|
||||
return fieldProfile(containerSecContext.SeccompProfile, m.seccompProfileRoot, fallbackToRuntimeDefault)
|
||||
}
|
||||
|
||||
// if container field does not exist, try container annotation (deprecated)
|
||||
@ -244,7 +251,7 @@ func (m *kubeGenericRuntimeManager) getSeccompProfilePath(annotations map[string
|
||||
|
||||
// when container seccomp is not defined, try to apply from pod field
|
||||
if podSecContext != nil && podSecContext.SeccompProfile != nil {
|
||||
return fieldProfile(podSecContext.SeccompProfile, m.seccompProfileRoot)
|
||||
return fieldProfile(podSecContext.SeccompProfile, m.seccompProfileRoot, fallbackToRuntimeDefault)
|
||||
}
|
||||
|
||||
// as last resort, try to apply pod annotation (deprecated)
|
||||
@ -252,13 +259,20 @@ func (m *kubeGenericRuntimeManager) getSeccompProfilePath(annotations map[string
|
||||
return annotationProfile(profile, m.seccompProfileRoot)
|
||||
}
|
||||
|
||||
if fallbackToRuntimeDefault {
|
||||
return v1.SeccompProfileRuntimeDefault
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
func fieldSeccompProfile(scmp *v1.SeccompProfile, profileRootPath string) *runtimeapi.SecurityProfile {
|
||||
// TODO: Move to RuntimeDefault as the default instead of Unconfined after discussion
|
||||
// with sig-node.
|
||||
func fieldSeccompProfile(scmp *v1.SeccompProfile, profileRootPath string, fallbackToRuntimeDefault bool) *runtimeapi.SecurityProfile {
|
||||
if scmp == nil {
|
||||
if fallbackToRuntimeDefault {
|
||||
return &runtimeapi.SecurityProfile{
|
||||
ProfileType: runtimeapi.SecurityProfile_RuntimeDefault,
|
||||
}
|
||||
}
|
||||
return &runtimeapi.SecurityProfile{
|
||||
ProfileType: runtimeapi.SecurityProfile_Unconfined,
|
||||
}
|
||||
@ -281,15 +295,21 @@ func fieldSeccompProfile(scmp *v1.SeccompProfile, profileRootPath string) *runti
|
||||
}
|
||||
|
||||
func (m *kubeGenericRuntimeManager) getSeccompProfile(annotations map[string]string, containerName string,
|
||||
podSecContext *v1.PodSecurityContext, containerSecContext *v1.SecurityContext) *runtimeapi.SecurityProfile {
|
||||
podSecContext *v1.PodSecurityContext, containerSecContext *v1.SecurityContext, fallbackToRuntimeDefault bool) *runtimeapi.SecurityProfile {
|
||||
// container fields are applied first
|
||||
if containerSecContext != nil && containerSecContext.SeccompProfile != nil {
|
||||
return fieldSeccompProfile(containerSecContext.SeccompProfile, m.seccompProfileRoot)
|
||||
return fieldSeccompProfile(containerSecContext.SeccompProfile, m.seccompProfileRoot, fallbackToRuntimeDefault)
|
||||
}
|
||||
|
||||
// when container seccomp is not defined, try to apply from pod field
|
||||
if podSecContext != nil && podSecContext.SeccompProfile != nil {
|
||||
return fieldSeccompProfile(podSecContext.SeccompProfile, m.seccompProfileRoot)
|
||||
return fieldSeccompProfile(podSecContext.SeccompProfile, m.seccompProfileRoot, fallbackToRuntimeDefault)
|
||||
}
|
||||
|
||||
if fallbackToRuntimeDefault {
|
||||
return &runtimeapi.SecurityProfile{
|
||||
ProfileType: runtimeapi.SecurityProfile_RuntimeDefault,
|
||||
}
|
||||
}
|
||||
|
||||
return &runtimeapi.SecurityProfile{
|
||||
|
@ -210,7 +210,7 @@ func TestFieldProfile(t *testing.T) {
|
||||
expectedProfile: "unconfined",
|
||||
},
|
||||
{
|
||||
description: "SeccompProfileTypeLocalhost should return unconfined",
|
||||
description: "SeccompProfileTypeLocalhost should return localhost",
|
||||
scmpProfile: &v1.SeccompProfile{
|
||||
Type: v1.SeccompProfileTypeLocalhost,
|
||||
LocalhostProfile: utilpointer.StringPtr("profile.json"),
|
||||
@ -221,7 +221,63 @@ func TestFieldProfile(t *testing.T) {
|
||||
}
|
||||
|
||||
for i, test := range tests {
|
||||
seccompProfile := fieldProfile(test.scmpProfile, test.rootPath)
|
||||
seccompProfile := fieldProfile(test.scmpProfile, test.rootPath, false)
|
||||
assert.Equal(t, test.expectedProfile, seccompProfile, "TestCase[%d]: %s", i, test.description)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFieldProfileDefaultSeccomp(t *testing.T) {
|
||||
tests := []struct {
|
||||
description string
|
||||
scmpProfile *v1.SeccompProfile
|
||||
rootPath string
|
||||
expectedProfile string
|
||||
}{
|
||||
{
|
||||
description: "no seccompProfile should return runtime/default",
|
||||
expectedProfile: v1.SeccompProfileRuntimeDefault,
|
||||
},
|
||||
{
|
||||
description: "type localhost without profile should return runtime/default",
|
||||
scmpProfile: &v1.SeccompProfile{
|
||||
Type: v1.SeccompProfileTypeLocalhost,
|
||||
},
|
||||
expectedProfile: v1.SeccompProfileRuntimeDefault,
|
||||
},
|
||||
{
|
||||
description: "unknown type should return runtime/default",
|
||||
scmpProfile: &v1.SeccompProfile{
|
||||
Type: "",
|
||||
},
|
||||
expectedProfile: v1.SeccompProfileRuntimeDefault,
|
||||
},
|
||||
{
|
||||
description: "SeccompProfileTypeRuntimeDefault should return runtime/default",
|
||||
scmpProfile: &v1.SeccompProfile{
|
||||
Type: v1.SeccompProfileTypeRuntimeDefault,
|
||||
},
|
||||
expectedProfile: "runtime/default",
|
||||
},
|
||||
{
|
||||
description: "SeccompProfileTypeUnconfined should return unconfined",
|
||||
scmpProfile: &v1.SeccompProfile{
|
||||
Type: v1.SeccompProfileTypeUnconfined,
|
||||
},
|
||||
expectedProfile: "unconfined",
|
||||
},
|
||||
{
|
||||
description: "SeccompProfileTypeLocalhost should return localhost",
|
||||
scmpProfile: &v1.SeccompProfile{
|
||||
Type: v1.SeccompProfileTypeLocalhost,
|
||||
LocalhostProfile: utilpointer.StringPtr("profile.json"),
|
||||
},
|
||||
rootPath: "/test/",
|
||||
expectedProfile: "localhost//test/profile.json",
|
||||
},
|
||||
}
|
||||
|
||||
for i, test := range tests {
|
||||
seccompProfile := fieldProfile(test.scmpProfile, test.rootPath, true)
|
||||
assert.Equal(t, test.expectedProfile, seccompProfile, "TestCase[%d]: %s", i, test.description)
|
||||
}
|
||||
}
|
||||
@ -411,7 +467,197 @@ func TestGetSeccompProfilePath(t *testing.T) {
|
||||
}
|
||||
|
||||
for i, test := range tests {
|
||||
seccompProfile := m.getSeccompProfilePath(test.annotation, test.containerName, test.podSc, test.containerSc)
|
||||
seccompProfile := m.getSeccompProfilePath(test.annotation, test.containerName, test.podSc, test.containerSc, false)
|
||||
assert.Equal(t, test.expectedProfile, seccompProfile, "TestCase[%d]: %s", i, test.description)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetSeccompProfilePathDefaultSeccomp(t *testing.T) {
|
||||
_, _, m, err := createTestRuntimeManager()
|
||||
require.NoError(t, err)
|
||||
|
||||
tests := []struct {
|
||||
description string
|
||||
annotation map[string]string
|
||||
podSc *v1.PodSecurityContext
|
||||
containerSc *v1.SecurityContext
|
||||
containerName string
|
||||
expectedProfile string
|
||||
}{
|
||||
{
|
||||
description: "no seccomp should return runtime/default",
|
||||
expectedProfile: v1.SeccompProfileRuntimeDefault,
|
||||
},
|
||||
{
|
||||
description: "annotations: no seccomp with containerName should return runtime/default",
|
||||
containerName: "container1",
|
||||
expectedProfile: v1.SeccompProfileRuntimeDefault,
|
||||
},
|
||||
{
|
||||
description: "annotations: pod runtime/default seccomp profile should return runtime/default",
|
||||
annotation: map[string]string{
|
||||
v1.SeccompPodAnnotationKey: v1.SeccompProfileRuntimeDefault,
|
||||
},
|
||||
expectedProfile: v1.SeccompProfileRuntimeDefault,
|
||||
},
|
||||
{
|
||||
description: "annotations: pod docker/default seccomp profile should return docker/default",
|
||||
annotation: map[string]string{
|
||||
v1.SeccompPodAnnotationKey: v1.DeprecatedSeccompProfileDockerDefault,
|
||||
},
|
||||
expectedProfile: "docker/default",
|
||||
},
|
||||
{
|
||||
description: "annotations: pod runtime/default seccomp profile with containerName should return runtime/default",
|
||||
annotation: map[string]string{
|
||||
v1.SeccompPodAnnotationKey: v1.SeccompProfileRuntimeDefault,
|
||||
},
|
||||
containerName: "container1",
|
||||
expectedProfile: v1.SeccompProfileRuntimeDefault,
|
||||
},
|
||||
{
|
||||
description: "annotations: pod docker/default seccomp profile with containerName should return docker/default",
|
||||
annotation: map[string]string{
|
||||
v1.SeccompPodAnnotationKey: v1.DeprecatedSeccompProfileDockerDefault,
|
||||
},
|
||||
containerName: "container1",
|
||||
expectedProfile: "docker/default",
|
||||
},
|
||||
{
|
||||
description: "annotations: pod unconfined seccomp profile should return unconfined",
|
||||
annotation: map[string]string{
|
||||
v1.SeccompPodAnnotationKey: v1.SeccompProfileNameUnconfined,
|
||||
},
|
||||
expectedProfile: "unconfined",
|
||||
},
|
||||
{
|
||||
description: "annotations: pod unconfined seccomp profile with containerName should return unconfined",
|
||||
annotation: map[string]string{
|
||||
v1.SeccompPodAnnotationKey: v1.SeccompProfileNameUnconfined,
|
||||
},
|
||||
containerName: "container1",
|
||||
expectedProfile: "unconfined",
|
||||
},
|
||||
{
|
||||
description: "annotations: pod localhost seccomp profile should return local profile path",
|
||||
annotation: map[string]string{
|
||||
v1.SeccompPodAnnotationKey: "localhost/chmod.json",
|
||||
},
|
||||
expectedProfile: "localhost/" + filepath.Join(fakeSeccompProfileRoot, "chmod.json"),
|
||||
},
|
||||
{
|
||||
description: "annotations: pod localhost seccomp profile with containerName should return local profile path",
|
||||
annotation: map[string]string{
|
||||
v1.SeccompPodAnnotationKey: "localhost/chmod.json",
|
||||
},
|
||||
containerName: "container1",
|
||||
expectedProfile: "localhost/" + filepath.Join(fakeSeccompProfileRoot, "chmod.json"),
|
||||
},
|
||||
{
|
||||
description: "annotations: container localhost seccomp profile with containerName should return local profile path",
|
||||
annotation: map[string]string{
|
||||
v1.SeccompContainerAnnotationKeyPrefix + "container1": "localhost/chmod.json",
|
||||
},
|
||||
containerName: "container1",
|
||||
expectedProfile: "localhost/" + filepath.Join(fakeSeccompProfileRoot, "chmod.json"),
|
||||
},
|
||||
{
|
||||
description: "annotations: container localhost seccomp profile should override pod profile",
|
||||
annotation: map[string]string{
|
||||
v1.SeccompPodAnnotationKey: v1.SeccompProfileNameUnconfined,
|
||||
v1.SeccompContainerAnnotationKeyPrefix + "container1": "localhost/chmod.json",
|
||||
},
|
||||
containerName: "container1",
|
||||
expectedProfile: "localhost/" + filepath.Join(fakeSeccompProfileRoot, "chmod.json"),
|
||||
},
|
||||
{
|
||||
description: "annotations: container localhost seccomp profile with unmatched containerName should return runtime/default",
|
||||
annotation: map[string]string{
|
||||
v1.SeccompContainerAnnotationKeyPrefix + "container1": "localhost/chmod.json",
|
||||
},
|
||||
containerName: "container2",
|
||||
expectedProfile: v1.SeccompProfileRuntimeDefault,
|
||||
},
|
||||
{
|
||||
description: "pod seccomp profile set to unconfined returns unconfined",
|
||||
podSc: &v1.PodSecurityContext{SeccompProfile: &v1.SeccompProfile{Type: v1.SeccompProfileTypeUnconfined}},
|
||||
expectedProfile: "unconfined",
|
||||
},
|
||||
{
|
||||
description: "container seccomp profile set to unconfined returns unconfined",
|
||||
containerSc: &v1.SecurityContext{SeccompProfile: &v1.SeccompProfile{Type: v1.SeccompProfileTypeUnconfined}},
|
||||
expectedProfile: "unconfined",
|
||||
},
|
||||
{
|
||||
description: "pod seccomp profile set to SeccompProfileTypeRuntimeDefault returns runtime/default",
|
||||
podSc: &v1.PodSecurityContext{SeccompProfile: &v1.SeccompProfile{Type: v1.SeccompProfileTypeRuntimeDefault}},
|
||||
expectedProfile: "runtime/default",
|
||||
},
|
||||
{
|
||||
description: "container seccomp profile set to SeccompProfileTypeRuntimeDefault returns runtime/default",
|
||||
containerSc: &v1.SecurityContext{SeccompProfile: &v1.SeccompProfile{Type: v1.SeccompProfileTypeRuntimeDefault}},
|
||||
expectedProfile: "runtime/default",
|
||||
},
|
||||
{
|
||||
description: "pod seccomp profile set to SeccompProfileTypeLocalhost returns 'localhost/' + LocalhostProfile",
|
||||
podSc: &v1.PodSecurityContext{SeccompProfile: &v1.SeccompProfile{Type: v1.SeccompProfileTypeLocalhost, LocalhostProfile: getLocal("filename")}},
|
||||
expectedProfile: "localhost/" + filepath.Join(fakeSeccompProfileRoot, "filename"),
|
||||
},
|
||||
{
|
||||
description: "pod seccomp profile set to SeccompProfileTypeLocalhost with empty LocalhostProfile returns runtime/default",
|
||||
podSc: &v1.PodSecurityContext{SeccompProfile: &v1.SeccompProfile{Type: v1.SeccompProfileTypeLocalhost}},
|
||||
expectedProfile: v1.SeccompProfileRuntimeDefault,
|
||||
},
|
||||
{
|
||||
description: "container seccomp profile set to SeccompProfileTypeLocalhost with empty LocalhostProfile returns runtime/default",
|
||||
containerSc: &v1.SecurityContext{SeccompProfile: &v1.SeccompProfile{Type: v1.SeccompProfileTypeLocalhost}},
|
||||
expectedProfile: v1.SeccompProfileRuntimeDefault,
|
||||
},
|
||||
{
|
||||
description: "container seccomp profile set to SeccompProfileTypeLocalhost returns 'localhost/' + LocalhostProfile",
|
||||
containerSc: &v1.SecurityContext{SeccompProfile: &v1.SeccompProfile{Type: v1.SeccompProfileTypeLocalhost, LocalhostProfile: getLocal("filename2")}},
|
||||
expectedProfile: "localhost/" + filepath.Join(fakeSeccompProfileRoot, "filename2"),
|
||||
},
|
||||
{
|
||||
description: "prioritise container field over pod field",
|
||||
podSc: &v1.PodSecurityContext{SeccompProfile: &v1.SeccompProfile{Type: v1.SeccompProfileTypeUnconfined}},
|
||||
containerSc: &v1.SecurityContext{SeccompProfile: &v1.SeccompProfile{Type: v1.SeccompProfileTypeRuntimeDefault}},
|
||||
expectedProfile: "runtime/default",
|
||||
},
|
||||
{
|
||||
description: "prioritise container field over container annotation, pod field and pod annotation",
|
||||
podSc: &v1.PodSecurityContext{SeccompProfile: &v1.SeccompProfile{Type: v1.SeccompProfileTypeLocalhost, LocalhostProfile: getLocal("field-pod-profile.json")}},
|
||||
containerSc: &v1.SecurityContext{SeccompProfile: &v1.SeccompProfile{Type: v1.SeccompProfileTypeLocalhost, LocalhostProfile: getLocal("field-cont-profile.json")}},
|
||||
annotation: map[string]string{
|
||||
v1.SeccompPodAnnotationKey: "localhost/annota-pod-profile.json",
|
||||
v1.SeccompContainerAnnotationKeyPrefix + "container1": "localhost/annota-cont-profile.json",
|
||||
},
|
||||
containerName: "container1",
|
||||
expectedProfile: "localhost/" + filepath.Join(fakeSeccompProfileRoot, "field-cont-profile.json"),
|
||||
},
|
||||
{
|
||||
description: "prioritise container annotation over pod field",
|
||||
podSc: &v1.PodSecurityContext{SeccompProfile: &v1.SeccompProfile{Type: v1.SeccompProfileTypeLocalhost, LocalhostProfile: getLocal("field-pod-profile.json")}},
|
||||
annotation: map[string]string{
|
||||
v1.SeccompPodAnnotationKey: "localhost/annota-pod-profile.json",
|
||||
v1.SeccompContainerAnnotationKeyPrefix + "container1": "localhost/annota-cont-profile.json",
|
||||
},
|
||||
containerName: "container1",
|
||||
expectedProfile: "localhost/" + filepath.Join(fakeSeccompProfileRoot, "annota-cont-profile.json"),
|
||||
},
|
||||
{
|
||||
description: "prioritise pod field over pod annotation",
|
||||
podSc: &v1.PodSecurityContext{SeccompProfile: &v1.SeccompProfile{Type: v1.SeccompProfileTypeLocalhost, LocalhostProfile: getLocal("field-pod-profile.json")}},
|
||||
annotation: map[string]string{
|
||||
v1.SeccompPodAnnotationKey: "localhost/annota-pod-profile.json",
|
||||
},
|
||||
containerName: "container1",
|
||||
expectedProfile: "localhost/" + filepath.Join(fakeSeccompProfileRoot, "field-pod-profile.json"),
|
||||
},
|
||||
}
|
||||
|
||||
for i, test := range tests {
|
||||
seccompProfile := m.getSeccompProfilePath(test.annotation, test.containerName, test.podSc, test.containerSc, true)
|
||||
assert.Equal(t, test.expectedProfile, seccompProfile, "TestCase[%d]: %s", i, test.description)
|
||||
}
|
||||
}
|
||||
@ -505,7 +751,101 @@ func TestGetSeccompProfile(t *testing.T) {
|
||||
}
|
||||
|
||||
for i, test := range tests {
|
||||
seccompProfile := m.getSeccompProfile(test.annotation, test.containerName, test.podSc, test.containerSc)
|
||||
seccompProfile := m.getSeccompProfile(test.annotation, test.containerName, test.podSc, test.containerSc, false)
|
||||
assert.Equal(t, test.expectedProfile, seccompProfile, "TestCase[%d]: %s", i, test.description)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetSeccompProfileDefaultSeccomp(t *testing.T) {
|
||||
_, _, m, err := createTestRuntimeManager()
|
||||
require.NoError(t, err)
|
||||
|
||||
unconfinedProfile := &runtimeapi.SecurityProfile{
|
||||
ProfileType: runtimeapi.SecurityProfile_Unconfined,
|
||||
}
|
||||
|
||||
runtimeDefaultProfile := &runtimeapi.SecurityProfile{
|
||||
ProfileType: runtimeapi.SecurityProfile_RuntimeDefault,
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
description string
|
||||
annotation map[string]string
|
||||
podSc *v1.PodSecurityContext
|
||||
containerSc *v1.SecurityContext
|
||||
containerName string
|
||||
expectedProfile *runtimeapi.SecurityProfile
|
||||
}{
|
||||
{
|
||||
description: "no seccomp should return RuntimeDefault",
|
||||
expectedProfile: runtimeDefaultProfile,
|
||||
},
|
||||
{
|
||||
description: "pod seccomp profile set to unconfined returns unconfined",
|
||||
podSc: &v1.PodSecurityContext{SeccompProfile: &v1.SeccompProfile{Type: v1.SeccompProfileTypeUnconfined}},
|
||||
expectedProfile: unconfinedProfile,
|
||||
},
|
||||
{
|
||||
description: "container seccomp profile set to unconfined returns unconfined",
|
||||
containerSc: &v1.SecurityContext{SeccompProfile: &v1.SeccompProfile{Type: v1.SeccompProfileTypeUnconfined}},
|
||||
expectedProfile: unconfinedProfile,
|
||||
},
|
||||
{
|
||||
description: "pod seccomp profile set to SeccompProfileTypeRuntimeDefault returns runtime/default",
|
||||
podSc: &v1.PodSecurityContext{SeccompProfile: &v1.SeccompProfile{Type: v1.SeccompProfileTypeRuntimeDefault}},
|
||||
expectedProfile: runtimeDefaultProfile,
|
||||
},
|
||||
{
|
||||
description: "container seccomp profile set to SeccompProfileTypeRuntimeDefault returns runtime/default",
|
||||
containerSc: &v1.SecurityContext{SeccompProfile: &v1.SeccompProfile{Type: v1.SeccompProfileTypeRuntimeDefault}},
|
||||
expectedProfile: runtimeDefaultProfile,
|
||||
},
|
||||
{
|
||||
description: "pod seccomp profile set to SeccompProfileTypeLocalhost returns 'localhost/' + LocalhostProfile",
|
||||
podSc: &v1.PodSecurityContext{SeccompProfile: &v1.SeccompProfile{Type: v1.SeccompProfileTypeLocalhost, LocalhostProfile: getLocal("filename")}},
|
||||
expectedProfile: &runtimeapi.SecurityProfile{
|
||||
ProfileType: runtimeapi.SecurityProfile_Localhost,
|
||||
LocalhostRef: filepath.Join(fakeSeccompProfileRoot, "filename"),
|
||||
},
|
||||
},
|
||||
{
|
||||
description: "pod seccomp profile set to SeccompProfileTypeLocalhost with empty LocalhostProfile returns unconfined",
|
||||
podSc: &v1.PodSecurityContext{SeccompProfile: &v1.SeccompProfile{Type: v1.SeccompProfileTypeLocalhost}},
|
||||
expectedProfile: unconfinedProfile,
|
||||
},
|
||||
{
|
||||
description: "container seccomp profile set to SeccompProfileTypeLocalhost with empty LocalhostProfile returns unconfined",
|
||||
containerSc: &v1.SecurityContext{SeccompProfile: &v1.SeccompProfile{Type: v1.SeccompProfileTypeLocalhost}},
|
||||
expectedProfile: unconfinedProfile,
|
||||
},
|
||||
{
|
||||
description: "container seccomp profile set to SeccompProfileTypeLocalhost returns 'localhost/' + LocalhostProfile",
|
||||
containerSc: &v1.SecurityContext{SeccompProfile: &v1.SeccompProfile{Type: v1.SeccompProfileTypeLocalhost, LocalhostProfile: getLocal("filename2")}},
|
||||
expectedProfile: &runtimeapi.SecurityProfile{
|
||||
ProfileType: runtimeapi.SecurityProfile_Localhost,
|
||||
LocalhostRef: filepath.Join(fakeSeccompProfileRoot, "filename2"),
|
||||
},
|
||||
},
|
||||
{
|
||||
description: "prioritise container field over pod field",
|
||||
podSc: &v1.PodSecurityContext{SeccompProfile: &v1.SeccompProfile{Type: v1.SeccompProfileTypeUnconfined}},
|
||||
containerSc: &v1.SecurityContext{SeccompProfile: &v1.SeccompProfile{Type: v1.SeccompProfileTypeRuntimeDefault}},
|
||||
expectedProfile: runtimeDefaultProfile,
|
||||
},
|
||||
{
|
||||
description: "prioritise container field over pod field",
|
||||
podSc: &v1.PodSecurityContext{SeccompProfile: &v1.SeccompProfile{Type: v1.SeccompProfileTypeLocalhost, LocalhostProfile: getLocal("field-pod-profile.json")}},
|
||||
containerSc: &v1.SecurityContext{SeccompProfile: &v1.SeccompProfile{Type: v1.SeccompProfileTypeLocalhost, LocalhostProfile: getLocal("field-cont-profile.json")}},
|
||||
containerName: "container1",
|
||||
expectedProfile: &runtimeapi.SecurityProfile{
|
||||
ProfileType: runtimeapi.SecurityProfile_Localhost,
|
||||
LocalhostRef: filepath.Join(fakeSeccompProfileRoot, "field-cont-profile.json"),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for i, test := range tests {
|
||||
seccompProfile := m.getSeccompProfile(test.annotation, test.containerName, test.podSc, test.containerSc, true)
|
||||
assert.Equal(t, test.expectedProfile, seccompProfile, "TestCase[%d]: %s", i, test.description)
|
||||
}
|
||||
}
|
||||
|
@ -141,6 +141,9 @@ type kubeGenericRuntimeManager struct {
|
||||
|
||||
// PodState provider instance
|
||||
podStateProvider podStateProvider
|
||||
|
||||
// Use RuntimeDefault as the default seccomp profile for all workloads.
|
||||
seccompDefault bool
|
||||
}
|
||||
|
||||
// KubeGenericRuntime is a interface contains interfaces for container runtime and command.
|
||||
@ -182,6 +185,7 @@ func NewKubeGenericRuntimeManager(
|
||||
legacyLogProvider LegacyLogProvider,
|
||||
logManager logs.ContainerLogManager,
|
||||
runtimeClassManager *runtimeclass.Manager,
|
||||
seccompDefault bool,
|
||||
) (KubeGenericRuntime, error) {
|
||||
kubeRuntimeManager := &kubeGenericRuntimeManager{
|
||||
recorder: recorder,
|
||||
@ -201,6 +205,7 @@ func NewKubeGenericRuntimeManager(
|
||||
logManager: logManager,
|
||||
runtimeClassManager: runtimeClassManager,
|
||||
logReduction: logreduction.NewLogReduction(identicalErrorDelay),
|
||||
seccompDefault: seccompDefault,
|
||||
}
|
||||
|
||||
typedVersion, err := kubeRuntimeManager.getTypedVersion()
|
||||
|
@ -36,9 +36,9 @@ func (m *kubeGenericRuntimeManager) determineEffectiveSecurityContext(pod *v1.Po
|
||||
|
||||
// TODO: Deprecated, remove after we switch to Seccomp field
|
||||
// set SeccompProfilePath.
|
||||
synthesized.SeccompProfilePath = m.getSeccompProfilePath(pod.Annotations, container.Name, pod.Spec.SecurityContext, container.SecurityContext)
|
||||
synthesized.SeccompProfilePath = m.getSeccompProfilePath(pod.Annotations, container.Name, pod.Spec.SecurityContext, container.SecurityContext, m.seccompDefault)
|
||||
|
||||
synthesized.Seccomp = m.getSeccompProfile(pod.Annotations, container.Name, pod.Spec.SecurityContext, container.SecurityContext)
|
||||
synthesized.Seccomp = m.getSeccompProfile(pod.Annotations, container.Name, pod.Spec.SecurityContext, container.SecurityContext, m.seccompDefault)
|
||||
|
||||
// set ApparmorProfile.
|
||||
synthesized.ApparmorProfile = apparmor.GetProfileNameFromPodAnnotations(pod.Annotations, container.Name)
|
||||
|
@ -864,6 +864,11 @@ type KubeletConfiguration struct {
|
||||
// Default: true
|
||||
// +optional
|
||||
EnableDebugFlagsHandler *bool `json:"enableDebugFlagsHandler,omitempty"`
|
||||
// SeccompDefault enables the use of `RuntimeDefault` as the default seccomp profile for all workloads.
|
||||
// This requires the corresponding SeccompDefault feature gate to be enabled as well.
|
||||
// Default: false
|
||||
// +optional
|
||||
SeccompDefault *bool `json:"seccompDefault,omitempty"`
|
||||
}
|
||||
|
||||
type KubeletAuthorizationMode string
|
||||
|
@ -321,6 +321,11 @@ func (in *KubeletConfiguration) DeepCopyInto(out *KubeletConfiguration) {
|
||||
*out = new(bool)
|
||||
**out = **in
|
||||
}
|
||||
if in.SeccompDefault != nil {
|
||||
in, out := &in.SeccompDefault, &out.SeccompDefault
|
||||
*out = new(bool)
|
||||
**out = **in
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user