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: ""