diff --git a/changelog/v0.40.5/globalFloatingUserId_in_delpoyment_templates.yaml b/changelog/v0.40.5/globalFloatingUserId_in_delpoyment_templates.yaml new file mode 100644 index 000000000..3225f920f --- /dev/null +++ b/changelog/v0.40.5/globalFloatingUserId_in_delpoyment_templates.yaml @@ -0,0 +1,9 @@ +changelog: + - type: NEW_FEATURE + issueLink: https://github.com/solo-io/gloo/issues/5034 + resolvesIssue: false + description: | + Adds the ability to render deployments templates with a reference global floatingUserId field. This field is used to globally unset + the runAsUser field in container securityContexts (like the painter's floatingUserId) and supresses the rendering of the + pod's securityContext. This feature is enabled by setting the GlobalFloatingUserIdPath in the Operator to the path of the global field, + and defaults to an empty string. \ No newline at end of file diff --git a/codegen/cmd_test.go b/codegen/cmd_test.go index 2a0ce0a8d..47e0a4584 100644 --- a/codegen/cmd_test.go +++ b/codegen/cmd_test.go @@ -2134,6 +2134,105 @@ roleRef: }), ) + DescribeTable("rendering with GlobalFloatingUserId", + func(floatingUserId bool) { + cmd := &Command{ + Chart: &Chart{ + Operators: []Operator{ + { + Name: "painter", + Deployment: Deployment{ + Container: Container{ + Image: Image{ + Tag: "v0.0.0", + Repository: "painter", + Registry: "quay.io/solo-io", + PullPolicy: "IfNotPresent", + }, + }, + }, + GlobalFloatingUserIdPath: ".Values.global.securitySettings.floatingUserId", + }, + }, + // Because the global override comes from .Values it has to be set here, not on the painter + Values: map[string]interface{}{ + "global": map[string]interface{}{ + "securitySettings": map[string]interface{}{ + "floatingUserId": floatingUserId, + }, + }, + }, + Data: Data{ + ApiVersion: "v1", + Description: "", + Name: "Painting Operator", + Version: "v0.0.1", + Home: "https://docs.solo.io/skv2/latest", + Sources: []string{ + "https://github.com/solo-io/skv2", + }, + }, + }, + + ManifestRoot: "codegen/test/chart", + } + + err := cmd.Execute() + Expect(err).NotTo(HaveOccurred()) + + runAsUser := 202020 + runAsGroup := 999 + painterValues := map[string]interface{}{ + "enabled": true, + "runAsUser": runAsUser, + "podSecurityContext": map[string]interface{}{ + "runAsUser": runAsUser, + "fsGroup": runAsGroup, + }, + } + + helmValues := map[string]interface{}{"painter": painterValues} + + renderedManifests := helmTemplate("./codegen/test/chart", helmValues) + + var renderedDeployment *appsv1.Deployment + decoder := kubeyaml.NewYAMLOrJSONDecoder(bytes.NewBuffer(renderedManifests), 4096) + for { + obj := &unstructured.Unstructured{} + err := decoder.Decode(obj) + if err != nil { + break + } + if obj.GetName() != "painter" || obj.GetKind() != "Deployment" { + continue + } + + bytes, err := obj.MarshalJSON() + Expect(err).NotTo(HaveOccurred()) + renderedDeployment = &appsv1.Deployment{} + err = json.Unmarshal(bytes, renderedDeployment) + Expect(err).NotTo(HaveOccurred()) + } + + Expect(renderedDeployment).NotTo(BeNil()) + renderedRunAsUser := renderedDeployment.Spec.Template.Spec.Containers[0].SecurityContext.RunAsUser + renderedPodSecurityContext := renderedDeployment.Spec.Template.Spec.SecurityContext + + // When using the global floatingUserId, the container runAsUser and RunAsUser should not be set + if floatingUserId { + Expect(renderedRunAsUser).To(BeNil()) + Expect(renderedPodSecurityContext).To(BeNil()) + } else { + Expect(*renderedRunAsUser).To(Equal(int64(runAsUser))) + Expect(*renderedPodSecurityContext.RunAsUser).To(Equal(int64(runAsUser))) + Expect(*renderedPodSecurityContext.FSGroup).To(Equal(int64(runAsGroup))) + } + + }, + Entry("Global floatingUserId is true", true), + Entry("Global floatingUserId is false", false), + ) + Describe("rendering template env vars", func() { var tmpDir string diff --git a/codegen/model/chart.go b/codegen/model/chart.go index 78f53cdce..a59b0845a 100644 --- a/codegen/model/chart.go +++ b/codegen/model/chart.go @@ -98,6 +98,9 @@ type Operator struct { // // E.g: `and (.Values.operator.customValueA) (.Values.operator.customValueB)` CustomEnableCondition string + + // Optional: if specified, will use this path in rendering template logic + GlobalFloatingUserIdPath string } func (o Operator) FormattedName() string { diff --git a/codegen/templates/chart/operator-deployment.yamltmpl b/codegen/templates/chart/operator-deployment.yamltmpl index 965053d30..d2bed07d1 100644 --- a/codegen/templates/chart/operator-deployment.yamltmpl +++ b/codegen/templates/chart/operator-deployment.yamltmpl @@ -87,6 +87,10 @@ spec: spec: serviceAccountName: [[ $operator.Name ]] {{- /* Override the default podSecurityContext config if it is set. */}} +[[- /* the GlobalFloatingUserId is expected to disable the pod security context */ -]] +[[- if $operator.GlobalFloatingUserIdPath ]] +{{- if not [[ $operator.GlobalFloatingUserIdPath ]] }} +[[- end ]] {{- if or ([[ (opVar $operator) ]].podSecurityContext) (eq "map[]" (printf "%v" [[ (opVar $operator) ]].podSecurityContext)) }} securityContext: {{ toYaml [[ (opVar $operator) ]].podSecurityContext | indent 8 }} @@ -96,6 +100,9 @@ spec: [[ toYaml $podSecurityContext | indent 8 ]] [[- end ]] {{- end }} +[[- if $operator.GlobalFloatingUserIdPath ]] [[/* end the "if" if GlobalFloatingUserId is being checked */]] +{{- end }} +[[- end ]] [[- if $volumes ]] volumes: [[ toYaml $volumes | indent 6 ]] @@ -192,7 +199,12 @@ spec: {} {{- else}} runAsNonRoot: true + [[- /* if there is a GlobalFloatingUserIdPath add it to the runAsuser logic */ -]] + [[- if $operator.GlobalFloatingUserIdPath ]] + {{- if not (or $[[ $operatorVar ]].floatingUserId [[ $operator.GlobalFloatingUserIdPath ]]) }} + [[- else ]] {{- if not $[[ $operatorVar ]].floatingUserId }} + [[- end ]] runAsUser: {{ printf "%.0f" (float64 $[[ $operatorVar ]].runAsUser) }} {{- end }} readOnlyRootFilesystem: true