Add full support for setting securityContext for restic restore container

Signed-off-by: MatthieuFin <matthieu2717@gmail.com>
This commit is contained in:
MatthieuFin
2021-08-31 17:03:25 +02:00
parent 7c75cd6cf8
commit effa09a42f
4 changed files with 186 additions and 24 deletions

1
go.mod
View File

@@ -43,6 +43,7 @@ require (
k8s.io/kube-aggregator v0.19.12
sigs.k8s.io/cluster-api v0.3.11-0.20210106212952-b6c1b5b3db3d
sigs.k8s.io/controller-runtime v0.7.1-0.20201215171748-096b2e07c091
sigs.k8s.io/yaml v1.2.0 // indirect
)
replace github.com/gogo/protobuf => github.com/gogo/protobuf v1.3.2

View File

@@ -139,11 +139,11 @@ func (a *ResticRestoreAction) Execute(input *velero.RestoreItemActionExecuteInpu
)
}
runAsRoot, runAsGroup, allowPrivilegeEscalation := getSecurityContext(log, config)
runAsUser, runAsGroup, allowPrivilegeEscalation, secCtx := getSecurityContext(log, config)
securityContext, err := kube.ParseSecurityContext(runAsRoot, runAsGroup, allowPrivilegeEscalation)
securityContext, err := kube.ParseSecurityContext(runAsUser, runAsGroup, allowPrivilegeEscalation, secCtx)
if err != nil {
log.Errorf("Using default resource values, couldn't parse resource requirements: %s.", err)
log.Errorf("Using default securityContext values, couldn't parse securityContext requirements: %s.", err)
}
initContainerBuilder := newResticInitContainerBuilder(image, string(input.Restore.UID))
@@ -245,15 +245,17 @@ func getResourceLimits(log logrus.FieldLogger, config *corev1.ConfigMap) (string
return config.Data["cpuLimit"], config.Data["memLimit"]
}
// getSecurityContext extracts securityContext runAsUser, runAsGroup, and allowPrivilegeEscalation from a ConfigMap.
func getSecurityContext(log logrus.FieldLogger, config *corev1.ConfigMap) (string, string, string) {
// getSecurityContext extracts securityContext runAsUser, runAsGroup, allowPrivilegeEscalation, and securityContext from a ConfigMap.
func getSecurityContext(log logrus.FieldLogger, config *corev1.ConfigMap) (string, string, string, string) {
if config == nil {
log.Debug("No config found for plugin")
return "", "", ""
return "", "", "", ""
}
return config.Data["secCtxRunAsUser"], config.Data["secCtxRunAsGroup"], config.Data["secCtxAllowPrivilegeEscalation"]
return config.Data["secCtxRunAsUser"],
config.Data["secCtxRunAsGroup"],
config.Data["secCtxAllowPrivilegeEscalation"],
config.Data["secCtx"]
}
// TODO eventually this can move to pkg/plugin/framework since it'll be used across multiple

View File

@@ -21,9 +21,10 @@ import (
"github.com/pkg/errors"
corev1 "k8s.io/api/core/v1"
"sigs.k8s.io/yaml"
)
func ParseSecurityContext(runAsUser string, runAsGroup string, allowPrivilegeEscalation string) (corev1.SecurityContext, error) {
func ParseSecurityContext(runAsUser string, runAsGroup string, allowPrivilegeEscalation string, secCtx string) (corev1.SecurityContext, error) {
securityContext := corev1.SecurityContext{}
if runAsUser != "" {
@@ -53,5 +54,12 @@ func ParseSecurityContext(runAsUser string, runAsGroup string, allowPrivilegeEsc
securityContext.AllowPrivilegeEscalation = &parsedAllowPrivilegeEscalation
}
if secCtx != "" {
err := yaml.UnmarshalStrict([]byte(secCtx), &securityContext)
if err != nil {
return securityContext, errors.WithStack(errors.Errorf(`Security context secCtx error: "%s"`, err))
}
}
return securityContext, nil
}

View File

@@ -30,6 +30,7 @@ func TestParseSecurityContext(t *testing.T) {
runAsUser string
runAsGroup string
allowPrivilegeEscalation string
secCtx string
}
tests := []struct {
name string
@@ -37,33 +38,184 @@ func TestParseSecurityContext(t *testing.T) {
wantErr bool
expected *corev1.SecurityContext
}{
{"valid security context", args{"1001", "999", "true"}, false, &corev1.SecurityContext{
RunAsUser: pointInt64(1001),
RunAsGroup: pointInt64(999),
AllowPrivilegeEscalation: boolptr.True(),
}},
{
"valid security context",
args{"1001", "999", "true", ``},
false,
&corev1.SecurityContext{
RunAsUser: pointInt64(1001),
RunAsGroup: pointInt64(999),
AllowPrivilegeEscalation: boolptr.True(),
},
},
{
"valid security context with override runAsUser",
args{"1001", "999", "true", `runAsUser: 2000`},
false,
&corev1.SecurityContext{
RunAsUser: pointInt64(2000),
RunAsGroup: pointInt64(999),
AllowPrivilegeEscalation: boolptr.True(),
},
},
{
"valid securityContext with comments only secCtx key",
args{"", "", "",`
capabilities:
drop:
- ALL
add:
- cap1
- cap2
sELinuxOptions:
user: userLabel
role: roleLabel
type: typeLabel
level: levelLabel
# user www-data
runAsUser: 3333
# group www-data
runAsGroup: 3333
runAsNonRoot: true
readOnlyRootFilesystem: true
allowPrivilegeEscalation: false`},
false,
&corev1.SecurityContext{
RunAsUser: pointInt64(3333),
RunAsGroup: pointInt64(3333),
Capabilities: &corev1.Capabilities{
Drop: []corev1.Capability{"ALL"},
Add: []corev1.Capability{"cap1", "cap2"},
},
SELinuxOptions: &corev1.SELinuxOptions{
User: "userLabel",
Role: "roleLabel",
Type: "typeLabel",
Level: "levelLabel",
},
RunAsNonRoot: boolptr.True(),
ReadOnlyRootFilesystem: boolptr.True(),
AllowPrivilegeEscalation: boolptr.False(),
},
},
{
"valid securityContext with secCtx key override runAsUser runAsGroup and allowPrivilegeEscalation",
args{"1001", "999", "true",`
capabilities:
drop:
- ALL
add:
- cap1
- cap2
sELinuxOptions:
user: userLabel
role: roleLabel
type: typeLabel
level: levelLabel
# user www-data
runAsUser: 3333
# group www-data
runAsGroup: 3333
runAsNonRoot: true
readOnlyRootFilesystem: true
allowPrivilegeEscalation: false`},
false,
&corev1.SecurityContext{
RunAsUser: pointInt64(3333),
RunAsGroup: pointInt64(3333),
Capabilities: &corev1.Capabilities{
Drop: []corev1.Capability{"ALL"},
Add: []corev1.Capability{"cap1", "cap2"},
},
SELinuxOptions: &corev1.SELinuxOptions{
User: "userLabel",
Role: "roleLabel",
Type: "typeLabel",
Level: "levelLabel",
},
RunAsNonRoot: boolptr.True(),
ReadOnlyRootFilesystem: boolptr.True(),
AllowPrivilegeEscalation: boolptr.False(),
},
},
{
"another valid security context",
args{"1001", "999", "false"}, false, &corev1.SecurityContext{
args{"1001", "999", "false", ""},
false,
&corev1.SecurityContext{
RunAsUser: pointInt64(1001),
RunAsGroup: pointInt64(999),
AllowPrivilegeEscalation: boolptr.False(),
},
},
{"security context without runAsGroup", args{"1001", "", ""}, false, &corev1.SecurityContext{
{"security context without runAsGroup", args{"1001", "", "", ""}, false, &corev1.SecurityContext{
RunAsUser: pointInt64(1001),
}},
{"security context without runAsUser", args{"", "999", ""}, false, &corev1.SecurityContext{
{"security context without runAsUser", args{"", "999", "", ""}, false, &corev1.SecurityContext{
RunAsGroup: pointInt64(999),
}},
{"empty context without runAsUser", args{"", "", ""}, false, &corev1.SecurityContext{}},
{"invalid security context runAsUser", args{"not a number", "", ""}, true, nil},
{"invalid security context runAsGroup", args{"", "not a number", ""}, true, nil},
{"invalid security context allowPrivilegeEscalation", args{"", "", "not a bool"}, true, nil},
{"empty context without runAsUser", args{"", "", "", ""}, false, &corev1.SecurityContext{}},
{
"invalid securityContext secCtx unknown key",
args{"", "", "",`
capabilitiesUnknownkey:
drop:
- ALL
add:
- cap1
- cap2
# user www-data
runAsUser: 3333
# group www-data
runAsGroup: 3333
runAsNonRoot: true
readOnlyRootFilesystem: true
allowPrivilegeEscalation: false`},
true, nil,
},
{
"invalid securityContext secCtx wrong value type string instead of bool",
args{"", "", "",`
capabilitiesUnknownkey:
drop:
- ALL
add:
- cap1
- cap2
# user www-data
runAsUser: 3333
# group www-data
runAsGroup: 3333
runAsNonRoot: plop
readOnlyRootFilesystem: true
allowPrivilegeEscalation: false`},
true, nil,
},
{
"invalid securityContext secCtx wrong value type string instead of int",
args{"", "", "",`
capabilitiesUnknownkey:
drop:
- ALL
add:
- cap1
- cap2
# user www-data
runAsUser: plop
# group www-data
runAsGroup: 3333
runAsNonRoot: true
readOnlyRootFilesystem: true
allowPrivilegeEscalation: false`},
true, nil,
},
{"invalid security context runAsUser", args{"not a number", "", "", ""}, true, nil},
{"invalid security context runAsGroup", args{"", "not a number", "", ""}, true, nil},
{"invalid security context allowPrivilegeEscalation", args{"", "", "not a bool", ""}, true, nil},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := ParseSecurityContext(tt.args.runAsUser, tt.args.runAsGroup, tt.args.allowPrivilegeEscalation)
got, err := ParseSecurityContext(tt.args.runAsUser, tt.args.runAsGroup, tt.args.allowPrivilegeEscalation, tt.args.secCtx)
if tt.wantErr {
assert.Error(t, err)
return
@@ -74,8 +226,7 @@ func TestParseSecurityContext(t *testing.T) {
tt.expected = &corev1.SecurityContext{}
}
assert.Equal(t, tt.expected.RunAsUser, got.RunAsUser)
assert.Equal(t, tt.expected.RunAsGroup, got.RunAsGroup)
assert.Equal(t, *tt.expected, got)
})
}
}