From b65324175354ca2ac9b2bb5ca86aa873e9ba84b3 Mon Sep 17 00:00:00 2001 From: TomRyan-321 <25472582+TomRyan-321@users.noreply.github.com> Date: Mon, 20 Apr 2026 15:11:33 +1000 Subject: [PATCH] feat: add Azure Key Vault CSI driver support to falcon-sensor, falcon-kac, and falcon-image-analyzer Adds SecretProviderClass templates and helper wiring to source Falcon credentials from Azure Key Vault via the Secrets Store CSI Driver. Shared vault configuration (vaultName, tenantID, clientID) can be set once under global.azure.keyVault in falcon-platform and overridden per component. --- helm-charts/falcon-image-analyzer/README.md | 58 ++++++- .../templates/_helpers.tpl | 51 ++++++- .../templates/daemonset.yaml | 16 +- .../templates/deployment.yaml | 16 +- .../templates/secret-provider-class.yaml | 59 ++++++++ .../falcon-image-analyzer/values.schema.json | 143 +++++++++++++++++- helm-charts/falcon-image-analyzer/values.yaml | 27 ++++ helm-charts/falcon-kac/README.md | 55 +++++++ helm-charts/falcon-kac/templates/_helpers.tpl | 46 +++++- .../templates/deployment_webhook.yaml | 13 ++ .../templates/secret-provider-class.yaml | 53 +++++++ helm-charts/falcon-kac/values.schema.json | 31 ++++ helm-charts/falcon-kac/values.yaml | 36 +++++ helm-charts/falcon-platform/README.md | 91 +++++++++++ helm-charts/falcon-platform/values.yaml | 20 +++ helm-charts/falcon-sensor/README.md | 67 ++++++++ .../falcon-sensor/templates/_helpers.tpl | 46 +++++- .../container_deployment_webhook.yaml | 13 ++ .../falcon-sensor/templates/daemonset.yaml | 13 ++ .../templates/secret-provider-class.yaml | 53 +++++++ helm-charts/falcon-sensor/values.schema.json | 31 ++++ helm-charts/falcon-sensor/values.yaml | 35 +++++ 22 files changed, 961 insertions(+), 12 deletions(-) create mode 100644 helm-charts/falcon-image-analyzer/templates/secret-provider-class.yaml create mode 100644 helm-charts/falcon-kac/templates/secret-provider-class.yaml create mode 100644 helm-charts/falcon-sensor/templates/secret-provider-class.yaml diff --git a/helm-charts/falcon-image-analyzer/README.md b/helm-charts/falcon-image-analyzer/README.md index 036810aa..749c7fc2 100644 --- a/helm-charts/falcon-image-analyzer/README.md +++ b/helm-charts/falcon-image-analyzer/README.md @@ -95,6 +95,11 @@ The following tables list the Falcon sensor configurable parameters and their de | `image.registryConfigJSON` optional | iar private registry secret in docker config format | None | | `azure.enabled` optional | Set to `true` if cluster is Azure AKS or self-managed on Azure nodes. | false | | `azure.azureConfig` optional | Azure config file path | `/etc/kubernetes/azure.json` | +| `azure.keyVault.enabled` optional | Enable Azure Key Vault provider for Secrets Store CSI Driver. Mutually exclusive with `crowdstrikeConfig.clientID`/`clientSecret` and `crowdstrikeConfig.existingSecret`. See [Azure Key Vault Integration](#azure-key-vault-integration). | `false` | +| `azure.keyVault.vaultName` optional | Azure Key Vault name | None | +| `azure.keyVault.tenantID` optional | Azure Tenant ID | None | +| `azure.keyVault.clientID` optional | Azure Workload Identity client ID. Only required if multiple managed identities are assigned to the node. | None | +| `podLabels` optional | Additional labels to add to pod metadata. Use to set `azure.workload.identity/use: "true"` for Azure Workload Identity. | `{}` | | `gcp.enabled` optional | Set to `true` if cluster is Google GKE or self-managed on Google Cloud GCP nodes. | false | | `exclusions.namespace` optional ( available in falcon-imageanalyzer >= 1.0.8 and Helm Chart v >= 1.1.3) | Set the value as a comma separate list of namespaces to be excluded. all pods in that namespace(s) will be excluded | "" | | `exclusions.registry` optional ( available in falcon-imageanalyzer >= 1.0.8 and Helm Chart v >= 1.1.3) | Set the value as a comma separate list of registries to be excluded. all images in that registry(s) will be excluded | "" | @@ -108,7 +113,7 @@ The following tables list the Falcon sensor configurable parameters and their de | `crowdstrikeConfig.enableKlogs` optional | Set to `true` for kubernetes api log verbosity. | false | | `crowdstrikeConfig.clientID` required | CrowdStrike Falcon OAuth API Client ID | None | | `crowdstrikeConfig.clientSecret` required | CrowdStrike Falcon OAuth API Client secret | None | -| `crowdstrikeConfig.cid` required | Customer ID (CID) | None | +| `crowdstrikeConfig.cid` required unless `azure.keyVault.enabled` is true and `falcon-cid` is stored in AKV | Customer ID (CID) | None | | `crowdstrikeConfig.dockerAPIToken` optional | Crowdstrike Artifactory Image Pull Token for pulling IAR image directly from `[CROWDSTRIKE_IMAGE_REGISTRY] described below` | None | | `crowdstrikeConfig.existingSecret` optional | Existing secret ref name of the customer Kubernetes cluster | None | | `crowdstrikeConfig.agentRegion` required | Region of the CrowdStrike API to connect to value should be one of `us-1/us-2/eu-1/gov1/gov2` | None | @@ -406,6 +411,57 @@ and a trust-relationship as Here `falcon-image-analyzer` is the namespace of IAR and `imageanalyzer-falcon-image-analyzer` is the name of the iar Service Account +### Azure Key Vault Integration + +The chart supports sourcing `AGENT_CLIENT_ID` and `AGENT_CLIENT_SECRET` from [Azure Key Vault](https://azure.microsoft.com/en-us/products/key-vault) via the [Secrets Store CSI Driver](https://secrets-store-csi-driver.sigs.k8s.io/) and the [Azure Key Vault provider](https://azure.github.io/secrets-store-csi-driver-provider-azure/). This avoids storing sensitive values in Helm values or Kubernetes Secrets directly. + +#### Prerequisites + +The following must be installed and configured on your AKS cluster before enabling this feature: + +- [Secrets Store CSI Driver](https://secrets-store-csi-driver.sigs.k8s.io/getting-started/installation) +- [Azure Key Vault Provider for Secrets Store CSI Driver](https://azure.github.io/secrets-store-csi-driver-provider-azure/docs/getting-started/installation/) +- [Azure Workload Identity](https://azure.github.io/azure-workload-identity/docs/installation.html) webhook installed on the cluster +- AKS cluster with OIDC issuer enabled (`az aks update --enable-oidc-issuer --name --resource-group `) +- A user-assigned managed identity with `Key Vault Secrets User` role on the vault +- A federated credential binding the managed identity to the chart's ServiceAccount in the `falcon-image-analyzer` namespace + +#### Required secrets in Azure Key Vault + +Create the following secrets in your Azure Key Vault before enabling the integration: + +| Secret name | Required | Value | +|:------------------------|:---------|:-----------------------------------------| +| `falcon-client-id` | Yes | CrowdStrike Falcon OAuth API Client ID | +| `falcon-client-secret` | Yes | CrowdStrike Falcon OAuth API Client Secret | +| `falcon-cid` | Only if `crowdstrikeConfig.cid` and `global.falcon.cid` are not set | CrowdStrike Customer ID (CID) | + +The `falcon-client-id` and `falcon-client-secret` secret names are fixed. `falcon-cid` is only fetched from AKV when CID is not supplied via `crowdstrikeConfig.cid` or `global.falcon.cid` — if either is set, `AGENT_CID` is sourced from the ConfigMap instead and `falcon-cid` does not need to exist in the vault. + +#### Configuration + +```yaml +azure: + keyVault: + enabled: true + vaultName: "my-keyvault" + tenantID: "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" + # clientID is optional - only required if multiple managed identities are assigned + clientID: "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" + +serviceAccount: + annotations: + azure.workload.identity/client-id: "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" + +# Add the Workload Identity label to the pod +podLabels: + azure.workload.identity/use: "true" +``` + +> [!NOTE] +> `azure.keyVault.enabled` cannot be combined with `crowdstrikeConfig.clientID`/`clientSecret` or `crowdstrikeConfig.existingSecret`. These are mutually exclusive secret sources for credentials. +> CID can still be supplied via `crowdstrikeConfig.cid` or `global.falcon.cid` alongside AKV — if either is set, `falcon-cid` is not fetched from the vault. + ### Authentication for Private Registries - If you are using ECR or cloud based Private Registries then assigning the IAM role to the iar service-account in `falcon-image-analyzer` namespace should be enough diff --git a/helm-charts/falcon-image-analyzer/templates/_helpers.tpl b/helm-charts/falcon-image-analyzer/templates/_helpers.tpl index 218a220d..ed2b729c 100644 --- a/helm-charts/falcon-image-analyzer/templates/_helpers.tpl +++ b/helm-charts/falcon-image-analyzer/templates/_helpers.tpl @@ -91,6 +91,18 @@ app.kubernetes.io/name: {{ include "falcon-image-analyzer.name" . }} app.kubernetes.io/instance: {{ .Release.Name }} {{- end }} +{{/* +Pod template labels — standard chart labels plus any user-supplied podLabels. +Use instead of falcon-image-analyzer.labels in pod template metadata. +Example use: azure.workload.identity/use: "true" for Azure Workload Identity. +*/}} +{{- define "falcon-image-analyzer.podLabels" -}} +{{- include "falcon-image-analyzer.labels" . }} +{{- with .Values.podLabels }} +{{ toYaml . }} +{{- end }} +{{- end }} + {{/* Create the name of the service account to use */}} @@ -234,16 +246,51 @@ Get Falcon CID from global value if it exists {{- end -}} {{/* -Get Falcon secret name from global value if it exists +Get Falcon secret name from global value if it exists. +When Azure Key Vault is enabled, returns the name of the secret that +SecretProviderClass.secretObjects will sync from AKV, so that the +existing envFrom/secretRef wiring picks it up unchanged. */}} {{- define "falcon-image-analyzer.falconSecretName" -}} -{{- if and .Values.global.falconSecret.secretName (not .Values.crowdstrikeConfig.existingSecret) -}} +{{- if (include "falcon-image-analyzer.akvEnabled" .) | eq "true" -}} +{{- printf "%s-akv" (include "falcon-image-analyzer.fullname" .) -}} +{{- else if and .Values.global.falconSecret.secretName (not .Values.crowdstrikeConfig.existingSecret) -}} {{- .Values.global.falconSecret.secretName -}} {{- else -}} {{- .Values.crowdstrikeConfig.existingSecret -}} {{- end -}} {{- end -}} +{{/* +Returns true when AKV is enabled locally or globally. +*/}} +{{- define "falcon-image-analyzer.akvEnabled" -}} +{{- if or .Values.azure.keyVault.enabled .Values.global.azure.keyVault.enabled -}} +true +{{- end -}} +{{- end -}} + +{{/* +Returns the effective AKV vault name (local overrides global). +*/}} +{{- define "falcon-image-analyzer.akvVaultName" -}} +{{- .Values.azure.keyVault.vaultName | default .Values.global.azure.keyVault.vaultName -}} +{{- end -}} + +{{/* +Returns the effective AKV tenant ID (local overrides global). +*/}} +{{- define "falcon-image-analyzer.akvTenantID" -}} +{{- .Values.azure.keyVault.tenantID | default .Values.global.azure.keyVault.tenantID -}} +{{- end -}} + +{{/* +Returns the effective AKV workload identity client ID (local overrides global). +*/}} +{{- define "falcon-image-analyzer.akvClientID" -}} +{{- .Values.azure.keyVault.clientID | default .Values.global.azure.keyVault.clientID -}} +{{- end -}} + {{/* Get container registry pull secret from global value if it exists */}} diff --git a/helm-charts/falcon-image-analyzer/templates/daemonset.yaml b/helm-charts/falcon-image-analyzer/templates/daemonset.yaml index 67ed8157..eea4a531 100644 --- a/helm-charts/falcon-image-analyzer/templates/daemonset.yaml +++ b/helm-charts/falcon-image-analyzer/templates/daemonset.yaml @@ -55,7 +55,8 @@ spec: checksum/config: {{ include (print $.Template.BasePath "/configmap.yaml") . | sha256sum }} checksum/secret: {{ include (print $.Template.BasePath "/secret.yaml") . | sha256sum }} labels: - {{- include "falcon-image-analyzer.labels" . | nindent 8 }} + {{- /* podLabels includes all standard chart labels plus optional user-supplied podLabels values */ -}} + {{- include "falcon-image-analyzer.podLabels" . | nindent 8 }} spec: {{- if or $imagePullSecret $registryConfigJson (.Values.crowdstrikeConfig.dockerAPIToken) }} imagePullSecrets: @@ -141,6 +142,11 @@ spec: {{- if .Values.azure.enabled }} - name: azure-config mountPath: /etc/kubernetes/azure.json + {{- end }} + {{- if .Values.azure.keyVault.enabled }} + - name: akv-secrets + mountPath: /mnt/secrets-store + readOnly: true {{- end }} - mountPath: /run/secrets/tls name: {{ include "falcon-image-analyzer.name" . }}-tls-certs @@ -157,6 +163,14 @@ spec: path: {{ .Values.azure.azureConfig }} type: File {{- end }} + {{- if .Values.azure.keyVault.enabled }} + - name: akv-secrets + csi: + driver: secrets-store.csi.k8s.io + readOnly: true + volumeAttributes: + secretProviderClass: {{ include "falcon-image-analyzer.fullname" . }}-akv + {{- end }} - name: {{ include "falcon-image-analyzer.name" . }}-tls-certs secret: secretName: {{ include "falcon-image-analyzer.name" . }}-tls diff --git a/helm-charts/falcon-image-analyzer/templates/deployment.yaml b/helm-charts/falcon-image-analyzer/templates/deployment.yaml index d9ae9fa3..beeab77e 100644 --- a/helm-charts/falcon-image-analyzer/templates/deployment.yaml +++ b/helm-charts/falcon-image-analyzer/templates/deployment.yaml @@ -56,7 +56,8 @@ spec: checksum/config: {{ include (print $.Template.BasePath "/configmap.yaml") . | sha256sum }} checksum/secret: {{ include (print $.Template.BasePath "/secret.yaml") . | sha256sum }} labels: - {{- include "falcon-image-analyzer.labels" . | nindent 8 }} + {{- /* podLabels includes all standard chart labels plus optional user-supplied podLabels values */ -}} + {{- include "falcon-image-analyzer.podLabels" . | nindent 8 }} spec: {{- if or $imagePullSecret $registryConfigJson (.Values.crowdstrikeConfig.dockerAPIToken) }} imagePullSecrets: @@ -152,6 +153,11 @@ spec: {{- if .Values.azure.enabled }} - name: azure-config mountPath: /etc/kubernetes/azure.json + {{- end }} + {{- if .Values.azure.keyVault.enabled }} + - name: akv-secrets + mountPath: /mnt/secrets-store + readOnly: true {{- end }} - mountPath: /run/secrets/tls name: {{ include "falcon-image-analyzer.name" . }}-tls-certs @@ -168,6 +174,14 @@ spec: path: {{ .Values.azure.azureConfig }} type: File {{- end }} + {{- if .Values.azure.keyVault.enabled }} + - name: akv-secrets + csi: + driver: secrets-store.csi.k8s.io + readOnly: true + volumeAttributes: + secretProviderClass: {{ include "falcon-image-analyzer.fullname" . }}-akv + {{- end }} - name: {{ include "falcon-image-analyzer.name" . }}-tls-certs secret: secretName: {{ include "falcon-image-analyzer.name" . }}-tls diff --git a/helm-charts/falcon-image-analyzer/templates/secret-provider-class.yaml b/helm-charts/falcon-image-analyzer/templates/secret-provider-class.yaml new file mode 100644 index 00000000..8ea194c9 --- /dev/null +++ b/helm-charts/falcon-image-analyzer/templates/secret-provider-class.yaml @@ -0,0 +1,59 @@ +{{- if (include "falcon-image-analyzer.akvEnabled" .) | eq "true" }} +{{- if .Values.crowdstrikeConfig.existingSecret }} + {{- fail "azure.keyVault and crowdstrikeConfig.existingSecret cannot be used together. Use one secret source." }} +{{- end }} +{{- if and .Values.crowdstrikeConfig.clientID .Values.crowdstrikeConfig.clientSecret }} + {{- fail "azure.keyVault and crowdstrikeConfig.clientID/clientSecret cannot be used together. Use one secret source." }} +{{- end }} +{{- if not (include "falcon-image-analyzer.akvVaultName" .) }} + {{- fail "azure.keyVault.vaultName (or global.azure.keyVault.vaultName) is required when AKV is enabled." }} +{{- end }} +{{- if not (include "falcon-image-analyzer.akvTenantID" .) }} + {{- fail "azure.keyVault.tenantID (or global.azure.keyVault.tenantID) is required when AKV is enabled." }} +{{- end }} +apiVersion: secrets-store.csi.x-k8s.io/v1 +kind: SecretProviderClass +metadata: + name: {{ include "falcon-image-analyzer.fullname" . }}-akv + namespace: {{ include "falcon-image-analyzer.namespace" . }} + labels: + {{- include "falcon-image-analyzer.labels" . | nindent 4 }} +spec: + provider: azure + parameters: + usePodIdentity: "false" + useVMManagedIdentity: "false" + {{- if (include "falcon-image-analyzer.akvClientID" .) }} + clientID: {{ include "falcon-image-analyzer.akvClientID" . | quote }} + {{- end }} + keyvaultName: {{ include "falcon-image-analyzer.akvVaultName" . | quote }} + tenantId: {{ include "falcon-image-analyzer.akvTenantID" . | quote }} + objects: | + array: + - | + objectName: "falcon-client-id" + objectType: secret + objectVersion: "" + - | + objectName: "falcon-client-secret" + objectType: secret + objectVersion: "" + {{- if not (or .Values.crowdstrikeConfig.cid .Values.global.falcon.cid) }} + - | + objectName: "falcon-cid" + objectType: secret + objectVersion: "" + {{- end }} + secretObjects: + - secretName: {{ include "falcon-image-analyzer.fullname" . }}-akv + type: Opaque + data: + - objectName: "falcon-client-id" + key: AGENT_CLIENT_ID + - objectName: "falcon-client-secret" + key: AGENT_CLIENT_SECRET + {{- if not (or .Values.crowdstrikeConfig.cid .Values.global.falcon.cid) }} + - objectName: "falcon-cid" + key: AGENT_CID + {{- end }} +{{- end }} diff --git a/helm-charts/falcon-image-analyzer/values.schema.json b/helm-charts/falcon-image-analyzer/values.schema.json index cd0e08b2..a390405d 100644 --- a/helm-charts/falcon-image-analyzer/values.schema.json +++ b/helm-charts/falcon-image-analyzer/values.schema.json @@ -98,6 +98,11 @@ "type": "object", "default": {} }, + "podLabels": { + "type": "object", + "default": {}, + "description": "Additional labels to add to pod metadata. Use to set azure.workload.identity/use: 'true' when using Azure Key Vault." + }, "podSecurityContext": { "type": "object", "default": {} @@ -130,6 +135,42 @@ "volumes": { "type": "array" }, + "azure": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean", + "default": false, + "description": "Mount azure.json from the node for AKS image pull authentication" + }, + "azureConfig": { + "type": "string", + "default": "/etc/kubernetes/azure.json" + }, + "keyVault": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean", + "default": false, + "description": "Enable Azure Key Vault provider for Secrets Store CSI Driver" + }, + "vaultName": { + "type": "string", + "description": "Azure Key Vault name" + }, + "tenantID": { + "type": "string", + "description": "Azure tenant ID" + }, + "clientID": { + "type": "string", + "description": "Azure Workload Identity client ID (optional, overrides pod-level identity)" + } + } + } + } + }, "crowdstrikeConfig": { "type": "object", "properties": { @@ -177,7 +218,6 @@ "string" ], "description": "CrowdStrike CID", - "pattern": "^[0-9a-fA-F]{32}-[0-9a-fA-F]{2}$", "example": [ "1234567890ABCDEF1234567890ABCDEF-12" ] @@ -235,6 +275,20 @@ "pattern": "^[0-9a-fA-F]{32}-[0-9a-fA-F]{2}$" } } + }, + "azure": { + "type": "object", + "properties": { + "keyVault": { + "type": "object", + "properties": { + "enabled": { "type": "boolean" }, + "vaultName": { "type": "string" }, + "tenantID": { "type": "string" }, + "clientID": { "type": "string" } + } + } + } } } } @@ -317,6 +371,28 @@ "const": null } } + }, + "azure": { + "properties": { + "keyVault": { + "properties": { + "enabled": { + "const": false + } + } + } + } + } + } + }, + "azure": { + "properties": { + "keyVault": { + "properties": { + "enabled": { + "const": false + } + } } } } @@ -336,6 +412,49 @@ } } }, + { + "if": { + "properties": { + "global": { + "properties": { + "azure": { + "properties": { + "keyVault": { + "properties": { + "enabled": { + "const": false + } + } + } + } + } + } + }, + "azure": { + "properties": { + "keyVault": { + "properties": { + "enabled": { + "const": false + } + } + } + } + } + } + }, + "then": { + "properties": { + "crowdstrikeConfig": { + "properties": { + "cid": { + "pattern": "^[0-9a-fA-F]{32}-[0-9a-fA-F]{2}$" + } + } + } + } + } + }, { "if": { "properties": { @@ -347,6 +466,28 @@ "const": "" } } + }, + "azure": { + "properties": { + "keyVault": { + "properties": { + "enabled": { + "const": false + } + } + } + } + } + } + }, + "azure": { + "properties": { + "keyVault": { + "properties": { + "enabled": { + "const": false + } + } } } } diff --git a/helm-charts/falcon-image-analyzer/values.yaml b/helm-charts/falcon-image-analyzer/values.yaml index 69b8c97e..05dac691 100644 --- a/helm-charts/falcon-image-analyzer/values.yaml +++ b/helm-charts/falcon-image-analyzer/values.yaml @@ -54,6 +54,8 @@ volumeMounts: podAnnotations: {} +podLabels: {} + podSecurityContext: {} securityContext: {} @@ -86,6 +88,25 @@ azure: # Path to the Kubernetes Azure config file on worker nodes azureConfig: /etc/kubernetes/azure.json + # Azure Key Vault provider for Secrets Store CSI Driver + # Requires the secrets-store-csi-driver and secrets-store-csi-driver-provider-azure + # to be installed on the cluster, along with Azure Workload Identity. + keyVault: + enabled: false + + # Azure Key Vault name (e.g. "my-keyvault") + vaultName: "" + + # Azure Tenant ID + tenantID: "" + + # Azure Workload Identity client ID (managed identity or app registration). + # Optional: only required when multiple managed identities are assigned to the node. + # When set, also annotate the ServiceAccount: + # serviceAccount.annotations: + # azure.workload.identity/client-id: "" + clientID: "" + # GCP GKE workload identity init container gcp: enabled: false @@ -230,3 +251,9 @@ global: containerRegistry: pullSecret: "" configJSON: "" + azure: + keyVault: + enabled: false + vaultName: "" + tenantID: "" + clientID: "" diff --git a/helm-charts/falcon-kac/README.md b/helm-charts/falcon-kac/README.md index 21148b5f..709ae909 100644 --- a/helm-charts/falcon-kac/README.md +++ b/helm-charts/falcon-kac/README.md @@ -218,5 +218,60 @@ The following tables lists the Falcon KAC configurable parameters and their defa | `admissionControl.enabled` | Enable Admission Control | `true` | | `falconSecret.enabled` | Enable k8s secrets to inject sensitive Falcon values | false (Must be true if falcon.cid is not set) | | `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`) | +| `azure.keyVault.enabled` | Enable Azure Key Vault provider for Secrets Store CSI Driver. Mutually exclusive with `falcon.cid` and `falconSecret.enabled`. See [Azure Key Vault Integration](#azure-key-vault-integration). | `false` | +| `azure.keyVault.vaultName` | Azure Key Vault name | None | +| `azure.keyVault.tenantID` | Azure Tenant ID | None | +| `azure.keyVault.clientID` | Azure Workload Identity client ID. Only required if multiple managed identities are assigned to the node. | None | +| `azure.keyVault.provisioningTokenSecretName` | Name of the AKV secret containing `FALCONCTL_OPT_PROVISIONING_TOKEN`. Leave empty to omit. | None | | `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 | + +## Azure Key Vault Integration + +The chart supports sourcing `FALCONCTL_OPT_CID` (and optionally `FALCONCTL_OPT_PROVISIONING_TOKEN`) from [Azure Key Vault](https://azure.microsoft.com/en-us/products/key-vault) via the [Secrets Store CSI Driver](https://secrets-store-csi-driver.sigs.k8s.io/) and the [Azure Key Vault provider](https://azure.github.io/secrets-store-csi-driver-provider-azure/). + +### Prerequisites + +The following must be installed and configured on your AKS cluster before enabling this feature: + +- [Secrets Store CSI Driver](https://secrets-store-csi-driver.sigs.k8s.io/getting-started/installation) +- [Azure Key Vault Provider for Secrets Store CSI Driver](https://azure.github.io/secrets-store-csi-driver-provider-azure/docs/getting-started/installation/) +- [Azure Workload Identity](https://azure.github.io/azure-workload-identity/docs/installation.html) webhook installed on the cluster +- AKS cluster with OIDC issuer enabled (`az aks update --enable-oidc-issuer --name --resource-group `) +- A user-assigned managed identity with `Key Vault Secrets User` role on the vault +- A federated credential binding the managed identity to the chart's ServiceAccount in the KAC namespace + +### Required secrets in Azure Key Vault + +Create the following secrets in your Azure Key Vault before enabling the integration: + +| Secret name (default) | Required | Value | +|:----------------------------|:---------|:-------------------------------| +| `falcon-cid` | Yes | CrowdStrike Customer ID (CID) | +| `falcon-provisioning-token` | No | Provisioning token | + +The CID secret must be named `falcon-cid` in AKV. The provisioning token secret name is configurable via `azure.keyVault.provisioningTokenSecretName`. + +### Configuration + +```yaml +azure: + keyVault: + enabled: true + vaultName: "my-keyvault" + tenantID: "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" + # clientID is optional - only required if multiple managed identities are assigned + clientID: "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" + provisioningTokenSecretName: "" # leave empty to omit + +serviceAccount: + annotations: + azure.workload.identity/client-id: "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" + +# Add the Workload Identity label to KAC pods +labels: + azure.workload.identity/use: "true" +``` + +> [!NOTE] +> `azure.keyVault.enabled` cannot be combined with `falcon.cid` or `falconSecret.enabled`. These are mutually exclusive secret sources. diff --git a/helm-charts/falcon-kac/templates/_helpers.tpl b/helm-charts/falcon-kac/templates/_helpers.tpl index 930e97e4..f96f1094 100644 --- a/helm-charts/falcon-kac/templates/_helpers.tpl +++ b/helm-charts/falcon-kac/templates/_helpers.tpl @@ -176,23 +176,63 @@ Get Falcon CID from global value if it exists {{- end -}} {{/* -Check if Falcon secret is enabled from global value if it exists +Check if Falcon secret is enabled from global value if it exists. +Returns true when Azure Key Vault is enabled, as AKV manages the secret. */}} {{- define "falcon-kac.falconSecretEnabled" -}} +{{- if (include "falcon-kac.akvEnabled" .) | eq "true" -}} +true +{{- else -}} {{- or .Values.global.falconSecret.enabled .Values.falconSecret.enabled -}} {{- end -}} +{{- end -}} {{/* -Get Falcon secret name from global value if it exists +Get Falcon secret name from global value if it exists. +When Azure Key Vault is enabled, returns the name of the secret that +SecretProviderClass.secretObjects will sync from AKV, so that the +existing envFrom/secretRef wiring picks it up unchanged. */}} {{- define "falcon-kac.falconSecretName" -}} -{{- if and .Values.global.falconSecret.secretName (not .Values.falconSecret.secretName) -}} +{{- if (include "falcon-kac.akvEnabled" .) | eq "true" -}} +{{- printf "%s-akv" (include "falcon-kac.fullname" .) -}} +{{- else if and .Values.global.falconSecret.secretName (not .Values.falconSecret.secretName) -}} {{- .Values.global.falconSecret.secretName -}} {{- else -}} {{- .Values.falconSecret.secretName | default "" -}} {{- end -}} {{- end -}} +{{/* +Returns true when AKV is enabled locally or globally. +*/}} +{{- define "falcon-kac.akvEnabled" -}} +{{- if or .Values.azure.keyVault.enabled .Values.global.azure.keyVault.enabled -}} +true +{{- end -}} +{{- end -}} + +{{/* +Returns the effective AKV vault name (local overrides global). +*/}} +{{- define "falcon-kac.akvVaultName" -}} +{{- .Values.azure.keyVault.vaultName | default .Values.global.azure.keyVault.vaultName -}} +{{- end -}} + +{{/* +Returns the effective AKV tenant ID (local overrides global). +*/}} +{{- define "falcon-kac.akvTenantID" -}} +{{- .Values.azure.keyVault.tenantID | default .Values.global.azure.keyVault.tenantID -}} +{{- end -}} + +{{/* +Returns the effective AKV workload identity client ID (local overrides global). +*/}} +{{- define "falcon-kac.akvClientID" -}} +{{- .Values.azure.keyVault.clientID | default .Values.global.azure.keyVault.clientID -}} +{{- end -}} + {{/* Validate one of falcon.cid or falconSecret is configured */}} diff --git a/helm-charts/falcon-kac/templates/deployment_webhook.yaml b/helm-charts/falcon-kac/templates/deployment_webhook.yaml index 7c84afda..f16e9831 100644 --- a/helm-charts/falcon-kac/templates/deployment_webhook.yaml +++ b/helm-charts/falcon-kac/templates/deployment_webhook.yaml @@ -187,6 +187,11 @@ spec: name: crowdstrike-falcon-vol0 - mountPath: /var/private name: crowdstrike-falcon-vol1 + {{- if .Values.azure.keyVault.enabled }} + - name: akv-secrets + mountPath: /mnt/secrets-store + readOnly: true + {{- end }} - name: falcon-watcher args: - "client" @@ -333,6 +338,14 @@ spec: - name: crowdstrike-falcon-vol2 emptyDir: sizeLimit: 64Mi + {{- if .Values.azure.keyVault.enabled }} + - name: akv-secrets + csi: + driver: secrets-store.csi.k8s.io + readOnly: true + volumeAttributes: + secretProviderClass: {{ include "falcon-kac.fullname" . }}-akv + {{- end }} --- {{- if .Values.admissionControl.enabled }} apiVersion: admissionregistration.k8s.io/v1 diff --git a/helm-charts/falcon-kac/templates/secret-provider-class.yaml b/helm-charts/falcon-kac/templates/secret-provider-class.yaml new file mode 100644 index 00000000..75150474 --- /dev/null +++ b/helm-charts/falcon-kac/templates/secret-provider-class.yaml @@ -0,0 +1,53 @@ +{{- if (include "falcon-kac.akvEnabled" .) | eq "true" }} +{{- if .Values.falconSecret.enabled }} + {{- fail "azure.keyVault and falconSecret cannot be used together. Use one secret source." }} +{{- end }} +{{- if include "falcon-kac.falconCid" . }} + {{- fail "azure.keyVault and falcon.cid cannot be used together. Use one secret source." }} +{{- end }} +{{- if not (include "falcon-kac.akvVaultName" .) }} + {{- fail "azure.keyVault.vaultName (or global.azure.keyVault.vaultName) is required when AKV is enabled." }} +{{- end }} +{{- if not (include "falcon-kac.akvTenantID" .) }} + {{- fail "azure.keyVault.tenantID (or global.azure.keyVault.tenantID) is required when AKV is enabled." }} +{{- end }} +apiVersion: secrets-store.csi.x-k8s.io/v1 +kind: SecretProviderClass +metadata: + name: {{ include "falcon-kac.fullname" . }}-akv + namespace: {{ include "falcon-kac.namespace" . }} + labels: + {{- include "falcon-kac.labels" . | nindent 4 }} +spec: + provider: azure + parameters: + usePodIdentity: "false" + useVMManagedIdentity: "false" + {{- if (include "falcon-kac.akvClientID" .) }} + clientID: {{ include "falcon-kac.akvClientID" . | quote }} + {{- end }} + keyvaultName: {{ include "falcon-kac.akvVaultName" . | quote }} + tenantId: {{ include "falcon-kac.akvTenantID" . | quote }} + objects: | + array: + - | + objectName: "falcon-cid" + objectType: secret + objectVersion: "" + {{- if .Values.azure.keyVault.provisioningTokenSecretName }} + - | + objectName: {{ .Values.azure.keyVault.provisioningTokenSecretName | quote }} + objectType: secret + objectVersion: "" + {{- end }} + secretObjects: + - secretName: {{ include "falcon-kac.fullname" . }}-akv + type: Opaque + data: + - objectName: "falcon-cid" + key: FALCONCTL_OPT_CID + {{- if .Values.azure.keyVault.provisioningTokenSecretName }} + - objectName: {{ .Values.azure.keyVault.provisioningTokenSecretName | quote }} + key: FALCONCTL_OPT_PROVISIONING_TOKEN + {{- end }} +{{- end }} diff --git a/helm-charts/falcon-kac/values.schema.json b/helm-charts/falcon-kac/values.schema.json index cab84cb6..a18960cf 100644 --- a/helm-charts/falcon-kac/values.schema.json +++ b/helm-charts/falcon-kac/values.schema.json @@ -427,6 +427,37 @@ "falconImageAnalyzerNamespace": { "type": "string", "default": "falcon-image-analyzer" + }, + "azure": { + "type": "object", + "properties": { + "keyVault": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean", + "default": false, + "description": "Enable Azure Key Vault provider for Secrets Store CSI Driver" + }, + "vaultName": { + "type": "string", + "description": "Azure Key Vault name" + }, + "tenantID": { + "type": "string", + "description": "Azure Tenant ID" + }, + "clientID": { + "type": "string", + "description": "Azure Workload Identity client ID (optional)" + }, + "provisioningTokenSecretName": { + "type": "string", + "description": "Name of the AKV secret containing FALCONCTL_OPT_PROVISIONING_TOKEN (optional)" + } + } + } + } } } } diff --git a/helm-charts/falcon-kac/values.yaml b/helm-charts/falcon-kac/values.yaml index 1bc66ba5..011a7881 100644 --- a/helm-charts/falcon-kac/values.yaml +++ b/helm-charts/falcon-kac/values.yaml @@ -72,6 +72,36 @@ falconSecret: enabled: false secretName: "" +# Azure Key Vault provider for Secrets Store CSI Driver. +# Requires secrets-store-csi-driver and secrets-store-csi-driver-provider-azure +# to be installed on the cluster, along with Azure Workload Identity. +# When enabled, FALCONCTL_OPT_CID (and optionally FALCONCTL_OPT_PROVISIONING_TOKEN) +# are sourced from Azure Key Vault rather than falcon.cid or falconSecret. +# To enable Azure Workload Identity on the pod, add the required label via the +# existing labels value: +# labels: +# azure.workload.identity/use: "true" +azure: + keyVault: + enabled: false + + # Azure Key Vault name (e.g. "my-keyvault") + vaultName: "" + + # Azure Tenant ID + tenantID: "" + + # Azure Workload Identity client ID (optional). + # Only required when multiple managed identities are assigned to the node. + # When set, also annotate the ServiceAccount: + # serviceAccount.annotations: + # azure.workload.identity/client-id: "" + clientID: "" + + # Name of the AKV secret containing the provisioning token (FALCONCTL_OPT_PROVISIONING_TOKEN). + # Leave empty to omit the provisioning token from AKV. + provisioningTokenSecretName: "" + # These variables can be used to customize the way KAC provides visibility into # resources on your Kubernetes cluster. The variables are initially set to the # default values which will be used if they are commented out. @@ -203,3 +233,9 @@ global: containerRegistry: pullSecret: "" configJSON: "" + azure: + keyVault: + enabled: false + vaultName: "" + tenantID: "" + clientID: "" diff --git a/helm-charts/falcon-platform/README.md b/helm-charts/falcon-platform/README.md index dcb443c5..57d64264 100644 --- a/helm-charts/falcon-platform/README.md +++ b/helm-charts/falcon-platform/README.md @@ -237,6 +237,10 @@ 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.azure.keyVault.enabled | false | Enable Azure Key Vault as the secret source for all components. See [Azure Key Vault Integration](#azure-key-vault-integration). | +| global.azure.keyVault.vaultName | "" | Azure Key Vault name shared across all components | +| global.azure.keyVault.tenantID | "" | Azure Tenant ID shared across all components | +| global.azure.keyVault.clientID | "" | Azure Workload Identity client ID (optional; required only when multiple managed identities are assigned) | > [!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`. @@ -299,6 +303,11 @@ The following falcon-sensor parameters apply to both Node and Container sensors | `falcon-sensor.falcon.cid` | Overrides global.falcon.cid | | `falcon-sensor.falconSecret.enabled` | Overrides global.falconSecret.enabled | | `falcon-sensor.falconSecret.secretName` | Overrides global.falconSecret.secretName | +| `falcon-sensor.azure.keyVault.enabled` | Overrides global.azure.keyVault.enabled for this component. See [Azure Key Vault Integration](#azure-key-vault-integration). | +| `falcon-sensor.azure.keyVault.vaultName` | Overrides global.azure.keyVault.vaultName for this component | +| `falcon-sensor.azure.keyVault.tenantID` | Overrides global.azure.keyVault.tenantID for this component | +| `falcon-sensor.azure.keyVault.clientID` | Overrides global.azure.keyVault.clientID for this component | +| `falcon-sensor.azure.keyVault.provisioningTokenSecretName` | AKV secret name for `FALCONCTL_OPT_PROVISIONING_TOKEN` (optional) | #### Falcon KAC Falcon KAC specific configurations must be prefixed with `falcon-kac`. For comprehensive configuration options please see the linked documentation below. @@ -323,6 +332,11 @@ Falcon KAC specific configurations must be prefixed with `falcon-kac`. For compr | `falcon-kac.falcon.cid` | Overrides global.falcon.cid | | `falcon-kac.falconSecret.enabled` | Overrides global.falconSecret.enabled | | `falcon-kac.falconSecret.secretName` | Overrides global.falconSecret.secretName | +| `falcon-kac.azure.keyVault.enabled` | Overrides global.azure.keyVault.enabled for this component. See [Azure Key Vault Integration](#azure-key-vault-integration). | +| `falcon-kac.azure.keyVault.vaultName` | Overrides global.azure.keyVault.vaultName for this component | +| `falcon-kac.azure.keyVault.tenantID` | Overrides global.azure.keyVault.tenantID for this component | +| `falcon-kac.azure.keyVault.clientID` | Overrides global.azure.keyVault.clientID for this component | +| `falcon-kac.azure.keyVault.provisioningTokenSecretName` | AKV secret name for `FALCONCTL_OPT_PROVISIONING_TOKEN` (optional) | #### Falcon Image Analyzer Falcon Image Analyzer specific configurations must be prefixed with `falcon-image-analyzer`. For comprehensive configuration options please see the linked documentation below. @@ -352,6 +366,10 @@ Falcon Image Analyzer specific configurations must be prefixed with `falcon-imag | `falcon-image-analyzer.image.registryConfigJSON` | Overrides global.containerRegistry.configJSON | | `falcon-image-analyzer.crowdstrikeConfig.cid` | Overrides global.falcon.cid | | `falcon-image-analyzer.crowdstrikeConfig.existingSecret` | Overrides global.falconSecret.secretName | +| `falcon-image-analyzer.azure.keyVault.enabled` | Overrides global.azure.keyVault.enabled for this component. See [Azure Key Vault Integration](#azure-key-vault-integration). | +| `falcon-image-analyzer.azure.keyVault.vaultName` | Overrides global.azure.keyVault.vaultName for this component | +| `falcon-image-analyzer.azure.keyVault.tenantID` | Overrides global.azure.keyVault.tenantID for this component | +| `falcon-image-analyzer.azure.keyVault.clientID` | Overrides global.azure.keyVault.clientID for this component | ### Using Existing Kubernetes Secrets @@ -411,6 +429,79 @@ helm install falcon-platform crowdstrike/falcon-platform --version 1.0.0 -n falc ## Upgrade Strategy +### Azure Key Vault Integration + +The Falcon Platform supports sourcing credentials from [Azure Key Vault](https://azure.microsoft.com/en-us/products/key-vault) via the [Secrets Store CSI Driver](https://secrets-store-csi-driver.sigs.k8s.io/) and the [Azure Key Vault provider](https://azure.github.io/secrets-store-csi-driver-provider-azure/). The vault name, tenant ID, and optional workload identity client ID can be configured once under `global.azure.keyVault` and shared across all components. Per-component values override globals when both are set. + +> [!NOTE] +> When AKV is enabled for `falcon-sensor` or `falcon-kac`, it is mutually exclusive with `falcon.cid`/`global.falcon.cid` and `falconSecret`. For `falcon-image-analyzer`, AKV is mutually exclusive with `crowdstrikeConfig.clientID`/`clientSecret` and `existingSecret`, but CID can still be supplied via `crowdstrikeConfig.cid` or `global.falcon.cid` alongside AKV — if either is set, `falcon-cid` is not fetched from the vault. + +#### Prerequisites (all components) + +- [Secrets Store CSI Driver](https://secrets-store-csi-driver.sigs.k8s.io/getting-started/installation) +- [Azure Key Vault Provider for Secrets Store CSI Driver](https://azure.github.io/secrets-store-csi-driver-provider-azure/docs/getting-started/installation/) +- [Azure Workload Identity](https://azure.github.io/azure-workload-identity/docs/installation.html) webhook installed on the cluster +- AKS cluster with OIDC issuer enabled +- A user-assigned managed identity with `Key Vault Secrets User` role on the vault, with federated credentials bound to each component's ServiceAccount in its respective namespace + +#### Required secrets per component + +| Component | Secret key (default) | Env var mapped | +|:-----------------------|:-------------------------|:-------------------------| +| falcon-sensor | `falcon-cid` | `FALCONCTL_OPT_CID` | +| falcon-sensor | `falcon-provisioning-token` (optional, configurable) | `FALCONCTL_OPT_PROVISIONING_TOKEN` | +| falcon-kac | `falcon-cid` | `FALCONCTL_OPT_CID` | +| falcon-kac | `falcon-provisioning-token` (optional, configurable) | `FALCONCTL_OPT_PROVISIONING_TOKEN` | +| falcon-image-analyzer | `falcon-client-id` | `AGENT_CLIENT_ID` | +| falcon-image-analyzer | `falcon-client-secret` | `AGENT_CLIENT_SECRET` | +| falcon-image-analyzer | `falcon-cid` (only if `crowdstrikeConfig.cid` / `global.falcon.cid` not set) | `AGENT_CID` | + +#### Example configuration + +Use `global.azure.keyVault` to configure the shared vault once. Secret names are fixed per component (`falcon-cid`, `falcon-client-id`, `falcon-client-secret`) — only the Workload Identity `serviceAccount.annotations` and pod labels must be configured per component. + +> [!NOTE] +> The global config populates vault connection details in each `SecretProviderClass`. It does **not** automatically set the per-component `serviceAccount.annotations` or pod labels required by Azure Workload Identity — those must still be configured per component as shown below. + +```yaml +global: + azure: + keyVault: + enabled: true + vaultName: "my-keyvault" + tenantID: "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" + # clientID: "" # optional: required only when multiple managed identities are assigned + +falcon-sensor: + serviceAccount: + annotations: + azure.workload.identity/client-id: "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" + node: + daemonset: + labels: + azure.workload.identity/use: "true" + +falcon-kac: + serviceAccount: + annotations: + azure.workload.identity/client-id: "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" + labels: + azure.workload.identity/use: "true" + +falcon-image-analyzer: + serviceAccount: + annotations: + azure.workload.identity/client-id: "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" + podLabels: + azure.workload.identity/use: "true" +``` + +To use a custom provisioning token secret name for `falcon-sensor` or `falcon-kac`, set `azure.keyVault.provisioningTokenSecretName`. + +Each component can still override `vaultName`, `tenantID`, or `clientID` individually if needed — per-component values take precedence over globals. + +## Upgrade Strategy + ### Install/Reinstall a Single Component When installing a new component for the first time, make sure the component namespace exists. `createComponentNamespaces` does not support namespace creation for Helm upgrades. diff --git a/helm-charts/falcon-platform/values.yaml b/helm-charts/falcon-platform/values.yaml index cce434c7..7f70b2a9 100644 --- a/helm-charts/falcon-platform/values.yaml +++ b/helm-charts/falcon-platform/values.yaml @@ -37,6 +37,18 @@ global: # $ cat ~/.docker/config.json | base64 - configJSON: "" + # Azure Key Vault integration - configure once to apply to all components. + # Shared values (vaultName, tenantID, clientID) are set here; per-component secret names + # are set under each component section (e.g. falcon-sensor.azure.keyVault.provisioningTokenSecretName). + # Individual component values take precedence over these globals when both are set. + # Requires secrets-store-csi-driver and secrets-store-csi-driver-provider-azure on the cluster. + azure: + keyVault: + enabled: false + # vaultName: "my-keyvault" + # tenantID: "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" + # clientID: "" # Optional: workload identity client ID when multiple managed identities are assigned + # Enable/disable individual components # Set enabled: true to deploy, enabled: false to skip @@ -45,12 +57,20 @@ global: falcon-sensor: enabled: true namespaceOverride: falcon-system + # AKV provisioning token (optional; shared vault config comes from global.azure.keyVault above): + # azure: + # keyVault: + # provisioningTokenSecretName: "falcon-provisioning-token" # Falcon Kubernetes Admission Controller - Policy enforcement # Override specific values for falcon-kac falcon-kac: enabled: true namespaceOverride: falcon-kac + # AKV provisioning token (optional; shared vault config comes from global.azure.keyVault above): + # azure: + # keyVault: + # provisioningTokenSecretName: "falcon-provisioning-token" # Falcon Image Analyzer - Container image security scanning # Override specific values for falcon-image-analyzer diff --git a/helm-charts/falcon-sensor/README.md b/helm-charts/falcon-sensor/README.md index 1a0dd707..da8a279f 100644 --- a/helm-charts/falcon-sensor/README.md +++ b/helm-charts/falcon-sensor/README.md @@ -137,6 +137,11 @@ The following tables lists the more common configurable parameters of the chart | `falcon.cloud` | CrowdStrike cloud region (`us-1`, `us-2`, `eu-1`, `us-gov-1`, `us-gov-2`)

**NOTE:** This option is supported by Falcon sensor version 7.28 and above | None | | `falconSecret.enabled` | Enable k8s secrets to inject sensitive Falcon values | false (Must be true if falcon.cid is not set) | | `falconSecret.secretName` | Existing k8s secret name to inject sensitive Falcon values.
The secret must be under the same namespace as the sensor deployment.

Secret name must be `"falcon-node-sensor-secret"` if deploying to a GKE Autopilot cluster. | None (Existing secret must include `FALCONCTL_OPT_CID`) | +| `azure.keyVault.enabled` | Enable Azure Key Vault provider for Secrets Store CSI Driver. Mutually exclusive with `falcon.cid` and `falconSecret.enabled`. See [Azure Key Vault Integration](#azure-key-vault-integration). | `false` | +| `azure.keyVault.vaultName` | Azure Key Vault name | None | +| `azure.keyVault.tenantID` | Azure Tenant ID | None | +| `azure.keyVault.clientID` | Azure Workload Identity client ID. Only required if multiple managed identities are assigned to the node. | None | +| `azure.keyVault.provisioningTokenSecretName` | Name of the AKV secret containing `FALCONCTL_OPT_PROVISIONING_TOKEN`. Leave empty to omit. | None | `falcon.cid` and `node.image.repository` are required values. @@ -286,6 +291,11 @@ The following tables lists the more common configurable parameters of the chart | `falcon.cid` | CrowdStrike Customer ID (CID) | None (Required if falconSecret.enabled is false) | | `falconSecret.enabled` | Enable k8s secrets to inject sensitive Falcon values | false (Must be true if falcon.cid is not set) | | `falconSecret.secretName` | Existing k8s secret name to inject sensitive Falcon values.
The secret must be under the same namespace as the sensor deployment. | None (Existing secret must include `FALCONCTL_OPT_CID`) | +| `azure.keyVault.enabled` | Enable Azure Key Vault provider for Secrets Store CSI Driver. Mutually exclusive with `falcon.cid` and `falconSecret.enabled`. See [Azure Key Vault Integration](#azure-key-vault-integration). | `false` | +| `azure.keyVault.vaultName` | Azure Key Vault name | None | +| `azure.keyVault.tenantID` | Azure Tenant ID | None | +| `azure.keyVault.clientID` | Azure Workload Identity client ID. Only required if multiple managed identities are assigned to the node. | None | +| `azure.keyVault.provisioningTokenSecretName` | Name of the AKV secret containing `FALCONCTL_OPT_PROVISIONING_TOKEN`. Leave empty to omit. | None | `falcon.cid` and `container.image.repository` are required values. @@ -361,6 +371,63 @@ helm upgrade --install falcon-helm crowdstrike/falcon-sensor \ --set container.sensorResources.requests.cpu="10m" ``` +### Azure Key Vault Integration + +The chart supports sourcing `FALCONCTL_OPT_CID` (and optionally `FALCONCTL_OPT_PROVISIONING_TOKEN`) from [Azure Key Vault](https://azure.microsoft.com/en-us/products/key-vault) via the [Secrets Store CSI Driver](https://secrets-store-csi-driver.sigs.k8s.io/) and the [Azure Key Vault provider](https://azure.github.io/secrets-store-csi-driver-provider-azure/). This applies to both the Node DaemonSet and the Container sidecar Deployment. + +#### Prerequisites + +The following must be installed and configured on your AKS cluster before enabling this feature: + +- [Secrets Store CSI Driver](https://secrets-store-csi-driver.sigs.k8s.io/getting-started/installation) +- [Azure Key Vault Provider for Secrets Store CSI Driver](https://azure.github.io/secrets-store-csi-driver-provider-azure/docs/getting-started/installation/) +- [Azure Workload Identity](https://azure.github.io/azure-workload-identity/docs/installation.html) webhook installed on the cluster +- AKS cluster with OIDC issuer enabled (`az aks update --enable-oidc-issuer --name --resource-group `) +- A user-assigned managed identity with `Key Vault Secrets User` role on the vault +- A federated credential binding the managed identity to the chart's ServiceAccount + +#### Required secrets in Azure Key Vault + +Create the following secrets in your Azure Key Vault before enabling the integration: + +| Secret name (default) | Required | Value | +|:----------------------------|:---------|:-------------------------------| +| `falcon-cid` | Yes | CrowdStrike Customer ID (CID) | +| `falcon-provisioning-token` | No | Provisioning token | + +The CID secret must be named `falcon-cid` in AKV. The provisioning token secret name is configurable via `azure.keyVault.provisioningTokenSecretName`. + +#### Configuration + +```yaml +azure: + keyVault: + enabled: true + vaultName: "my-keyvault" + tenantID: "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" + # clientID is optional - only required if multiple managed identities are assigned + clientID: "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" + provisioningTokenSecretName: "" # leave empty to omit + +serviceAccount: + annotations: + azure.workload.identity/client-id: "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" + +# Add the Workload Identity label to node DaemonSet pods +node: + daemonset: + labels: + azure.workload.identity/use: "true" + +# Add the Workload Identity label to container sidecar Deployment pods +container: + labels: + azure.workload.identity/use: "true" +``` + +> [!NOTE] +> `azure.keyVault.enabled` cannot be combined with `falcon.cid` or `falconSecret.enabled`. These are mutually exclusive secret sources. + ### Uninstall Helm Chart > [!NOTE] diff --git a/helm-charts/falcon-sensor/templates/_helpers.tpl b/helm-charts/falcon-sensor/templates/_helpers.tpl index 84e95acc..90dc439e 100644 --- a/helm-charts/falcon-sensor/templates/_helpers.tpl +++ b/helm-charts/falcon-sensor/templates/_helpers.tpl @@ -216,23 +216,63 @@ Get Falcon CID from global value if it exists {{- end -}} {{/* -Check if Falcon secret is enabled from global value if it exists +Check if Falcon secret is enabled from global value if it exists. +Returns true when Azure Key Vault is enabled, as AKV manages the secret. */}} {{- define "falcon-sensor.falconSecretEnabled" -}} +{{- if (include "falcon-sensor.akvEnabled" .) | eq "true" -}} +true +{{- else -}} {{- or .Values.global.falconSecret.enabled .Values.falconSecret.enabled -}} {{- end -}} +{{- end -}} {{/* -Get Falcon secret name from global value if it exists +Get Falcon secret name from global value if it exists. +When Azure Key Vault is enabled, returns the name of the secret that +SecretProviderClass.secretObjects will sync from AKV, so that the +existing envFrom/secretRef wiring picks it up unchanged. */}} {{- define "falcon-sensor.falconSecretName" -}} -{{- if and .Values.global.falconSecret.secretName (not .Values.falconSecret.secretName) -}} +{{- if (include "falcon-sensor.akvEnabled" .) | eq "true" -}} +{{- printf "%s-akv" (include "falcon-sensor.fullname" .) -}} +{{- else if and .Values.global.falconSecret.secretName (not .Values.falconSecret.secretName) -}} {{- .Values.global.falconSecret.secretName -}} {{- else -}} {{- .Values.falconSecret.secretName | default "" -}} {{- end -}} {{- end -}} +{{/* +Returns true when AKV is enabled locally or globally. +*/}} +{{- define "falcon-sensor.akvEnabled" -}} +{{- if or .Values.azure.keyVault.enabled .Values.global.azure.keyVault.enabled -}} +true +{{- end -}} +{{- end -}} + +{{/* +Returns the effective AKV vault name (local overrides global). +*/}} +{{- define "falcon-sensor.akvVaultName" -}} +{{- .Values.azure.keyVault.vaultName | default .Values.global.azure.keyVault.vaultName -}} +{{- end -}} + +{{/* +Returns the effective AKV tenant ID (local overrides global). +*/}} +{{- define "falcon-sensor.akvTenantID" -}} +{{- .Values.azure.keyVault.tenantID | default .Values.global.azure.keyVault.tenantID -}} +{{- end -}} + +{{/* +Returns the effective AKV workload identity client ID (local overrides global). +*/}} +{{- define "falcon-sensor.akvClientID" -}} +{{- .Values.azure.keyVault.clientID | default .Values.global.azure.keyVault.clientID -}} +{{- end -}} + {{/* Validate one of falcon.cid or falconSecret is configured */}} diff --git a/helm-charts/falcon-sensor/templates/container_deployment_webhook.yaml b/helm-charts/falcon-sensor/templates/container_deployment_webhook.yaml index bbe7e1b5..6948f816 100644 --- a/helm-charts/falcon-sensor/templates/container_deployment_webhook.yaml +++ b/helm-charts/falcon-sensor/templates/container_deployment_webhook.yaml @@ -169,6 +169,11 @@ spec: mountPath: /tmp/CrowdStrike readOnly: true {{- end }} + {{- if .Values.azure.keyVault.enabled }} + - name: akv-secrets + mountPath: /mnt/secrets-store + readOnly: true + {{- end }} readinessProbe: httpGet: path: /live @@ -212,6 +217,14 @@ spec: path: {{ .Values.container.azure.azureConfig }} type: File {{- end }} + {{- if .Values.azure.keyVault.enabled }} + - name: akv-secrets + csi: + driver: secrets-store.csi.k8s.io + readOnly: true + volumeAttributes: + secretProviderClass: {{ include "falcon-sensor.fullname" . }}-akv + {{- end }} serviceAccountName: {{ .Values.serviceAccount.name }} --- apiVersion: v1 diff --git a/helm-charts/falcon-sensor/templates/daemonset.yaml b/helm-charts/falcon-sensor/templates/daemonset.yaml index 445fdeaa..826e35ce 100644 --- a/helm-charts/falcon-sensor/templates/daemonset.yaml +++ b/helm-charts/falcon-sensor/templates/daemonset.yaml @@ -190,10 +190,23 @@ spec: volumeMounts: - name: falconstore mountPath: /opt/CrowdStrike/falconstore + {{- if .Values.azure.keyVault.enabled }} + - name: akv-secrets + mountPath: /mnt/secrets-store + readOnly: true + {{- end }} volumes: - name: falconstore hostPath: path: /opt/CrowdStrike/falconstore + {{- if .Values.azure.keyVault.enabled }} + - name: akv-secrets + csi: + driver: secrets-store.csi.k8s.io + readOnly: true + volumeAttributes: + secretProviderClass: {{ include "falcon-sensor.fullname" . }}-akv + {{- end }} serviceAccountName: {{ .Values.serviceAccount.name }} terminationGracePeriodSeconds: {{ .Values.node.terminationGracePeriod }} {{- if or .Values.node.daemonset.priorityClassName .Values.node.gke.autopilot }} diff --git a/helm-charts/falcon-sensor/templates/secret-provider-class.yaml b/helm-charts/falcon-sensor/templates/secret-provider-class.yaml new file mode 100644 index 00000000..0c4eaaf9 --- /dev/null +++ b/helm-charts/falcon-sensor/templates/secret-provider-class.yaml @@ -0,0 +1,53 @@ +{{- if (include "falcon-sensor.akvEnabled" .) | eq "true" }} +{{- if .Values.falconSecret.enabled }} + {{- fail "azure.keyVault and falconSecret cannot be used together. Use one secret source." }} +{{- end }} +{{- if include "falcon-sensor.falconCid" . }} + {{- fail "azure.keyVault and falcon.cid cannot be used together. Use one secret source." }} +{{- end }} +{{- if not (include "falcon-sensor.akvVaultName" .) }} + {{- fail "azure.keyVault.vaultName (or global.azure.keyVault.vaultName) is required when AKV is enabled." }} +{{- end }} +{{- if not (include "falcon-sensor.akvTenantID" .) }} + {{- fail "azure.keyVault.tenantID (or global.azure.keyVault.tenantID) is required when AKV is enabled." }} +{{- end }} +apiVersion: secrets-store.csi.x-k8s.io/v1 +kind: SecretProviderClass +metadata: + name: {{ include "falcon-sensor.fullname" . }}-akv + namespace: {{ include "falcon-sensor.namespace" . }} + labels: + {{- include "falcon-sensor.labels" . | nindent 4 }} +spec: + provider: azure + parameters: + usePodIdentity: "false" + useVMManagedIdentity: "false" + {{- if (include "falcon-sensor.akvClientID" .) }} + clientID: {{ include "falcon-sensor.akvClientID" . | quote }} + {{- end }} + keyvaultName: {{ include "falcon-sensor.akvVaultName" . | quote }} + tenantId: {{ include "falcon-sensor.akvTenantID" . | quote }} + objects: | + array: + - | + objectName: "falcon-cid" + objectType: secret + objectVersion: "" + {{- if .Values.azure.keyVault.provisioningTokenSecretName }} + - | + objectName: {{ .Values.azure.keyVault.provisioningTokenSecretName | quote }} + objectType: secret + objectVersion: "" + {{- end }} + secretObjects: + - secretName: {{ include "falcon-sensor.fullname" . }}-akv + type: Opaque + data: + - objectName: "falcon-cid" + key: FALCONCTL_OPT_CID + {{- if .Values.azure.keyVault.provisioningTokenSecretName }} + - objectName: {{ .Values.azure.keyVault.provisioningTokenSecretName | quote }} + key: FALCONCTL_OPT_PROVISIONING_TOKEN + {{- end }} +{{- end }} diff --git a/helm-charts/falcon-sensor/values.schema.json b/helm-charts/falcon-sensor/values.schema.json index e78a3b3a..e7758fe0 100644 --- a/helm-charts/falcon-sensor/values.schema.json +++ b/helm-charts/falcon-sensor/values.schema.json @@ -524,6 +524,37 @@ }, "fullnameOverride": { "type": "string" + }, + "azure": { + "type": "object", + "properties": { + "keyVault": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean", + "default": false, + "description": "Enable Azure Key Vault provider for Secrets Store CSI Driver" + }, + "vaultName": { + "type": "string", + "description": "Azure Key Vault name" + }, + "tenantID": { + "type": "string", + "description": "Azure Tenant ID" + }, + "clientID": { + "type": "string", + "description": "Azure Workload Identity client ID (optional)" + }, + "provisioningTokenSecretName": { + "type": "string", + "description": "Name of the AKV secret containing FALCONCTL_OPT_PROVISIONING_TOKEN (optional)" + } + } + } + } } } } diff --git a/helm-charts/falcon-sensor/values.yaml b/helm-charts/falcon-sensor/values.yaml index 6b15d418..68c49a9e 100644 --- a/helm-charts/falcon-sensor/values.yaml +++ b/helm-charts/falcon-sensor/values.yaml @@ -286,6 +286,35 @@ falconSecret: enabled: false secretName: "" +# Azure Key Vault provider for Secrets Store CSI Driver. +# Requires secrets-store-csi-driver and secrets-store-csi-driver-provider-azure +# to be installed on the cluster, along with Azure Workload Identity. +# When enabled, FALCONCTL_OPT_CID (and optionally FALCONCTL_OPT_PROVISIONING_TOKEN) +# are sourced from Azure Key Vault rather than falcon.cid or falconSecret. +# To enable Azure Workload Identity on the pod, add the required label via the +# existing node.daemonset.labels or container.labels values: +# azure.workload.identity/use: "true" +azure: + keyVault: + enabled: false + + # Azure Key Vault name (e.g. "my-keyvault") + vaultName: "" + + # Azure Tenant ID + tenantID: "" + + # Azure Workload Identity client ID (optional). + # Only required when multiple managed identities are assigned to the node. + # When set, also annotate the ServiceAccount: + # serviceAccount.annotations: + # azure.workload.identity/client-id: "" + clientID: "" + + # Name of the AKV secret containing the provisioning token (FALCONCTL_OPT_PROVISIONING_TOKEN). + # Leave empty to omit the provisioning token from AKV. + provisioningTokenSecretName: "" + # Override various naming aspects of this chart # Only edit these if you know what you're doing nameOverride: "" @@ -301,3 +330,9 @@ global: containerRegistry: pullSecret: "" configJSON: "" + azure: + keyVault: + enabled: false + vaultName: "" + tenantID: "" + clientID: ""