diff --git a/helm-charts/falcon-image-analyzer/README.md b/helm-charts/falcon-image-analyzer/README.md index abe06653..5d9762cd 100644 --- a/helm-charts/falcon-image-analyzer/README.md +++ b/helm-charts/falcon-image-analyzer/README.md @@ -19,6 +19,7 @@ more. - [Installing using Helm Chart](#install-using-helm-chart) - [Deployment considerations](#deployment-considerations) - [Pod Security Standards](#pod-security-standards) + - [OpenShift Compatibility](#openshift-compatibility) - [Temp Mounts](#temp-volume-mount) - [IAM Roles](#aws-iam-roles-for-service-accounts) - [Authentication for Private Registries](#authentication-for-private-registries) @@ -40,7 +41,6 @@ The Falcon Image Analyzer Helm chart has been tested to deploy on the following * Azure Kubernetes Service (AKS) * Google Kubernetes Engine (GKE) * SUSE Rancher K3s -* Red Hat OpenShift Kubernetes ## Helm Chart Support for Falcon Image Analyzer Versions @@ -136,10 +136,10 @@ The `[CROWDSTRIKE_IMAGE_REGISTRY]` can be replaced with below registries based o -| Region | ImageName | +| Region | ImageName | |:-------|:-------------------------------------------------------------------------------------------| | `us-1` | `registry.crowdstrike.com/falcon-imageanalyzer/release/falcon-imageanalyzer` | -| `us-2` | `registry.crowdstrike.com/falcon-imageanalyzer/release/falcon-imageanalyzer` | +| `us-2` | `registry.crowdstrike.com/falcon-imageanalyzer/release/falcon-imageanalyzer` | | `eu-1` | `registry.crowdstrike.com/falcon-imageanalyzer/release/falcon-imageanalyzer` | | `gov1` | `registry.laggar.gcw.crowdstrike.com/falcon-imageanalyzer/release/falcon-imageanalyzer` | | `gov2` | `registry.us-gov-2.crowdstrike.mil/falcon-imageanalyzer/release/falcon-imageanalyzer` | @@ -247,8 +247,8 @@ From IAR ver >-= 1.0.20 IAR will dynamically switch between PRIMARY and SECONDAR IAR requires internet access to your assigned CrowdStrike authentication API and upload servers. If your network requires it, configure your allowlists with your assigned CrowdStrike cloud servers. -| Region | Authentication API | PRIMARY Scan Upload Servers | SECONDARY Scan Upload Servers | -|:----:|:--:|:---------------------------------------------------:|----------------------------------------| +| Region | Authentication API | PRIMARY Scan Upload Servers | SECONDARY Scan Upload Servers | +|:----:|:--:|:---------------------------------------------------:|----------------------------------------| | US-1 | https://api.crowdstrike.com | https://container-upload.us-1.crowdstrike.com | https://api.crowdstrike.com | | US-2 | https://api.us-2.crowdstrike.com | https://container-upload.us-2.crowdstrike.com | https://api.us-2.crowdstrike.com | | EU-1 | https://api.eu-1.crowdstrike.com | https://container-upload.eu-1.crowdstrike.com | https://api.eu-1.crowdstrike.com | @@ -257,8 +257,8 @@ If your network requires it, configure your allowlists with your assigned CrowdS #### ***For IAR versions < 1.0.20 both Authtentication/Upload Servers point to the same -| Region | Authentication API | Scan Upload Servers | -|:----:|:--:|:--------------------------------------:| +| Region | Authentication API | Scan Upload Servers | +|:----:|:--:|:--------------------------------------:| | US-1 | https://api.crowdstrike.com | https://api.crowdstrike.com | | US-2 | https://api.us-2.crowdstrike.com | https://api.us-2.crowdstrike.com | | EU-1 | https://api.eu-1.crowdstrike.com | https://api.eu-1.crowdstrike.com | @@ -298,18 +298,53 @@ For a successful deployment, you will want to ensure that: ### Pod Security Standards -Starting with Kubernetes 1.25, Pod Security Standards will be enforced. Setting the appropriate Pod Security Standards policy needs to be performed by adding a label to the namespace. Run the following command, and replace `my-existing-namespace` with the namespace that you have installed the falcon sensors, for example: `falcon-image-analyzer`. -``` -kubectl label --overwrite ns my-existing-namespace \ - pod-security.kubernetes.io/enforce=privileged -``` +Starting with Kubernetes 1.25, Pod Security Standards (PSS) are enforced via the Pod Security Admission (PSA) controller. Falcon Image Analyzer requires `privileged` PSS labels on its namespace because it accesses the container runtime socket (DaemonSet mode) or runs as root (Deployment mode). -If you want to silence the warning and change the auditing level for the Pod Security Standard, add the following labels: -``` -kubectl label ns --overwrite my-existing-namespace pod-security.kubernetes.io/audit=privileged -kubectl label ns --overwrite my-existing-namespace pod-security.kubernetes.io/warn=privileged +Apply the required labels to the namespace before installing: + +```bash +kubectl label --overwrite ns \ + pod-security.kubernetes.io/enforce=privileged \ + pod-security.kubernetes.io/warn=privileged \ + pod-security.kubernetes.io/audit=privileged ``` +In automated testing environments, set `testing.labelNamespace: true` to apply these labels automatically via a pre-install Job. This requires outbound access to `docker.io` and is not suitable for air-gapped or registry-restricted environments. + +### OpenShift Compatibility + +> **Note:** OpenShift is **not a recommended** configuration for this Helm chart. The +> [official Red Hat certified CrowdStrike Falcon Operator](https://catalog.redhat.com/en/software/container-stacks/detail/62f2d38f76d039249424703d) +> is the recommended installation method for OpenShift clusters. + +#### Security Context Constraints + +The required privileges differ depending on which workload mode is enabled: + +- **DaemonSet mode** (`daemonset.enabled: true`): Requires a privileged SCC to access the container runtime socket + (`/run/containerd/containerd.sock`, `/run/crio/crio.sock`, etc.) and, for CRI-O, additional hostPath mounts. +- **Deployment mode** (`deployment.enabled: true`): Requires only permission to run as root (UID 0) with all + capabilities dropped. No host access is needed. + +Set `openshift.enabled: true` and `openshift.createSCC: true` to have the chart create the appropriate SCC automatically. The SCC grants only the +minimum permissions required for the active workload mode. The SCC is managed as a standard Helm release resource and +will be created on install and removed on uninstall. + +**Helm User Permissions:** When `openshift.createSCC: true`, the user or service account running Helm must have +permission to create, update, and delete `SecurityContextConstraints` resources at the cluster level. + +To manage the SCC outside of Helm, set `openshift.createSCC: false`, define `openshift.sccName` with the name of your +SCC, and ensure the SCC is created prior to deployment. The SCC must grant the permissions described above to the IAR +service account. + +#### OpenShift Values + +| Parameter | Description | Default | +|:----------------------|:---------------------------------------------------------------------------------------------------------------------------|:----------------------| +| `openshift.enabled` | Enable OpenShift compatibility mode | `false` | +| `openshift.createSCC` | Create a `SecurityContextConstraints` resource granting the workload the required privileges for the active workload mode | `true` | +| `openshift.sccName` | Name of the SCC to create or use. If empty, defaults to the release fullname | `""` (auto-generated) | + ### Temp Volume Mount In order to perform image scan, IAR will pull the image and un-compress it for traversal through layers and image config and manifest. For this, IAR will use a temp space that is added as a mount of type `emptyDir` . The idea of the storage here is to accommodate the max size image that one could run in the kubernetes. @@ -317,7 +352,7 @@ By Default, this is set to `20Gi` but can be overridden by the customer by addin ``` # This is a mandatory mount for both deployment and daemonset. # this is used as a tmp working space for image storage. -# adjust this space to any comfortable value. the temp ssize limit should be equal to +# adjust this space to any comfortable value. the temp ssize limit should be equal to # 2 X to the largest image possible to run in the container. # for e.g. if the largest possible image is in the range of 4g put 8Gi as the value. volumes: @@ -330,7 +365,7 @@ volumes: ### AWS IAM Roles for Service Accounts -- **KIAM OR Kube2IAM.** +- **KIAM OR Kube2IAM.** For the IAR to detect cloud as AWS, it should be able to retrieve sts token to assume role to retrieve ECR Tokens. There are 2 options for that . If your EKS cluster us using the **kiam** or **kube2iam** admission controller, add annotations for the IAR service account in the `config_values.yaml` as stated below, before installing. Make sure the roles have trust-relationship to allow @@ -449,8 +484,8 @@ use the above secret as `"my-app-ns:regcred,my-app-ns:regcred2"` `autoDiscoverCredentials` if set to true, the IAR will try to discover the docker-registry secrets across all namespaces. if autoDiscoverCredentials is set to `true` then the provided credentials are ignored. Note that the secret -should be existing of type docker-registry in ANY namespace on the cluster to be discovered. -IAR will NOT pick up any new secret added to the cluster after IAR is running. +should be existing of type docker-registry in ANY namespace on the cluster to be discovered. +IAR will NOT pick up any new secret added to the cluster after IAR is running. For that a restart of IAR is needed. ### PROXY Usage @@ -468,11 +503,11 @@ If a customer us using proxy settings. Please make sure to add the registry doma This is so that the IAR can connect to the registries without proxy and authenticate if needed using secrets provided or download the public free images. ***Note that some registries domains also have other urls based on the auth challenge that is sent by the registry service. Please make sure to add those as well to ```NO_PROXY``` -for e.g. for gitlab registries there exists the -- registry domain ```my-reg.gitlab.com``` +for e.g. for gitlab registries there exists the +- registry domain ```my-reg.gitlab.com``` - and the other ```www.gitlab.com``` -- The above is very registry provider specific. One needs to ensure nothing ie being blocked by Proxy +- The above is very registry provider specific. One needs to ensure nothing ie being blocked by Proxy ### Pod Eviction If for some reason pod evictions are observed in the Cluster due to exceeding ephemeral storage @@ -520,7 +555,7 @@ metadata: #### Images Images can be excluded by adding the full image name in the below section of the `config_values.yaml` - Image without registries will be defaulted to `index.docker.io` -- Images without tags will be defaulted to `latest` tag +- Images without tags will be defaulted to `latest` tag ``` exclusions: imageName: "myregistry.io/mynamespace/myimage:tag1,myregistry.io/mynamespace/myimage@sha2561234678901209" @@ -556,7 +591,7 @@ kind: Deployment / Daemonset metadata: name: myapp namespace: mynamespace - + spec: replicas: 1 template: diff --git a/helm-charts/falcon-image-analyzer/templates/_helpers.tpl b/helm-charts/falcon-image-analyzer/templates/_helpers.tpl index 218a220d..0f7778fe 100644 --- a/helm-charts/falcon-image-analyzer/templates/_helpers.tpl +++ b/helm-charts/falcon-image-analyzer/templates/_helpers.tpl @@ -265,3 +265,28 @@ Get container registry config json from global value if it exists {{- .Values.image.registryConfigJSON | default "" -}} {{- end -}} {{- end -}} + +{{/* +OpenShift SCC name. Uses openshift.sccName if set, otherwise defaults to the fullname. +*/}} +{{- define "falcon-image-analyzer.sccName" -}} +{{- if .Values.openshift.sccName -}} +{{- .Values.openshift.sccName -}} +{{- else -}} +{{- include "falcon-image-analyzer.fullname" . -}} +{{- end -}} +{{- end -}} + +{{/* +OpenShift mode enabled — true if either chart-level or global is true. +*/}} +{{- define "falcon-image-analyzer.openshiftEnabled" -}} +{{- or .Values.openshift.enabled (default false .Values.global.openshift.enabled) -}} +{{- end -}} + +{{/* +OpenShift createSCC — false if either chart-level or global disables it. +*/}} +{{- define "falcon-image-analyzer.openshiftCreateSCC" -}} +{{- and .Values.openshift.createSCC .Values.global.openshift.createSCC -}} +{{- end -}} diff --git a/helm-charts/falcon-image-analyzer/templates/cluster-role-read-access.yaml b/helm-charts/falcon-image-analyzer/templates/cluster-role-read-access.yaml index 0cd2237a..a1efab22 100644 --- a/helm-charts/falcon-image-analyzer/templates/cluster-role-read-access.yaml +++ b/helm-charts/falcon-image-analyzer/templates/cluster-role-read-access.yaml @@ -17,3 +17,13 @@ rules: - get - watch - list +{{- if and (include "falcon-image-analyzer.openshiftEnabled" . | eq "true") (not (include "falcon-image-analyzer.openshiftCreateSCC" . | eq "true")) .Values.openshift.sccName }} + - apiGroups: + - security.openshift.io + resources: + - securitycontextconstraints + resourceNames: + - {{ include "falcon-image-analyzer.sccName" . }} + verbs: + - use +{{- end }} diff --git a/helm-charts/falcon-image-analyzer/templates/daemonset.yaml b/helm-charts/falcon-image-analyzer/templates/daemonset.yaml index 67ed8157..4d56908a 100644 --- a/helm-charts/falcon-image-analyzer/templates/daemonset.yaml +++ b/helm-charts/falcon-image-analyzer/templates/daemonset.yaml @@ -49,6 +49,9 @@ spec: metadata: annotations: sensor.falcon-system.crowdstrike.com/injection: disabled + {{- if include "falcon-image-analyzer.openshiftEnabled" . | eq "true" }} + openshift.io/scc: {{ include "falcon-image-analyzer.sccName" . }} + {{- end }} {{- with .Values.podAnnotations }} {{- toYaml . | nindent 8 }} {{- end }} diff --git a/helm-charts/falcon-image-analyzer/templates/deployment.yaml b/helm-charts/falcon-image-analyzer/templates/deployment.yaml index d9ae9fa3..5f27b0ef 100644 --- a/helm-charts/falcon-image-analyzer/templates/deployment.yaml +++ b/helm-charts/falcon-image-analyzer/templates/deployment.yaml @@ -50,6 +50,9 @@ spec: metadata: annotations: sensor.falcon-system.crowdstrike.com/injection: disabled + {{- if include "falcon-image-analyzer.openshiftEnabled" . | eq "true" }} + openshift.io/scc: {{ include "falcon-image-analyzer.sccName" . }} + {{- end }} {{- with .Values.podAnnotations }} {{- toYaml . | nindent 8 }} {{- end }} diff --git a/helm-charts/falcon-image-analyzer/templates/scc.yaml b/helm-charts/falcon-image-analyzer/templates/scc.yaml new file mode 100644 index 00000000..ace34a1e --- /dev/null +++ b/helm-charts/falcon-image-analyzer/templates/scc.yaml @@ -0,0 +1,50 @@ +{{- if and (include "falcon-image-analyzer.openshiftEnabled" . | eq "true") (include "falcon-image-analyzer.openshiftCreateSCC" . | eq "true") }} +apiVersion: security.openshift.io/v1 +kind: SecurityContextConstraints +metadata: + name: {{ include "falcon-image-analyzer.sccName" . }} + labels: + {{- include "falcon-image-analyzer.labels" . | nindent 4 }} + annotations: + kubernetes.io/description: >- + {{- if .Values.daemonset.enabled }} + Grants the Falcon Image Analyzer DaemonSet the privileges required to + access the container runtime socket on each node for image scanning. + {{- else }} + Grants the Falcon Image Analyzer Deployment permission to run as root + (UID 0) for image analysis. No host access is granted. + {{- end }} +allowHostDirVolumePlugin: {{ or .Values.daemonset.enabled .Values.azure.enabled }} +allowHostIPC: false +allowHostNetwork: {{ .Values.hostNetwork | default false }} +allowHostPID: false +allowHostPorts: false +allowPrivilegeEscalation: {{ .Values.daemonset.enabled }} +allowPrivilegedContainer: {{ .Values.daemonset.enabled }} +allowedCapabilities: [] +defaultAddCapabilities: [] +fsGroup: + type: RunAsAny +readOnlyRootFilesystem: false +requiredDropCapabilities: [] +runAsUser: + type: RunAsAny +seLinuxContext: + type: RunAsAny +seccompProfiles: + - runtime/default +supplementalGroups: + type: RunAsAny +users: + - system:serviceaccount:{{ include "falcon-image-analyzer.namespace" . }}:{{ .Values.serviceAccount.name | default (include "falcon-image-analyzer.fullname" .) }} +groups: [] +volumes: + - configMap + - downwardAPI + - emptyDir + - projected + - secret + {{- if or .Values.daemonset.enabled .Values.azure.enabled }} + - hostPath + {{- end }} +{{- end }} diff --git a/helm-charts/falcon-image-analyzer/templates/tests/setup-namespace-labels.yaml b/helm-charts/falcon-image-analyzer/templates/tests/setup-namespace-labels.yaml new file mode 100644 index 00000000..2b7b59ea --- /dev/null +++ b/helm-charts/falcon-image-analyzer/templates/tests/setup-namespace-labels.yaml @@ -0,0 +1,77 @@ +{{- if .Values.testing.labelNamespace }} +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ include "falcon-image-analyzer.fullname" . }}-ns-labeler + namespace: {{ include "falcon-image-analyzer.namespace" . }} + labels: + {{- include "falcon-image-analyzer.labels" . | nindent 4 }} + annotations: + "helm.sh/hook": pre-install,pre-upgrade + "helm.sh/hook-weight": "-10" + "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: {{ include "falcon-image-analyzer.fullname" . }}-ns-labeler + labels: + {{- include "falcon-image-analyzer.labels" . | nindent 4 }} + annotations: + "helm.sh/hook": pre-install,pre-upgrade + "helm.sh/hook-weight": "-10" + "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded +rules: + - apiGroups: [""] + resources: ["namespaces"] + verbs: ["get", "patch"] +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: {{ include "falcon-image-analyzer.fullname" . }}-ns-labeler + labels: + {{- include "falcon-image-analyzer.labels" . | nindent 4 }} + annotations: + "helm.sh/hook": pre-install,pre-upgrade + "helm.sh/hook-weight": "-10" + "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded +subjects: + - kind: ServiceAccount + name: {{ include "falcon-image-analyzer.fullname" . }}-ns-labeler + namespace: {{ include "falcon-image-analyzer.namespace" . }} +roleRef: + kind: ClusterRole + name: {{ include "falcon-image-analyzer.fullname" . }}-ns-labeler + apiGroup: rbac.authorization.k8s.io +--- +apiVersion: batch/v1 +kind: Job +metadata: + name: {{ include "falcon-image-analyzer.fullname" . }}-ns-labeler + namespace: {{ include "falcon-image-analyzer.namespace" . }} + labels: + {{- include "falcon-image-analyzer.labels" . | nindent 4 }} + annotations: + "helm.sh/hook": pre-install,pre-upgrade + "helm.sh/hook-weight": "-5" + "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded +spec: + template: + spec: + serviceAccountName: {{ include "falcon-image-analyzer.fullname" . }}-ns-labeler + restartPolicy: OnFailure + containers: + - name: kubectl + image: docker.io/bitnami/kubectl + command: + - kubectl + - label + - namespace + - {{ include "falcon-image-analyzer.namespace" . }} + - "pod-security.kubernetes.io/enforce=privileged" + - "pod-security.kubernetes.io/warn=privileged" + - "pod-security.kubernetes.io/audit=privileged" + - "--overwrite" +{{- end }} diff --git a/helm-charts/falcon-image-analyzer/templates/tests/test-cluster-permissions.yaml b/helm-charts/falcon-image-analyzer/templates/tests/test-cluster-permissions.yaml index 3e5a562a..7b93f567 100644 --- a/helm-charts/falcon-image-analyzer/templates/tests/test-cluster-permissions.yaml +++ b/helm-charts/falcon-image-analyzer/templates/tests/test-cluster-permissions.yaml @@ -34,6 +34,7 @@ rules: verbs: - get - list + - watch - apiGroups: - rbac.authorization.k8s.io resources: @@ -42,6 +43,22 @@ rules: verbs: - get - list +{{- if (include "falcon-image-analyzer.openshiftEnabled" . | eq "true") }} +- apiGroups: + - security.openshift.io + resources: + - securitycontextconstraints + verbs: + - get +- apiGroups: + - security.openshift.io + resources: + - securitycontextconstraints + resourceNames: + - {{ include "falcon-image-analyzer.sccName" . }} + verbs: + - use +{{- end }} --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding diff --git a/helm-charts/falcon-image-analyzer/templates/tests/test-image-analyzer-running.yaml b/helm-charts/falcon-image-analyzer/templates/tests/test-image-analyzer-running.yaml index f06707a7..045ff929 100644 --- a/helm-charts/falcon-image-analyzer/templates/tests/test-image-analyzer-running.yaml +++ b/helm-charts/falcon-image-analyzer/templates/tests/test-image-analyzer-running.yaml @@ -16,6 +16,7 @@ spec: containers: - name: kubectl image: docker.io/bitnami/kubectl + imagePullPolicy: IfNotPresent command: - /bin/sh - -c @@ -23,7 +24,18 @@ spec: FAILED=0 echo "--- Waiting for pods to initialize ---" - sleep 10 + {{- if .Values.daemonset.enabled }} + kubectl rollout status daemonset \ + "{{ include "falcon-image-analyzer.fullname" . }}" \ + -n "{{ include "falcon-image-analyzer.namespace" . }}" \ + --timeout=300s + {{- else }} + kubectl wait deployment \ + "{{ include "falcon-image-analyzer.fullname" . }}" \ + -n "{{ include "falcon-image-analyzer.namespace" . }}" \ + --for=condition=Available \ + --timeout=300s + {{- end }} echo "--- Checking all Image Analyzer pods are running ---" KUBECMD=$(kubectl get pods -n "{{ include "falcon-image-analyzer.namespace" . }}" \ @@ -141,7 +153,7 @@ spec: echo "--- Checking pod annotations ---" PODS=$(kubectl get pods -n "{{ include "falcon-image-analyzer.namespace" . }}" \ -l "app.kubernetes.io/name={{ include "falcon-image-analyzer.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" \ - -o json 2>&1 | jq -r '.items[] | select(.metadata.annotations["helm.sh/hook"] == null) | .metadata.name') + -o json 2>/dev/null | jq -r '.items[] | select(.metadata.annotations["helm.sh/hook"] == null) | .metadata.name') if [ -z "${PODS}" ]; then echo "[FAIL]: No Image Analyzer pods found" FAILED=1 @@ -159,6 +171,22 @@ spec: done fi + {{- if .Values.testing.labelNamespace }} + echo "--- Checking PSS namespace labels ---" + NS_NAME="{{ include "falcon-image-analyzer.namespace" . }}" + for LABEL in "pod-security.kubernetes.io/enforce=privileged" "pod-security.kubernetes.io/warn=privileged" "pod-security.kubernetes.io/audit=privileged"; do + KEY="${LABEL%%=*}" + VALUE="${LABEL##*=}" + ACTUAL=$(kubectl get namespace "${NS_NAME}" -o json 2>&1 | jq -r --arg key "${KEY}" '.metadata.labels[$key] // ""') + if [ "${ACTUAL}" != "${VALUE}" ]; then + echo "[FAIL]: Namespace '${NS_NAME}' label '${KEY}' is '${ACTUAL}', expected '${VALUE}'" + FAILED=1 + else + echo "[OK]: Namespace '${NS_NAME}' has label '${KEY}=${VALUE}'" + fi + done + {{- end }} + echo "--- Checking service ---" SVC_NAME="{{ include "falcon-image-analyzer.iarAgentService" . }}" if ! kubectl get service "${SVC_NAME}" -n "{{ include "falcon-image-analyzer.namespace" . }}" > /dev/null 2>&1; then diff --git a/helm-charts/falcon-image-analyzer/templates/tests/test-openshift.yaml b/helm-charts/falcon-image-analyzer/templates/tests/test-openshift.yaml new file mode 100644 index 00000000..bf672d07 --- /dev/null +++ b/helm-charts/falcon-image-analyzer/templates/tests/test-openshift.yaml @@ -0,0 +1,88 @@ +{{- if and .Values.testing.enabled (include "falcon-image-analyzer.openshiftEnabled" . | eq "true") }} +apiVersion: v1 +kind: Pod +metadata: + name: "{{ include "falcon-image-analyzer.fullname" . }}-test-openshift" + namespace: {{ include "falcon-image-analyzer.namespace" . }} + labels: + app.kubernetes.io/name: {{ include "falcon-image-analyzer.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/managed-by: {{ .Release.Service }} + app.kubernetes.io/component: test + helm.sh/chart: {{ include "falcon-image-analyzer.chart" . }} + annotations: + "helm.sh/hook": test-success +spec: + containers: + - name: kubectl + image: docker.io/bitnami/kubectl + imagePullPolicy: IfNotPresent + command: + - /bin/sh + - -c + - | + FAILED=0 + + {{- if (include "falcon-image-analyzer.openshiftCreateSCC" . | eq "true") }} + echo "--- Checking SCC exists ---" + if ! kubectl get scc "{{ include "falcon-image-analyzer.sccName" . }}" > /dev/null 2>&1; then + echo "[FAIL]: SCC '{{ include "falcon-image-analyzer.sccName" . }}' not found" + FAILED=1 + else + echo "[OK]: SCC '{{ include "falcon-image-analyzer.sccName" . }}' exists" + fi + + echo "--- Checking service account is bound to SCC ---" + SA_ENTRY="system:serviceaccount:{{ include "falcon-image-analyzer.namespace" . }}:{{ .Values.serviceAccount.name | default (include "falcon-image-analyzer.fullname" .) }}" + SCC_USERS=$(kubectl get scc "{{ include "falcon-image-analyzer.sccName" . }}" -o jsonpath="{.users}" 2>&1) + if ! echo "${SCC_USERS}" | grep -q "${SA_ENTRY}"; then + echo "[FAIL]: Service account '${SA_ENTRY}' is not bound to SCC" + echo "${SCC_USERS}" + FAILED=1 + else + echo "[OK]: Service account is bound to SCC" + fi + {{- end }} + + echo "--- Checking {{ if .Values.daemonset.enabled }}DaemonSet{{ else }}Deployment{{ end }} pods have openshift.io/scc annotation ---" + sleep 10 + EXPECTED_SCC="{{ include "falcon-image-analyzer.sccName" . }}" + # Exclude test pods by filtering out pods with helm.sh/hook annotation + PODS=$(kubectl get pods -n "{{ include "falcon-image-analyzer.namespace" . }}" \ + -l "app.kubernetes.io/name={{ include "falcon-image-analyzer.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" \ + -o json 2>&1 | jq -r '.items[] | select(.metadata.annotations["helm.sh/hook"] == null) | .metadata.name') + if [ -z "${PODS}" ]; then + echo "[FAIL]: No image analyzer pods found" + FAILED=1 + else + for POD in ${PODS}; do + SCC_ANNOTATION=$(kubectl get pod "${POD}" \ + -n "{{ include "falcon-image-analyzer.namespace" . }}" \ + -o jsonpath="{.metadata.annotations.openshift\.io/scc}" 2>&1) + if [ -z "${SCC_ANNOTATION}" ]; then + echo "[FAIL]: Pod '${POD}' is missing openshift.io/scc annotation" + FAILED=1 + elif [ "${SCC_ANNOTATION}" != "${EXPECTED_SCC}" ]; then + echo "[FAIL]: Pod '${POD}' has SCC '${SCC_ANNOTATION}' but expected '${EXPECTED_SCC}'" + FAILED=1 + else + echo "[OK]: Pod '${POD}' is using SCC '${SCC_ANNOTATION}'" + fi + done + fi + + if [ "${FAILED}" -eq 1 ]; then + exit 1 + fi + exit 0 + securityContext: + runAsNonRoot: true + runAsUser: 1000 + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + capabilities: + drop: + - ALL + serviceAccountName: {{ include "falcon-image-analyzer.fullname" . }}-test-sa + restartPolicy: Never +{{- end }} diff --git a/helm-charts/falcon-image-analyzer/values.schema.json b/helm-charts/falcon-image-analyzer/values.schema.json index cd0e08b2..c4a4eaa8 100644 --- a/helm-charts/falcon-image-analyzer/values.schema.json +++ b/helm-charts/falcon-image-analyzer/values.schema.json @@ -237,6 +237,36 @@ } } } + }, + "openshift": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean", + "default": false + }, + "createSCC": { + "type": "boolean", + "default": true + }, + "sccName": { + "type": "string", + "default": "" + } + } + }, + "testing": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean", + "default": false + }, + "labelNamespace": { + "type": "boolean", + "default": false + } + } } }, "oneOf": [ diff --git a/helm-charts/falcon-image-analyzer/values.yaml b/helm-charts/falcon-image-analyzer/values.yaml index 69b8c97e..b49e712f 100644 --- a/helm-charts/falcon-image-analyzer/values.yaml +++ b/helm-charts/falcon-image-analyzer/values.yaml @@ -125,6 +125,21 @@ exclusions: # NOTE That setting this to true will also set the dnsPolicy: "ClusterFirstWithHostNet" hostNetwork: false +# OpenShift configuration. +# NOTE: OpenShift is NOT A RECOMMENDED configuration. The official Red Hat certified operator +# (https://catalog.redhat.com/en/software/container-stacks/detail/62f2d38f76d039249424703d) is the +# recommended installation method for OpenShift clusters. +openshift: + # Set to true to enable OpenShift compatibility mode. + enabled: false + # Set to true to create a SecurityContextConstraints (SCC) resource granting the + # workload the privileges it requires. In DaemonSet mode this includes privileged + # access and hostPath volumes; in Deployment mode only runAsUser:0 is required. + createSCC: true + # Name of the SCC to create or use. If empty, defaults to the release fullname. + # Set createSCC: false and provide a name here to reference an existing SCC. + sccName: "" + # Define ImageAnalyzer POD DNS Policy, defaults to "ClusterFirstWithHostNet" when hostNetwork = true dnsPolicy: @@ -230,3 +245,14 @@ global: containerRegistry: pullSecret: "" configJSON: "" + openshift: + enabled: false + createSCC: true + +# Deploys the test suite during install for testing purposes. +testing: + enabled: false + # Set to true to apply privileged PSS labels to the install namespace as a + # pre-install hook. Requires outbound access to docker.io. Not suitable for + # air-gapped or registry-restricted environments. + labelNamespace: false diff --git a/helm-charts/falcon-kac/README.md b/helm-charts/falcon-kac/README.md index bd340de3..70da88d1 100644 --- a/helm-charts/falcon-kac/README.md +++ b/helm-charts/falcon-kac/README.md @@ -35,7 +35,6 @@ The Falcon Kubernetes Admission Controller has been deployed and tested on these - Amazon Elastic Kubernetes Service (EKS) - Google Kubernetes Engine (GKE) - Microsoft Azure Kubernetes Service (AKS) -- Red Hat OpenShift Container Platform 4.6 and later ## Helm Chart Support for Falcon Admission Controller Versions @@ -146,22 +145,36 @@ CID checksum, and then click **Copy your Customer ID checksum to the clipboard** --set image.registryConfigJSON=$IMAGE_PULL_TOKEN ``` -- Verify that the Falcon KAC deployment is ready and the corresponding pod has a Running status: +## OpenShift Compatibility - ``` - kubectl get deployments,pods -n falcon-kac - NAME READY UP-TO-DATE AVAILABLE AGE - falcon-kac 1/1 1 1 7d2h +> **Note:** OpenShift is **not a recommended** configuration for this Helm chart. The +> [official Red Hat certified CrowdStrike Falcon Operator](https://catalog.redhat.com/en/software/container-stacks/detail/62f2d38f76d039249424703d) +> is the recommended installation method for OpenShift clusters. - NAME READY STATUS RESTARTS AGE - falcon-kac-7cc7dd57fc-pvzzf 2/2 Running 0 7d2h - ``` -- Verify that the Falcon KAC has an AID: - ``` - kubectl exec deployment/falcon-kac -n falcon-kac -c falcon-ac -- falconctl -g --aid - ``` - **Tip**: An AID is assigned to the Falcon KAC when it communicates with the Falcon cloud. If the Falcon KAC has an AID - that is not all zeros, it is installed and running properly. +In the default configuration, all containers run as non-root with no privilege escalation and no host access, which +satisfies the built-in `restricted-v2` SCC without any additional configuration. + +### Security Context Constraints + +If `hostNetwork` must be enabled (required when a custom CNI prevents control plane nodes from communicating directly +with pods), a custom SCC that permits host network access is required. The chart can create this SCC automatically. + +The SCC is managed as a standard Helm release resource and will be created on install and removed on uninstall. + +**Helm User Permissions:** When `openshift.createSCC: true`, the user or service account running Helm must have +permission to create, update, and delete `SecurityContextConstraints` resources at the cluster level. + +To use an existing SCC instead, set `openshift.createSCC=false`, define `openshift.sccName` with the name of your SCC, +and ensure the SCC is created prior to deployment. The SCC must be bound to the service account manually before +installing. + +### OpenShift Values + +| Parameter | Description | Default | +|:-----------------------|:--------------------------------------------------------------------------------------------------------------------|:-------------------------------| +| `openshift.enabled` | Enable OpenShift compatibility mode | `false` | +| `openshift.createSCC` | Create a `SecurityContextConstraints` resource granting the Deployment service account host network access | `true` | +| `openshift.sccName` | Name of the SCC to create or use. If empty, defaults to the release fullname | `""` (auto-generated) | ## Update Falcon KAC @@ -232,3 +245,4 @@ The following tables lists the Falcon KAC configurable parameters and their defa | `falconSecret.secretName` | Existing k8s secret name to inject sensitive Falcon values.
The secret must be under the same namespace as the KAC deployment. | None (Existing secret must include `FALCONCTL_OPT_CID`) | | `clusterName` | Manually set cluster name for self-hosted Kubernetes clusters where auto-discovery fails (e.g., MicroK8s). Displayed as hostname in Host Management UI. | None (auto-discovery used) | | `falconImageAnalyzerNamespace` | Falcon Image Analyzer namespace | falcon-image-analyzer | +| `hostNetwork` | Enable host network mode. Required when a custom CNI prevents control plane to pod communication. | `false` | diff --git a/helm-charts/falcon-kac/templates/_helpers.tpl b/helm-charts/falcon-kac/templates/_helpers.tpl index 930e97e4..50338e93 100644 --- a/helm-charts/falcon-kac/templates/_helpers.tpl +++ b/helm-charts/falcon-kac/templates/_helpers.tpl @@ -231,3 +231,28 @@ Get container registry config json from global value if it exists {{- .Values.image.registryConfigJSON | default "" -}} {{- end -}} {{- end -}} + +{{/* +OpenShift SCC name. Uses openshift.sccName if set, otherwise defaults to the fullname. +*/}} +{{- define "falcon-kac.sccName" -}} +{{- if .Values.openshift.sccName -}} +{{- .Values.openshift.sccName -}} +{{- else -}} +{{- include "falcon-kac.fullname" . -}} +{{- end -}} +{{- end -}} + +{{/* +OpenShift mode enabled — true if either chart-level or global is true. +*/}} +{{- define "falcon-kac.openshiftEnabled" -}} +{{- or .Values.openshift.enabled (default false .Values.global.openshift.enabled) -}} +{{- end -}} + +{{/* +OpenShift createSCC — false if either chart-level or global disables it. +*/}} +{{- define "falcon-kac.openshiftCreateSCC" -}} +{{- and .Values.openshift.createSCC .Values.global.openshift.createSCC -}} +{{- end -}} diff --git a/helm-charts/falcon-kac/templates/clusterrole.yaml b/helm-charts/falcon-kac/templates/clusterrole.yaml index ee2f12ad..554bdc9c 100644 --- a/helm-charts/falcon-kac/templates/clusterrole.yaml +++ b/helm-charts/falcon-kac/templates/clusterrole.yaml @@ -95,4 +95,14 @@ rules: verbs: - get - list - - watch \ No newline at end of file + - watch +{{- if and (include "falcon-kac.openshiftEnabled" . | eq "true") (not (include "falcon-kac.openshiftCreateSCC" . | eq "true")) .Values.openshift.sccName }} +- apiGroups: + - security.openshift.io + resources: + - securitycontextconstraints + resourceNames: + - {{ include "falcon-kac.sccName" . }} + verbs: + - use +{{- end }} diff --git a/helm-charts/falcon-kac/templates/deployment_webhook.yaml b/helm-charts/falcon-kac/templates/deployment_webhook.yaml index 7c84afda..efb02b42 100644 --- a/helm-charts/falcon-kac/templates/deployment_webhook.yaml +++ b/helm-charts/falcon-kac/templates/deployment_webhook.yaml @@ -87,6 +87,9 @@ spec: annotations: sensor.falcon-system.crowdstrike.com/injection: disabled checksum/configmap: {{ include (print $.Template.BasePath "/configmap.yaml") . | sha256sum | quote }} + {{- if and (include "falcon-kac.openshiftEnabled" . | eq "true") .Values.hostNetwork }} + openshift.io/scc: {{ include "falcon-kac.sccName" . }} + {{- end }} {{- if or (.Values.autoDeploymentUpdate) (.Values.podAnnotations) }} {{- if .Values.autoDeploymentUpdate }} rollme: {{ randAlphaNum 5 | quote }} diff --git a/helm-charts/falcon-kac/templates/scc.yaml b/helm-charts/falcon-kac/templates/scc.yaml new file mode 100644 index 00000000..09f6438c --- /dev/null +++ b/helm-charts/falcon-kac/templates/scc.yaml @@ -0,0 +1,50 @@ +{{- if and (include "falcon-kac.openshiftEnabled" . | eq "true") (include "falcon-kac.openshiftCreateSCC" . | eq "true") .Values.hostNetwork }} +apiVersion: security.openshift.io/v1 +kind: SecurityContextConstraints +metadata: + name: {{ include "falcon-kac.sccName" . }} + labels: + {{- include "falcon-kac.labels" . | nindent 4 }} + annotations: + kubernetes.io/description: >- + Grants the Falcon KAC Deployment the host network and host ports (4443, 4080) + access required when a custom CNI is in use. All other privileges remain at + the restricted level. +allowHostDirVolumePlugin: false +allowHostIPC: false +allowHostNetwork: true +allowHostPID: false +allowHostPorts: true +allowPrivilegeEscalation: false +allowPrivilegedContainer: false +allowedCapabilities: [] +defaultAddCapabilities: [] +fsGroup: + type: MustRunAs + ranges: + - min: 1 + max: 65535 +readOnlyRootFilesystem: true +requiredDropCapabilities: + - ALL +runAsUser: + type: MustRunAsNonRoot +seLinuxContext: + type: MustRunAs +seccompProfiles: + - runtime/default +supplementalGroups: + type: MustRunAs + ranges: + - min: 1 + max: 65535 +users: + - system:serviceaccount:{{ include "falcon-kac.namespace" . }}:{{ .Values.serviceAccount.name }} +groups: [] +volumes: + - configMap + - downwardAPI + - emptyDir + - projected + - secret +{{- end }} diff --git a/helm-charts/falcon-kac/templates/tests/test-cluster-permissions.yaml b/helm-charts/falcon-kac/templates/tests/test-cluster-permissions.yaml index ca4b27a2..5daddbb1 100644 --- a/helm-charts/falcon-kac/templates/tests/test-cluster-permissions.yaml +++ b/helm-charts/falcon-kac/templates/tests/test-cluster-permissions.yaml @@ -33,6 +33,7 @@ rules: verbs: - get - list + - watch - apiGroups: - rbac.authorization.k8s.io resources: @@ -48,6 +49,22 @@ rules: verbs: - get - list +{{- if (include "falcon-kac.openshiftEnabled" . | eq "true") }} +- apiGroups: + - security.openshift.io + resources: + - securitycontextconstraints + verbs: + - get +- apiGroups: + - security.openshift.io + resources: + - securitycontextconstraints + resourceNames: + - {{ include "falcon-kac.sccName" . }} + verbs: + - use +{{- end }} --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding diff --git a/helm-charts/falcon-kac/templates/tests/test-kac-running.yaml b/helm-charts/falcon-kac/templates/tests/test-kac-running.yaml index 8f94c425..a6547b17 100644 --- a/helm-charts/falcon-kac/templates/tests/test-kac-running.yaml +++ b/helm-charts/falcon-kac/templates/tests/test-kac-running.yaml @@ -12,6 +12,7 @@ spec: containers: - name: kubectl image: docker.io/bitnami/kubectl + imagePullPolicy: IfNotPresent command: - /bin/sh - -c @@ -19,7 +20,11 @@ spec: FAILED=0 echo "--- Waiting for pods to initialize ---" - sleep 10 + kubectl wait deployment \ + "{{ include "falcon-kac.name" . }}" \ + -n "{{ include "falcon-kac.namespace" . }}" \ + --for=condition=Available \ + --timeout=300s echo "--- Checking all KAC pods are running ---" # Exclude test pods by filtering out pods with helm.sh/hook annotation @@ -33,7 +38,7 @@ spec: fi echo "--- Checking ConfigMap ---" - CM_NAME="{{ include "falcon-kac.name" . }}-config" + CM_NAME="{{ include "falcon-kac.fullname" . }}-config" if ! kubectl get configmap "${CM_NAME}" -n "{{ include "falcon-kac.namespace" . }}" > /dev/null 2>&1; then echo "[FAIL]: ConfigMap '${CM_NAME}' not found" FAILED=1 @@ -101,7 +106,7 @@ spec: # Exclude test pods by filtering out pods with helm.sh/hook annotation PODS=$(kubectl get pods -n "{{ include "falcon-kac.namespace" . }}" \ -l "app={{ include "falcon-kac.name" . }}" \ - -o json 2>&1 | jq -r '.items[] | select(.metadata.annotations["helm.sh/hook"] == null) | .metadata.name') + -o json 2>/dev/null | jq -r '.items[] | select(.metadata.annotations["helm.sh/hook"] == null) | .metadata.name') if [ -z "${PODS}" ]; then echo "[FAIL]: No KAC pods found" FAILED=1 diff --git a/helm-charts/falcon-kac/templates/tests/test-openshift.yaml b/helm-charts/falcon-kac/templates/tests/test-openshift.yaml new file mode 100644 index 00000000..66a9c747 --- /dev/null +++ b/helm-charts/falcon-kac/templates/tests/test-openshift.yaml @@ -0,0 +1,90 @@ +{{- if and .Values.testing.enabled (include "falcon-kac.openshiftEnabled" . | eq "true") }} +apiVersion: v1 +kind: Pod +metadata: + name: "{{ include "falcon-kac.fullname" . }}-test-openshift" + namespace: {{ include "falcon-kac.namespace" . }} + labels: + {{- include "falcon-kac.labels" . | nindent 4 }} + annotations: + "helm.sh/hook": test-success +spec: + containers: + - name: kubectl + image: docker.io/bitnami/kubectl + imagePullPolicy: IfNotPresent + command: + - /bin/sh + - -c + - | + FAILED=0 + + {{- if and (include "falcon-kac.openshiftCreateSCC" . | eq "true") .Values.hostNetwork }} + echo "--- Checking SCC exists ---" + if ! kubectl get scc "{{ include "falcon-kac.sccName" . }}" > /dev/null 2>&1; then + echo "[FAIL]: SCC '{{ include "falcon-kac.sccName" . }}' not found" + FAILED=1 + else + echo "[OK]: SCC '{{ include "falcon-kac.sccName" . }}' exists" + fi + + echo "--- Checking service account is bound to SCC ---" + SA_ENTRY="system:serviceaccount:{{ include "falcon-kac.namespace" . }}:{{ .Values.serviceAccount.name }}" + SCC_USERS=$(kubectl get scc "{{ include "falcon-kac.sccName" . }}" -o jsonpath="{.users}" 2>&1) + if ! echo "${SCC_USERS}" | grep -q "${SA_ENTRY}"; then + echo "[FAIL]: Service account '${SA_ENTRY}' is not bound to SCC" + echo "${SCC_USERS}" + FAILED=1 + else + echo "[OK]: Service account is bound to SCC" + fi + {{- end }} + + {{- if .Values.hostNetwork }} + echo "--- Checking Deployment pods have openshift.io/scc annotation ---" + sleep 10 + EXPECTED_SCC="{{ include "falcon-kac.sccName" . }}" + # Exclude test pods by filtering out pods with helm.sh/hook annotation + PODS=$(kubectl get pods -n "{{ include "falcon-kac.namespace" . }}" \ + -l "app={{ include "falcon-kac.name" . }}" \ + -o json 2>&1 | jq -r '.items[] | select(.metadata.annotations["helm.sh/hook"] == null) | .metadata.name') + if [ -z "${PODS}" ]; then + echo "[FAIL]: No KAC pods found" + FAILED=1 + else + for POD in ${PODS}; do + SCC_ANNOTATION=$(kubectl get pod "${POD}" \ + -n "{{ include "falcon-kac.namespace" . }}" \ + -o jsonpath="{.metadata.annotations.openshift\.io/scc}" 2>&1) + if [ -z "${SCC_ANNOTATION}" ]; then + echo "[FAIL]: Pod '${POD}' is missing openshift.io/scc annotation" + FAILED=1 + elif [ "${SCC_ANNOTATION}" != "${EXPECTED_SCC}" ]; then + echo "[FAIL]: Pod '${POD}' has SCC '${SCC_ANNOTATION}' but expected '${EXPECTED_SCC}'" + FAILED=1 + else + echo "[OK]: Pod '${POD}' is using SCC '${SCC_ANNOTATION}'" + fi + done + fi + {{- else }} + echo "--- Skipping SCC annotation check (hostNetwork is disabled) ---" + {{- end }} + + if [ "${FAILED}" -eq 1 ]; then + exit 1 + fi + exit 0 + securityContext: + runAsNonRoot: true + runAsUser: 1000 + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + seccompProfile: + type: RuntimeDefault + capabilities: + drop: + - ALL + serviceAccountName: {{ include "falcon-kac.fullname" . }}-test-sa + restartPolicy: Never +{{- end }} diff --git a/helm-charts/falcon-kac/values.schema.json b/helm-charts/falcon-kac/values.schema.json index cab84cb6..3af1866b 100644 --- a/helm-charts/falcon-kac/values.schema.json +++ b/helm-charts/falcon-kac/values.schema.json @@ -368,6 +368,32 @@ "type": "boolean", "default": "false" }, + "openshift": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean", + "default": false + }, + "createSCC": { + "type": "boolean", + "default": true + }, + "sccName": { + "type": "string", + "default": "" + } + } + }, + "testing": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean", + "default": false + } + } + }, "dnsPolicy": { "type": [ "null", diff --git a/helm-charts/falcon-kac/values.yaml b/helm-charts/falcon-kac/values.yaml index 1bc66ba5..fbbdf850 100644 --- a/helm-charts/falcon-kac/values.yaml +++ b/helm-charts/falcon-kac/values.yaml @@ -186,6 +186,20 @@ webhook: # custom CNI is in use where control plane nodes cannot establish network communication with pods. hostNetwork: false +# OpenShift configuration. +# NOTE: OpenShift is NOT A RECOMMENDED configuration. The official Red Hat certified operator +# (https://catalog.redhat.com/en/software/container-stacks/detail/62f2d38f76d039249424703d) is the +# recommended installation method for OpenShift clusters. +openshift: + # Set to true to enable OpenShift compatibility mode. + enabled: false + # Set to true to create a SecurityContextConstraints (SCC) resource. Only applies when + # openshift.enabled and hostNetwork are also true; the default restricted-v2 SCC is sufficient otherwise. + createSCC: true + # Name of the SCC to create or use. If empty, defaults to the release fullname. + # Set createSCC: false and provide a name here to reference an existing SCC. + sccName: "" + # Define Falcon KAC POD DNS Policy, follows cluster default when not set and sets "ClusterFirstWithHostNet" when hostNetwork = true unless overriden dnsPolicy: @@ -203,3 +217,10 @@ global: containerRegistry: pullSecret: "" configJSON: "" + openshift: + enabled: false + createSCC: true + +# Deploys the test suite during install for testing purposes. +testing: + enabled: false diff --git a/helm-charts/falcon-platform/README.md b/helm-charts/falcon-platform/README.md index 7df63d71..f3d34fd9 100644 --- a/helm-charts/falcon-platform/README.md +++ b/helm-charts/falcon-platform/README.md @@ -333,6 +333,8 @@ Global settings apply to all components unless component specific values are set | global.falconSecret.secretName | "" | Name of existing Kubernetes secret with sensitive data necessary for Falcon component installation | | global.containerRegistry.configJSON | "" | Your container registry config json as a base64 encoded string | | global.containerRegistry.pullSecret | "" | Name of existing container registry pull secret as an alternative to `registryConfigJSON` | +| global.openshift.enabled | false | Enable OpenShift compatibility mode for all Falcon components | +| global.openshift.createSCC | true | Create SecurityContextConstraints resources. Set to `false` to manage SCCs outside of Helm | > [!NOTE] Any existing secrets for `falconSecret` or `containerRegistry.pullSecret` must exist in the namespace dedicated to the respective Falcon component before installing the Helm chart. For example, you must already have an existing secret matching `global.falconSecret.secretName` in the `falcon-sensor` default namespace, or custom namespace you choose for your `falcon-sensor.namespaceOverride`. diff --git a/helm-charts/falcon-platform/values.schema.json b/helm-charts/falcon-platform/values.schema.json index 8e05428c..a9a47199 100644 --- a/helm-charts/falcon-platform/values.schema.json +++ b/helm-charts/falcon-platform/values.schema.json @@ -59,6 +59,22 @@ "description": "Base64 encoded docker config json for accessing private registries" } } + }, + "openshift": { + "type": "object", + "description": "Global OpenShift compatibility configuration applied to all Falcon components", + "properties": { + "enabled": { + "type": "boolean", + "description": "Enable OpenShift compatibility mode for all components", + "default": false + }, + "createSCC": { + "type": "boolean", + "description": "Create SecurityContextConstraints resources. Set to false to manage SCCs outside of Helm.", + "default": true + } + } } } }, diff --git a/helm-charts/falcon-platform/values.yaml b/helm-charts/falcon-platform/values.yaml index cce434c7..f4142d14 100644 --- a/helm-charts/falcon-platform/values.yaml +++ b/helm-charts/falcon-platform/values.yaml @@ -37,6 +37,17 @@ global: # $ cat ~/.docker/config.json | base64 - configJSON: "" + # OpenShift compatibility configuration. + # When enabled, each sub-chart will create the SecurityContextConstraints resource required + # for its workload. Chart-level openshift values take precedence over these globals. + # NOTE: OpenShift is not a recommended configuration. The official Red Hat certified + # CrowdStrike Falcon Operator is the recommended installation method for OpenShift clusters. + openshift: + # Set to true to enable OpenShift compatibility mode for all Falcon components. + enabled: false + # Set to false to manage SecurityContextConstraints resources outside of Helm. + createSCC: true + # Enable/disable individual components # Set enabled: true to deploy, enabled: false to skip diff --git a/helm-charts/falcon-sensor/README.md b/helm-charts/falcon-sensor/README.md index 85a0ff6d..46421ce7 100644 --- a/helm-charts/falcon-sensor/README.md +++ b/helm-charts/falcon-sensor/README.md @@ -39,6 +39,12 @@ The Falcon Helm chart has been tested to deploy on the following Kubernetes dist * Google Kubernetes Engine (GKE) * Rancher K3s +> **OpenShift:** OpenShift is **not a recommended** configuration for this Helm chart. The +> [official Red Hat certified CrowdStrike Falcon Operator](https://catalog.redhat.com/en/software/container-stacks/detail/62f2d38f76d039249424703d) +> is the recommended installation method for OpenShift clusters. A best-effort +> compatibility mode is available for the node DaemonSet only (see +> [OpenShift Compatibility](#openshift-compatibility) below). + # Dependencies 1. Requires a x86_64 or ARM64 Kubernetes cluster @@ -604,3 +610,57 @@ After validating the removal of the `/opt/Crowdstrike` directory, the cleanup Da ```bash helm uninstall falcon-helm -n \ ``` + +## OpenShift Compatibility + +> **Note:** OpenShift is **not a recommended** configuration for this Helm chart. The +> [official Red Hat certified CrowdStrike Falcon Operator](https://catalog.redhat.com/en/software/container-stacks/detail/62f2d38f76d039249424703d) +> is the recommended installation method for OpenShift clusters. + +This chart provides a best-effort compatibility mode for deploying the Falcon node sensor as a DaemonSet on OpenShift clusters. **The container sensor (`container.enabled`) is not supported on OpenShift** and will produce an error if enabled alongside `node.openshift.enabled`. + +### Security Context Constraints + +OpenShift uses Security Context Constraints (SCC) to control pod privileges. Because the Falcon node sensor requires privileged host access (`hostPID`, `hostIPC`, `hostNetwork`, and a privileged container), the default `restricted` SCC is insufficient. + +When `node.openshift.enabled=true` and `node.openshift.createSCC=true`, the chart creates an SCC named `-falcon-sensor-node-sensor` and binds it to the DaemonSet and cleanup DaemonSet service accounts. + +**Helm User Permissions:** When `node.openshift.createSCC: true`, the user or service account running Helm must have permission to create, update, and delete `SecurityContextConstraints` resources at the cluster level. + +To use an existing SCC instead, set `node.openshift.createSCC=false`, define `node.openshift.sccName` with the name of your SCC, and ensure the SCC is created prior to deployment. The SCC must be bound to the service accounts manually before installing. + +OpenShift runs Pod Security Admission in warn/audit mode alongside SCCs. To suppress PSA warnings for the install namespace, label it before installing: + +```bash +kubectl label namespace falcon-system \ + pod-security.kubernetes.io/enforce=privileged \ + pod-security.kubernetes.io/warn=privileged \ + pod-security.kubernetes.io/audit=privileged +``` + +### OpenShift Values + +| Parameter | Description | Default | +|:-------------------------------|:--------------------------------------------------------------------------------------------------------------|:------------------------------------------| +| `node.openshift.enabled` | Enable OpenShift compatibility mode for the node DaemonSet | `false` | +| `node.openshift.createSCC` | Create a `SecurityContextConstraints` resource granting the DaemonSet service account the required privileges | `true` | +| `node.openshift.sccName` | Name of the SCC to create or use. If empty, defaults to `-falcon-sensor-node-sensor` | `""` (auto-generated from release name) | + +### Pod Security Standards (PSS) Namespace Labeling + +Kubernetes 1.23+ enforces Pod Security Standards (PSS) which can block privileged pods like the Falcon node sensor. Apply the required `privileged` PSS labels to the install namespace before installing: + +```bash +kubectl label namespace \ + pod-security.kubernetes.io/enforce=privileged \ + pod-security.kubernetes.io/warn=privileged \ + pod-security.kubernetes.io/audit=privileged +``` + +In automated testing environments, set `testing.labelNamespace: true` to apply these labels automatically via a pre-install Job. This requires outbound access to `docker.io` and is not suitable for air-gapped or registry-restricted environments. + +### Installing on OpenShift + +Set `node.openshift.enabled=true` to enable OpenShift compatibility mode. The chart will automatically create and manage the required SCC unless `node.openshift.createSCC=false` is set. + +For additional configuration options, see the [OpenShift Values](#openshift-values) table above. diff --git a/helm-charts/falcon-sensor/templates/_helpers.tpl b/helm-charts/falcon-sensor/templates/_helpers.tpl index 2dba1244..ec17bb7e 100644 --- a/helm-charts/falcon-sensor/templates/_helpers.tpl +++ b/helm-charts/falcon-sensor/templates/_helpers.tpl @@ -337,3 +337,37 @@ Validate AITap configuration. {{- fail "AITap: 'container.aitap.aidrSecretName' is required when 'container.aitap.useExistingSecret' is true. Set this to the name of the existing secret that contains the AI-DR collector token." -}} {{- end -}} {{- end -}} + +{{/* +OpenShift SCC name for the node DaemonSet. +*/}} +{{- define "falcon-sensor.sccName" -}} +{{- if .Values.node.openshift.sccName -}} +{{- .Values.node.openshift.sccName -}} +{{- else -}} +{{- printf "%s-node-sensor" (include "falcon-sensor.fullname" .) -}} +{{- end -}} +{{- end -}} + +{{/* +OpenShift mode enabled — true if either chart-level or global is true. +*/}} +{{- define "falcon-sensor.openshiftEnabled" -}} +{{- or .Values.node.openshift.enabled (default false .Values.global.openshift.enabled) -}} +{{- end -}} + +{{/* +OpenShift createSCC — false if either chart-level or global disables it. +*/}} +{{- define "falcon-sensor.openshiftCreateSCC" -}} +{{- and .Values.node.openshift.createSCC .Values.global.openshift.createSCC -}} +{{- end -}} + +{{/* +Validate OpenShift configuration: container sensor is not supported in OpenShift mode. +*/}} +{{- define "falcon-sensor.validateOpenshiftConfig" -}} +{{- if and (include "falcon-sensor.openshiftEnabled" .) .Values.container.enabled -}} +{{- fail "OpenShift mode (node.openshift.enabled) is only supported with the node DaemonSet. The container sensor (container.enabled) is not fully supported on OpenShift." -}} +{{- end -}} +{{- end -}} diff --git a/helm-charts/falcon-sensor/templates/clusterrole.yaml b/helm-charts/falcon-sensor/templates/clusterrole.yaml index 064932fc..498b3401 100644 --- a/helm-charts/falcon-sensor/templates/clusterrole.yaml +++ b/helm-charts/falcon-sensor/templates/clusterrole.yaml @@ -39,4 +39,14 @@ rules: - watch - list {{- end }} +{{- if and (include "falcon-sensor.openshiftEnabled" . | eq "true") (not (include "falcon-sensor.openshiftCreateSCC" . | eq "true")) .Values.node.openshift.sccName }} +- apiGroups: + - security.openshift.io + resources: + - securitycontextconstraints + resourceNames: + - {{ include "falcon-sensor.sccName" . }} + verbs: + - use +{{- end }} {{- end }} diff --git a/helm-charts/falcon-sensor/templates/container_deployment_webhook.yaml b/helm-charts/falcon-sensor/templates/container_deployment_webhook.yaml index bbe7e1b5..1f0eef79 100644 --- a/helm-charts/falcon-sensor/templates/container_deployment_webhook.yaml +++ b/helm-charts/falcon-sensor/templates/container_deployment_webhook.yaml @@ -1,4 +1,5 @@ {{- if .Values.container.enabled }} +{{- include "falcon-sensor.validateOpenshiftConfig" . }} {{- $namespace := (include "falcon-sensor.namespace" .) -}} {{- $name := (printf "%s-injector" (include "falcon-sensor.name" .)) -}} {{- $fullName := (printf "%s.%s.svc" $name $namespace) -}} diff --git a/helm-charts/falcon-sensor/templates/daemonset.yaml b/helm-charts/falcon-sensor/templates/daemonset.yaml index 445fdeaa..4d2e9d93 100644 --- a/helm-charts/falcon-sensor/templates/daemonset.yaml +++ b/helm-charts/falcon-sensor/templates/daemonset.yaml @@ -43,6 +43,9 @@ spec: metadata: annotations: {{ .Values.node.daemonset.podAnnotationKey }}: disabled + {{- if include "falcon-sensor.openshiftEnabled" . | eq "true" }} + openshift.io/scc: {{ include "falcon-sensor.sccName" . }} + {{- end }} {{- range $key, $value := .Values.node.podAnnotations }} {{ $key }}: {{ $value | quote }} {{- end }} diff --git a/helm-charts/falcon-sensor/templates/node_cleanup.yaml b/helm-charts/falcon-sensor/templates/node_cleanup.yaml index 383aa557..7178b910 100644 --- a/helm-charts/falcon-sensor/templates/node_cleanup.yaml +++ b/helm-charts/falcon-sensor/templates/node_cleanup.yaml @@ -41,6 +41,9 @@ spec: metadata: annotations: {{ .Values.node.daemonset.podAnnotationKey }}: disabled + {{- if include "falcon-sensor.openshiftEnabled" . | eq "true" }} + openshift.io/scc: {{ include "falcon-sensor.sccName" . }} + {{- end }} {{- range $key, $value := .Values.node.podAnnotations }} {{ $key }}: {{ $value | quote }} {{- end }} diff --git a/helm-charts/falcon-sensor/templates/scc.yaml b/helm-charts/falcon-sensor/templates/scc.yaml new file mode 100644 index 00000000..64563860 --- /dev/null +++ b/helm-charts/falcon-sensor/templates/scc.yaml @@ -0,0 +1,52 @@ +{{- if and .Values.node.enabled (include "falcon-sensor.openshiftEnabled" . | eq "true") (include "falcon-sensor.openshiftCreateSCC" . | eq "true") }} +# NOTE: Helm on an OpenShift cluster is NOT THE RECOMMENDED configuration. The official Red Hat certified falcon-operator +# (https://catalog.redhat.com/en/software/container-stacks/detail/62f2d38f76d039249424703d) is the +# recommended installation method for OpenShift clusters. +apiVersion: security.openshift.io/v1 +kind: SecurityContextConstraints +metadata: + name: {{ include "falcon-sensor.sccName" . }} + labels: + {{- include "falcon-sensor.labels" . | nindent 4 }} + annotations: + kubernetes.io/description: >- + Grants the Falcon node sensor DaemonSet the privileges required to monitor + and protect cluster nodes. This SCC is required because the Falcon sensor + runs as a privileged process in the host's kernel. Using the falcon-helm + charts on OpenShift is not the recommended configuration; use the official + Red Hat certified operator for production deployments. +allowHostDirVolumePlugin: true +allowHostIPC: true +allowHostNetwork: true +allowHostPID: true +allowHostPorts: false +allowPrivilegeEscalation: true +allowPrivilegedContainer: true +allowedCapabilities: + - '*' +defaultAddCapabilities: [] +fsGroup: + type: RunAsAny +readOnlyRootFilesystem: false +requiredDropCapabilities: [] +runAsUser: + type: RunAsAny +seLinuxContext: + type: RunAsAny +seccompProfiles: + - '*' +supplementalGroups: + type: RunAsAny +users: + - system:serviceaccount:{{ include "falcon-sensor.namespace" . }}:{{ .Values.serviceAccount.name }} + - system:serviceaccount:{{ include "falcon-sensor.namespace" . }}:{{ include "falcon-sensor.cleanupServiceAccountName" . }} +groups: [] +volumes: + - configMap + - downwardAPI + - emptyDir + - hostPath + - persistentVolumeClaim + - projected + - secret +{{- end }} diff --git a/helm-charts/falcon-sensor/templates/tests/setup-namespace-labels.yaml b/helm-charts/falcon-sensor/templates/tests/setup-namespace-labels.yaml new file mode 100644 index 00000000..c16bd2bc --- /dev/null +++ b/helm-charts/falcon-sensor/templates/tests/setup-namespace-labels.yaml @@ -0,0 +1,77 @@ +{{- if and .Values.testing.labelNamespace .Values.node.enabled }} +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ include "falcon-sensor.fullname" . }}-ns-labeler + namespace: {{ include "falcon-sensor.namespace" . }} + labels: + {{- include "falcon-sensor.labels" . | nindent 4 }} + annotations: + "helm.sh/hook": pre-install,pre-upgrade + "helm.sh/hook-weight": "-10" + "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: {{ include "falcon-sensor.fullname" . }}-ns-labeler + labels: + {{- include "falcon-sensor.labels" . | nindent 4 }} + annotations: + "helm.sh/hook": pre-install,pre-upgrade + "helm.sh/hook-weight": "-10" + "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded +rules: + - apiGroups: [""] + resources: ["namespaces"] + verbs: ["get", "patch"] +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: {{ include "falcon-sensor.fullname" . }}-ns-labeler + labels: + {{- include "falcon-sensor.labels" . | nindent 4 }} + annotations: + "helm.sh/hook": pre-install,pre-upgrade + "helm.sh/hook-weight": "-10" + "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded +subjects: + - kind: ServiceAccount + name: {{ include "falcon-sensor.fullname" . }}-ns-labeler + namespace: {{ include "falcon-sensor.namespace" . }} +roleRef: + kind: ClusterRole + name: {{ include "falcon-sensor.fullname" . }}-ns-labeler + apiGroup: rbac.authorization.k8s.io +--- +apiVersion: batch/v1 +kind: Job +metadata: + name: {{ include "falcon-sensor.fullname" . }}-ns-labeler + namespace: {{ include "falcon-sensor.namespace" . }} + labels: + {{- include "falcon-sensor.labels" . | nindent 4 }} + annotations: + "helm.sh/hook": pre-install,pre-upgrade + "helm.sh/hook-weight": "-5" + "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded +spec: + template: + spec: + serviceAccountName: {{ include "falcon-sensor.fullname" . }}-ns-labeler + restartPolicy: OnFailure + containers: + - name: kubectl + image: docker.io/bitnami/kubectl + command: + - kubectl + - label + - namespace + - {{ include "falcon-sensor.namespace" . }} + - "pod-security.kubernetes.io/enforce=privileged" + - "pod-security.kubernetes.io/warn=privileged" + - "pod-security.kubernetes.io/audit=privileged" + - "--overwrite" +{{- end }} diff --git a/helm-charts/falcon-sensor/templates/tests/test-cluster-permissions.yaml b/helm-charts/falcon-sensor/templates/tests/test-cluster-permissions.yaml index bc7f30b7..bb03b520 100644 --- a/helm-charts/falcon-sensor/templates/tests/test-cluster-permissions.yaml +++ b/helm-charts/falcon-sensor/templates/tests/test-cluster-permissions.yaml @@ -34,6 +34,7 @@ rules: verbs: - get - list + - watch - apiGroups: - rbac.authorization.k8s.io resources: @@ -50,6 +51,22 @@ rules: verbs: - get - list +{{- if (include "falcon-sensor.openshiftEnabled" . | eq "true") }} +- apiGroups: + - security.openshift.io + resources: + - securitycontextconstraints + verbs: + - get +- apiGroups: + - security.openshift.io + resources: + - securitycontextconstraints + resourceNames: + - {{ include "falcon-sensor.sccName" . }} + verbs: + - use +{{- end }} --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding diff --git a/helm-charts/falcon-sensor/templates/tests/test-ds-sensor-running.yaml b/helm-charts/falcon-sensor/templates/tests/test-ds-sensor-running.yaml index 98a09588..478b0865 100644 --- a/helm-charts/falcon-sensor/templates/tests/test-ds-sensor-running.yaml +++ b/helm-charts/falcon-sensor/templates/tests/test-ds-sensor-running.yaml @@ -20,7 +20,10 @@ spec: FAILED=0 echo "--- Waiting for pods to initialize ---" - sleep 10 + kubectl rollout status daemonset \ + "{{ include "falcon-sensor.fullname" . }}" \ + -n "{{ include "falcon-sensor.namespace" . }}" \ + --timeout=300s echo "--- Checking all sensor pods are running ---" KUBECMD=$(kubectl get pods -n "{{ include "falcon-sensor.namespace" . }}" -l "app.kubernetes.io/component=kernel_sensor" --field-selector=status.phase!=Running --no-headers 2>&1) @@ -109,6 +112,22 @@ spec: done fi + {{- if .Values.testing.labelNamespace }} + echo "--- Checking PSS namespace labels ---" + NS_NAME="{{ include "falcon-sensor.namespace" . }}" + for LABEL in "pod-security.kubernetes.io/enforce=privileged" "pod-security.kubernetes.io/warn=privileged" "pod-security.kubernetes.io/audit=privileged"; do + KEY="${LABEL%%=*}" + VALUE="${LABEL##*=}" + ACTUAL=$(kubectl get namespace "${NS_NAME}" -o json 2>&1 | jq -r --arg key "${KEY}" '.metadata.labels[$key] // ""') + if [ "${ACTUAL}" != "${VALUE}" ]; then + echo "[FAIL]: Namespace '${NS_NAME}' label '${KEY}' is '${ACTUAL}', expected '${VALUE}'" + FAILED=1 + else + echo "[OK]: Namespace '${NS_NAME}' has label '${KEY}=${VALUE}'" + fi + done + {{- end }} + echo "--- Checking pod labels ---" for POD in ${PODS}; do # Check component label diff --git a/helm-charts/falcon-sensor/templates/tests/test-openshift.yaml b/helm-charts/falcon-sensor/templates/tests/test-openshift.yaml new file mode 100644 index 00000000..7ac82c74 --- /dev/null +++ b/helm-charts/falcon-sensor/templates/tests/test-openshift.yaml @@ -0,0 +1,107 @@ +{{- if and .Values.testing.enabled .Values.node.enabled (include "falcon-sensor.openshiftEnabled" . | eq "true") }} +apiVersion: v1 +kind: Pod +metadata: + name: "{{ include "falcon-sensor.fullname" . }}-test-openshift" + namespace: {{ include "falcon-sensor.namespace" . }} + labels: + {{- include "falcon-sensor.labels" . | nindent 4 }} + annotations: + "helm.sh/hook": test-success +spec: + containers: + - name: kubectl + image: docker.io/bitnami/kubectl + imagePullPolicy: IfNotPresent + command: + - /bin/sh + - -c + - | + FAILED=0 + + {{- if (include "falcon-sensor.openshiftCreateSCC" . | eq "true") }} + echo "--- Checking SCC exists ---" + if ! kubectl get scc "{{ include "falcon-sensor.sccName" . }}" > /dev/null 2>&1; then + echo "[FAIL]: SCC '{{ include "falcon-sensor.sccName" . }}' not found" + FAILED=1 + else + echo "[OK]: SCC '{{ include "falcon-sensor.sccName" . }}' exists" + fi + + echo "--- Checking service account is bound to SCC ---" + SA_ENTRY="system:serviceaccount:{{ include "falcon-sensor.namespace" . }}:{{ .Values.serviceAccount.name }}" + SCC_USERS=$(kubectl get scc "{{ include "falcon-sensor.sccName" . }}" -o jsonpath="{.users}" 2>&1) + if ! echo "${SCC_USERS}" | grep -q "${SA_ENTRY}"; then + echo "[FAIL]: Service account '${SA_ENTRY}' is not bound to SCC" + echo "${SCC_USERS}" + FAILED=1 + else + echo "[OK]: Service account is bound to SCC" + fi + {{- end }} + + echo "--- Checking DaemonSet pods have openshift.io/scc annotation ---" + sleep 10 + EXPECTED_SCC="{{ include "falcon-sensor.sccName" . }}" + + # Check main sensor DaemonSet pods + SENSOR_PODS=$(kubectl get pods -n "{{ include "falcon-sensor.namespace" . }}" \ + -l "app.kubernetes.io/component=kernel_sensor" \ + -o jsonpath="{.items[*].metadata.name}" 2>&1) + if [ -z "${SENSOR_PODS}" ]; then + echo "[FAIL]: No DaemonSet sensor pods found" + FAILED=1 + else + for POD in ${SENSOR_PODS}; do + SCC_ANNOTATION=$(kubectl get pod "${POD}" \ + -n "{{ include "falcon-sensor.namespace" . }}" \ + -o jsonpath="{.metadata.annotations.openshift\.io/scc}" 2>&1) + if [ -z "${SCC_ANNOTATION}" ]; then + echo "[FAIL]: Sensor pod '${POD}' is missing openshift.io/scc annotation" + FAILED=1 + elif [ "${SCC_ANNOTATION}" != "${EXPECTED_SCC}" ]; then + echo "[FAIL]: Sensor pod '${POD}' has SCC '${SCC_ANNOTATION}' but expected '${EXPECTED_SCC}'" + FAILED=1 + else + echo "[OK]: Sensor pod '${POD}' is using SCC '${SCC_ANNOTATION}'" + fi + done + fi + + # Check cleanup DaemonSet pods if they exist + CLEANUP_PODS=$(kubectl get pods -n "{{ include "falcon-sensor.namespace" . }}" \ + -l "app.kubernetes.io/component=cleanup" \ + -o jsonpath="{.items[*].metadata.name}" 2>&1) + if [ -n "${CLEANUP_PODS}" ]; then + echo "--- Checking cleanup DaemonSet pods have openshift.io/scc annotation ---" + for POD in ${CLEANUP_PODS}; do + SCC_ANNOTATION=$(kubectl get pod "${POD}" \ + -n "{{ include "falcon-sensor.namespace" . }}" \ + -o jsonpath="{.metadata.annotations.openshift\.io/scc}" 2>&1) + if [ -z "${SCC_ANNOTATION}" ]; then + echo "[FAIL]: Cleanup pod '${POD}' is missing openshift.io/scc annotation" + FAILED=1 + elif [ "${SCC_ANNOTATION}" != "${EXPECTED_SCC}" ]; then + echo "[FAIL]: Cleanup pod '${POD}' has SCC '${SCC_ANNOTATION}' but expected '${EXPECTED_SCC}'" + FAILED=1 + else + echo "[OK]: Cleanup pod '${POD}' is using SCC '${SCC_ANNOTATION}'" + fi + done + fi + + if [ "${FAILED}" -eq 1 ]; then + exit 1 + fi + exit 0 + securityContext: + runAsNonRoot: true + runAsUser: 1000 + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + capabilities: + drop: + - ALL + serviceAccountName: {{ include "falcon-sensor.fullname" . }}-test-sa + restartPolicy: Never +{{- end }} diff --git a/helm-charts/falcon-sensor/templates/tests/test-sidecar-sensor-running.yaml b/helm-charts/falcon-sensor/templates/tests/test-sidecar-sensor-running.yaml index 8ef7bb36..85a7a298 100644 --- a/helm-charts/falcon-sensor/templates/tests/test-sidecar-sensor-running.yaml +++ b/helm-charts/falcon-sensor/templates/tests/test-sidecar-sensor-running.yaml @@ -65,6 +65,7 @@ spec: containers: - name: kubectl image: docker.io/bitnami/kubectl + imagePullPolicy: IfNotPresent command: - /bin/sh - -c @@ -72,7 +73,11 @@ spec: FAILED=0 echo "--- Waiting for pods to initialize ---" - sleep 10 + kubectl wait deployment \ + "{{ include "falcon-sensor.fullname" . }}" \ + -n "{{ include "falcon-sensor.namespace" . }}" \ + --for=condition=Available \ + --timeout=300s echo "--- Checking all injector pods are running ---" KUBECMD=$(kubectl get pods -n "{{ include "falcon-sensor.namespace" . }}" -l "app.kubernetes.io/component=container_sensor" --field-selector=status.phase!=Running --no-headers 2>&1) diff --git a/helm-charts/falcon-sensor/values.schema.json b/helm-charts/falcon-sensor/values.schema.json index 6cf3e645..18306a2d 100644 --- a/helm-charts/falcon-sensor/values.schema.json +++ b/helm-charts/falcon-sensor/values.schema.json @@ -296,6 +296,23 @@ "example": [ "my_prod_cluster" ] + }, + "openshift": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean", + "default": false + }, + "createSCC": { + "type": "boolean", + "default": true + }, + "sccName": { + "type": "string", + "default": "" + } + } } } }, @@ -562,6 +579,10 @@ "enabled": { "type": "boolean", "default": "false" + }, + "labelNamespace": { + "type": "boolean", + "default": false } } }, diff --git a/helm-charts/falcon-sensor/values.yaml b/helm-charts/falcon-sensor/values.yaml index ec5dd1dd..44bccb0b 100644 --- a/helm-charts/falcon-sensor/values.yaml +++ b/helm-charts/falcon-sensor/values.yaml @@ -111,6 +111,20 @@ node: # Enable, to run cleanup for the prior daemonset deployment cleanupOnly: false + # OpenShift configuration. + # NOTE: OpenShift is NOT A RECOMMENDED configuration. The official Red Hat certified operator + # (https://catalog.redhat.com/en/software/container-stacks/detail/62f2d38f76d039249424703d) is the + # recommended installation method for OpenShift clusters. + # These settings apply only to the node DaemonSet and have no effect on the container sensor. + openshift: + # Set to true to enable OpenShift compatibility mode for the node DaemonSet. + enabled: false + # Set to true to create a SecurityContextConstraints (SCC) resource granting the + # DaemonSet service account the privileges required by the Falcon node sensor. + createSCC: true + # Name of the SCC to create or use. If empty, defaults to "-falcon-sensor-node-sensor" + sccName: "" + container: # When enabled, Helm chart deploys the Falcon Container Sensor to Pods through Webhooks enabled: false @@ -289,6 +303,10 @@ serviceAccount: # Deploys the test suite during install for testing purposes. testing: enabled: false + # Set to true to apply privileged PSS labels to the install namespace as a + # pre-install hook. Requires outbound access to docker.io. Not suitable for + # air-gapped or registry-restricted environments. + labelNamespace: false # falcon.cid is required unless falconSecret.enabled is true and FALCONCTL_OPT_CID is already defined in an existing secret. falcon: @@ -323,3 +341,6 @@ global: containerRegistry: pullSecret: "" configJSON: "" + openshift: + enabled: false + createSCC: true