Security¶
Authentication model, multi-tenancy authorization, encryption, source verification, and hardening for FluxCD.
FluxCD delegates security almost entirely to Kubernetes-native mechanisms. Unlike ArgoCD, which ships its own Casbin RBAC engine and Dex SSO server, FluxCD has no built-in identity provider or authorization system. Security is enforced through Kubernetes RBAC, ServiceAccount impersonation, and controller-level flags.
Authentication¶
Controller-to-Cluster Authentication¶
Each Flux controller runs under a ServiceAccount in the flux-system namespace. The controller uses that ServiceAccount's credentials to communicate with the Kubernetes API server. There is no separate authentication layer.
- source-controller: Authenticates to Git repositories via SSH keys, HTTPS tokens, or basic auth credentials stored as Kubernetes Secrets
- kustomize-controller / helm-controller: Impersonates the ServiceAccount specified in
.spec.serviceAccountNameon eachKustomizationorHelmReleaseresource, inheriting that account's RBAC permissions - notification-controller: Uses its own ServiceAccount to post events and read
Alert/ProviderCRDs
Git Repository Authentication¶
FluxCD supports multiple authentication methods for private Git repositories:
| Method | Secret Fields | Use Case |
|---|---|---|
| SSH (ed25519/rsa) | identity (private key), known_hosts |
Most common for GitHub/GitLab private repos |
| HTTPS with token | username, password (personal access token) |
Azure DevOps, GitHub App tokens |
| Basic auth | username, password |
Self-hosted Git servers |
| OIDC (GitHub App) | clientID, clientSecret, idToken |
Short-lived tokens from GitHub OIDC |
# SSH authentication for a private GitRepository
apiVersion: source.toolkit.fluxcd.io/v1
kind: GitRepository
metadata:
name: private-repo
namespace: flux-system
spec:
interval: 5m
url: ssh://[email protected]/org/private-repo.git
ref:
branch: main
secretRef:
name: ssh-credentials
Authorization (Multi-Tenancy and RBAC)¶
FluxCD implements multi-tenancy through Kubernetes RBAC and controller-level isolation flags. There is no separate RBAC engine.
ServiceAccount Impersonation¶
The kustomize-controller and helm-controller impersonate the ServiceAccount specified in each resource's .spec.serviceAccountName. This means:
- Each tenant team creates a dedicated ServiceAccount in their namespace
- The controller inherits only the permissions granted to that ServiceAccount via
RoleandRoleBinding - If no
serviceAccountNameis specified, the controller uses the--default-service-accountflag value (set todefaultin multi-tenant setups)
Multi-Tenancy Lockdown¶
To enforce hard multi-tenancy isolation, apply these controller flags during bootstrap:
| Flag | Controllers | Effect |
|---|---|---|
--no-cross-namespace-refs=true |
kustomize-controller, helm-controller, notification-controller, image-reflector-controller, image-automation-controller | Prevents tenants from referencing Flux resources (sources, secrets) in other namespaces |
--no-remote-bases=true |
kustomize-controller | Prevents Kustomize from fetching remote bases from arbitrary URLs |
--default-service-account=default |
kustomize-controller, helm-controller | Falls back to the default ServiceAccount in the tenant namespace when no serviceAccountName is specified |
# Kustomize patches applied during flux bootstrap
patches:
- patch: |
- op: add
path: /spec/template/spec/containers/0/args/-
value: --no-cross-namespace-refs=true
target:
kind: Deployment
name: "(kustomize-controller|helm-controller|notification-controller)"
- patch: |
- op: add
path: /spec/template/spec/containers/0/args/-
value: --no-remote-bases=true
target:
kind: Deployment
name: "kustomize-controller"
- patch: |
- op: add
path: /spec/template/spec/containers/0/args/-
value: --default-service-account=default
target:
kind: Deployment
name: "(kustomize-controller|helm-controller)"
Tenant Isolation Model¶
graph TB
subgraph Flux["flux-system namespace"]
SC["source-controller"]
KC["kustomize-controller"]
end
subgraph TenantA["team-a namespace"]
SA_A["ServiceAccount: team-a"]
RB_A["RoleBinding"]
GitRepoA["GitRepository"]
KustA["Kustomization"]
end
subgraph TenantB["team-b namespace"]
SA_B["ServiceAccount: team-b"]
RB_B["RoleBinding"]
GitRepoB["GitRepository"]
KustB["Kustomization"]
end
GitRepoA --> SC
GitRepoB --> SC
KustA -->|"impersonates SA: team-a"| KC
KustB -->|"impersonates SA: team-b"| KC
RB_A -.->|"grants limited permissions"| SA_A
RB_B -.->|"grants limited permissions"| SA_B
With --no-cross-namespace-refs=true, team-a's Kustomization cannot reference team-b's GitRepository, and vice versa.
Encryption and Secret Management¶
SOPS Integration¶
FluxCD natively supports Mozilla SOPS (Secrets OPerationS) for encrypting secrets in Git. The kustomize-controller decrypts SOPS-encrypted files at reconciliation time.
Supported encryption backends:
| Backend | Configuration | Use Case |
|---|---|---|
| Age | age.agekey in a K8s Secret |
Recommended; simple, modern encryption |
| PGP/GPG | pgp in .sops.yaml |
Legacy; broader key management ecosystem |
| AWS KMS | kms in .sops.yaml |
AWS-native deployments |
| GCP KMS | gcp_kms in .sops.yaml |
GCP-native deployments |
| Azure Key Vault | azure_kv in .sops.yaml |
Azure-native deployments |
The decryption key is stored as a Kubernetes Secret in the controller's namespace:
apiVersion: v1
kind: Secret
metadata:
name: sops-age
namespace: flux-system
stringData:
age.agekey: |
AGE-SECRET-KEY-1...
Configure the kustomize-controller to use the decryption key via Kustomize patches:
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
patches:
- patch: |
apiVersion: apps/v1
kind: Deployment
metadata:
name: kustomize-controller
spec:
template:
spec:
containers:
- name: manager
env:
- name: SOPS_AGE_KEY_FILE
value: /sops-age/age.agekey
volumeMounts:
- name: sops-age
mountPath: /sops-age
volumes:
- name: sops-age
secret:
secretName: sops-age
Key Management
The SOPS decryption private key must be stored as a Kubernetes Secret in the cluster. Protect this Secret with RBAC (only kustomize-controller should have read access) and consider using an external secret management system to inject it.
Source Verification¶
FluxCD can verify the authenticity and integrity of source artifacts before reconciling them.
GPG Commit Signing Verification¶
For GitRepository resources, FluxCD can verify that commits are signed by a trusted GPG key:
apiVersion: source.toolkit.fluxcd.io/v1
kind: GitRepository
metadata:
name: verified-repo
namespace: flux-system
spec:
interval: 5m
url: https://github.com/org/secure-repo
ref:
branch: main
verify:
mode: HEAD
secretRef:
name: gpg-public-keys
Cosign OCI Artifact Verification¶
For OCIRepository resources, FluxCD can verify signatures using Sigstore Cosign:
apiVersion: source.toolkit.fluxcd.io/v1
kind: OCIRepository
metadata:
name: verified-oci
namespace: flux-system
spec:
interval: 5m
url: oci://ghcr.io/org/manifests
ref:
semver: ">=1.0.0"
verify:
provider: cosign
secretRef:
name: cosign-pub
Container Image Verification¶
All Flux controller images are signed using Cosign with GitHub OIDC (keyless signing). Verify at deployment time:
cosign verify ghcr.io/fluxcd/source-controller:v1.0.0 \
--certificate-identity-regexp='^https://github\.com/fluxcd/.*$' \
--certificate-oidc-issuer=https://token.actions.githubusercontent.com
Enforce verification at the cluster level using a Kyverno policy that rejects unsigned Flux images.
Hardening Checklist¶
- Enable
--no-cross-namespace-refs=trueon all controllers for multi-tenancy - Enable
--no-remote-bases=trueon kustomize-controller - Set
--default-service-account=defaulton kustomize-controller and helm-controller - Use SOPS or External Secrets Operator for secrets management (never commit plaintext)
- Enable GPG or Cosign source verification on all
GitRepositoryandOCIRepositoryresources - Store SOPS decryption keys in K8s Secrets with strict RBAC (only controller SA has read)
- Apply NetworkPolicies to restrict controller egress (source-controller to Git hosts only)
- Run controllers with
SecurityContext(non-root, read-only root filesystem, drop all capabilities) - Verify Flux controller image signatures with Cosign or Kyverno admission policies
- Use short-lived SSH keys or OIDC tokens for Git repository authentication
- Keep
flux-systemnamespace restricted; tenants should not have access to it