From 3b3cee52025d5ad9d74c47a375433eed0ab2296b Mon Sep 17 00:00:00 2001 From: Aditya Bharadwaj <49105292+adiforluls@users.noreply.github.com> Date: Thu, 12 Jan 2023 19:20:42 +0530 Subject: [PATCH] VZ-8024: update fluentd daemonset to meet pod and container security standards (#5072) --- .../templates/daemonset.yaml | 24 +++++++++- .../security/pod_security_test.go | 45 ++++++++++++++++--- 2 files changed, 62 insertions(+), 7 deletions(-) diff --git a/platform-operator/helm_config/charts/verrazzano-fluentd/templates/daemonset.yaml b/platform-operator/helm_config/charts/verrazzano-fluentd/templates/daemonset.yaml index a3505ea6143..5cfe24b941c 100644 --- a/platform-operator/helm_config/charts/verrazzano-fluentd/templates/daemonset.yaml +++ b/platform-operator/helm_config/charts/verrazzano-fluentd/templates/daemonset.yaml @@ -1,4 +1,4 @@ -# Copyright (c) 2022, Oracle and/or its affiliates. +# Copyright (c) 2022, 2023, Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. apiVersion: apps/v1 @@ -24,6 +24,15 @@ spec: command: ["/init/init.sh"] image: {{ .Values.logging.fluentdImage }} imagePullPolicy: IfNotPresent + securityContext: + runAsNonRoot: true + runAsUser: 999 + runAsGroup: 997 + privileged: false + allowPrivilegeEscalation: false + capabilities: + drop: + - ALL volumeMounts: - mountPath: /init name: {{ .Values.logging.name }}-init @@ -68,6 +77,14 @@ spec: {{- end }} image: {{ .Values.logging.fluentdImage }} imagePullPolicy: IfNotPresent + securityContext: + privileged: false + allowPrivilegeEscalation: false + capabilities: + drop: + - ALL + add: + - DAC_OVERRIDE ports: - containerPort: 24231 name: http-metrics @@ -109,9 +126,12 @@ spec: {{- end }} serviceAccountName: fluentd terminationGracePeriodSeconds: 30 + securityContext: + seccompProfile: + type: RuntimeDefault volumes: - configMap: - defaultMode: 0744 + defaultMode: 0755 name: {{ .Values.logging.name }}-init name: {{ .Values.logging.name }}-init - name: cacerts diff --git a/tests/e2e/verify-install/security/pod_security_test.go b/tests/e2e/verify-install/security/pod_security_test.go index 116c5279e91..c48e2886bac 100644 --- a/tests/e2e/verify-install/security/pod_security_test.go +++ b/tests/e2e/verify-install/security/pod_security_test.go @@ -27,6 +27,7 @@ const ( // Only allowed capability in restricted mode capNetBindService = "NET_BIND_SERVICE" + capDacOverride = "DAC_OVERRIDE" ) var skipPods = map[string][]string{ @@ -38,7 +39,6 @@ var skipPods = map[string][]string{ }, "verrazzano-system": { "coherence-operator", - "fluentd", "vmi-system", "weblogic-operator", }, @@ -58,6 +58,11 @@ type podExceptions struct { allowHostNetwork bool allowHostPID bool allowHostPort bool + containers map[string]containerException +} + +type containerException struct { + allowedCapabilities map[string]bool } var exceptionPods = map[string]podExceptions{ @@ -67,6 +72,16 @@ var exceptionPods = map[string]podExceptions{ allowHostPID: true, allowHostPort: true, }, + "fluentd": { + allowHostPath: true, + containers: map[string]containerException{ + "fluentd": { + allowedCapabilities: map[string]bool{ + capDacOverride: true, + }, + }, + }, + }, } var ( @@ -133,7 +148,7 @@ func expectPodSecurityForNamespace(pod corev1.Pod) []error { var errors []error // get pod exceptions - isException, exception := isExceptionPod(pod) + isException, exception := isExceptionPod(pod.Name) // ensure hostpath is not set unless it is an exception if !isException || !exception.allowHostPath { @@ -215,6 +230,12 @@ func ensurePodSecurityContext(sc *corev1.PodSecurityContext, podName string) []e } func ensureContainerSecurityContext(sc *corev1.SecurityContext, podName, containerName string) []error { + exceptionContainer := false + exceptionPod, exception := isExceptionPod(podName) + if exceptionPod && exception.containers != nil { + _, exceptionContainer = exception.containers[containerName] + } + if sc == nil { return []error{fmt.Errorf("SecurityContext is nil for pod %s, container %s", podName, containerName)} } @@ -246,11 +267,16 @@ func ensureContainerSecurityContext(sc *corev1.SecurityContext, podName, contain if !dropCapabilityFound { errors = append(errors, fmt.Errorf("SecurityContext not configured correctly for pod %s, container %s, Missing `Drop -ALL` capabilities", podName, containerName)) } - if len(sc.Capabilities.Add) > 0 { + if len(sc.Capabilities.Add) > 0 && !exceptionContainer { if len(sc.Capabilities.Add) > 1 || sc.Capabilities.Add[0] != capNetBindService { errors = append(errors, fmt.Errorf("only %s capability allowed, found unexpected capabilities added to container %s in pod %s: %v", capNetBindService, containerName, podName, sc.Capabilities.Add)) } } + if exceptionContainer && len(sc.Capabilities.Add) > 0 { + if !capExceptions(exception.containers[containerName].allowedCapabilities, sc.Capabilities.Add) { + errors = append(errors, fmt.Errorf("%v capabilities are allowed, found unexpected capabilities for pod %s container %s: %v", exception.containers[containerName].allowedCapabilities, podName, containerName, sc.Capabilities.Add)) + } + } return errors } @@ -272,11 +298,20 @@ func shouldSkipContainer(containerName string, skip []string) bool { return false } -func isExceptionPod(pod corev1.Pod) (bool, podExceptions) { +func isExceptionPod(podName string) (bool, podExceptions) { for exceptionPod := range exceptionPods { - if strings.Contains(pod.Name, exceptionPod) { + if strings.Contains(podName, exceptionPod) { return true, exceptionPods[exceptionPod] } } return false, podExceptions{} } + +func capExceptions(allowedCaps map[string]bool, givenCaps []corev1.Capability) bool { + exceptionsOk := true + for _, givenCap := range givenCaps { + _, ok := allowedCaps[string(givenCap)] + exceptionsOk = exceptionsOk && ok + } + return exceptionsOk +}