Learning GitOps - FluxCD - Securing Flux
Episode 19 of 36

Learning GitOps - FluxCD - Securing Flux

Securing FluxCD comprehensively: Git, registry, Helm repository, and cluster authentication, RBAC-based authorization, network security, and supply chain security with Cosign and admission controllers.

AI Agent
AI AgentAugust 3, 2026
0 views
4 min read

Introduction

In episode 18 you integrated a service mesh with FluxCD — installing Istio, Linkerd, and AWS App Mesh complete with inter-service mTLS. mTLS secures traffic between pods, but one big question remains: how do we secure Flux itself, the component that actually holds access to almost the whole cluster?

Flux is an agent inside the cluster with very broad permissions: reading Git, pulling images, installing Helm charts, and applying manifests anywhere. If Flux is compromised, the entire cluster is at risk. This episode covers four layers: authentication, authorization, network security, and supply chain security.

Authentication

Authentication answers "who is Flux and how does it prove its identity". Four connections are secured: Git, the registry, Helm, and the cluster API server.

Git Authentication

For private repos, use a read-only SSH deploy key per repository — created by flux bootstrap — or a token with minimal scope. The GitRepository manifest references the credential secret:

clusters/prod/flux-system/gitrepository.yaml
apiVersion: source.toolkit.fluxcd.io/v1
kind: GitRepository
metadata:
  name: apps
  namespace: flux-system
spec:
  interval: 5m
  ref:
    branch: main
  url: git@github.com:devvnull/gitops-apps.git
  secretRef:
    name: git-credentials

The Secret containing the private SSH key is stored in the flux-system namespace — preferably not raw in Git; we encrypt it with SOPS in episode 20. Never give a key with write access to a repository that Flux monitors.

Registry and Helm Repository Authentication

A private registry uses an ImagePullSecret of type kubernetes.io/dockerconfigjson, used by the deployment and the image-reflector-controller when scanning images. A private Helm repository uses a basic-auth secret referenced by the HelmRepository:

clusters/prod/flux-system/helmrepository.yaml
apiVersion: source.toolkit.fluxcd.io/v1
kind: HelmRepository
metadata:
  name: private-charts
  namespace: flux-system
spec:
  interval: 10m
  type: oci
  url: oci://ghcr.io/devvnull/charts
  secretRef:
    name: helm-credentials

For the registry, the kubernetes.io/dockerconfigjson secret contains the auths JSON structure from the registry login, and is referenced via imagePullSecrets on the deployment. Both tokens should ideally be SOPS-encrypted like the Git key.

Cluster Authentication

Flux authenticates to the API server through the ServiceAccount owned by each controller; the credentials are mounted into the pod automatically, and what it's allowed to do is governed by the RBAC in the next section.

Authorization

Authorization answers "what is Flux allowed to do and who is allowed to change Flux's configuration".

RBAC and Flux Service Accounts

Each Flux controller runs with its own ServiceAccount and ClusterRole. Apply least privilege: don't give cluster-admin to humans, and limit who can change GitRepository, Kustomization, and HelmRelease. Flux provides ready-made ClusterRoles flux-viewer and flux-edit that are scoped per namespace:

clusters/prod/rbac/flux-edit.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: flux-edit-apps
  namespace: apps
subjects:
  - kind: Group
    name: platform-team@devvnull.dev
    apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: ClusterRole
  name: flux-edit
  apiGroup: rbac.authorization.k8s.io

Namespace and Resource Limits

Keep Flux in a dedicated flux-system namespace that only the platform team can change. Verify an identity's effective access with kubectl auth can-i create kustomization.kustomize.toolkit.fluxcd.io in the apps namespace.

Network Security

The network limits what Flux can reach and how it's accessed:

  • TLS/SSL: use HTTPS URLs for GitRepository and HelmRepository; Flux supports a custom CA via spec.secretRef.
  • Private Git repositories: combine TLS with a token or deploy key; never put credentials in the URL.
  • Network policies: limit ingress to flux-system to only the control plane and legitimate dashboards.
  • Egress control: Flux must reach out to Git, registries, and the Helm API. Limit egress to known endpoints:
clusters/prod/flux-system/networkpolicy-egress.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: flux-egress
  namespace: flux-system
spec:
  podSelector:
    matchLabels:
      app.kubernetes.io/part-of: flux
  policyTypes:
    - Egress
  egress:
    - ports:
        - port: 443
        - port: 22

Run flux check --pre to verify, or flux install --network-policy so Flux creates a default NetworkPolicy at install time.

Supply Chain Security

The most important layer: making sure only legitimate artifacts enter the cluster.

Verifying Sources and Image Signatures

The source controller verifies Cosign signatures on OCI artifacts before serving them; the public key is stored as a secret referenced by the OCIRepository:

clusters/prod/apps/ocirepository.yaml
apiVersion: source.toolkit.fluxcd.io/v1beta2
kind: OCIRepository
metadata:
  name: api
  namespace: apps
spec:
  interval: 5m
  url: oci://ghcr.io/devvnull/api
  verify:
    provider: cosign
    secretRef:
      name: cosign-public-key

In CI, the image is signed before being pushed, then verified with cosign sign --key cosign.key ghcr.io/devvnull/api:1.2.3 and cosign verify --key cosign.pub ghcr.io/devvnull/api:1.2.3.

Admission Controller as the Last Gate

Verification at the source doesn't guarantee an image isn't altered at runtime; layer an admission controller — Kyverno or OPA Gatekeeper — that forces every Pod to meet a policy, for example that images must be signed:

clusters/prod/policies/require-signature.yaml
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: require-image-signature
spec:
  validationFailureAction: Enforce
  rules:
    - name: verify-cosign
      match:
        any:
          - resources:
              kinds:
                - Pod
      verifyImages:
        - image: "*"
          key: |-

This policy and other policies are declared in Git and synced by Flux — policy as code. Kyverno can also enforce resource limits, forbid privileged containers, and make sure security labels exist.

Closing

This episode built four defense layers for Flux: authentication for Git, registry, Helm, and the cluster; RBAC-based authorization with least privilege; network security through TLS and NetworkPolicy; and supply chain security with Cosign and admission controllers. Security is always layered — no single layer is enough.

The key takeaways:

  • Least privilege everywhere: read-only deploy keys for Git, tokens with minimal scope for the registry and Helm.
  • Flux stays in a dedicated namespace: flux-system isolated with strict RBAC and NetworkPolicy.
  • Egress is controlled: Flux may only reach known endpoints.
  • Signatures as proof: Cosign verifies sources, Kyverno or OPA ensure only legitimate images run.
  • Security configuration is also code: all policies are declared in Git so they can be reviewed and audited.

Flux security is now solid, but the secrets — SSH keys, tokens, passwords — are still stored raw in Git in the examples above. In the next episode, episode 20, we'll learn Secrets Management with SOPS: encrypting secrets in the repo, automatic Flux decryption, and key rotation. See you in episode 20!

Learning GitOps - FluxCD - Securing Flux | Learn FluxCD & GitOps