Vam acabar 04-02 amb la base de dades mercadofresco-pedidos xifrada amb AES-256 i una clau de KMS
amb política, rotació i separació de funcions. I tot i així, qualsevol que s'assegui davant del
portàtil del Luis es pot connectar a aquesta base de dades, perquè la contrasenya de l'usuari
mfadmin està escrita en text pla com a mínim en cinc llocs:
- Al
user datade la plantilla de llançamentlt-mercadofresco-tienda, que es pot llegir des del servei de metadades de qualsevol instància de la botiga. - A
/etc/mercadofresco/tienda.confdins de cada instància. - En un fitxer
.envque el Luis es va enviar a si mateix per xat quan va muntar l'entorn de desenvolupament. - A l'historial d'ordres de la sessió on la va fer servir per primer cop.
- Al cap de la Marta, que la va triar fa catorze mesos i que no ha canviat des d'aleshores.
El xifratge no ajuda aquí. Com vam veure a la primera taula de 04-02, el xifratge en repòs protegeix del disc robat i de l'snapshot compartit per error, però no protegeix d'unes credencials vàlides en mans equivocades. Aquesta lliçó resol exactament aquest problema.
AWS ofereix dos serveis per custodiar valors de configuració i credencials: AWS Systems Manager Parameter Store i AWS Secrets Manager. No són competidors, són complementaris, i la meitat d'aquesta lliçó consisteix a saber quin fer servir per a cada cosa.
Advertiment. Els exemples són didàctics i les credencials, fictícies. Tota gestió real de credencials, rotació i compliment (RGPD, PCI DSS) ha de ser revisada per un professional de seguretat abans d'aplicar-se a un entorn amb dades de clients. Un secret mal migrat —especialment una rotació mal configurada— deixa l'aplicació fora de servei i sense manera evident de tornar enrere. No facis servir mai credencials reals en un entorn de proves.
Contingut
- Per què les credencials al codi són un problema estructural
- Què passa exactament quan un secret arriba a Git
- Configuració i secret no són el mateix
- Parameter Store: tipus de paràmetre
- Jerarquia per rutes, versions i nivells
- La configuració de MercadoFresco a Parameter Store
- Secrets Manager: secrets, versions i etapes
- Xifratge amb KMS i polítiques de recurs
- Rotació automàtica: el cicle de quatre passos
- Configurar la rotació de
mercadofresco/produccion/rds/mfadmin - Usuari únic enfront d'alternança de dos usuaris
- Taula comparativa i la recomanació per a MercadoFresco
- Consum des de l'aplicació: boto3 i memòria cau
- Consum des de Lambda, EC2, ECS i CloudFormation
- Permisos mínims per llegir un secret
- La migració: treure la contrasenya del
user data - Què no cal guardar aquí
- Auditoria de l'accés als secrets
- Detecció de secrets filtrats al repositori
- Cost, quotes i neteja
Per què les credencials al codi són un problema estructural
No és descuit, és un problema de disseny. Una credencial escrita en un fitxer de configuració té quatre propietats que la condemnen:
| Propietat | Conseqüència |
|---|---|
| Es copia | Cada desplegament, cada còpia de seguretat, cada portàtil nou multiplica les còpies |
| No caduca | La contrasenya de mfadmin fa catorze mesos que és la mateixa, i en podria fer deu anys |
| No deixa rastre | Ningú no sap qui l'ha llegit; no hi ha registre possible |
| No es pot revocar sense trencar res | Canviar-la implica actualitzar tots els llocs alhora |
La quarta és la que perpetua el problema. La Marta sap que la contrasenya s'hauria de canviar, però canviar-la significa editar la plantilla de llançament, reiniciar totes les instàncies de l'ASG, avisar el Luis i creuar els dits. Com que el risc de l'operació sembla més gran que el de no fer res, no es fa mai. La rotació automàtica trenca aquest cercle, i per això és el cor d'aquesta lliçó.
Què passa exactament quan un secret arriba a Git
Convé ser concret, perquè l'error més car és creure que n'hi ha prou d'esborrar-lo al commit següent:
# El Luis se n'adona i «ho arregla»
git rm --cached .env
git commit -m "Treure el .env del repositori"
git pushAixò no elimina res. El fitxer continua a l'historial, accessible amb
git show <commit-anterior>:.env. I si el repositori va estar en un servidor compartit o algú
en va fer un clone, aquestes còpies també el tenen. L'únic procediment correcte és:
- Rota la credencial immediatament. Considera-la compromesa des del moment en què es va escriure. Aquest pas és obligatori i no és opcional.
- Reescriure l'historial (
git filter-repo, BFG) i forçar el push. Això trenca els clons de tot l'equip, així que cal coordinar-ho. - Buscar als registres si la credencial s'ha fet servir des d'algun lloc inesperat.
- Afegir un escàner que impedeixi que torni a passar.
L'ordre importa: rotar primer. Reescriure l'historial mentre la credencial continua sent vàlida és tractar el símptoma. Si el repositori és públic, els bots que rastregen GitHub troben credencials d'AWS en qüestió de minuts, i el cost d'un compte utilitzat per minar criptomonedes es compta en milers de dòlars al dia.
Configuració i secret no són el mateix
Aquesta distinció decideix quin servei fer servir:
| Configuració | Secret | |
|---|---|---|
| Exemple | Nom del bucket, mida de pàgina, temps d'espera | Contrasenya, clau d'API, token |
| Pot aparèixer en un registre? | Sí, sense conseqüències | Mai |
| Necessita rotar? | No | Sí, periòdicament |
| Qui la pot veure? | Tot l'equip tècnic | Només qui la necessita |
| Servei | Parameter Store | Secrets Manager |
Casos frontera que convé resoldre per endavant:
- El nom d'usuari de la base de dades (
mfadmin) és configuració. La contrasenya és secret. Però es guarden junts, perquè la rotació canvia tots dos en el cas d'alternança. - El punt d'enllaç d'RDS és configuració: no és secret i ja és públic dins la teva VPC.
- Una clau d'API d'un proveïdor de pagaments és un secret, encara que sigui d'un entorn de proves.
- L'identificador de la distribució de CloudFront (
E2QWERTY123ABC) és configuració.
Parameter Store: tipus de paràmetre
Parameter Store és un component d'AWS Systems Manager. Guarda parells clau-valor amb tres tipus:
| Tipus | Xifrat | Ús |
|---|---|---|
String |
No | Valors simples: noms, URL, números |
StringList |
No | Llista separada per comes: eu-west-1a,eu-west-1b |
SecureString |
Sí, amb KMS | Valors sensibles de perfil baix |
# Un paràmetre simple
aws ssm put-parameter \
--name "/mercadofresco/produccion/tienda/nombre-bucket-fotos" \
--value "mercadofresco-catalogo-fotos" \
--type String \
--description "Bucket del cataleg de fotos de la botiga" \
--tags Key=Proyecto,Value=mercadofresco Key=Entorno,Value=produccion \
--profile mercadofresco-dev
# Una llista
aws ssm put-parameter \
--name "/mercadofresco/produccion/red/zonas" \
--value "eu-west-1a,eu-west-1b" \
--type StringList \
--profile mercadofresco-dev
# Un valor xifrat amb la nostra clau de 04-02
aws ssm put-parameter \
--name "/mercadofresco/produccion/tienda/clave-api-mensajeria" \
--value "ficticia-1234567890" \
--type SecureString \
--key-id alias/mercadofresco-datos \
--profile mercadofresco-devNotes sobre SecureString:
- Si no indiques
--key-id, es fa servir la clau gestionada per AWSalias/aws/ssm, que és gratuïta però no controlable. Per a valors que importen, fes servir la teva clau. - Per llegir el valor desxifrat cal
--with-decryptioni permískms:Decryptsobre la clau. És la mateixa doble autorització d'SSE-KMS.
aws ssm get-parameter \
--name "/mercadofresco/produccion/tienda/clave-api-mensajeria" \
--with-decryption \
--query 'Parameter.Value' --output text \
--profile mercadofresco-devJerarquia per rutes, versions i nivells
La jerarquia per rutes és la característica que fa útil Parameter Store. Els noms s'estructuren com a rutes i es poden llegir en bloc:
/mercadofresco/
├── produccion/
│ ├── tienda/
│ │ ├── nombre-bucket-fotos
│ │ ├── url-cdn
│ │ ├── pedidos-por-pagina
│ │ └── tiempo-espera-segundos
│ ├── basedatos/
│ │ ├── punto-enlace
│ │ ├── punto-enlace-lectura
│ │ ├── nombre-bd
│ │ └── usuario
│ └── red/
│ └── zonas
└── desarrollo/
└── tienda/
└── ...Amb una sola crida l'aplicació carrega tota la seva configuració:
aws ssm get-parameters-by-path \
--path "/mercadofresco/produccion/tienda/" \
--recursive --with-decryption \
--query 'Parameters[].[Name,Value]' --output table \
--profile mercadofresco-devI la jerarquia es converteix directament en control d'accés: una política que concedeix
ssm:GetParametersByPath sobre /mercadofresco/produccion/* deixa fora tot l'entorn de
desenvolupament sense enumerar ni un sol paràmetre.
Versions. Cada put-parameter --overwrite crea una versió nova i conserva les anteriors. Es
pot llegir una versió concreta amb nom:numero, i etiquetar una versió amb un àlies:
aws ssm put-parameter --name "/mercadofresco/produccion/tienda/pedidos-por-pagina" \
--value "50" --type String --overwrite --profile mercadofresco-dev
aws ssm label-parameter-version \
--name "/mercadofresco/produccion/tienda/pedidos-por-pagina" \
--parameter-version 3 --labels estable --profile mercadofresco-dev
# Llegir una versió concreta o una etiqueta
aws ssm get-parameter --name "/mercadofresco/produccion/tienda/pedidos-por-pagina:2" \
--profile mercadofresco-dev
aws ssm get-parameter --name "/mercadofresco/produccion/tienda/pedidos-por-pagina:estable" \
--profile mercadofresco-devTornar enrere després d'un canvi desafortunat és canviar l'etiqueta estable a la versió anterior. No
hi ha cap desplegament pel mig.
Nivells.
| Estàndard | Avançat | |
|---|---|---|
| Paràmetres per compte i regió | 10.000 | 100.000 |
| Mida del valor | 4 KB | 8 KB |
| Polítiques de paràmetre (caducitat, avisos) | No | Sí |
| Cost d'emmagatzematge | Gratis | 0,05 USD per paràmetre i mes |
| Cost de les crides | Gratis fins a 40/s | 0,05 USD per cada 10.000 |
MercadoFresco cap de sobres al nivell estàndard: zero euros per tota la seva configuració. El nivell avançat es justifica quan necessites més de 4 KB —un certificat, per exemple— o les polítiques de caducitat, que avisen quan un paràmetre fa massa temps que no canvia.
Hi ha a més un mode de rendiment alt (--parameter-tier, amb ssm:GetParameters a 3.000
peticions per segon) que es factura a part; només cal si l'aplicació llegeix paràmetres al
camí crític de cada petició, cosa que no hauria de fer: per a això hi ha la memòria cau.
La configuració de MercadoFresco a Parameter Store
crear() {
aws ssm put-parameter --name "$1" --value "$2" --type "${3:-String}" --overwrite \
--tags Key=Proyecto,Value=mercadofresco Key=Entorno,Value=produccion \
Key=Componente,Value=tienda Key=Propietario,Value=marta \
Key=CentroCoste,Value=tecnologia \
--profile mercadofresco-dev
}
crear "/mercadofresco/produccion/basedatos/punto-enlace" \
"mercadofresco-pedidos.abc123.eu-west-1.rds.amazonaws.com"
crear "/mercadofresco/produccion/basedatos/punto-enlace-lectura" \
"mercadofresco-pedidos-lectura.abc123.eu-west-1.rds.amazonaws.com"
crear "/mercadofresco/produccion/basedatos/nombre-bd" "pedidos"
crear "/mercadofresco/produccion/basedatos/usuario" "mfadmin"
crear "/mercadofresco/produccion/tienda/nombre-bucket-fotos" "mercadofresco-catalogo-fotos"
crear "/mercadofresco/produccion/tienda/url-cdn" "https://d111111abcdef8.cloudfront.net"
crear "/mercadofresco/produccion/tienda/pedidos-por-pagina" "50"
crear "/mercadofresco/produccion/tienda/tiempo-espera-segundos" "30"
crear "/mercadofresco/produccion/tienda/tema-alertas" \
"arn:aws:sns:eu-west-1:111122223333:alertas-mercadofresco"Fixa't que l'usuari mfadmin és aquí i la contrasenya no. El nom d'usuari és
configuració; la contrasenya va a l'altre servei. I observa l'efecte col·lateral: el user data de
lt-mercadofresco-tienda, que fins ara era una llista de valors incrustats, passa a ser una
crida genèrica idèntica a tots els entorns.
Secrets Manager: secrets, versions i etapes
Un secret de Secrets Manager és un valor —normalment un JSON— xifrat amb KMS, amb versions i un mecanisme de rotació. La peça que cal entendre bé són les etapes de versió.
Cada versió d'un secret porta una o diverses etiquetes d'etapa:
| Etapa | Significat |
|---|---|
AWSCURRENT |
La versió vigent. És la que retorna get-secret-value per defecte |
AWSPENDING |
Versió candidata durant una rotació en curs, encara no validada |
AWSPREVIOUS |
La versió anterior, conservada per poder tornar enrere |
Aquestes tres etiquetes són el mecanisme que fa que la rotació no talli el servei. Durant uns
segons coexisteixen la contrasenya vella (AWSCURRENT) i la nova (AWSPENDING), i només quan la
nova s'ha provat s'intercanvien les etiquetes. Cap aplicació no es queda sense credencial vàlida
en cap moment.
Crear el secret de MercadoFresco:
aws secretsmanager create-secret \
--name "mercadofresco/produccion/rds/mfadmin" \
--description "Credencials de l'usuari mfadmin de mercadofresco-pedidos" \
--kms-key-id alias/mercadofresco-datos \
--secret-string '{
"engine": "postgres",
"host": "mercadofresco-pedidos.abc123.eu-west-1.rds.amazonaws.com",
"port": 5432,
"dbname": "pedidos",
"username": "mfadmin",
"password": "ContrasenaFicticiaTemporal2026",
"dbInstanceIdentifier": "mercadofresco-pedidos"
}' \
--tags Key=Proyecto,Value=mercadofresco Key=Entorno,Value=produccion \
Key=Componente,Value=basedatos Key=Propietario,Value=marta \
Key=CentroCoste,Value=tecnologia \
--profile mercadofresco-devL'estructura del JSON no és arbitrària: aquests noms de camp (engine, host, port,
dbname, username, password) són exactament els que espera la funció de rotació gestionada
d'AWS per a PostgreSQL. Si els canvies, la rotació no funcionarà. És un dels detalls que fa perdre
més temps.
--kms-key-id alias/mercadofresco-datos connecta aquesta lliçó amb l'anterior: el secret es xifra
amb la clau que vam construir a 04-02, i per tant queda subjecte a la seva política. Qui no pugui fer
servir la clau no podrà llegir el secret encara que tingui secretsmanager:GetSecretValue. Un altre
cop la doble autorització.
Xifratge amb KMS i polítiques de recurs
Com un bucket, una cua o una clau, un secret admet política de recurs. Serveix per a dues coses:
accés entre comptes i reforç amb Deny.
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "SoloLaTiendaYMartaLeenEsteSecreto",
"Effect": "Deny",
"Principal": "*",
"Action": "secretsmanager:GetSecretValue",
"Resource": "*",
"Condition": {
"StringNotEquals": {
"aws:PrincipalArn": [
"arn:aws:iam::111122223333:role/rol-mercadofresco-tienda",
"arn:aws:iam::111122223333:user/marta",
"arn:aws:iam::111122223333:role/rol-rotacion-mfadmin"
]
}
}
}
]
}És un Deny amb StringNotEquals: «denega la lectura a tothom que no sigui un d'aquests tres».
Recorda de 04-01 que un Deny guanya sempre, així que ni una política d'identitat amb
secretsmanager:* sobre * se'l pot saltar. És la manera més robusta de blindar el secret més
sensible del compte, i cal incloure a la llista el rol de rotació o la rotació fallarà.
aws secretsmanager put-resource-policy \
--secret-id mercadofresco/produccion/rds/mfadmin \
--resource-policy file:///tmp/politica-secreto.json \
--block-public-policy \
--profile mercadofresco-dev--block-public-policy rebutja la política si concedís accés públic. Fes-lo servir sempre.
Rotació automàtica: el cicle de quatre passos
Aquí hi ha el valor diferencial de Secrets Manager. La rotació l'executa una funció Lambda, que AWS proporciona ja escrita per als motors de base de dades habituals, i que s'invoca quatre vegades amb un pas diferent cada cop.
sequenceDiagram
participant SM as Secrets Manager
participant L as Lambda de rotacio<br/>SecretsManagerRDSPostgreSQL...
participant DB as mercadofresco-pedidos
participant A as Botiga (aplicacio)
Note over SM,A: Dia 30: toca rotar
SM->>L: Step 1 - createSecret
L->>L: Genera contrasenya aleatoria
L->>SM: PutSecretValue(etapa=AWSPENDING)
Note over SM: AWSCURRENT = vella<br/>AWSPENDING = nova
SM->>L: Step 2 - setSecret
L->>DB: ALTER USER mfadmin PASSWORD 'nova'
Note over DB: Ara la BD accepta la NOVA
SM->>L: Step 3 - testSecret
L->>DB: Connectar amb AWSPENDING i fer SELECT
DB-->>L: OK
Note over L: Si falla aqui, s'avorta<br/>i AWSCURRENT segueix intacta
SM->>L: Step 4 - finishSecret
L->>SM: Moure AWSCURRENT a la versio nova
Note over SM: nova = AWSCURRENT<br/>vella = AWSPREVIOUS
A->>SM: GetSecretValue (propera lectura)
SM-->>A: contrasenya nova
Els quatre passos, amb el que importa de cadascun:
| Pas | Què fa | Què passa si falla |
|---|---|---|
createSecret |
Genera la contrasenya nova i la desa com a AWSPENDING. No toca la base de dades |
No canvia res; es reintenta |
setSecret |
Canvia la contrasenya a la base de dades | Moment delicat: la BD pot tenir la nova i el secret no haver-la promocionat |
testSecret |
Es connecta amb la nova i executa una consulta de prova | S'avorta la rotació; AWSCURRENT continua sent la vella |
finishSecret |
Mou l'etiqueta AWSCURRENT a la versió nova |
Es reintenta |
La conseqüència pràctica més important és la que gairebé ningú no anticipa: entre setSecret i
finishSecret hi ha una finestra en què la base de dades ja té la contrasenya nova però
AWSCURRENT encara retorna la vella. Si la teva aplicació desa el secret a la memòria cau durant
una hora i obre una connexió nova just en aquesta finestra, rebrà un error d'autenticació.
Per això el patró correcte a l'aplicació és:
- Desar el secret a la memòria cau (una lectura per petició és caríssima i lenta).
- En rebre un error d'autenticació, invalidar la memòria cau i reintentar un cop.
Aquest reintent d'una línia és el que converteix la rotació en una cosa transparent. Sense ell, cada rotació produeix un grapat d'errors als registres.
Configurar la rotació de mercadofresco/produccion/rds/mfadmin
La manera més senzilla és deixar que Secrets Manager creï la Lambda des de la seva plantilla gestionada:
aws secretsmanager rotate-secret \
--secret-id mercadofresco/produccion/rds/mfadmin \
--rotation-lambda-arn arn:aws:lambda:eu-west-1:111122223333:function:rotacion-mfadmin \
--rotation-rules '{"AutomaticallyAfterDays": 30, "Duration": "2h",
"ScheduleExpression": "cron(0 3 ? * TUE *)"}' \
--profile mercadofresco-devEls paràmetres de --rotation-rules:
AutomaticallyAfterDays: 30: cada 30 dies.ScheduleExpression: quan exactament. Aquí, els dimarts a les 3 de la matinada. És deliberat: mai un dijous ni un divendres, pel pic de comandes.Duration: "2h": finestra de dues hores dins la qual pot començar.
La Lambda de rotació necessita tres coses que s'obliden constantment:
- Accés de xarxa a la base de dades. Ha d'estar a la VPC
vpc-mercadofresco, a les subxarxessnet-mercadofresco-app-a/-b, isg-mercadofresco-basedatosha d'acceptar el 5432 des del seu grup de seguretat. Aquí s'aplica el que vam veure a 03-02: referenciar l'SG d'origen en lloc d'un CIDR. - Accés a l'API de Secrets Manager. Com que és a subxarxes privades i la seva sortida a internet
passa per
nat-mercadofresco-a, o bé s'accepta aquest cost o bé es crea un endpoint de VPC d'interfície per asecretsmanager, igual que vam fer ambvpce-mercadofresco-s3a 03-01. Sense una de les dues coses, la Lambda es queda penjada fins a esgotar el temps d'espera i el símptoma no diu res. - Permisos. El seu rol necessita
secretsmanager:GetSecretValue,PutSecretValue,UpdateSecretVersionStage,DescribeSecret,GetRandomPassword, méskms:Decryptikms:GenerateDataKeysobrealias/mercadofresco-datos.
Rotació manual immediata, molt útil per provar abans de confiar en el calendari:
aws secretsmanager rotate-secret \
--secret-id mercadofresco/produccion/rds/mfadmin \
--rotate-immediately \
--profile mercadofresco-dev
# Veure el resultat
aws secretsmanager describe-secret \
--secret-id mercadofresco/produccion/rds/mfadmin \
--query '{Rotacio:RotationEnabled, Ultima:LastRotatedDate, Proxima:NextRotationDate,
Versions:VersionIdsToStages}' \
--profile mercadofresco-devProva la rotació en desenvolupament abans d'activar-la a producció. És el consell més important
d'aquesta lliçó: una rotació mal configurada deixa la botiga sense base de dades, i l'error de
setSecret a mitges pot requerir canviar la contrasenya a mà per recuperar-se.
Usuari únic enfront d'alternança de dos usuaris
Hi ha dues estratègies de rotació, i l'elecció té conseqüències reals de disponibilitat:
| Usuari únic | Alternança de dos usuaris | |
|---|---|---|
| Com funciona | Canvia la contrasenya del mateix usuari | Alterna entre mfadmin_a i mfadmin_b |
| Finestra sense credencial vàlida | Existeix, de segons | No existeix |
| Complexitat | Baixa | Mitjana: cal crear i mantenir dos usuaris |
| Connexions obertes | Continuen funcionant fins a reconnectar | Igual |
| Quan | Aplicacions amb reintent correcte | Càrregues crítiques sense marge d'error |
MercadoFresco comença amb usuari únic perquè és més simple i la botiga implementa el reintent.
Quan el volum creixi —o quan entrin en joc contenidors que arrenquen i moren
constantment, al mòdul 10— l'alternança de dos usuaris serà l'opció sensata. La plantilla
gestionada d'AWS existeix per als dos casos:
SecretsManagerRDSPostgreSQLRotationSingleUser i ...RotationMultiUser.
Taula comparativa i la recomanació per a MercadoFresco
| Secrets Manager | Parameter Store | |
|---|---|---|
| Rotació automàtica | Sí, gestionada, amb Lambda | No |
| Cost | 0,40 USD per secret i mes + 0,05 USD/10.000 crides | Gratis al nivell estàndard |
| Mida màxima | 64 KB | 4 KB (8 KB avançat) |
| Xifratge | Sempre, amb KMS | Només SecureString |
| Polítiques de recurs | Sí | No |
| Accés entre comptes | Sí | No directament |
| Jerarquia per rutes | No (però es fan servir / per convenció) |
Sí, amb lectura en bloc |
| Versions | Sí, amb etapes | Sí, amb etiquetes |
| Integració amb RDS | Nativa, credencials gestionades | No |
| Rèplica entre regions | Sí, automàtica | No |
| Accés des de Parameter Store | Sí: /aws/reference/secretsmanager/<nom> |
— |
| Quan fer-lo servir | Credencials que han de rotar | Configuració i secrets de perfil baix |
La recomanació per a MercadoFresco, escrita com a norma de l'equip:
Tot allò que sigui una credencial capaç de donar accés a dades de clients va a Secrets Manager, amb rotació activada. Tota la resta va a Parameter Store.
Aplicat:
| Valor | On | Per què |
|---|---|---|
Contrasenya de mfadmin |
Secrets Manager | Dona accés a dades personals; ha de rotar |
| Clau d'API de la passarel·la de pagament | Secrets Manager | Credencial de tercers; ha de rotar |
Usuari mfadmin |
Parameter Store | És configuració |
| Punt d'enllaç d'RDS | Parameter Store | No és secret |
| Noms de bucket, URL del CDN | Parameter Store | Configuració pura |
| Clau d'API de missatgeria de comandes | Parameter Store SecureString |
Sensible però de baix impacte i sense rotació automàtica disponible |
Cost d'aquesta divisió: 2 secrets × 0,40 = 0,80 USD al mes, més uns cèntims de crides. Tota la configuració, gratis. Si ho haguéssim ficat tot a Secrets Manager, els 25 valors costarien 10 USD mensuals; si ho haguéssim ficat tot a Parameter Store, no tindríem rotació. La divisió no és purisme, és economia.
Un detall pràctic molt còmode: Parameter Store pot llegir secrets de Secrets Manager fent servir
el prefix /aws/reference/secretsmanager/. Així l'aplicació utilitza una sola API per a tot:
aws ssm get-parameter \
--name "/aws/reference/secretsmanager/mercadofresco/produccion/rds/mfadmin" \
--with-decryption --query 'Parameter.Value' --output text \
--profile mercadofresco-devConsum des de l'aplicació: boto3 i memòria cau
La versió ingènua —llegir el secret a cada petició— té tres problemes: latència de desenes de mil·lisegons, cost per crida i risc de superar la quota de peticions. La correcta fa servir memòria cau amb invalidació davant d'error d'autenticació:
import boto3
import json
import psycopg2
from botocore.exceptions import ClientError
SECRET = "mercadofresco/produccion/rds/mfadmin"
sessio = boto3.Session(region_name="eu-west-1")
sm = sessio.client("secretsmanager")
_cache = {"valor": None}
def obtenir_credencials(forcar_recarrega: bool = False) -> dict:
"""Retorna les credencials, fent servir la cache tret que es forci la recarrega."""
if _cache["valor"] is None or forcar_recarrega:
resposta = sm.get_secret_value(SecretId=SECRET)
_cache["valor"] = json.loads(resposta["SecretString"])
return _cache["valor"]
def connectar():
"""Connecta a la base de dades i reintenta UN cop si la credencial ha rotat."""
for intent in (1, 2):
credencials = obtenir_credencials(forcar_recarrega=(intent == 2))
try:
return psycopg2.connect(
host=credencials["host"],
port=credencials["port"],
dbname=credencials["dbname"],
user=credencials["username"],
password=credencials["password"],
connect_timeout=5,
)
except psycopg2.OperationalError as error:
if "authentication" not in str(error).lower() or intent == 2:
raise
# La contrasenya ha rotat: s'invalida la cache i es reintentaEl bucle for intent in (1, 2) és tota la lògica que cal: primer intent amb la memòria cau,
segon amb recàrrega forçada, i només es reintenta si l'error és d'autenticació. Un error de xarxa
o de temps d'espera es propaga tal qual, perquè recarregar el secret no ho arreglaria.
Per a producció existeix la biblioteca oficial, que afegeix caducitat per temps i és més completa:
import boto3
from aws_secretsmanager_caching import SecretCache, SecretCacheConfig
client = boto3.client("secretsmanager", region_name="eu-west-1")
cache = SecretCache(
config=SecretCacheConfig(
secret_refresh_interval=3600, # refrescar cada hora
max_cache_size=1024,
),
client=client,
)
credencials = json.loads(cache.get_secret_string("mercadofresco/produccion/rds/mfadmin"))Amb secret_refresh_interval=3600 es fan 24 crides al dia per instància en lloc d'una per
petició: de desenes de milers a dues dotzenes.
Consum des de Lambda, EC2, ECS i CloudFormation
Lambda: l'extensió de paràmetres i secrets. AWS publica una capa que arrenca un petit
servidor HTTP local dins l'entorn d'execució i en fa la memòria cau per tu. S'afegeix la capa, es
configura amb variables d'entorn i es consulta per localhost:
import os
import json
import urllib.request
PORT = os.environ.get("PARAMETERS_SECRETS_EXTENSION_HTTP_PORT", "2773")
TOKEN = os.environ["AWS_SESSION_TOKEN"]
def llegir_secret(nom: str) -> dict:
url = f"http://localhost:{PORT}/secretsmanager/get?secretId={nom}"
peticio = urllib.request.Request(url)
peticio.add_header("X-Aws-Parameters-Secrets-Token", TOKEN)
with urllib.request.urlopen(peticio) as resposta:
return json.loads(json.loads(resposta.read())["SecretString"])
def handler(esdeveniment, context):
credencials = llegir_secret("mercadofresco/produccion/rds/mfadmin")
...L'extensió manté la memòria cau entre invocacions que reutilitzen el mateix entorn d'execució —recorda el model d'arrencades en fred i en calent de 02-05—, així que a la pràctica una funció molt invocada fa una crida cada cinc minuts en lloc d'una per invocació.
EC2. Amb el rol rol-mercadofresco-tienda assignat, boto3 troba les credencials sol. No
hi ha res a configurar tret dels permisos.
ECS. Les definicions de tasca permeten injectar secrets directament com a variables d'entorn, sense que el codi cridi l'API. Es veurà a 10-01.
CloudFormation: referències dinàmiques. Es pot referenciar un secret sense escriure'l a la plantilla:
Resources:
BaseDeDades:
Type: AWS::RDS::DBInstance
Properties:
DBInstanceIdentifier: mercadofresco-pedidos
MasterUsername: '{{resolve:ssm:/mercadofresco/produccion/basedatos/usuario}}'
MasterUserPassword: '{{resolve:secretsmanager:mercadofresco/produccion/rds/mfadmin:SecretString:password}}'La sintaxi {{resolve:secretsmanager:<secret>:SecretString:<camp>}} es resol en el moment del
desplegament i el valor no apareix mai a la plantilla ni als esdeveniments de la pila. Es
desenvolupa a 09-01.
Permisos mínims per llegir un secret
Aquesta és la política que s'adjunta a rol-mercadofresco-tienda:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "LeerSoloElSecretoDeLaBaseDeDatos",
"Effect": "Allow",
"Action": ["secretsmanager:GetSecretValue", "secretsmanager:DescribeSecret"],
"Resource": "arn:aws:secretsmanager:eu-west-1:111122223333:secret:mercadofresco/produccion/rds/mfadmin-??????"
},
{
"Sid": "DescifrarConLaClaveDeDatos",
"Effect": "Allow",
"Action": "kms:Decrypt",
"Resource": "arn:aws:kms:eu-west-1:111122223333:key/1234abcd-12ab-34cd-56ef-1234567890ab",
"Condition": {
"StringEquals": {
"kms:ViaService": "secretsmanager.eu-west-1.amazonaws.com"
}
}
},
{
"Sid": "LeerLaConfiguracionDeProduccion",
"Effect": "Allow",
"Action": ["ssm:GetParameter", "ssm:GetParameters", "ssm:GetParametersByPath"],
"Resource": "arn:aws:ssm:eu-west-1:111122223333:parameter/mercadofresco/produccion/*"
}
]
}Tres detalls que importen:
- Els sis interrogants finals de l'ARN. Secrets Manager afegeix un sufix aleatori de 6
caràcters a l'ARN de cada secret (
...mfadmin-AbCdEf). Si escrius l'ARN sense sufix, la política no coincideix amb res.-??????fa servir el comodí d'un caràcter per cobrir-ho. És l'error número u amb Secrets Manager i el seu símptoma —AccessDeniedamb una política aparentment correcta— desconcerta qualsevol. L'alternativa és acabar en-*, una mica més laxa. kms:ViaServicelimitat asecretsmanager. La botiga pot desxifrar a través de Secrets Manager i d'S3 (per la declaració de 04-02), però no pot cridarkms:Decryptdirectament contra qualsevol blob que aconsegueixi.ssm:GetParametersByPathacotat a/mercadofresco/produccion/*. La botiga de producció no veu la configuració de desenvolupament, ni a l'inrevés.
I el que no hi ha: secretsmanager:ListSecrets, que revelaria els noms de tots els secrets del
compte, ni accés a cap altre secret. La botiga llegeix exactament un. Si demà s'afegeix el
secret de la passarel·la de pagament, caldrà una decisió explícita per concedir-lo.
La migració: treure la contrasenya del user data
Estat inicial del user data de lt-mercadofresco-tienda, tal com va quedar a 02-01:
#!/bin/bash
cat > /etc/mercadofresco/tienda.conf <<'EOF'
DB_HOST=mercadofresco-pedidos.abc123.eu-west-1.rds.amazonaws.com
DB_USER=mfadmin
DB_PASSWORD=ContrasenaEnClaroQueTodosVen
EOF
systemctl start mercadofresco-tiendaEl pla de migració, en ordre i sense tallar el servei:
flowchart TD
A["1. Crear el secret amb la<br/>contrasenya ACTUAL"] --> B["2. Crear els parametres<br/>de configuracio"]
B --> C["3. Afegir permisos a<br/>rol-mercadofresco-tienda"]
C --> D["4. Canviar el codi:<br/>llegir del secret amb reintent"]
D --> E["5. Nova versio de<br/>lt-mercadofresco-tienda SENSE la clau"]
E --> F["6. Renovacio progressiva<br/>de l'ASG"]
F --> G["7. Verificar que tot funciona"]
G --> H["8. Rotacio manual de prova"]
H --> I["9. Activar rotacio cada 30 dies"]
I --> J["10. Netejar: .env, historial,<br/>fitxers de configuracio"]
El pas 1 és crucial i contraintuïtiu: es guarda la contrasenya actual, sense canviar-la. Així els passos 1 a 7 no alteren res funcionalment i es poden revertir en qualsevol moment. La contrasenya només canvia al pas 8, quan tota la resta ja està provada.
Nou user data, sense ni una credencial:
#!/bin/bash
set -euo pipefail
REGION=eu-west-1
RUTA=/mercadofresco/produccion
# Configuració no sensible des de Parameter Store
DB_HOST=$(aws ssm get-parameter --region $REGION \
--name "$RUTA/basedatos/punto-enlace" --query 'Parameter.Value' --output text)
DB_USER=$(aws ssm get-parameter --region $REGION \
--name "$RUTA/basedatos/usuario" --query 'Parameter.Value' --output text)
cat > /etc/mercadofresco/tienda.conf <<EOF
DB_HOST=$DB_HOST
DB_USER=$DB_USER
SECRET_BD=mercadofresco/produccion/rds/mfadmin
REGION=$REGION
EOF
systemctl start mercadofresco-tiendaLa contrasenya no s'escriu en cap fitxer: el user data deixa el nom del secret, i
l'aplicació el llegeix en memòria en arrencar i quan el necessita. El user data, que és visible
des de les metadades de la instància, ja no conté res aprofitable.
Desplegament amb renovació progressiva, com a 02-01:
aws ec2 create-launch-template-version \
--launch-template-name lt-mercadofresco-tienda \
--source-version '$Latest' \
--launch-template-data "{\"UserData\":\"$(base64 -w0 /tmp/user-data-nuevo.sh)\"}" \
--profile mercadofresco-dev
aws autoscaling start-instance-refresh \
--auto-scaling-group-name asg-mercadofresco-tienda \
--preferences '{"MinHealthyPercentage": 90, "InstanceWarmup": 120}' \
--profile mercadofresco-devI per fi el pas 10, la neteja, que és la part que s'oblida:
- Esborrar
/etc/mercadofresco/tienda.confde les instàncies antigues (que es destrueixen soles amb la renovació). - Esborrar el
.envdels portàtils i reescriure l'historial de Git si va arribar a pujar-se. - Purgar l'historial d'ordres de les sessions on es va fer servir.
- I, sobretot, rotar: la contrasenya anterior s'ha de considerar compromesa.
Què no cal guardar aquí
Ni Secrets Manager ni Parameter Store no són un magatzem de propòsit general:
| No guardis | Per què | On va |
|---|---|---|
| Claus d'accés d'IAM d'un rol | No haurien d'existir | Rols i credencials temporals (04-01) |
| Fitxers o binaris | Límit de 64 KB / 4 KB, i cost | S3 xifrat amb KMS |
| Dades personals de clients | No és una base de dades | RDS xifrada |
| Certificats TLS públics | Hi ha un servei dedicat i gratuït | ACM |
| Claus privades SSH de llarga vida | Haurien de ser efímeres | EC2 Instance Connect, Session Manager |
| Configuració que canvia a cada petició | Latència i quotes | Memòria cau o base de dades |
Un cas que mereix menció: per a RDS existeixen a més les credencials gestionades
(--manage-master-user-password), on RDS crea i rota el secret automàticament sense que
configuris res. És el més senzill si la base de dades és nova; MercadoFresco no ho fa servir perquè
mercadofresco-pedidos ja existia, però per a una instància nova és la primera opció a considerar.
Auditoria de l'accés als secrets
Cada crida a GetSecretValue queda registrada a CloudTrail (05-03), amb qui, quan, des de
quina IP i amb quin resultat. És la propietat que cap fitxer .env no pot oferir:
aws cloudtrail lookup-events \
--lookup-attributes AttributeKey=EventName,AttributeValue=GetSecretValue \
--start-time 2026-08-01T00:00:00Z \
--query 'Events[].[EventTime,Username,CloudTrailEvent]' \
--output text \
--profile mercadofresco-devEl que la Marta vigila:
- Lectures des de principals inesperats —qualsevol que no sigui
rol-mercadofresco-tiendao el rol de rotació. - Lectures fora d'horari o des d'IP desconegudes.
- Un pic de lectures, que suggereix que algú està enumerant secrets.
DeleteSecretoPutResourcePolicy, que haurien de ser raríssims.
A 05-01 convertirem això en una alarma real de CloudWatch que avisi el tema
alertas-mercadofresco; de moment n'hi ha prou de saber que el rastre existeix.
Detecció de secrets filtrats al repositori
Prevenir és més barat que rotar. Dues capes:
Al portàtil, abans del commit. git-secrets instal·la un hook que rebutja commits amb patrons
de credencials:
git secrets --install
git secrets --register-aws
# Patrons propis de MercadoFresco
git secrets --add 'mfadmin.*password'
git secrets --add --allowed 'ContrasenaFicticia' # excepcio per a la documentacio
git secrets --scan-history--scan-history revisa tot l'historial, no només l'estat actual. Executar-lo el primer cop en
un repositori amb anys de vida sol donar sorpreses.
Al pipeline. L'escaneig local es pot saltar; el del pipeline no. Eines com
gitleaks, trufflehog o l'escaneig de secrets de la plataforma s'executen a cada
git push i aturen la construcció. L'integrarem a la fase de compilació a 08-02, juntament
amb les proves i l'anàlisi estàtica.
I una regla d'higiene que val més que qualsevol eina:
Cost, quotes i neteja
| Concepte | Preu |
|---|---|
| Secrets Manager: per secret | 0,40 USD al mes (prorratejat per hora) |
| Secrets Manager: crides a l'API | 0,05 USD per cada 10.000 |
| Secrets Manager: rèplica en una altra regió | 0,40 USD addicionals per regió |
| Parameter Store estàndard | Gratis (10.000 paràmetres, 40 peticions/s) |
| Parameter Store avançat | 0,05 USD per paràmetre i mes + 0,05 USD/10.000 crides |
| Rotació (invocacions de Lambda) | Preu normal de Lambda: cèntims a l'any |
| KMS | Ja comptabilitzat a 04-02 |
Càlcul per a MercadoFresco:
| Concepte | Quantitat | Cost mensual |
|---|---|---|
Secrets (mfadmin + passarel·la de pagament) |
2 | 0,80 USD |
| Crides a Secrets Manager (6 instàncies × 24 lectures/dia × 30) | ~4.320 | 0,02 USD |
| Paràmetres a Parameter Store estàndard | 25 | 0,00 USD |
| Crides a Parameter Store (arrencades d'instància) | ~2.000 | 0,00 USD |
| Lambda de rotació | 1 invocació ×4 passos al mes | ~0,00 USD |
| Total | 0,82 USD/mes |
Menys d'un dòlar al mes per eliminar el risc més comú i més car d'una infraestructura. Compara-ho amb el cost d'una bretxa notificable sota el RGPD i la discussió es tanca sola.
Neteja. Un secret no s'esborra immediatament: hi ha un període de recuperació d'entre 7 i 30 dies, per la mateixa raó que a KMS.
# Programar l'esborrat amb 30 dies de recuperació (recomanat)
aws secretsmanager delete-secret \
--secret-id mercadofresco/produccion/rds/mfadmin \
--recovery-window-in-days 30 --profile mercadofresco-dev
# Penedir-se'n
aws secretsmanager restore-secret \
--secret-id mercadofresco/produccion/rds/mfadmin --profile mercadofresco-dev
# Esborrat immediat: irreversible, només en proves
aws secretsmanager delete-secret \
--secret-id secreto-de-prueba --force-delete-without-recovery --profile mercadofresco-dev
# Paràmetres: esborrat immediat, sense xarxa de seguretat
aws ssm delete-parameter --name "/mercadofresco/desarrollo/tienda/prueba" \
--profile mercadofresco-devCompte amb l'asimetria: els paràmetres s'esborren a l'instant i no es recuperen. Abans d'esborrar en bloc una ruta de Parameter Store, exporta'n una còpia.
Errors Habituals i Consells
Oblidar el sufix de sis caràcters a l'ARN del secret. És l'error més freqüent amb Secrets
Manager. L'ARN real és ...secret:mercadofresco/produccion/rds/mfadmin-AbCdEf, i una política que
acabi en mfadmin sense més no coincideix amb res. Fes servir -?????? o -*.
Desar el secret a la memòria cau sense invalidar-la davant d'un error d'autenticació. Cada rotació produeix llavors una tongada d'errors. El reintent únic són quatre línies de codi i ho resol.
Posar la Lambda de rotació fora de la VPC. No es podrà connectar a mercadofresco-pedidos i la
rotació fallarà a setSecret, que és el pitjor moment possible. Ha d'estar a les subxarxes
d'aplicació i sg-mercadofresco-basedatos ha d'acceptar el seu grup de seguretat.
Oblidar la sortida a l'API de Secrets Manager des de la subxarxa privada. La Lambda es queda penjada fins a esgotar el temps d'espera i el missatge d'error no diu res útil. Necessita NAT o un endpoint d'interfície.
Canviar els noms dels camps del JSON del secret. La funció de rotació gestionada espera
engine, host, port, dbname, username, password. Si els reanomenes «perquè es llegeixin
millor», la rotació no funciona.
Activar la rotació a producció sense haver-la provat. Prova-la primer en una còpia de la base
de dades, i amb --rotate-immediately per no esperar 30 dies a descobrir que alguna cosa falla.
Guardar-ho tot a Secrets Manager perquè és «el lloc segur». 25 valors són 10 USD al mes en lloc de 0,80. La configuració no sensible va a Parameter Store.
Programar la rotació un dijous a la nit. El pic de MercadoFresco és el divendres. Rota els dimarts de matinada, quan hi ha dos dies laborables per endavant per reaccionar.
Consell: fes servir una convenció de noms des del primer dia.
<projecte>/<entorn>/<servei>/<recurs> permet escriure polítiques per prefix i veure d'un
cop d'ull a quin entorn pertany cada cosa.
Consell: guarda el punt d'enllaç dins del secret a més de a Parameter Store. És el que espera la rotació gestionada, i evita que l'aplicació hagi de combinar dues fonts per obrir una connexió.
Consell: posa una alarma sobre NextRotationDate. Una rotació que fa 45 dies que no s'executa
en un secret configurat a 30 és una rotació trencada que ningú no ha vist.
Consell: audita cada trimestre qui pot llegir cada secret. L'accés creix sol, i
list-secrets amb les polítiques de recurs dona la foto en un minut.
Exercicis
Exercici 1: repartir valors entre els dos serveis
MercadoFresco integra una passarel·la de pagament i un proveïdor de missatgeria. Apareixen aquests vuit valors:
- Clau d'API de producció de la passarel·la de pagament (rota cada 90 dies per política del proveïdor).
- URL base de l'API de la passarel·la:
https://api.pasarela.example/v2. - Identificador de comerç:
MF-2026-ES-0042. - Secret de signatura dels webhooks entrants de la passarel·la.
- Temps màxim d'espera de la passarel·la: 8 segons.
- Usuari i contrasenya de l'SFTP del proveïdor de missatgeria.
- Llista de codis postals amb repartiment en 24 h.
- Contrasenya d'una segona base de dades d'informes, de només lectura.
Decideix per a cadascun el servei, el tipus i el nom complet, seguint la convenció de MercadoFresco, i justifica els tres casos que consideris més discutibles. Calcula el cost mensual resultant.
Exercici 2: diagnosticar una rotació que falla
La Marta activa la rotació de mercadofresco/produccion/rds/mfadmin. La rotació s'executa i falla.
Als registres de la Lambda apareix:
[ERROR] setSecret: Unable to connect to database with previous secret
of secret arn mercadofresco/produccion/rds/mfadmin-AbCdEfI a Secrets Manager, describe-secret mostra una versió amb etapa AWSPENDING que fa tres
hores que hi és, mentre AWSCURRENT continua apuntant a la versió antiga.
Respon: (a) en quin pas del cicle ha fallat i què significa exactament aquest missatge?; (b) enumera en ordre les quatre causes més probables i com comprovar cadascuna; (c) està l'aplicació caiguda en aquest moment i per què?; (d) què cal netejar abans de reintentar-ho?
Exercici 3: escriure la política del rol de rotació
Escriu la política de permisos completa del rol rol-rotacion-mfadmin, que assumeix la Lambda de
rotació. Ha de poder: llegir i escriure el secret mercadofresco/produccion/rds/mfadmin (i només
aquest), moure etapes de versió, generar contrasenyes aleatòries, fer servir alias/mercadofresco-datos
a través de Secrets Manager, escriure els seus propis registres i funcionar dins de vpc-mercadofresco.
Solucions
Solució 1
| # | Valor | Servei | Tipus | Nom |
|---|---|---|---|---|
| 1 | Clau d'API de pagament | Secrets Manager | — | mercadofresco/produccion/pasarela/clave-api |
| 2 | URL de la passarel·la | Parameter Store | String |
/mercadofresco/produccion/pasarela/url-base |
| 3 | Identificador de comerç | Parameter Store | String |
/mercadofresco/produccion/pasarela/id-comercio |
| 4 | Secret de signatura de webhooks | Secrets Manager | — | mercadofresco/produccion/pasarela/secreto-webhook |
| 5 | Temps d'espera | Parameter Store | String |
/mercadofresco/produccion/pasarela/tiempo-espera-segundos |
| 6 | Credencials SFTP | Secrets Manager | — | mercadofresco/produccion/mensajeria/sftp |
| 7 | Codis postals de 24 h | Parameter Store | StringList |
/mercadofresco/produccion/reparto/codigos-postales-24h |
| 8 | Contrasenya de la BD d'informes | Secrets Manager | — | mercadofresco/produccion/rds/informes |
Casos discutibles:
- L'identificador de comerç (3). Sembla sensible perquè identifica l'empresa davant del proveïdor, però per si sol no autoritza res: sense la clau d'API no serveix de res, i apareix a les factures. És configuració. Si el proveïdor el tractés com a credencial, canviaria la decisió.
- El secret de signatura de webhooks (4). Podria semblar configuració perquè no «obre» res: només es fa servir per verificar signatures entrants. Però qui el conegui pot falsificar notificacions de pagament confirmat, és a dir, aconseguir comandes gratis. És un secret de primer ordre.
- Els codis postals (7). No és secret en absolut —està publicat al web— però és
configuració que canvia sovint i convé tenir-la fora del codi per poder ampliar la
cobertura sense desplegar.
StringListambget-parameteri--query 'Parameter.Value'retorna la cadena separada per comes.
Cost: 4 secrets nous × 0,40 = 1,60 USD/mes, més els 2 anteriors = 2,40 USD/mes a Secrets Manager. Parameter Store: 4 paràmetres més, 0 USD. Si s'haguessin ficat els vuit valors a Secrets Manager: 3,20 USD només per aquests vuit, quatre vegades més, sense cap avantatge per als quatre que no roten.
Solució 2
(a) Ha fallat al pas setSecret, el segon del cicle. El missatge concret —«unable to
connect with previous secret»— diu que la Lambda ha intentat connectar-se a la base de dades amb la
credencial actual (AWSCURRENT) per poder executar l'ALTER USER, i no ha pogut. És a dir:
el problema no és la contrasenya nova, és que la Lambda no aconsegueix autenticar-se amb la vella.
(b) Quatre causes, en ordre de probabilitat:
- Xarxa. La Lambda no és a
vpc-mercadofresco, o és a les subxarxes equivocades, osg-mercadofresco-basedatosno accepta el 5432 des del seu grup de seguretat. Comprovació:aws lambda get-function-configuration --query 'VpcConfig'i revisar les regles d'entrada de l'SG de la base de dades ambdescribe-security-groups. - La contrasenya d'
AWSCURRENTno és la real. Si en crear el secret s'hi va posar una contrasenya inventada en lloc de la que realment témfadmin, la Lambda no es pot autenticar. Comprovació: intentar connectar-s'hi manualment ambpsqlamb el valor d'AWSCURRENTdes d'un bastió. - Els camps del JSON no són els esperats. Si falta
dbname,portoengine, o si estan escrits d'una altra manera, la Lambda construeix malament la cadena de connexió. Comprovació: llegir el secret i comparar camp a camp amb el format esperat. - El punt d'enllaç és incorrecte —per exemple, va quedar l'antic després de la migració a la
instància xifrada de 04-02, que va canviar el DNS. Comprovació:
aws rds describe-db-instances --query 'DBInstances[].Endpoint.Address'i comparar.
(c) No, l'aplicació no està caiguda. I aquesta és exactament la virtut del disseny d'etapes:
AWSCURRENT continua apuntant a la versió antiga, que és la que continua funcionant a la base de
dades, i és la que retorna get-secret-value. La rotació fallida és un problema operatiu urgent,
però no un incident de servei. Si hagués fallat després de setSecret —amb la contrasenya ja
canviada a la base de dades però AWSCURRENT sense promocionar— llavors sí que hi hauria tall.
(d) Abans de reintentar-ho cal eliminar la versió penjada a AWSPENDING, o Secrets Manager
intentarà continuar la rotació a mitges en lloc de començar de zero:
aws secretsmanager describe-secret --secret-id mercadofresco/produccion/rds/mfadmin \
--query 'VersionIdsToStages' --profile mercadofresco-dev
aws secretsmanager update-secret-version-stage \
--secret-id mercadofresco/produccion/rds/mfadmin \
--version-stage AWSPENDING \
--remove-from-version-id <id-de-la-versio-pendent> \
--profile mercadofresco-devDesprés, corregir la causa, provar en desenvolupament i reintentar amb --rotate-immediately.
Solució 3
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "GestionarSoloEsteSecreto",
"Effect": "Allow",
"Action": [
"secretsmanager:DescribeSecret",
"secretsmanager:GetSecretValue",
"secretsmanager:PutSecretValue",
"secretsmanager:UpdateSecretVersionStage"
],
"Resource": "arn:aws:secretsmanager:eu-west-1:111122223333:secret:mercadofresco/produccion/rds/mfadmin-??????",
"Condition": {
"StringEquals": {
"secretsmanager:resource/AllowRotationLambdaArn":
"arn:aws:lambda:eu-west-1:111122223333:function:rotacion-mfadmin"
}
}
},
{
"Sid": "GenerarContrasenasAleatorias",
"Effect": "Allow",
"Action": "secretsmanager:GetRandomPassword",
"Resource": "*"
},
{
"Sid": "UsarLaClaveSoloViaSecretsManager",
"Effect": "Allow",
"Action": ["kms:Decrypt", "kms:GenerateDataKey"],
"Resource": "arn:aws:kms:eu-west-1:111122223333:key/1234abcd-12ab-34cd-56ef-1234567890ab",
"Condition": {
"StringEquals": {
"kms:ViaService": "secretsmanager.eu-west-1.amazonaws.com"
}
}
},
{
"Sid": "Registros",
"Effect": "Allow",
"Action": ["logs:CreateLogStream", "logs:PutLogEvents"],
"Resource": "arn:aws:logs:eu-west-1:111122223333:log-group:/aws/lambda/rotacion-mfadmin:*"
},
{
"Sid": "InterfazDeRedEnLaVPC",
"Effect": "Allow",
"Action": [
"ec2:CreateNetworkInterface",
"ec2:DescribeNetworkInterfaces",
"ec2:DeleteNetworkInterface"
],
"Resource": "*"
}
]
}Justificació de cada bloc:
- El secret, un de sol, amb el sufix
-??????i la condiciósecretsmanager:resource/AllowRotationLambdaArn, que assegura que aquest rol només pot operar sobre secrets la rotació dels quals està assignada precisament a aquesta Lambda. És una defensa extra davant que algú reutilitzi el rol. GetRandomPasswordno admet ARN de recurs; per això"Resource": "*". No és perillós: genera cadenes aleatòries i no dona accés a res.- KMS acotat amb
kms:ViaServicea Secrets Manager: el rol no pot desxifrar objectes d'S3 amb la mateixa clau. - Els registres del grup propi de la funció, amb el
:*final. - Les tres accions d'
ec2:són obligatòries per a qualsevol Lambda dins d'una VPC: crea i destrueix una interfície de xarxa elàstica a cada arrencada en fred. No admeten ARN concret perquè la interfície encara no existeix quan es comprova el permís. És una de les poquíssimes vegades en què"Resource": "*"és inevitable, i es pot acotar amb condicions sobreec2:Subnetiec2:SecurityGroupsi es vol apurar.
No hi apareix secretsmanager:ListSecrets, que revelaria l'inventari de secrets del compte, ni
cap acció d'RDS: la Lambda canvia la contrasenya connectant-s'hi per SQL, no per l'API d'AWS.
Conclusió
La contrasenya de mfadmin ja no és on no havia de ser. Ha sortit del user data de
lt-mercadofresco-tienda, del fitxer de configuració de les instàncies, del .env del Luis i de
l'historial d'ordres, i viu a mercadofresco/produccion/rds/mfadmin, un secret xifrat amb
alias/mercadofresco-datos, protegit per una política de recurs que denega la seva lectura a tot
principal que no sigui rol-mercadofresco-tienda, marta o el rol de rotació, i que rota sola
cada 30 dies, els dimarts de matinada, sense que ningú no se n'hagi de recordar ni assumir el risc
de fer-ho a mà.
Entens per què la rotació no talla el servei: el mecanisme d'etapes de versió
—AWSCURRENT, AWSPENDING, AWSPREVIOUS— i el cicle de quatre passos createSecret →
setSecret → testSecret → finishSecret, on la contrasenya nova es crea, s'aplica
i es prova abans de promocionar-se, i on una fallada a testSecret avorta tot el procés deixant la
vella intacta. Saps també on és la finestra perillosa —entre setSecret i finishSecret— i per què
l'aplicació ha d'invalidar la memòria cau i reintentar un cop davant d'un error d'autenticació:
quatre línies que converteixen la rotació en una cosa invisible.
Distingeixes configuració de secret, i amb això quin dels dos serveis fer servir. La configuració
de MercadoFresco viu a Parameter Store sota /mercadofresco/produccion/..., amb jerarquia per
rutes que permet carregar-la sencera amb una crida i escriure permisos per prefix, amb versions i
etiquetes per tornar enrere sense desplegar, i gratis al nivell estàndard. Els secrets viuen a
Secrets Manager, a 0,40 USD cadascun. El total de la lliçó és 0,82 USD al mes, i saps
argumentar per què ficar-ho tot al servei car hauria costat deu vegades més sense aportar res.
En la pràctica saps llegir un secret des de boto3 amb memòria cau i invalidació, des de Lambda amb
l'extensió de paràmetres i secrets que en fa memòria cau entre invocacions, des d'EC2 amb el rol i
sense configurar res, i des de CloudFormation amb referències dinàmiques
{{resolve:secretsmanager:...}} que no escriuen mai el valor a la plantilla (09-01); a ECS
s'injecten a la definició de tasca i ho veurem a 10-01. Coneixes els permisos mínims per llegir un
secret —inclòs el sufix de sis caràcters de l'ARN, l'error que fa perdre més temps— i saps que el
rastre de cada lectura queda a CloudTrail (05-03) i que l'escaneig de secrets s'automatitza a 08-02.
MercadoFresco ja té la casa endreçada cap endins: identitats mínimes, dades xifrades i
credencials rotades. Però tot això protegeix d'amenaces que passen per la porta de l'API. Queda
l'altra meitat del problema, la que arriba per la porta del davant i no necessita cap credencial.
Recorda la regla DENY numerada 50 amb què a 03-02 vam bloquejar aquella IP que feia milers de
peticions per minut: va funcionar perquè era una adreça. Si demà arriben peticions des de deu
mil adreces diferents repartides per tot el món, aquesta regla no serveix de res, la NACL té un
límit dur d'entrades, i el grup d'Auto Scaling reaccionarà com està dissenyat per reaccionar
—arrencant instàncies— convertint l'atac en una factura. A la lliçó 04-04, «AWS Shield»,
veurem què és realment un atac de denegació de servei distribuïda, en què es diferencien els
atacs volumètrics dels de capa d'aplicació, quina protecció ja tens activada i gratis a
CloudFront, Route 53 i l'ALB, què hi afegeix Shield Advanced per 3.000 USD al mes —i si això té
sentit per a una empresa de la mida de MercadoFresco—, i com es distingeix en calent un atac d'un
pic legítim d'un divendres a la tarda.
Curs d'AWS
Mòdul 1: Introducció a AWS
- Què és AWS?
- Configuració del teu compte d'AWS
- Infraestructura global d'AWS
- Consola d'administració d'AWS
- AWS CLI i SDK
Mòdul 2: Serveis principals d'AWS
Mòdul 3: Xarxes i lliurament de contingut
- Amazon VPC
- Grups de seguretat i llistes de control d'accés
- Elastic Load Balancing
- Amazon CloudFront
- Route 53
Mòdul 4: Seguretat i identitat
- AWS Identity and Access Management (IAM)
- AWS Key Management Service (KMS)
- Secrets Manager i Parameter Store
- AWS Shield
- AWS WAF
Mòdul 5: Monitoratge i gestió
- Amazon CloudWatch
- AWS X-Ray i traçabilitat distribuïda
- AWS CloudTrail
- AWS Config
- AWS Trusted Advisor
Mòdul 6: Bases de dades
- Com triar la base de dades adequada
- Amazon DynamoDB
- Amazon Aurora
- Amazon Redshift
- Amazon ElastiCache
Mòdul 7: Integració d'aplicacions
- Amazon SQS
- Amazon SNS
- Amazon EventBridge
- AWS Step Functions
- Patrons d'integració: idempotència, reintents i cues de missatges fallits
