Skip to content

Security

Threat Model

ESO operates with elevated privileges inside the Kubernetes cluster. It can create, read, update, and delete Secrets in all namespaces and has access to external secret provider credentials. Understanding its threat surface is critical.

Threat Vector Impact Mitigation
Compromised ESO pod Full read access to all synced secrets Restrict with network policies, pod security standards
Leaked provider credentials Access to external secret store Use short-lived tokens, IRSA, Workload Identity
Malicious ExternalSecret Exfiltrate secrets from provider to attacker-controlled namespace RBAC on ExternalSecret creation, OPA/Kyverno policies
Malicious SecretStore Route secret fetching through attacker-controlled endpoint Validate provider configs, restrict SecretStore creation
PushSecret abuse Push cluster secrets to attacker-controlled external store Restrict PushSecret RBAC, audit PushSecret creation
Privilege escalation via ClusterSecretStore Cross-namespace secret access Use namespace conditions on ClusterSecretStore

RBAC Model

Cluster Operator Permissions

Cluster operators manage the ESO deployment and ClusterSecretStores. They require broad permissions:

  • external-secrets.io/clustersecretstores -- CRUD at cluster scope.
  • external-secrets.io/externalsecrets -- Read/list across namespaces for troubleshooting.
  • Core Kubernetes secrets -- Read/write across namespaces (delegated to ESO ServiceAccount).

Application Developer Permissions

Application developers define ExternalSecrets within their namespaces. They should have limited scope:

# Example: Developer can create ExternalSecrets in their namespace only
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: external-secret-developer
  namespace: my-app
rules:
  - apiGroups: ["external-secrets.io"]
    resources: ["externalsecrets"]
    verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
  - apiGroups: ["external-secrets.io"]
    resources: ["secretstores"]
    verbs: ["get", "list", "watch"]

SecretStore Creation

Granting developers the ability to create SecretStore resources allows them to define arbitrary provider connections. In most environments, only cluster operators should create SecretStores, while developers reference pre-approved stores.

ESO Controller ServiceAccount

The ESO controller runs with a ServiceAccount that has broad permissions. The Helm chart creates:

  • ClusterRole with permissions to manage Secrets across all namespaces.
  • ClusterRole with permissions to manage all ESO CRDs.
  • ClusterRoleBinding binding these to the ESO ServiceAccount.

Restrict this by running multiple controllers with different ServiceAccounts, each scoped to specific namespaces or controller names.

Provider Credential Security

AWS (IRSA / Pod Identity)

The recommended approach for AWS is IAM Roles for Service Accounts (IRSA) or EKS Pod Identity, which eliminates static credentials:

apiVersion: external-secrets.io/v1
kind: SecretStore
metadata:
  name: aws-secretsmanager
spec:
  provider:
    aws:
      service: SecretsManager
      region: us-east-1
      auth:
        jwt:
          serviceAccountRef:
            name: eso-sa

With IRSA, the eso-sa ServiceAccount is annotated with an IAM role ARN. AWS SDK in the ESO pod exchanges the ServiceAccount JWT token for temporary STS credentials. No static accessKeyID or secretAccessKey is stored in Kubernetes.

GCP (Workload Identity)

For GCP Secret Manager, use Workload Identity to bind a Kubernetes ServiceAccount to a GCP Service Account:

apiVersion: external-secrets.io/v1
kind: SecretStore
metadata:
  name: gcp-secret-manager
spec:
  provider:
    gcpsm:
      projectID: my-project
      auth:
        workloadIdentity:
          serviceAccountRef:
            name: eso-workload-id-sa
          clusterLocation: us-central1
          clusterName: my-gke-cluster

HashiCorp Vault (Kubernetes Auth)

When using Vault as the provider, the Kubernetes auth method allows ESO pods to authenticate using their ServiceAccount JWT token:

apiVersion: external-secrets.io/v1
kind: SecretStore
metadata:
  name: vault-store
spec:
  provider:
    vault:
      server: "https://vault.example.com"
      path: "secret"
      version: "v2"
      auth:
        kubernetes:
          mountPath: "kubernetes"
          role: "eso-role"
          serviceAccountRef:
            name: eso-vault-sa

Static Token Risks

Avoid using tokenSecretRef with static Vault tokens in production. These tokens never expire unless manually revoked. Prefer the Kubernetes auth method or AppRole with periodic secret IDs.

Namespace Isolation

SecretStore (Namespaced)

A SecretStore is namespaced and can only be referenced by ExternalSecret resources in the same namespace. Credentials referenced by the SecretStore (via secretRef) must also exist in the same namespace. This provides natural namespace isolation.

ClusterSecretStore (Cluster-Scoped)

A ClusterSecretStore can be referenced by ExternalSecret resources in any namespace. To restrict access, use the conditions field:

apiVersion: external-secrets.io/v1
kind: ClusterSecretStore
metadata:
  name: restricted-store
spec:
  conditions:
    - namespaces:
        - "team-a"
        - "team-b"
      secretRef:
        namespace: "eso-system"
  provider:
    vault:
      server: "https://vault.example.com"
      path: "secret"
      auth:
        kubernetes:
          mountPath: "kubernetes"
          role: "eso-role"

This restricts the ClusterSecretStore to only serve ExternalSecret resources in the team-a and team-b namespaces.

Cross-Namespace References

Some SecretStore fields allow referencing Kubernetes Secrets in other namespaces (e.g., certSecretRef.namespace). In multi-tenant environments, carefully audit these cross-namespace references to prevent privilege escalation.

PushSecret Security Considerations

PushSecret reverses the normal data flow by pushing Kubernetes Secrets to external providers. This introduces additional security concerns:

  1. Data exfiltration risk -- A malicious PushSecret could push secrets to an attacker-controlled external provider. Restrict PushSecret creation with RBAC and admission policies.
  2. Provider write permissions -- The SecretStore referenced by a PushSecret needs write access to the external provider. Grant minimum required permissions.
  3. Deletion policy -- PushSecretDeletionPolicyDelete removes secrets from the external provider when the PushSecret is deleted. Ensure this does not cause unintended data loss.
  4. Update policy -- Configure PushSecretUpdatePolicy to control whether existing provider secrets are overwritten.
# Restrict PushSecret creation with RBAC
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: pushsecret-restricted
  namespace: my-app
rules:
  - apiGroups: ["external-secrets.io"]
    resources: ["pushsecrets"]
    verbs: ["get", "list"]  # No create/update/delete

Admission Control Integration

Use Kubernetes admission controllers (OPA Gatekeeper, Kyverno) to enforce policies on ESO resources:

  • Allowed providers -- Restrict which provider types can be configured in SecretStores.
  • Allowed namespaces -- Restrict which namespaces can create ExternalSecrets or PushSecrets.
  • Template validation -- Ensure ExternalSecret templates do not inject malicious content.
  • Creation policy enforcement -- Prevent creationPolicy: Merge or None in sensitive namespaces.

Network Security

  • Deploy ESO in a dedicated namespace with strict network policies.
  • Restrict egress traffic from the ESO controller to only the required provider endpoints.
  • Use TLS for all provider connections. Verify certificates using caBundle or caProvider in SecretStore configuration.
  • For Vault, use mutual TLS (tls.certSecretRef and tls.keySecretRef) when the Vault server requires client certificates.

Audit and Observability

  • ESO emits Kubernetes events for reconciliation successes and failures. Monitor these events for anomalous patterns.
  • The status field on ExternalSecret and SecretStore resources reflects the current state (Ready, SecretSynced, SecretSyncedError).
  • Log all changes to ESO CRDs via Kubernetes audit logging.
  • Monitor for unexpected Secret modifications that correlate with ExternalSecret reconciliation events.