Security¶
Related Notes
secrets/external-secrets-operator/index | secrets/external-secrets-operator/architecture | secrets/external-secrets-operator/architecture | secrets/external-secrets-operator/operations
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:
ClusterRolewith permissions to manageSecretsacross all namespaces.ClusterRolewith permissions to manage all ESO CRDs.ClusterRoleBindingbinding 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:
- Data exfiltration risk -- A malicious PushSecret could push secrets to an attacker-controlled external provider. Restrict PushSecret creation with RBAC and admission policies.
- Provider write permissions -- The SecretStore referenced by a PushSecret needs write access to the external provider. Grant minimum required permissions.
- Deletion policy --
PushSecretDeletionPolicyDeleteremoves secrets from the external provider when the PushSecret is deleted. Ensure this does not cause unintended data loss. - Update policy -- Configure
PushSecretUpdatePolicyto 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: MergeorNonein 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
caBundleorcaProviderin SecretStore configuration. - For Vault, use mutual TLS (
tls.certSecretRefandtls.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
statusfield 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.