Skip to main content

Extra

O que acontece se deletarmos uma secret na AWS que esta mapeada?

❯ k get secrets                                                                                  
NAME TYPE DATA AGE
app-auth kubernetes.io/basic-auth 2 104m
minha-secret Opaque 1 37m
minha-secret-k8s Opaque 4 129m

❯ k describe es external-secret-auth
Name: external-secret-auth
Namespace: default
Labels: <none>
Annotations: <none>
API Version: external-secrets.io/v1
Kind: ExternalSecret
Metadata:
Creation Timestamp: 2025-07-01T01:13:27Z
Generation: 1
Resource Version: 3407507
UID: acf4b93d-b1ee-4c36-b0fa-0a1ed172ae10
Spec:
Data From:
Extract:
Conversion Strategy: Default
Decoding Strategy: None
Key: test/app
Metadata Policy: None
Refresh Interval: 1m
Secret Store Ref:
Kind: ClusterSecretStore
Name: aws-secrets-manager
Target:
Creation Policy: Owner
Deletion Policy: Retain
Name: app-auth
Template:
Engine Version: v2
Merge Policy: Replace
Metadata:
Labels:
App: my-app
Type: kubernetes.io/basic-auth
Status:
Binding:
Name: app-auth
Conditions:
Last Transition Time: 2025-07-01T01:13:27Z
Message: secret synced
Reason: SecretSynced
Status: True # Atençã aqui!
Type: Ready
Refresh Time: 2025-07-01T02:58:27Z
Synced Resource Version: 1-dd100cb940ef5edeb926b19bde61ff16c82209bcb92fb4574e67bbcf
Events: <none>

Deletei a test/app.

Se você deletar uma secret na AWS que está sendo usada por um ExternalSecret, o que acontece vai depender da configuração e do comportamento padrão do ESO:

  • O ESO tenta sincronizar a cada refreshInterval.
  • Se ele não encontrar a secret no provider, ele não apaga o Secret do Kubernetes por padrão.
  • Mas o .status.conditions do ExternalSecret entra em erro com algo tipo:
❯ k describe es external-secret-auth
Name: external-secret-auth
Namespace: default
Labels: <none>
Annotations: <none>
API Version: external-secrets.io/v1
Kind: ExternalSecret
Metadata:
Creation Timestamp: 2025-07-01T01:13:27Z
Generation: 1
Resource Version: 3408307
UID: acf4b93d-b1ee-4c36-b0fa-0a1ed172ae10
Spec:
Data From:
Extract:
Conversion Strategy: Default
Decoding Strategy: None
Key: test/app
Metadata Policy: None
Refresh Interval: 1m
Secret Store Ref:
Kind: ClusterSecretStore
Name: aws-secrets-manager
Target:
Creation Policy: Owner
Deletion Policy: Retain
Name: app-auth
Template:
Engine Version: v2
Merge Policy: Replace
Metadata:
Labels:
App: my-app
Type: kubernetes.io/basic-auth
Status:
Binding:
Name: app-auth
Conditions:
Last Transition Time: 2025-07-01T03:00:27Z
Message: could not get secret data from provider # Message
Reason: SecretSyncedError # Error
Status: False # Já está como status false
Type: Ready
Refresh Time: 2025-07-01T02:59:27Z
Synced Resource Version: 1-dd100cb940ef5edeb926b19bde61ff16c82209bcb92fb4574e67bbcf
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Warning UpdateFailed 2m18s external-secrets error processing spec.dataFrom[0].extract, err: operation error Secrets Manager: GetSecretValue, https response error StatusCode: 400, RequestID: 4dee00d1-6413-4f21-adad-5e7b5bebf05d, InvalidRequestException: You can't perform this operation on the secret because it was marked for deletion.

O ESO não apaga o Secret do K8s só porque a secret do provider sumiu. Isso é proposital, pra não causar quebra de app por erro externo. Se você retornar a secret o status ficará true e a mensagem de erro sumirá.

Dica de boas práticas: Sempre tenha alertas nos .status.conditions dos ExternalSecrets com Prometheus ou outro sistema. Valide se o .status.conditions[].status == False e reason == SecretSyncedError.

ClusterExternalSecret

Se você tem 20 namespaces e quer replicar um mesmo ExternalSecret, sem copiar o YAML 20 vezes... existe o ClusterExternalSecret.

apiVersion: external-secrets.io/v1beta1
kind: ClusterExternalSecret
metadata:
name: sync-para-todos
spec:
namespaceSelector:
matchLabels:
sync-db: "true"
template:
metadata:
name: db-secret
spec:
refreshInterval: 1m
secretStoreRef:
name: aws-secrets-manager
kind: ClusterSecretStore
target:
name: minha-secret-k8s
creationPolicy: Owner
data:
- secretKey: DB_USER
remoteRef:
key: test/app
property: username
- secretKey: DB_PASS
remoteRef:
key: test/app
property: password

  • O ESO vai criar um ExternalSecret chamado db-secret em cada namespace com sync-db: "true".
  • Esse ExternalSecret vai ter exatamente o conteúdo do template.
  • Cada namespace receberá sua própria Secret chamada minha-secret-k8s.

Se quiser algo mais dinâmico você ainda pode usar o Go Template para mudar o nome da secret por namespace.

###...
spec:
refreshInterval: 1m
secretStoreRef:
name: aws-secrets-manager
kind: ClusterSecretStore
target:
name: app-secret-{{ .metadata.namespace }}
creationPolicy: Owner
###...

Secret de Múltiplos Providers (Multi-Backend)

Ter um SecretStore para AWS, outro para GCP, outro para Vault e combinar tudo no mesmo ExternalSecret, pegando dados de cada um via data ou dataFrom.

apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata:
name: multi-provider-secret
namespace: default
spec:
refreshInterval: 1h
target:
name: minha-secret-final
creationPolicy: Owner
# O secretStoreRef no nível do spec (global) é opcional se todos os campos usam um store específico no data.
data:
- secretKey: DB_USER
remoteRef:
key: my-app/credentials
property: username

secretStoreRef:
name: aws-secretstore
kind: ClusterSecretStore

- secretKey: GCP_API_KEY
remoteRef:
key: gcp-secret-id
secretStoreRef:
name: gcp-secretstore
kind: ClusterSecretStore

- secretKey: VAULT_TOKEN
remoteRef:
key: secret/data/my-app
property: token
secretStoreRef:
name: vault-secretstore
kind: ClusterSecretStore

E ter uma única secret juntando várias partes esperando um resultado tipo esse na secret.

data:
DB_USER: <vem da AWS>
GCP_API_KEY: <vem do GCP Secrets Manager>
VAULT_TOKEN: <vem do HashiCorp Vault>