Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[1.26] Automated cherry pick of #117020: Return error for localhost seccomp type with no localhost #117116

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
60 changes: 34 additions & 26 deletions pkg/kubelet/kuberuntime/helpers.go
Expand Up @@ -211,32 +211,36 @@ func toKubeRuntimeStatus(status *runtimeapi.RuntimeStatus) *kubecontainer.Runtim
return &kubecontainer.RuntimeStatus{Conditions: conditions}
}

func fieldProfile(scmp *v1.SeccompProfile, profileRootPath string, fallbackToRuntimeDefault bool) string {
func fieldProfile(scmp *v1.SeccompProfile, profileRootPath string, fallbackToRuntimeDefault bool) (string, error) {
if scmp == nil {
if fallbackToRuntimeDefault {
return v1.SeccompProfileRuntimeDefault
return v1.SeccompProfileRuntimeDefault, nil
}
return ""
return "", nil
}
if scmp.Type == v1.SeccompProfileTypeRuntimeDefault {
return v1.SeccompProfileRuntimeDefault
return v1.SeccompProfileRuntimeDefault, nil
}
if scmp.Type == v1.SeccompProfileTypeLocalhost && scmp.LocalhostProfile != nil && len(*scmp.LocalhostProfile) > 0 {
fname := filepath.Join(profileRootPath, *scmp.LocalhostProfile)
return v1.SeccompLocalhostProfileNamePrefix + fname
if scmp.Type == v1.SeccompProfileTypeLocalhost {
if scmp.LocalhostProfile != nil && len(*scmp.LocalhostProfile) > 0 {
fname := filepath.Join(profileRootPath, *scmp.LocalhostProfile)
return v1.SeccompLocalhostProfileNamePrefix + fname, nil
} else {
return "", fmt.Errorf("localhostProfile must be set if seccompProfile type is Localhost.")
}
}
if scmp.Type == v1.SeccompProfileTypeUnconfined {
return v1.SeccompProfileNameUnconfined
return v1.SeccompProfileNameUnconfined, nil
}

if fallbackToRuntimeDefault {
return v1.SeccompProfileRuntimeDefault
return v1.SeccompProfileRuntimeDefault, nil
}
return ""
return "", nil
}

func (m *kubeGenericRuntimeManager) getSeccompProfilePath(annotations map[string]string, containerName string,
podSecContext *v1.PodSecurityContext, containerSecContext *v1.SecurityContext, fallbackToRuntimeDefault bool) string {
podSecContext *v1.PodSecurityContext, containerSecContext *v1.SecurityContext, fallbackToRuntimeDefault bool) (string, error) {
// container fields are applied first
if containerSecContext != nil && containerSecContext.SeccompProfile != nil {
return fieldProfile(containerSecContext.SeccompProfile, m.seccompProfileRoot, fallbackToRuntimeDefault)
Expand All @@ -248,42 +252,46 @@ func (m *kubeGenericRuntimeManager) getSeccompProfilePath(annotations map[string
}

if fallbackToRuntimeDefault {
return v1.SeccompProfileRuntimeDefault
return v1.SeccompProfileRuntimeDefault, nil
}

return ""
return "", nil
}

func fieldSeccompProfile(scmp *v1.SeccompProfile, profileRootPath string, fallbackToRuntimeDefault bool) *runtimeapi.SecurityProfile {
func fieldSeccompProfile(scmp *v1.SeccompProfile, profileRootPath string, fallbackToRuntimeDefault bool) (*runtimeapi.SecurityProfile, error) {
if scmp == nil {
if fallbackToRuntimeDefault {
return &runtimeapi.SecurityProfile{
ProfileType: runtimeapi.SecurityProfile_RuntimeDefault,
}
}, nil
}
return &runtimeapi.SecurityProfile{
ProfileType: runtimeapi.SecurityProfile_Unconfined,
}
}, nil
}
if scmp.Type == v1.SeccompProfileTypeRuntimeDefault {
return &runtimeapi.SecurityProfile{
ProfileType: runtimeapi.SecurityProfile_RuntimeDefault,
}
}, nil
}
if scmp.Type == v1.SeccompProfileTypeLocalhost && scmp.LocalhostProfile != nil && len(*scmp.LocalhostProfile) > 0 {
fname := filepath.Join(profileRootPath, *scmp.LocalhostProfile)
return &runtimeapi.SecurityProfile{
ProfileType: runtimeapi.SecurityProfile_Localhost,
LocalhostRef: fname,
if scmp.Type == v1.SeccompProfileTypeLocalhost {
if scmp.LocalhostProfile != nil && len(*scmp.LocalhostProfile) > 0 {
fname := filepath.Join(profileRootPath, *scmp.LocalhostProfile)
return &runtimeapi.SecurityProfile{
ProfileType: runtimeapi.SecurityProfile_Localhost,
LocalhostRef: fname,
}, nil
} else {
return nil, fmt.Errorf("localhostProfile must be set if seccompProfile type is Localhost.")
}
}
return &runtimeapi.SecurityProfile{
ProfileType: runtimeapi.SecurityProfile_Unconfined,
}
}, nil
}

func (m *kubeGenericRuntimeManager) getSeccompProfile(annotations map[string]string, containerName string,
podSecContext *v1.PodSecurityContext, containerSecContext *v1.SecurityContext, fallbackToRuntimeDefault bool) *runtimeapi.SecurityProfile {
podSecContext *v1.PodSecurityContext, containerSecContext *v1.SecurityContext, fallbackToRuntimeDefault bool) (*runtimeapi.SecurityProfile, error) {
// container fields are applied first
if containerSecContext != nil && containerSecContext.SeccompProfile != nil {
return fieldSeccompProfile(containerSecContext.SeccompProfile, m.seccompProfileRoot, fallbackToRuntimeDefault)
Expand All @@ -297,10 +305,10 @@ func (m *kubeGenericRuntimeManager) getSeccompProfile(annotations map[string]str
if fallbackToRuntimeDefault {
return &runtimeapi.SecurityProfile{
ProfileType: runtimeapi.SecurityProfile_RuntimeDefault,
}
}, nil
}

return &runtimeapi.SecurityProfile{
ProfileType: runtimeapi.SecurityProfile_Unconfined,
}
}, nil
}
116 changes: 76 additions & 40 deletions pkg/kubelet/kuberuntime/helpers_linux_test.go
Expand Up @@ -223,17 +223,18 @@ func TestFieldProfile(t *testing.T) {
scmpProfile *v1.SeccompProfile
rootPath string
expectedProfile string
expectedError string
}{
{
description: "no seccompProfile should return empty",
expectedProfile: "",
},
{
description: "type localhost without profile should return empty",
description: "type localhost without profile should return error",
scmpProfile: &v1.SeccompProfile{
Type: v1.SeccompProfileTypeLocalhost,
},
expectedProfile: "",
expectedError: "localhostProfile must be set if seccompProfile type is Localhost.",
},
{
description: "unknown type should return empty",
Expand Down Expand Up @@ -268,8 +269,13 @@ func TestFieldProfile(t *testing.T) {
}

for i, test := range tests {
seccompProfile := fieldProfile(test.scmpProfile, test.rootPath, false)
assert.Equal(t, test.expectedProfile, seccompProfile, "TestCase[%d]: %s", i, test.description)
seccompProfile, err := fieldProfile(test.scmpProfile, test.rootPath, false)
if test.expectedError != "" {
assert.EqualError(t, err, test.expectedError, "TestCase[%d]: %s", i, test.description)
} else {
assert.NoError(t, err, "TestCase[%d]: %s", i, test.description)
assert.Equal(t, test.expectedProfile, seccompProfile, "TestCase[%d]: %s", i, test.description)
}
}
}

Expand All @@ -279,17 +285,18 @@ func TestFieldProfileDefaultSeccomp(t *testing.T) {
scmpProfile *v1.SeccompProfile
rootPath string
expectedProfile string
expectedError string
}{
{
description: "no seccompProfile should return runtime/default",
expectedProfile: v1.SeccompProfileRuntimeDefault,
},
{
description: "type localhost without profile should return runtime/default",
description: "type localhost without profile should return error",
scmpProfile: &v1.SeccompProfile{
Type: v1.SeccompProfileTypeLocalhost,
},
expectedProfile: v1.SeccompProfileRuntimeDefault,
expectedError: "localhostProfile must be set if seccompProfile type is Localhost.",
},
{
description: "unknown type should return runtime/default",
Expand Down Expand Up @@ -324,8 +331,13 @@ func TestFieldProfileDefaultSeccomp(t *testing.T) {
}

for i, test := range tests {
seccompProfile := fieldProfile(test.scmpProfile, test.rootPath, true)
assert.Equal(t, test.expectedProfile, seccompProfile, "TestCase[%d]: %s", i, test.description)
seccompProfile, err := fieldProfile(test.scmpProfile, test.rootPath, true)
if test.expectedError != "" {
assert.EqualError(t, err, test.expectedError, "TestCase[%d]: %s", i, test.description)
} else {
assert.NoError(t, err, "TestCase[%d]: %s", i, test.description)
assert.Equal(t, test.expectedProfile, seccompProfile, "TestCase[%d]: %s", i, test.description)
}
}
}

Expand All @@ -340,6 +352,7 @@ func TestGetSeccompProfilePath(t *testing.T) {
containerSc *v1.SecurityContext
containerName string
expectedProfile string
expectedError string
}{
{
description: "no seccomp should return empty",
Expand Down Expand Up @@ -376,14 +389,14 @@ func TestGetSeccompProfilePath(t *testing.T) {
expectedProfile: seccompLocalhostPath("filename"),
},
{
description: "pod seccomp profile set to SeccompProfileTypeLocalhost with empty LocalhostProfile returns empty",
podSc: &v1.PodSecurityContext{SeccompProfile: &v1.SeccompProfile{Type: v1.SeccompProfileTypeLocalhost}},
expectedProfile: "",
description: "pod seccomp profile set to SeccompProfileTypeLocalhost with empty LocalhostProfile returns error",
podSc: &v1.PodSecurityContext{SeccompProfile: &v1.SeccompProfile{Type: v1.SeccompProfileTypeLocalhost}},
expectedError: "localhostProfile must be set if seccompProfile type is Localhost.",
},
{
description: "container seccomp profile set to SeccompProfileTypeLocalhost with empty LocalhostProfile returns empty",
containerSc: &v1.SecurityContext{SeccompProfile: &v1.SeccompProfile{Type: v1.SeccompProfileTypeLocalhost}},
expectedProfile: "",
description: "container seccomp profile set to SeccompProfileTypeLocalhost with empty LocalhostProfile returns error",
containerSc: &v1.SecurityContext{SeccompProfile: &v1.SeccompProfile{Type: v1.SeccompProfileTypeLocalhost}},
expectedError: "localhostProfile must be set if seccompProfile type is Localhost.",
},
{
description: "container seccomp profile set to SeccompProfileTypeLocalhost returns 'localhost/' + LocalhostProfile",
Expand All @@ -399,8 +412,13 @@ func TestGetSeccompProfilePath(t *testing.T) {
}

for i, test := range tests {
seccompProfile := m.getSeccompProfilePath(test.annotation, test.containerName, test.podSc, test.containerSc, false)
assert.Equal(t, test.expectedProfile, seccompProfile, "TestCase[%d]: %s", i, test.description)
seccompProfile, err := m.getSeccompProfilePath(test.annotation, test.containerName, test.podSc, test.containerSc, false)
if test.expectedError != "" {
assert.EqualError(t, err, test.expectedError, "TestCase[%d]: %s", i, test.description)
} else {
assert.NoError(t, err, "TestCase[%d]: %s", i, test.description)
assert.Equal(t, test.expectedProfile, seccompProfile, "TestCase[%d]: %s", i, test.description)
}
}
}

Expand All @@ -415,6 +433,7 @@ func TestGetSeccompProfilePathDefaultSeccomp(t *testing.T) {
containerSc *v1.SecurityContext
containerName string
expectedProfile string
expectedError string
}{
{
description: "no seccomp should return runtime/default",
Expand Down Expand Up @@ -451,14 +470,14 @@ func TestGetSeccompProfilePathDefaultSeccomp(t *testing.T) {
expectedProfile: seccompLocalhostPath("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: "pod seccomp profile set to SeccompProfileTypeLocalhost with empty LocalhostProfile returns error",
podSc: &v1.PodSecurityContext{SeccompProfile: &v1.SeccompProfile{Type: v1.SeccompProfileTypeLocalhost}},
expectedError: "localhostProfile must be set if seccompProfile type is Localhost.",
},
{
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 with empty LocalhostProfile returns error",
containerSc: &v1.SecurityContext{SeccompProfile: &v1.SeccompProfile{Type: v1.SeccompProfileTypeLocalhost}},
expectedError: "localhostProfile must be set if seccompProfile type is Localhost.",
},
{
description: "container seccomp profile set to SeccompProfileTypeLocalhost returns 'localhost/' + LocalhostProfile",
Expand All @@ -474,8 +493,13 @@ func TestGetSeccompProfilePathDefaultSeccomp(t *testing.T) {
}

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)
seccompProfile, err := m.getSeccompProfilePath(test.annotation, test.containerName, test.podSc, test.containerSc, true)
if test.expectedError != "" {
assert.EqualError(t, err, test.expectedError, "TestCase[%d]: %s", i, test.description)
} else {
assert.NoError(t, err, "TestCase[%d]: %s", i, test.description)
assert.Equal(t, test.expectedProfile, seccompProfile, "TestCase[%d]: %s", i, test.description)
}
}
}

Expand All @@ -498,6 +522,7 @@ func TestGetSeccompProfile(t *testing.T) {
containerSc *v1.SecurityContext
containerName string
expectedProfile *runtimeapi.SecurityProfile
expectedError string
}{
{
description: "no seccomp should return unconfined",
Expand Down Expand Up @@ -532,14 +557,14 @@ func TestGetSeccompProfile(t *testing.T) {
},
},
{
description: "pod seccomp profile set to SeccompProfileTypeLocalhost with empty LocalhostProfile returns unconfined",
podSc: &v1.PodSecurityContext{SeccompProfile: &v1.SeccompProfile{Type: v1.SeccompProfileTypeLocalhost}},
expectedProfile: unconfinedProfile,
description: "pod seccomp profile set to SeccompProfileTypeLocalhost with empty LocalhostProfile returns error",
podSc: &v1.PodSecurityContext{SeccompProfile: &v1.SeccompProfile{Type: v1.SeccompProfileTypeLocalhost}},
expectedError: "localhostProfile must be set if seccompProfile type is Localhost.",
},
{
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 with empty LocalhostProfile returns error",
containerSc: &v1.SecurityContext{SeccompProfile: &v1.SeccompProfile{Type: v1.SeccompProfileTypeLocalhost}},
expectedError: "localhostProfile must be set if seccompProfile type is Localhost.",
},
{
description: "container seccomp profile set to SeccompProfileTypeLocalhost returns 'localhost/' + LocalhostProfile",
Expand Down Expand Up @@ -568,8 +593,13 @@ func TestGetSeccompProfile(t *testing.T) {
}

for i, test := range tests {
seccompProfile := m.getSeccompProfile(test.annotation, test.containerName, test.podSc, test.containerSc, false)
assert.Equal(t, test.expectedProfile, seccompProfile, "TestCase[%d]: %s", i, test.description)
seccompProfile, err := m.getSeccompProfile(test.annotation, test.containerName, test.podSc, test.containerSc, false)
if test.expectedError != "" {
assert.EqualError(t, err, test.expectedError, "TestCase[%d]: %s", i, test.description)
} else {
assert.NoError(t, err, "TestCase[%d]: %s", i, test.description)
assert.Equal(t, test.expectedProfile, seccompProfile, "TestCase[%d]: %s", i, test.description)
}
}
}

Expand All @@ -592,6 +622,7 @@ func TestGetSeccompProfileDefaultSeccomp(t *testing.T) {
containerSc *v1.SecurityContext
containerName string
expectedProfile *runtimeapi.SecurityProfile
expectedError string
}{
{
description: "no seccomp should return RuntimeDefault",
Expand Down Expand Up @@ -626,14 +657,14 @@ func TestGetSeccompProfileDefaultSeccomp(t *testing.T) {
},
},
{
description: "pod seccomp profile set to SeccompProfileTypeLocalhost with empty LocalhostProfile returns unconfined",
podSc: &v1.PodSecurityContext{SeccompProfile: &v1.SeccompProfile{Type: v1.SeccompProfileTypeLocalhost}},
expectedProfile: unconfinedProfile,
description: "pod seccomp profile set to SeccompProfileTypeLocalhost with empty LocalhostProfile returns error",
podSc: &v1.PodSecurityContext{SeccompProfile: &v1.SeccompProfile{Type: v1.SeccompProfileTypeLocalhost}},
expectedError: "localhostProfile must be set if seccompProfile type is Localhost.",
},
{
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 with empty LocalhostProfile returns error",
containerSc: &v1.SecurityContext{SeccompProfile: &v1.SeccompProfile{Type: v1.SeccompProfileTypeLocalhost}},
expectedError: "localhostProfile must be set if seccompProfile type is Localhost.",
},
{
description: "container seccomp profile set to SeccompProfileTypeLocalhost returns 'localhost/' + LocalhostProfile",
Expand Down Expand Up @@ -662,8 +693,13 @@ func TestGetSeccompProfileDefaultSeccomp(t *testing.T) {
}

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)
seccompProfile, err := m.getSeccompProfile(test.annotation, test.containerName, test.podSc, test.containerSc, true)
if test.expectedError != "" {
assert.EqualError(t, err, test.expectedError, "TestCase[%d]: %s", i, test.description)
} else {
assert.NoError(t, err, "TestCase[%d]: %s", i, test.description)
assert.Equal(t, test.expectedProfile, seccompProfile, "TestCase[%d]: %s", i, test.description)
}
}
}

Expand Down
11 changes: 9 additions & 2 deletions pkg/kubelet/kuberuntime/security_context.go
Expand Up @@ -37,9 +37,16 @@ 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, m.seccompDefault)
var err error
synthesized.SeccompProfilePath, err = m.getSeccompProfilePath(pod.Annotations, container.Name, pod.Spec.SecurityContext, container.SecurityContext, m.seccompDefault)
if err != nil {
return nil, err
}

synthesized.Seccomp = m.getSeccompProfile(pod.Annotations, container.Name, pod.Spec.SecurityContext, container.SecurityContext, m.seccompDefault)
synthesized.Seccomp, err = m.getSeccompProfile(pod.Annotations, container.Name, pod.Spec.SecurityContext, container.SecurityContext, m.seccompDefault)
if err != nil {
return nil, err
}

// set ApparmorProfile.
synthesized.ApparmorProfile = apparmor.GetProfileNameFromPodAnnotations(pod.Annotations, container.Name)
Expand Down