AWS External Secrets — Secrets Manager path convention
When the platform AWS addon installs a ClusterSecretStore (addons/kustomize/aws/external_secrets/), every workload namespace can talk to the same store. To keep tenants from pointing ExternalSecret objects at another team’s Secrets Manager names, the platform can enforce a cluster-scoped path layout with Kyverno (deny-external-secrets).
Path layout
Use this hierarchy in AWS Secrets Manager secret names (the remoteRef.key / dataFrom.extract.key values):
| Scope | Prefix pattern | Who may reference it |
|---|---|---|
| Cluster shared | <clusterName>/global/SECRETS + suffix |
Any namespace |
| Namespace-owned | <clusterName>/<namespace>/SECRETS + suffix |
Only that same Kubernetes namespace |
Examples for cluster dev:
- Allowed from any namespace:
dev/global/SECRETS/platform-ca-bundle - Allowed only from namespace
frontend:dev/frontend/SECRETS/api-token - Denied from namespace
frontend:dev/backend/testordev/backend/SECRETS/db(not underdev/global/SECRETS…and not underdev/frontend/SECRETS…)
The fixed segments global and SECRETS default names can be changed in Helm values (policies.denyExternalSecrets.aws.globalSegment and secretsPrefix).
What enforces this
-
Kyverno — Rule
aws-secrets-manager-pathson policydeny-external-secretsruns only when: -
policies.denyExternalSecrets.useAwsSecretsManagerPathsistrue, and policies.denyExternalSecrets.aws.clusterNameis non-empty, and- The
ExternalSecretusesspec.secretStoreRef.kind: ClusterSecretStoreand the configured store name (defaultsecrets-store).
Other stores (for example a namespace SecretStore) are not checked by this rule.
-
Cluster name — For AWS clusters, the platform ApplicationSet passes
policies.denyExternalSecrets.aws.clusterNamefrom the cluster definition labelcluster_name(seeaddons/helm/oss.yamlunderkyverno_policies). -
Platform defaults —
config/kyverno_policies/aws.yamlturns onuseAwsSecretsManagerPaths. Non-AWS clouds keep legacy behaviour unless you enable the AWS-style paths in values.
Enabling and tuning
In your workloads repo, merge values under config/kyverno_policies/ as described in Kyverno policy configuration. Relevant keys:
policies:
denyExternalSecrets:
enabled: true
validationFailureAction: enforce
useAwsSecretsManagerPaths: true
aws:
clusterName: "" # Usually injected from cluster metadata by the platform
clusterSecretStoreName: secrets-store
globalSegment: global
secretsPrefix: SECRETS
IAM (defence in depth)
Kyverno only controls what users declare in Kubernetes. Mirror the same boundaries in IAM for the role used by External Secrets (IRSA): allow secretsmanager:GetSecretValue (and any other required actions) on ARNs whose name matches cluster/global/SECRETS* and cluster/*/SECRETS* for the appropriate namespaces, or scope with a permission boundary your org prefers. Exact IAM wiring depends on how you attach the controller role; keep policy and IAM aligned.
Limitations
- The rule keys off
spec.secretStoreRefonly. If that field is omitted and eachdata[]entry usessourceRef.storeRefinstead, the AWS path check is skipped for that object; prefer a single top-levelsecretStoreRefwhen using the platformClusterSecretStore. - Validation applies to
spec.data[].remoteRef.keyand todataFrom[].extract.key. Entries that use onlyfind,rewrite, or generators are not covered by the key prefix checks; use a dedicated store or additional policies if those patterns must be locked down. - Admission-time only (
background: falsefor this policy); clusters must run the Kyverno webhook for enforcement.
Legacy (non-AWS) behaviour
If AWS path mode is off or clusterName is empty, deny-external-secrets uses rule namespace-prefix-keys: each key must start with <namespace>/, which matches the previous namespace-prefix model for non-AWS setups.