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

feat: add support for security context #87

Merged
merged 2 commits into from
Mar 15, 2023
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion examples/app/templates/daemonset.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ spec:
containers:
- env:
- name: KUBERNETES_CLUSTER_DOMAIN
value: {{ .Values.kubernetesClusterDomain }}
value: {{ quote .Values.kubernetesClusterDomain }}
image: {{ .Values.fluentdElasticsearch.fluentdElasticsearch.image.repository }}:{{
.Values.fluentdElasticsearch.fluentdElasticsearch.image.tag | default .Chart.AppVersion
}}
Expand Down
8 changes: 4 additions & 4 deletions examples/app/templates/deployment.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ spec:
key: VAR2
name: {{ include "app.fullname" . }}-my-secret-vars
- name: KUBERNETES_CLUSTER_DOMAIN
value: {{ .Values.kubernetesClusterDomain }}
value: {{ quote .Values.kubernetesClusterDomain }}
image: {{ .Values.myapp.app.image.repository }}:{{ .Values.myapp.app.image.tag
| default .Chart.AppVersion }}
livenessProbe:
Expand All @@ -53,8 +53,8 @@ spec:
initialDelaySeconds: 5
periodSeconds: 10
resources: {{- toYaml .Values.myapp.app.resources | nindent 10 }}
securityContext:
allowPrivilegeEscalation: false
securityContext: {{- toYaml .Values.myapp.app.containerSecurityContext | nindent
8 }}
volumeMounts:
- mountPath: /my_config.yaml
name: manager-config
Expand All @@ -73,7 +73,7 @@ spec:
- --v=10
env:
- name: KUBERNETES_CLUSTER_DOMAIN
value: {{ .Values.kubernetesClusterDomain }}
value: {{ quote .Values.kubernetesClusterDomain }}
image: {{ .Values.myapp.proxySidecar.image.repository }}:{{ .Values.myapp.proxySidecar.image.tag
| default .Chart.AppVersion }}
name: proxy-sidecar
Expand Down
2 changes: 2 additions & 0 deletions examples/app/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ mySecretVars:
var2: ""
myapp:
app:
containerSecurityContext:
allowPrivilegeEscalation: false
image:
repository: controller
tag: latest
Expand Down
12 changes: 6 additions & 6 deletions examples/operator/templates/deployment.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ spec:
- --v=10
env:
- name: KUBERNETES_CLUSTER_DOMAIN
value: {{ .Values.kubernetesClusterDomain }}
value: {{ quote .Values.kubernetesClusterDomain }}
image: {{ .Values.controllerManager.kubeRbacProxy.image.repository }}:{{ .Values.controllerManager.kubeRbacProxy.image.tag
| default .Chart.AppVersion }}
name: kube-rbac-proxy
Expand All @@ -53,9 +53,9 @@ spec:
key: VAR1
name: {{ include "operator.fullname" . }}-secret-vars
- name: VAR2
value: {{ .Values.controllerManager.manager.env.var2 }}
value: {{ quote .Values.controllerManager.manager.env.var2 }}
- name: VAR3_MY_ENV
value: {{ .Values.controllerManager.manager.env.var3MyEnv }}
value: {{ quote .Values.controllerManager.manager.env.var3MyEnv }}
- name: VAR4
valueFrom:
configMapKeyRef:
Expand All @@ -71,7 +71,7 @@ spec:
divisor: "0"
resource: limits.cpu
- name: KUBERNETES_CLUSTER_DOMAIN
value: {{ .Values.kubernetesClusterDomain }}
value: {{ quote .Values.kubernetesClusterDomain }}
image: {{ .Values.controllerManager.manager.image.repository }}:{{ .Values.controllerManager.manager.image.tag
| default .Chart.AppVersion }}
imagePullPolicy: {{ .Values.controllerManager.manager.imagePullPolicy }}
Expand All @@ -90,8 +90,8 @@ spec:
periodSeconds: 10
resources: {{- toYaml .Values.controllerManager.manager.resources | nindent 10
}}
securityContext:
allowPrivilegeEscalation: false
securityContext: {{- toYaml .Values.controllerManager.manager.containerSecurityContext
| nindent 8 }}
volumeMounts:
- mountPath: /controller_manager_config.yaml
name: manager-config
Expand Down
11 changes: 11 additions & 0 deletions examples/operator/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,17 @@ controllerManager:
repository: gcr.io/kubebuilder/kube-rbac-proxy
tag: v0.8.0
manager:
containerSecurityContext:
allowPrivilegeEscalation: false
capabilities:
drop:
- ALL
privileged: false
readOnlyRootFilesystem: true
runAsNonRoot: true
runAsUser: 65532
seccompProfile:
type: RuntimeDefault
env:
var2: ciao
var3MyEnv: ciao
Expand Down
4 changes: 4 additions & 0 deletions pkg/processor/deployment/deployment.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"github.com/arttor/helmify/pkg/cluster"
"github.com/arttor/helmify/pkg/processor"
"github.com/arttor/helmify/pkg/processor/imagePullSecrets"
securityContext "github.com/arttor/helmify/pkg/processor/security-context"

"github.com/arttor/helmify/pkg/helmify"
yamlformat "github.com/arttor/helmify/pkg/yaml"
Expand Down Expand Up @@ -163,6 +164,8 @@ func (d deployment) Process(appMeta helmify.AppMetadata, obj *unstructured.Unstr
imagePullSecrets.ProcessSpecMap(specMap, &values)
}

securityContext.ProcessContainerSecurityContext(nameCamel, specMap, &values)

// process nodeSelector if presented:
if len(depl.Spec.Template.Spec.NodeSelector) != 0 {
err = unstructured.SetNestedField(specMap, fmt.Sprintf(`{{- toYaml .Values.%s.nodeSelector | nindent 8 }}`, nameCamel), "nodeSelector")
Expand All @@ -179,6 +182,7 @@ func (d deployment) Process(appMeta helmify.AppMetadata, obj *unstructured.Unstr
if err != nil {
return true, nil, err
}

spec = strings.ReplaceAll(spec, "'", "")

return true, &result{
Expand Down
1 change: 0 additions & 1 deletion pkg/processor/imagePullSecrets/imagePullSecrets.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,4 @@ func ProcessSpecMap(specMap map[string]interface{}, values *helmify.Values) {
specMap["imagePullSecrets"] = helmExpression
(*values)["imagePullSecrets"] = []string{}
}

}
40 changes: 40 additions & 0 deletions pkg/processor/security-context/container_security_context.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package security_context

import (
"fmt"

"github.com/arttor/helmify/pkg/helmify"
"github.com/iancoleman/strcase"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
)

const (
sc = "securityContext"
cscValueName = "containerSecurityContext"
helmTemplate = "{{- toYaml .Values.%[1]s.%[2]s.containerSecurityContext | nindent 8 }}"
)

// ProcessContainerSecurityContext adds 'securityContext' to the podSpec in specMap, if it doesn't have one already defined.
func ProcessContainerSecurityContext(nameCamel string, specMap map[string]interface{}, values *helmify.Values) {
if _, defined := specMap["containers"]; defined {
containers, _, _ := unstructured.NestedSlice(specMap, "containers")
for _, container := range containers {
castedContainer := container.(map[string]interface{})
containerName := strcase.ToLowerCamel(castedContainer["name"].(string))
if _, defined2 := castedContainer["securityContext"]; defined2 {
setSecContextValue(nameCamel, containerName, castedContainer, values)
}
}
unstructured.SetNestedSlice(specMap, containers, "containers")
}
}

func setSecContextValue(resourceName string, containerName string, castedContainer map[string]interface{}, values *helmify.Values) {
if castedContainer["securityContext"] != nil {
unstructured.SetNestedField(*values, castedContainer["securityContext"], resourceName, containerName, cscValueName)

valueString := fmt.Sprintf(helmTemplate, resourceName, containerName)

unstructured.SetNestedField(castedContainer, valueString, sc)
}
}
147 changes: 147 additions & 0 deletions pkg/processor/security-context/container_security_context_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
package security_context

import (
"testing"

"github.com/arttor/helmify/pkg/helmify"
"github.com/stretchr/testify/assert"
)

func TestProcessContainerSecurityContext(t *testing.T) {
type args struct {
nameCamel string
specMap map[string]interface{}
values *helmify.Values
}
tests := []struct {
name string
args args
want *helmify.Values
}{
{
name: "test with empty specMap",
args: args{
nameCamel: "someResourceName",
specMap: map[string]interface{}{},
values: &helmify.Values{},
},
want: &helmify.Values{},
},
{
name: "test with single container",
args: args{
nameCamel: "someResourceName",
specMap: map[string]interface{}{
"containers": []interface{}{
map[string]interface{}{
"name": "SomeContainerName",
"securityContext": map[string]interface{}{
"privileged": true,
},
},
},
},
values: &helmify.Values{},
},
want: &helmify.Values{
"someResourceName": map[string]interface{}{
"someContainerName": map[string]interface{}{
"containerSecurityContext": map[string]interface{}{
"privileged": true,
},
},
},
},
},
{
name: "test with multiple containers",
args: args{
nameCamel: "someResourceName",
specMap: map[string]interface{}{
"containers": []interface{}{
map[string]interface{}{
"name": "FirstContainer",
"securityContext": map[string]interface{}{
"privileged": true,
},
},
map[string]interface{}{
"name": "SecondContainer",
"securityContext": map[string]interface{}{
"allowPrivilegeEscalation": true,
},
},
},
},
values: &helmify.Values{},
},
want: &helmify.Values{
"someResourceName": map[string]interface{}{
"firstContainer": map[string]interface{}{
"containerSecurityContext": map[string]interface{}{
"privileged": true,
},
},
"secondContainer": map[string]interface{}{
"containerSecurityContext": map[string]interface{}{
"allowPrivilegeEscalation": true,
},
},
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
ProcessContainerSecurityContext(tt.args.nameCamel, tt.args.specMap, tt.args.values)
assert.Equal(t, tt.want, tt.args.values)
})
}
}

func Test_setSecContextValue(t *testing.T) {
type args struct {
resourceName string
containerName string
castedContainer map[string]interface{}
values *helmify.Values
fieldName string
useRenderedHelmTemplate bool
}
tests := []struct {
name string
args args
want *helmify.Values
}{
{
name: "simple test with single container and single value",
args: args{
resourceName: "someResource",
containerName: "someContainer",
castedContainer: map[string]interface{}{
"securityContext": map[string]interface{}{
"someField": "someValue",
},
},
values: &helmify.Values{},
fieldName: "someField",
useRenderedHelmTemplate: false,
},
want: &helmify.Values{
"someResource": map[string]interface{}{
"someContainer": map[string]interface{}{
"containerSecurityContext": map[string]interface{}{
"someField": "someValue",
},
},
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
setSecContextValue(tt.args.resourceName, tt.args.containerName, tt.args.castedContainer, tt.args.values)
assert.Equal(t, tt.want, tt.args.values)
})
}
}
9 changes: 9 additions & 0 deletions test_data/k8s-operator-kustomize.output
Original file line number Diff line number Diff line change
Expand Up @@ -665,6 +665,15 @@ spec:
memory: 20Mi
securityContext:
allowPrivilegeEscalation: false
capabilities:
drop:
- ALL
privileged: false
readOnlyRootFilesystem: true
runAsNonRoot: true
runAsUser: 65532
seccompProfile:
type: RuntimeDefault
securityContext:
runAsNonRoot: true
nodeSelector:
Expand Down