En tancar el mòdul 3 vam deixar una llista incòmoda damunt la taula. MercadoFresco és a internet,
serveix des de la vora, escala sola i sobreviu a la caiguda d'una zona de disponibilitat; però
l'usuari mercadofresco-admin pot fer literalment qualsevol cosa al compte 111122223333, els rols
rol-mercadofresco-tienda i rol-lambda-miniaturas es van crear sobre la marxa perquè les coses
funcionessin, i encara ningú no ha escrit ni una sola política pensant en què és el mínim que cada
peça necessita.
Això no és un detall pendent. És la capa de la qual depèn tota la resta: si les identitats estan malament, el xifratge no serveix de res, perquè qui pot cridar l'API pot llegir les dades ja desxifrades. Per això IAM és la primera lliçó del mòdul i la més llarga.
AWS Identity and Access Management (IAM) és el servei que respon, a cadascuna dels milers de milions de crides que rep l'API d'AWS cada segon, a dues preguntes: qui ets? i et deixo fer això?. En aquesta lliçó la Marta converteix aquestes dues preguntes en polítiques JSON concretes per a MercadoFresco.
Advertiment. Els exemples d'aquesta lliçó són didàctics i estan simplificats perquè s'entenguin. Qualsevol configuració d'identitats, permisos o compliment que s'hagi d'aplicar sobre dades reals de clients —i més si entra en l'àmbit del RGPD o de PCI DSS— ha de ser revisada per un professional de seguretat o de compliment abans d'arribar a producció. Tots els identificadors, comptes i dades d'aquest curs són ficticis.
Contingut
- Autenticació i autorització: dues preguntes diferents
- El vocabulari d'IAM
- Anatomia d'un ARN
- Usuaris, grups i el problema de les claus d'accés
- Rols: identitats que ningú no posseeix
sts:AssumeRolei la relació de confiança- Els sis tipus de política
- Anatomia d'una política JSON
- Comodins, variables de política i condicions
- La lògica d'avaluació de polítiques
- Formalitzar
rol-mercadofresco-tienda - Formalitzar
rol-lambda-miniaturas - Perfils d'instància
- Privilegi mínim a la pràctica: llegir els
AccessDenied - IAM Access Analyzer i la generació de polítiques
- Les persones de MercadoFresco: usuaris i grups
- MFA obligatori per condició
- Rotació de claus i informe de credencials
- IAM Identity Center i federació
- Eines de diagnòstic
- Cost i neteja
Autenticació i autorització: dues preguntes diferents
Es confonen constantment i són mecanismes separats:
| Autenticació | Autorització | |
|---|---|---|
| Pregunta | Qui ets? | Pots fer això? |
| Mecanisme | Signatura criptogràfica de la petició (SigV4), contrasenya + MFA a la consola | Avaluació de polítiques |
| Resultat | Un principal identificat | Allow o Deny |
| On falla | InvalidClientTokenId, SignatureDoesNotMatch |
AccessDenied |
Quan el Luis executa aws s3 ls amb el perfil mercadofresco-dev, la CLI signa la petició amb la
clau secreta. AWS recalcula la signatura; si coincideix, sap qui és el Luis (autenticació). Només
llavors busca totes les polítiques aplicables i decideix si el deixa llistar buckets (autorització).
Distingir-les estalvia hores de depuració: si l'error és AccessDenied, les credencials són
correctes i el problema és en una política. Si l'error és de signatura, ni tan sols s'ha arribat a
mirar els permisos.
El vocabulari d'IAM
Aquests set termes apareixen a tota la documentació d'AWS i convé fixar-los ara.
| Terme | Què és | Exemple a MercadoFresco |
|---|---|---|
| Principal | Qui fa la petició | El Luis, rol-mercadofresco-tienda, el servei s3.amazonaws.com |
| Identitat | Objecte d'IAM al qual es poden adjuntar polítiques | Usuari luis, grup mercadofresco-desarrollo, rol rol-lambda-miniaturas |
| Entitat | Identitat que AWS pot autenticar | Usuari o rol (un grup no és entitat: ningú no inicia sessió com a grup) |
| Recurs | L'objecte sobre el qual s'actua | El bucket mercadofresco-catalogo-fotos, la instància mercadofresco-tienda-01 |
| Acció | Operació de l'API | s3:GetObject, ec2:TerminateInstances, kms:Decrypt |
| Política | Document JSON que concedeix o denega | La que escrivim dins de tres apartats |
| Sessió | Credencials temporals resultat d'assumir un rol | La sessió que la instància obté del servei de metadades |
La distinció entre identitat i entitat sembla pedanteria i no ho és: explica per què no pots
«iniciar sessió com el grup d'analítica» ni per què un grup no pot aparèixer com a Principal en una
política de bucket. Els grups són només un contenidor de permisos per a usuaris.
Anatomia d'un ARN
L'Amazon Resource Name és l'identificador únic i global de qualsevol recurs d'AWS. Apareix a totes les polítiques, així que cal saber llegir-lo caràcter a caràcter:
| Camp | Què significa | Valors típics |
|---|---|---|
arn |
Prefix fix | Sempre arn |
partition |
Partició d'AWS | aws (global), aws-cn (Xina), aws-us-gov (GovCloud) |
service |
Espai de noms del servei | s3, ec2, iam, kms, lambda |
region |
Regió | eu-west-1; buit en serveis globals (IAM, S3, Route 53) |
account-id |
Compte de 12 dígits | 111122223333; buit a S3 |
resource |
Tipus i identificador | Separats per /, : o res, segons el servei |
Exemples reals de MercadoFresco, amb la peculiaritat de cadascun:
arn:aws:s3:::mercadofresco-catalogo-fotos
arn:aws:s3:::mercadofresco-catalogo-fotos/productos/tomate-rama.jpg
arn:aws:iam::111122223333:user/luis
arn:aws:iam::111122223333:role/rol-mercadofresco-tienda
arn:aws:iam::aws:policy/ReadOnlyAccess
arn:aws:ec2:eu-west-1:111122223333:instance/i-0abc123def4567890
arn:aws:rds:eu-west-1:111122223333:db:mercadofresco-pedidos
arn:aws:kms:eu-west-1:111122223333:key/1234abcd-12ab-34cd-56ef-1234567890ab
arn:aws:kms:eu-west-1:111122223333:alias/mercadofresco-datos
arn:aws:lambda:eu-west-1:111122223333:function:mercadofresco-generar-miniaturas
arn:aws:sqs:eu-west-1:111122223333:mercadofresco-miniaturas-fallidas
arn:aws:elasticloadbalancing:eu-west-1:111122223333:loadbalancer/app/alb-mercadofresco-tienda/50dc6c495c0c9188Quatre observacions que eviten errors freqüents:
- S3 no porta regió ni compte (
arn:aws:s3:::, tres dos punts seguits). Els noms de bucket són globalment únics, així que no calen. Escriurearn:aws:s3:eu-west-1:111122223333:mercadofresco-catalogo-fotosés un error clàssic i la política simplement no coincidirà mai. - El bucket i els objectes són recursos diferents.
arn:aws:s3:::mercadofresco-catalogo-fotosserveix per as3:ListBucket;arn:aws:s3:::mercadofresco-catalogo-fotos/*serveix per as3:GetObject. Confondre'ls produeix l'AccessDeniedmés comú del món. - IAM és global: regió buida. I les polítiques gestionades per AWS fan servir
awsal camp de compte:arn:aws:iam::aws:policy/ReadOnlyAccess. - RDS fa servir
:com a separador (:db:mercadofresco-pedidos), no/. Cada servei tria, no hi ha regla general; cal mirar la documentació o copiar l'ARN des de la consola.
Usuaris, grups i el problema de les claus d'accés
Un usuari d'IAM és una identitat permanent amb credencials de llarga durada: contrasenya per a la
consola i, opcionalment, un parell de claus d'accés (AKIA... + clau secreta) per a l'API.
I aquí és on hi ha el problema. Una clau d'accés:
- No caduca mai per si sola.
- És un secret en text pla que algú ha de desar en algun lloc.
- Si es filtra a un repositori de Git, a un tiquet de suport o al porta-retalls equivocat, qui la tingui és aquell usuari, sense més comprovacions.
Els bots que rastregen GitHub troben claus d'AWS filtrades en qüestió de minuts i engeguen instàncies per minar criptomonedes. La factura arriba abans que l'avís.
D'aquí ve la regla que governa la resta de la lliçó:
Les claus d'accés són l'últim recurs, no el primer. Una EC2, una Lambda, un contenidor o un pipeline mai no han de portar claus d'accés. Per a això existeixen els rols.
Un grup és una col·lecció d'usuaris que comparteixen permisos. No té credencials, no es pot assumir i no es pot niar dins d'un altre grup. La seva única funció és que, quan entri algú nou a l'equip del Luis, no calgui recordar quines set polítiques cal adjuntar-li.
Rols: identitats que ningú no posseeix
Un rol d'IAM és una identitat amb polítiques de permisos però sense credencials permanents. Ningú no «és» un rol: les entitats l'assumeixen temporalment i reben credencials que caduquen.
flowchart LR
A["Instancia<br/>mercadofresco-tienda-01"] -->|"1 demana credencials<br/>al servei de metadades"| B["IMDSv2<br/>169.254.169.254"]
B -->|"2 sts:AssumeRole"| C["rol-mercadofresco-tienda"]
C -->|"3 credencials temporals<br/>caduquen en ~6 h"| A
A -->|"4 crida signada"| D["S3<br/>mercadofresco-catalogo-fotos"]
L'important del diagrama és el pas 3: les credencials caduquen i es renoven soles abans de caducar. Si algú les roba, té una finestra de minuts o hores, no d'anys. I no hi ha res per rotar, ni res per desar, ni res que es pugui filtrar a Git perquè no existeix en cap fitxer.
Els rols es fan servir en quatre escenaris:
| Escenari | Qui l'assumeix | Exemple |
|---|---|---|
| Rol de servei | Un servei d'AWS | EC2, Lambda, ECS |
| Rol entre comptes | Un usuari o rol d'un altre compte | El compte de producció i el de desenvolupament (mòdul 9) |
| Rol federat | Un usuari autenticat per un proveïdor extern | Google Workspace, Entra ID, IAM Identity Center |
| Canvi de barret | Un usuari del mateix compte | La Marta assumeix rol-mercadofresco-emergencia només quan ho necessita |
sts:AssumeRole i la relació de confiança
Tot rol té dues polítiques, i confondre-les és l'error conceptual més freqüent d'IAM:
| Política | Nom tècnic | Respon a | Quantes |
|---|---|---|---|
| De confiança | Trust policy / AssumeRolePolicyDocument |
Qui pot assumir aquest rol? | Exactament 1 |
| De permisos | Polítiques adjuntes | Què pot fer qui l'assumeixi? | Fins a 10 gestionades + inline |
La política de confiança és l'única política basada en recurs on el recurs és el mateix rol. Aquesta
és la de rol-mercadofresco-tienda, que només pot assumir el servei EC2:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "PermitirQueEC2AsumaElRol",
"Effect": "Allow",
"Principal": { "Service": "ec2.amazonaws.com" },
"Action": "sts:AssumeRole"
}
]
}Línia a línia:
Principal.Service: qui l'assumeix no és una persona sinó el servei EC2. Quan llancis una instància amb aquest rol, EC2 cridaràsts:AssumeRoleen nom teu.Action: sts:AssumeRole: l'acció especial que emet credencials temporals. N'hi ha variants:sts:AssumeRoleWithWebIdentity(Cognito, OIDC) ists:AssumeRoleWithSAML(federació empresarial).- No hi ha
Resource: el recurs és implícitament el rol al qual pertany la política.
Per a rols entre comptes hi ha una condició imprescindible, l'ExternalId, que evita l'atac del
confused deputy (un tercer que coneix l'ARN del teu rol i aconsegueix que algú l'assumeixi en nom
seu):
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": { "AWS": "arn:aws:iam::444455556666:root" },
"Action": "sts:AssumeRole",
"Condition": {
"StringEquals": { "sts:ExternalId": "mercadofresco-proveedor-logistica-2026" },
"Bool": { "aws:MultiFactorAuthPresent": "true" }
}
}
]
}"AWS": "arn:aws:iam::444455556666:root" no vol dir l'usuari root d'aquell compte: vol dir «el
compte 444455556666 pot delegar en qui vulgui». És la manera habitual d'escriure-ho, i el compte de
destinació ha de concedir a més sts:AssumeRole als seus propis usuaris. Les dues bandes s'han de
posar d'acord: sense això no hi ha accés entre comptes.
Els sis tipus de política
| Tipus | S'adjunta a | Efecte | Ús a MercadoFresco |
|---|---|---|---|
| Basada en identitat | Usuari, grup, rol | Concedeix permisos al principal | La majoria de les que escriurem |
| Basada en recurs | Bucket, clau KMS, cua, secret, rol | Concedeix accés al recurs, indicant Principal |
Política de bucket, política de clau KMS |
| Límit de permisos | Usuari o rol | Sostre màxim: no concedeix res, només limita | El sostre dels rols que creï el Luis |
| SCP | Compte o OU d'Organizations | Sostre de tot el compte | Es veu en detall a 09-04 |
| Política de sessió | Es passa en assumir un rol | Redueix els permisos d'aquella sessió concreta | Sessions acotades del pipeline (mòdul 8) |
| ACL | Bucket S3 (heretat) | Mecanisme antic, anterior a IAM | No fer-lo servir; desactivades per defecte des del 2023 |
Les dues primeres són les que es fan servir cada dia, i hi ha una diferència decisiva entre elles:
- Una política d'identitat diu: «el Luis pot llegir aquell bucket». Viu amb el Luis.
- Una política de recurs diu: «aquell bucket deixa que el llegeixi el Luis». Viu amb el bucket, i a més és l'única manera de concedir accés des d'un altre compte sense rols.
I una conseqüència pràctica: quan el principal i el recurs són al mateix compte, n'hi ha prou que una de les dues concedeixi el permís. Quan són a comptes diferents, calen totes dues.
Les polítiques gestionades per AWS (AmazonS3ReadOnlyAccess, AdministratorAccess...) són còmodes
per començar i gairebé sempre massa àmplies. AmazonS3ReadOnlyAccess concedeix lectura sobre
tots els buckets del compte, inclosa mercadofresco-copias-basedatos. Serveixen per prototipar;
no per a producció.
Anatomia d'una política JSON
Totes les polítiques tenen la mateixa estructura. Aquesta és la plantilla completa, amb tots els elements possibles:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "IdentificadorLegibleOpcional",
"Effect": "Allow",
"Principal": { "AWS": "arn:aws:iam::111122223333:role/rol-mercadofresco-tienda" },
"Action": ["s3:GetObject", "s3:PutObject"],
"NotAction": [],
"Resource": "arn:aws:s3:::mercadofresco-catalogo-fotos/productos/*",
"NotResource": [],
"Condition": {
"StringEquals": { "aws:PrincipalTag/Proyecto": "mercadofresco" }
}
}
]
}| Element | Obligatori | Què fa |
|---|---|---|
Version |
Sí | Sempre 2012-10-17. No és la data de la teva política: és la versió del llenguatge. Sense ella no funcionen les variables de política |
Statement |
Sí | Llista de declaracions. S'avaluen totes, no s'atura a la primera |
Sid |
No | Identificador llegible. Útil per depurar i obligatòriament únic en polítiques de recurs |
Effect |
Sí | Allow o Deny |
Principal |
Només en polítiques de recurs | Qui. En polítiques d'identitat està prohibit (el principal és l'amo) |
Action |
Sí | servei:Operació. Admet comodins |
Resource |
Sí (excepte en confiança) | Sobre quin ARN |
Condition |
No | Quan s'aplica |
NotAction i NotResource volen dir «tot excepte». Són perillosos: "NotAction": "s3:*" amb
"Effect": "Allow" concedeix tota la resta d'AWS. Fes-los servir gairebé exclusivament amb
Deny.
Comodins, variables de política i condicions
Comodins. * substitueix qualsevol seqüència de caràcters, ? un sol caràcter:
| Patró | Coincideix amb |
|---|---|
s3:Get* |
s3:GetObject, s3:GetBucketPolicy, s3:GetObjectAcl... |
s3:* |
Totes les accions d'S3 |
* |
Totes les accions d'AWS (això és AdministratorAccess) |
arn:aws:s3:::mercadofresco-* |
Tots els buckets de MercadoFresco |
arn:aws:s3:::mercadofresco-catalogo-fotos/miniaturas/* |
Només els objectes sota aquell prefix |
Compte amb s3:Get*: inclou s3:GetBucketPolicy, que permet llegir la política del bucket, i
s3:GetObjectVersion, que permet llegir versions antigues fins i tot d'objectes «esborrats».
Variables de política. Se substitueixen en el moment de l'avaluació:
| Variable | Valor |
|---|---|
${aws:username} |
Nom de l'usuari d'IAM |
${aws:userid} |
Identificador únic del principal |
${aws:PrincipalTag/Clave} |
Etiqueta del principal |
${s3:prefix} |
Prefix sol·licitat en una operació d'S3 |
Amb això, una sola política dona a cada persona la seva carpeta privada al bucket de desenvolupament:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "CadaUnoEnSuCarpeta",
"Effect": "Allow",
"Action": ["s3:GetObject", "s3:PutObject", "s3:DeleteObject"],
"Resource": "arn:aws:s3:::mercadofresco-tienda-web-desarrollo/personal/${aws:username}/*"
}
]
}Adjuntada al grup mercadofresco-desarrollo, el Luis només pot tocar personal/luis/, i qui entri
demà només personal/<el-seu-nom>/, sense escriure ni una política més.
Condicions útils. El bloc Condition té la forma
{ "Operador": { "ClauDeCondició": "valor" } }:
| Clau | Operador típic | Per a què |
|---|---|---|
aws:SecureTransport |
Bool |
Exigir HTTPS: "Bool": {"aws:SecureTransport": "false"} amb Deny |
aws:SourceIp |
IpAddress |
Restringir a la IP de l'oficina 192.168.10.0/24 (no funciona si la petició passa per un punt d'enllaç de VPC) |
aws:RequestedRegion |
StringEquals |
Confinar l'activitat a eu-west-1 |
aws:PrincipalTag/Clave |
StringEquals |
Control d'accés per atributs (ABAC) |
aws:MultiFactorAuthPresent |
Bool |
Exigir MFA |
aws:CurrentTime |
DateGreaterThan |
Accessos temporals amb caducitat |
s3:x-amz-server-side-encryption |
StringEquals |
Obligar que tot objecte es pugi xifrat |
kms:ViaService |
StringEquals |
Que la clau només es faci servir a través d'un servei concret (04-02) |
Un avís important sobre aws:SourceIp: si la petició viatja per un punt d'enllaç de VPC —com
vpce-mercadofresco-s3, que vam muntar a 03-01—, la IP d'origen és privada i la condició falla. Per
a aquest cas la clau correcta és aws:SourceVpce.
Un altre avís: aws:MultiFactorAuthPresent és fals, no absent, a les sessions de rol d'un
servei. Si apliques aquesta condició a un rol d'EC2, el trenques.
La lògica d'avaluació de polítiques
Aquest és el cor d'IAM i val la pena memoritzar-lo. AWS reuneix totes les polítiques aplicables i aplica tres regles per aquest ordre:
- Tot està denegat per defecte (denegació implícita).
- Un
Allowexplícit en qualsevol política aplicable ho permet. - Un
Denyexplícit en qualsevol política guanya sempre, sense excepció.
Dit d'una altra manera: denegació explícita > permís explícit > denegació implícita.
flowchart TD
A["Peticio autenticada"] --> B{"Hi ha un Deny explicit<br/>en ALGUNA politica?"}
B -->|"Si"| Z["DENEGADA"]
B -->|"No"| C{"Ho permet la SCP<br/>d'Organizations?"}
C -->|"No"| Z
C -->|"Si o no aplica"| D{"Ho permet la politica<br/>de recurs?"}
D -->|"Si, i amb Principal explicit"| Y["PERMESA"]
D -->|"No concedeix"| E{"Es dins del<br/>limit de permisos?"}
E -->|"No"| Z
E -->|"Si o no aplica"| F{"Ho permet la politica<br/>de sessio?"}
F -->|"No"| Z
F -->|"Si o no aplica"| G{"Ho permet alguna politica<br/>basada en identitat?"}
G -->|"Si"| Y
G -->|"No"| Z
Conseqüències pràctiques que cal interioritzar:
- No existeix la «prioritat» entre polítiques. No hi ha números de regla com a les NACL de 03-02.
Un sol
Denyperdut en una política oblidada bloqueja tot, i trobar-lo és feina de detectiu. - L'ordre de les declaracions dins d'una política és irrellevant. S'avaluen totes.
- Ni l'usuari root escapa a un
Denyd'una SCP. És exactament per això que les SCP són l'eina de govern de 09-04. - Límit de permisos i política d'identitat es creuen: el permís efectiu és la intersecció de tots dos. Si el límit permet només S3 i la política concedeix S3 i EC2, el resultat és només S3.
I l'asimetria entre mateix compte i comptes diferents, resumida:
| Situació | Què cal |
|---|---|
| Principal i recurs al mateix compte | N'hi ha prou que una de les dues polítiques concedeixi (identitat o recurs) |
| Comptes diferents | Calen totes dues: la d'identitat al compte d'origen i la de recurs al de destinació |
| Excepcions (KMS, IAM) | La política de recurs sempre ha de concedir, encara que sigui el mateix compte |
Aquesta última fila és la font número u de sorpreses a 04-02: si la política de la clau KMS no et nomena, no la pots fer servir encara que siguis administrador del compte.
Formalitzar rol-mercadofresco-tienda
Fins ara la instància mercadofresco-tienda-01 funcionava amb permisos genèrics. Escriurem la
política que necessita exactament, ni un permís més. La botiga ha de:
- Llegir les fotos de
mercadofresco-catalogo-fotos/productos/. - Escriure les miniatures generades a
miniaturas/. - Publicar mètriques de negoci (comandes per hora) a CloudWatch.
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "LeerFotosDeProducto",
"Effect": "Allow",
"Action": "s3:GetObject",
"Resource": [
"arn:aws:s3:::mercadofresco-catalogo-fotos/productos/*",
"arn:aws:s3:::mercadofresco-catalogo-fotos/miniaturas/*"
]
},
{
"Sid": "ListarSoloLosPrefijosNecesarios",
"Effect": "Allow",
"Action": "s3:ListBucket",
"Resource": "arn:aws:s3:::mercadofresco-catalogo-fotos",
"Condition": {
"StringLike": {
"s3:prefix": ["productos/*", "miniaturas/*"]
}
}
},
{
"Sid": "EscribirMiniaturasCifradas",
"Effect": "Allow",
"Action": "s3:PutObject",
"Resource": "arn:aws:s3:::mercadofresco-catalogo-fotos/miniaturas/*",
"Condition": {
"StringEquals": {
"s3:x-amz-server-side-encryption": "aws:kms"
}
}
},
{
"Sid": "PublicarMetricasDeNegocio",
"Effect": "Allow",
"Action": "cloudwatch:PutMetricData",
"Resource": "*",
"Condition": {
"StringEquals": {
"cloudwatch:namespace": "MercadoFresco/Tienda"
}
}
},
{
"Sid": "ProhibirTraficoSinCifrar",
"Effect": "Deny",
"Action": "s3:*",
"Resource": [
"arn:aws:s3:::mercadofresco-catalogo-fotos",
"arn:aws:s3:::mercadofresco-catalogo-fotos/*"
],
"Condition": {
"Bool": { "aws:SecureTransport": "false" }
}
}
]
}Declaració per declaració:
LeerFotosDeProducto: noméss3:GetObject, i només sota dos prefixos. La botiga no pot llegir res fora deproductos/iminiaturas/, ni tan sols dins del mateix bucket. Tampoc no pot esborrar ni llegir versions antigues.ListarSoloLosPrefijosNecesarios:s3:ListBucketactua sobre el bucket, no sobre els objectes, per això l'ARN va sense/*. La condiciós3:prefiximpedeix que algú llisti el bucket sencera i descobreixi quins altres prefixos existeixen. És una fuita d'informació subtil però real.EscribirMiniaturasCifradas:s3:PutObjectlimitat aminiaturas/, i exigint que la pujada inclogui la capçalera de xifratge amb KMS. Si el codi s'oblida de xifrar, la pujada es rebutja. Això connecta directament ambalias/mercadofresco-datosa 04-02.PublicarMetricasDeNegocio:cloudwatch:PutMetricDatano admet ARN de recurs, cal posar-hi"Resource": "*". Però s'acota ambcloudwatch:namespace, de manera que la botiga només pot escriure al seu propi espai de noms i no pot falsejar les mètriques d'un altre component.ProhibirTraficoSinCifrar: unDenyde reforç. Recorda la regla: unDenyguanya sempre, fins i tot davant delsAllowde dalt. Si per la raó que sigui es fes una crida per HTTP sense TLS, es rebutja.
Fixa't en el que no hi ha: ni s3:DeleteObject, ni s3:*, ni accés a
mercadofresco-copias-basedatos, ni a mercadofresco-informes-analitica, ni ec2:*. Si demà algú
aconsegueix executar codi a la instància, això és tot el que s'emporta.
Creació per CLI:
# 1. Política de confiança (qui pot assumir el rol)
cat > /tmp/confianza-ec2.json <<'JSON'
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Principal": { "Service": "ec2.amazonaws.com" },
"Action": "sts:AssumeRole"
}]
}
JSON
aws iam create-role \
--role-name rol-mercadofresco-tienda \
--assume-role-policy-document file:///tmp/confianza-ec2.json \
--description "Rol de les instancies de la botiga de MercadoFresco" \
--max-session-duration 3600 \
--tags Key=Proyecto,Value=mercadofresco Key=Entorno,Value=produccion \
Key=Componente,Value=tienda Key=Propietario,Value=marta \
Key=CentroCoste,Value=tecnologia \
--profile mercadofresco-dev
# 2. Política de permisos (què pot fer)
aws iam create-policy \
--policy-name pol-mercadofresco-tienda \
--policy-document file:///tmp/pol-tienda.json \
--description "Permisos minims de la botiga: llegir cataleg, escriure miniatures, metriques" \
--profile mercadofresco-dev
# 3. Unir les dues coses
aws iam attach-role-policy \
--role-name rol-mercadofresco-tienda \
--policy-arn arn:aws:iam::111122223333:policy/pol-mercadofresco-tienda \
--profile mercadofresco-dev--max-session-duration 3600 limita les sessions a una hora. Per als rols d'EC2 el servei de
metadades renova automàticament, així que reduir-ho no trenca res i escurça la finestra d'un robatori
de credencials.
Formalitzar rol-lambda-miniaturas
La funció mercadofresco-generar-miniaturas de 02-05 es dispara quan apareix un objecte a
productos/, genera la miniatura i l'escriu a miniaturas/. Les seves necessitats són semblants
però no idèntiques, i a més ha d'escriure els seus registres i enviar els errors a la cua.
Política de confiança —només canvia el Principal:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": { "Service": "lambda.amazonaws.com" },
"Action": "sts:AssumeRole"
}
]
}Política de permisos:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "EscribirRegistrosDeLaPropiaFuncion",
"Effect": "Allow",
"Action": ["logs:CreateLogStream", "logs:PutLogEvents"],
"Resource": "arn:aws:logs:eu-west-1:111122223333:log-group:/aws/lambda/mercadofresco-generar-miniaturas:*"
},
{
"Sid": "LeerElOriginal",
"Effect": "Allow",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::mercadofresco-catalogo-fotos/productos/*"
},
{
"Sid": "EscribirLaMiniatura",
"Effect": "Allow",
"Action": "s3:PutObject",
"Resource": "arn:aws:s3:::mercadofresco-catalogo-fotos/miniaturas/*"
},
{
"Sid": "UsarLaClaveDelBucket",
"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": "s3.eu-west-1.amazonaws.com" }
}
},
{
"Sid": "EnviarLosFallosALaCola",
"Effect": "Allow",
"Action": "sqs:SendMessage",
"Resource": "arn:aws:sqs:eu-west-1:111122223333:mercadofresco-miniaturas-fallidas"
}
]
}Quatre detalls que importen:
logs:CreateLogGroupno hi és. El grup es crea una vegada, a mà o amb infraestructura com a codi, i la funció només necessita escriure-hi. És un permís menys.- L'ARN del grup de registres acaba en
:*: és la sintaxi de CloudWatch Logs per incloure els fluxos. Sense aquest sufix la funció no escriu res i l'error és silenciós. kms:ViaServicelimita l'ús de la clau a peticions que arribin a través d'S3. Si algú roba les credencials de la funció, no pot cridarkms:Decryptdirectament per desxifrar una altra cosa. És una de les condicions més útils de tot IAM i la desenvolupem a 04-02.- La clau es nomena pel seu ARN de clau, no per l'àlies. Les polítiques d'identitat admeten àlies en alguns contextos, però l'ARN és inequívoc i no canvia si algú reassigna l'àlies.
Amb el rol assignat, el codi de la funció no té ni una credencial. boto3.client("s3") troba les
credencials temporals tot sol a través de les variables d'entorn que Lambda injecta. Aquesta és tota
la màgia.
Perfils d'instància
Aquí hi ha una peça que gairebé ningú no entén fins que li falla: una instància EC2 no pot rebre un rol directament. Necessita un perfil d'instància (instance profile), que és un contenidor amb exactament un rol a dins.
flowchart LR
A["mercadofresco-tienda-01"] --> B["Perfil d'instancia<br/>rol-mercadofresco-tienda"]
B --> C["Rol d'IAM<br/>rol-mercadofresco-tienda"]
C --> D["Politica<br/>pol-mercadofresco-tienda"]
Quan crees el rol des de la consola triant el cas d'ús «EC2», AWS crea el perfil d'instància amb el mateix nom automàticament i ni te n'assabentes. Quan el crees per CLI, no. Cal fer-ho a mà:
aws iam create-instance-profile \
--instance-profile-name rol-mercadofresco-tienda \
--profile mercadofresco-dev
aws iam add-role-to-instance-profile \
--instance-profile-name rol-mercadofresco-tienda \
--role-name rol-mercadofresco-tienda \
--profile mercadofresco-dev
# Associar-lo a la plantilla de llançament (versió nova)
aws ec2 create-launch-template-version \
--launch-template-name lt-mercadofresco-tienda \
--source-version '$Latest' \
--launch-template-data '{"IamInstanceProfile":{"Name":"rol-mercadofresco-tienda"}}' \
--profile mercadofresco-devSi intentes llançar una instància amb un rol que no té perfil, l'error és
Value (rol-mercadofresco-tienda) for parameter iamInstanceProfile.name is invalid, que no diu en
absolut què passa. Ara ja ho saps.
I una comprovació des de dins de la instància, fent servir IMDSv2 com vam veure a 02-01:
TOKEN=$(curl -sX PUT "http://169.254.169.254/latest/api/token" \
-H "X-aws-ec2-metadata-token-ttl-seconds: 21600")
curl -s -H "X-aws-ec2-metadata-token: $TOKEN" \
http://169.254.169.254/latest/meta-data/iam/security-credentials/
# Retorna: rol-mercadofresco-tienda
# I amb aquest nom s'obtenen les credencials temporals i la seva data de caducitat:
curl -s -H "X-aws-ec2-metadata-token: $TOKEN" \
http://169.254.169.254/latest/meta-data/iam/security-credentials/rol-mercadofresco-tiendaPrivilegi mínim a la pràctica: llegir els AccessDenied
El principi de privilegi mínim diu que cada identitat ha de tenir els permisos mínims necessaris per a la seva funció, i res més. Tothom hi està d'acord i gairebé ningú no l'aplica, perquè endevinar per endavant quins permisos calen és impossible.
El mètode que sí que funciona és el contrari:
flowchart TD
A["Comencar SENSE permisos"] --> B["Executar el cas d'us real"]
B --> C{"AccessDenied?"}
C -->|"Si"| D["Llegir el missatge:<br/>accio + recurs exactes"]
D --> E["Afegir NOMES aquest permis"]
E --> B
C -->|"No"| F["Repetir amb els casos<br/>menys frequents"]
F --> G["Politica minima real"]
La clau és que els missatges d'error d'AWS són extraordinàriament precisos:
An error occurred (AccessDenied) when calling the PutObject operation:
User: arn:aws:sts::111122223333:assumed-role/rol-mercadofresco-tienda/i-0abc123def4567890
is not authorized to perform: s3:PutObject
on resource: "arn:aws:s3:::mercadofresco-catalogo-fotos/miniaturas/tomate-rama.jpg"
because no identity-based policy allows the s3:PutObject actionAquest missatge et dona les quatre dades que necessites: el principal exacte, l'acció exacta, el recurs exacte i per què ha fallat. Aquesta última frase és or:
| Frase final de l'error | Què significa |
|---|---|
because no identity-based policy allows |
Denegació implícita: falta un Allow. Afegeix-lo |
with an explicit deny in an identity-based policy |
Hi ha un Deny en una política adjunta al principal |
with an explicit deny in a resource-based policy |
El Deny és a la política del bucket, cua o clau |
with an explicit deny in a service control policy |
Ho bloqueja una SCP d'Organizations (09-04) |
with an explicit deny in a permissions boundary |
El límit de permisos no ho inclou |
Fixa't també en l'ARN del principal: arn:aws:sts::111122223333:assumed-role/rol-.../i-0abc....
Aquest és l'aspecte d'una sessió de rol assumit: comença per sts, no per iam, i l'últim
segment és el nom de sessió, que per a EC2 és l'identificador de la instància. Veure-ho així confirma
immediatament que la instància fa servir el rol i no unes claus oblidades.
IAM Access Analyzer i la generació de polítiques
Hi ha dues eines que fan aquesta feina per tu.
Generació de polítiques a partir de l'activitat. Access Analyzer llegeix l'historial de CloudTrail (el servei d'auditoria que veurem a fons a 05-03) d'un rol durant un període i escriu la política que aquell rol realment ha fet servir. És la manera més ràpida de retallar permisos existents:
aws accessanalyzer start-policy-generation \
--policy-generation-details '{"principalArn":"arn:aws:iam::111122223333:role/rol-mercadofresco-tienda"}' \
--cloud-trail-details '{
"trails": [{"cloudTrailArn":"arn:aws:cloudtrail:eu-west-1:111122223333:trail/mercadofresco-auditoria",
"regions":["eu-west-1"],"allRegions":false}],
"accessRole":"arn:aws:iam::111122223333:role/rol-access-analyzer",
"startTime":"2026-07-01T00:00:00Z"
}' \
--profile mercadofresco-devAdvertiment important: la política generada reflecteix el que es va fer servir a la finestra analitzada. Si el procés de tancament mensual només s'executa el dia 30 i analitzes de l'1 al 15, aquell permís no hi apareixerà i trencaràs el tancament. Analitza com a mínim un cicle complet de negoci, i per a MercadoFresco això vol dir incloure un divendres de pic i un final de mes.
Troballes d'accés extern. L'analitzador revisa polítiques de recurs (buckets, cues, claus, rols) i avisa de tot allò a què pot accedir algú de fora del teu compte o organització. És gratuït i detecta en segons el bucket que algú va obrir «només per a una prova»:
aws accessanalyzer create-analyzer \
--analyzer-name mercadofresco-acceso-externo \
--type ACCOUNT \
--profile mercadofresco-dev
aws accessanalyzer list-findings \
--analyzer-arn arn:aws:access-analyzer:eu-west-1:111122223333:analyzer/mercadofresco-acceso-externo \
--query 'findings[?status==`ACTIVE`].[resource,principal,action]' \
--output table \
--profile mercadofresco-devA més existeix l'analitzador d'accés no utilitzat, que assenyala rols, usuaris i permisos que fa mesos que no s'usen. Aquest sí que té cost (de l'ordre de 0,20 USD per identitat analitzada al mes) i és l'eina natural per a la neteja semestral.
Les persones de MercadoFresco: usuaris i grups
Fins ara només existien mercadofresco-admin i el root. La Marta crea l'estructura humana:
| Grup | Qui | Què pot fer |
|---|---|---|
mercadofresco-administracion |
La Marta | Administració completa amb MFA obligatori |
mercadofresco-desarrollo |
El Luis | EC2, Lambda, S3 de desenvolupament, lectura de registres; res a producció |
mercadofresco-analitica |
La Sara | Només lectura de mercadofresco-informes-analitica i consultes |
for g in mercadofresco-administracion mercadofresco-desarrollo mercadofresco-analitica; do
aws iam create-group --group-name "$g" --profile mercadofresco-dev
done
aws iam create-user --user-name marta \
--tags Key=Proyecto,Value=mercadofresco Key=Propietario,Value=marta \
--profile mercadofresco-dev
aws iam add-user-to-group --user-name marta \
--group-name mercadofresco-administracion --profile mercadofresco-devLa política de la Sara és el millor exemple de privilegi mínim per a un perfil no tècnic:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "ListarSoloElBucketDeInformes",
"Effect": "Allow",
"Action": ["s3:ListBucket", "s3:GetBucketLocation"],
"Resource": "arn:aws:s3:::mercadofresco-informes-analitica"
},
{
"Sid": "DescargarInformes",
"Effect": "Allow",
"Action": ["s3:GetObject", "s3:GetObjectVersion"],
"Resource": "arn:aws:s3:::mercadofresco-informes-analitica/*"
},
{
"Sid": "SoloDesdeEuWest1",
"Effect": "Deny",
"Action": "*",
"Resource": "*",
"Condition": {
"StringNotEquals": {
"aws:RequestedRegion": ["eu-west-1", "us-east-1"]
}
}
}
]
}- La Sara pot llistar i descarregar, mai pujar ni esborrar. Si el seu portàtil s'infecta, no pot destruir els informes.
- La tercera declaració confina tota la seva activitat a
eu-west-1. S'hi inclouus-east-1perquè els serveis globals (IAM, CloudFront, Route 53) signen les seves peticions contra aquella regió i sense ella la Sara no podria ni canviar la seva pròpia contrasenya. - És una política d'identitat, així que no porta
Principal.
MFA obligatori per condició
Exigir MFA a la consola és una casella; exigir-lo per a l'API requereix una política. Aquesta és la que la Marta adjunta als tres grups, i és el patró canònic d'AWS:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "PermitirGestionarLasPropiasCredenciales",
"Effect": "Allow",
"Action": [
"iam:ChangePassword",
"iam:GetUser",
"iam:CreateVirtualMFADevice",
"iam:EnableMFADevice",
"iam:ListMFADevices",
"iam:ResyncMFADevice"
],
"Resource": [
"arn:aws:iam::111122223333:user/${aws:username}",
"arn:aws:iam::111122223333:mfa/${aws:username}"
]
},
{
"Sid": "DenegarTodoLoDemasSinMFA",
"Effect": "Deny",
"NotAction": [
"iam:CreateVirtualMFADevice",
"iam:EnableMFADevice",
"iam:GetUser",
"iam:ListMFADevices",
"iam:ListVirtualMFADevices",
"iam:ResyncMFADevice",
"sts:GetSessionToken",
"iam:ChangePassword"
],
"Resource": "*",
"Condition": {
"BoolIfExists": { "aws:MultiFactorAuthPresent": "false" }
}
}
]
}Tres punts delicats:
- El primer
Statementés imprescindible: sense ell, un usuari nou sense MFA no podria registrar el seu MFA, i quedaria bloquejat per sempre. És la paradoxa de l'ou i la gallina d'aquesta política. NotActionaquí sí que és correcte perquè va ambDeny: «denega tot excepte el necessari per configurar l'MFA».BoolIfExistsen lloc deBool. Si la clau no existeix al context —cosa que passa en certes crides de servei—,Boolfaria que la condició no coincidís i elDenyno s'aplicaria.BoolIfExiststracta l'absència com afalse, és a dir, com a «sense MFA», que és el comportament segur.
Compte: adjuntar això a un rol de servei ho trenca tot, perquè les sessions d'EC2 o de Lambda mai no porten MFA. És exclusivament per a grups d'humans.
Rotació de claus i informe de credencials
Si un usuari necessita claus d'accés (per exemple el Luis per al perfil mercadofresco-dev al seu
portàtil), cal rotar-les. IAM permet dues claus actives simultàniament, i aquesta és exactament
la raó per la qual existeixen: rotar sense interrupció.
# 1. Crear la segona clau
aws iam create-access-key --user-name luis --profile mercadofresco-dev
# 2. Actualitzar ~/.aws/credentials amb la nova, provar durant uns dies
# 3. Desactivar l'antiga (sense esborrar-la: es pot revertir en segons)
aws iam update-access-key --user-name luis \
--access-key-id AKIAIOSFODNN7EXAMPLE --status Inactive --profile mercadofresco-dev
# 4. Confirmar que res no s'ha trencat i esborrar-la definitivament
aws iam delete-access-key --user-name luis \
--access-key-id AKIAIOSFODNN7EXAMPLE --profile mercadofresco-devL'informe de credencials és un CSV amb l'estat de tots els usuaris del compte i és la revisió trimestral de la Marta:
aws iam generate-credential-report --profile mercadofresco-dev
aws iam get-credential-report --query 'Content' --output text \
--profile mercadofresco-dev | base64 --decode > /tmp/credenciales.csvLes columnes que cal mirar: mfa_active (ha de ser true en tots els humans),
access_key_1_last_used_date (si és N/A, la clau no s'ha fet servir mai: esborra-la),
access_key_1_last_rotated (més de 90 dies: rota-la) i password_last_used (més de 90 dies:
l'usuari probablement ja no treballa aquí).
IAM Identity Center i federació
Tot l'anterior té un límit evident: no escala a persones. Amb tres empleats és manejable; amb trenta, crear un usuari d'IAM per persona i per compte és un desastre operatiu, i quan algú se'n va cal recordar-se d'esborrar-lo a tot arreu.
La solució moderna és AWS IAM Identity Center (abans AWS SSO):
- Els usuaris viuen en un únic directori: el mateix d'Identity Center, Microsoft Entra ID, Okta o Google Workspace.
- Es defineixen conjunts de permisos (permission sets), que no són res més que rols d'IAM que Identity Center crea automàticament a cada compte.
- La persona entra per un portal, tria compte i rol, i rep credencials temporals. No hi ha claus d'accés permanents enlloc.
- Quan algú deixa l'empresa se'l desactiva al directori corporatiu i perd l'accés a tots els comptes d'AWS a l'instant.
- És gratuït i s'integra amb la CLI v2:
aws configure ssoiaws sso login.
| Usuaris d'IAM | IAM Identity Center | |
|---|---|---|
| Credencials | Permanents | Temporals |
| Alta/baixa | Manual i per compte | Centralitzada al directori |
| Multicompte | Un usuari per compte | Un inici de sessió, tots els comptes |
| Cost | Gratis | Gratis |
| Quan | Casos residuals | El recomanat per a persones |
MercadoFresco té avui tres persones i un compte, així que els usuaris d'IAM són raonables. Tan bon punt hi hagi un segon compte —cosa que passarà a 09-04 en separar desenvolupament de producció amb Organizations— la migració a Identity Center deixa de ser opcional.
Eines de diagnòstic
Quatre eines que la Marta fa servir quan alguna cosa no quadra.
1. sts get-caller-identity. La primera pregunta sempre és «qui soc ara mateix?»:
{
"UserId": "AIDAI23HXD2O5EXAMPLE",
"Account": "111122223333",
"Arn": "arn:aws:iam::111122223333:user/luis"
}Si l'Arn no és el que esperaves, el problema no és de permisos sinó de credencials: revisa la
precedència que vam veure a 01-05 (variables d'entorn abans que perfil, perfil abans que rol
d'instància).
2. Simulador de polítiques. Avalua una acció sense executar-la:
aws iam simulate-principal-policy \
--policy-source-arn arn:aws:iam::111122223333:role/rol-mercadofresco-tienda \
--action-names s3:DeleteObject s3:GetObject \
--resource-arns "arn:aws:s3:::mercadofresco-catalogo-fotos/productos/tomate-rama.jpg" \
--query 'EvaluationResults[].[EvalActionName,EvalDecision]' \
--output table \
--profile mercadofresco-dev-------------------------------------
| SimulatePrincipalPolicy |
+------------------+----------------+
| s3:DeleteObject | implicitDeny |
| s3:GetObject | allowed |
+------------------+----------------+Exactament el que volíem: pot llegir, no pot esborrar. El simulador no avalua polítiques de recurs d'altres comptes ni totes les condicions contextuals, així que és una ajuda, no una prova.
3. --dry-run. Moltes operacions d'EC2 l'accepten i comproven permisos sense fer res:
aws ec2 terminate-instances --instance-ids i-0abc123def4567890 --dry-run \
--profile mercadofresco-dev
# UnauthorizedOperation -> no té permís
# DryRunOperation -> sí que el té (i no ha acabat res)4. get-account-authorization-details. L'abocament complet d'usuaris, grups, rols i polítiques
del compte. És la foto per auditar o versionar a Git:
aws iam get-account-authorization-details --profile mercadofresco-dev \
> /tmp/iam-mercadofresco-$(date +%F).json
# Qui té permisos d'administrador?
aws iam get-account-authorization-details --profile mercadofresco-dev \
--query 'Policies[?PolicyName==`AdministratorAccess`]'I en boto3, una comprovació que la Marta executa al repàs trimestral:
import boto3
from datetime import datetime, timezone
sessio = boto3.Session(profile_name="mercadofresco-dev", region_name="eu-west-1")
iam = sessio.client("iam")
ara = datetime.now(timezone.utc)
for pagina in iam.get_paginator("list_users").paginate():
for usuari in pagina["Users"]:
nom = usuari["UserName"]
# Té MFA?
mfa = iam.list_mfa_devices(UserName=nom)["MFADevices"]
sense_mfa = "SENSE MFA" if not mfa else "ok"
# Claus antigues?
for clau in iam.list_access_keys(UserName=nom)["AccessKeyMetadata"]:
dies = (ara - clau["CreateDate"]).days
avis = "ROTAR" if dies > 90 else "ok"
print(f"{nom:20} {clau['AccessKeyId']} {dies:4} dies {avis} {sense_mfa}")L'script recorre tots els usuaris, comprova si tenen MFA registrat i calcula l'antiguitat de cada
clau d'accés. get_paginator és necessari perquè list_users retorna com a màxim 100 usuaris per
crida; amb paginador, boto3 s'encarrega de recórrer totes les pàgines.
Cost i neteja
IAM és gratuït. Usuaris, grups, rols, polítiques i crides a STS no costen res. Tampoc no costa IAM Identity Center ni l'analitzador d'accés extern d'Access Analyzer. L'única cosa amb preu és l'analitzador d'accés no utilitzat, de l'ordre de 0,20 USD per identitat analitzada al mes: per a les ~8 identitats de MercadoFresco, menys de 2 USD mensuals.
Que sigui gratis no vol dir que no tingui cost: el cost d'IAM és operatiu. Cada política mal escrita és una bretxa o una incidència. I hi ha quotes que convé conèixer: 5.000 usuaris per compte, 1.000 rols, 10 polítiques gestionades per identitat, 6.144 caràcters per política gestionada i 2.048 per política inline d'usuari.
Per desfer el d'aquesta lliçó, l'ordre importa: no es pot esborrar un rol amb polítiques adjuntes ni un perfil d'instància amb un rol a dins.
aws iam remove-role-from-instance-profile \
--instance-profile-name rol-mercadofresco-tienda --role-name rol-mercadofresco-tienda \
--profile mercadofresco-dev
aws iam delete-instance-profile --instance-profile-name rol-mercadofresco-tienda \
--profile mercadofresco-dev
aws iam detach-role-policy --role-name rol-mercadofresco-tienda \
--policy-arn arn:aws:iam::111122223333:policy/pol-mercadofresco-tienda --profile mercadofresco-dev
aws iam delete-role --role-name rol-mercadofresco-tienda --profile mercadofresco-dev
aws iam delete-policy --policy-arn arn:aws:iam::111122223333:policy/pol-mercadofresco-tienda \
--profile mercadofresco-devPer esborrar un usuari cal treure abans contrasenya, claus, dispositius MFA, pertinences a grups i polítiques adjuntes. És tediós expressament.
Errors Habituals i Consells
Confondre l'ARN del bucket amb el dels objectes. arn:aws:s3:::el-meu-bucket serveix per a
ListBucket; arn:aws:s3:::el-meu-bucket/* per a GetObject. Gairebé totes les polítiques d'S3
necessiten les dues declaracions. Si la teva aplicació pot descarregar un objecte del qual sap el
nom però no llistar el bucket, et falta la primera.
Posar Principal en una política d'identitat. És un error de sintaxi: AWS el rebutja. El
principal d'una política d'identitat és la identitat a la qual s'adjunta.
Fer servir Bool on cal BoolIfExists. Amb Bool, si la clau de condició no és present al
context de la petició, la condició no coincideix i el teu Deny no s'aplica. Regla pràctica: en
condicions de tipus Deny que depenen d'una clau que pot faltar, fes servir sempre la variant
IfExists.
Adjuntar AdministratorAccess «temporalment» per desbloquejar alguna cosa. Mai no és temporal.
Si necessites desbloquejar un lliurament a les onze de la nit, fes servir el simulador per saber quin
permís falta i afegeix aquell permís concret; trigues dos minuts més i no deixes una bomba al compte.
Oblidar el perfil d'instància. Crear el rol per CLI no crea el perfil. Recorda-ho quan l'error
parli d'un iamInstanceProfile.name invàlid.
Posar claus d'accés al user data o en variables d'entorn d'una instància. Qualsevol que tingui
accés a la instància —o a les metadades, si IMDSv1 està actiu— les llegeix. Fes servir el rol.
Sempre.
Creure que un Deny d'una política es pot «anul·lar» amb un Allow en una altra. No es pot. Si
alguna cosa està denegada i no trobes on, busca en aquest ordre: SCP, límit de permisos, política de
recurs, polítiques d'identitat. I fes servir el missatge d'error, que et diu en quina és.
Consell: anomena les polítiques amb un prefix propi. pol-mercadofresco-* es distingeix d'un cop
d'ull de les gestionades per AWS en qualsevol llistat i evita adjuntar la que no toca.
Consell: versiona les polítiques a Git. Un fitxer .json per política, revisat per una altra
persona abans d'aplicar-se. Les polítiques són codi i mereixen el mateix tracte; a 09-01 i 09-02 les
convertirem directament en plantilles de CloudFormation i en constructes de CDK.
Consell: repassa l'informe de credencials cada tres mesos. Cinc minuts que detecten claus de persones que ja no hi són, usuaris sense MFA i claus de dos anys.
Exercicis
Exercici 1: escriure la política d'un rol nou
MercadoFresco afegeix una funció Lambda anomenada mercadofresco-informe-ventas que s'executa cada
nit. Necessita: llegir tots els objectes de mercadofresco-copias-basedatos sota el prefix
exportaciones/, escriure l'informe resultant a mercadofresco-informes-analitica sota ventas/,
escriure els seus propis registres a CloudWatch Logs i publicar un missatge al tema SNS
alertas-mercadofresco quan acabi. El compte és 111122223333 i la regió eu-west-1.
Escriu la política de confiança i la política de permisos completes, aplicant privilegi mínim. Justifica cada ARN.
Exercici 2: diagnosticar una denegació
El Luis executa un script des de la instància mercadofresco-tienda-01 i rep:
An error occurred (AccessDenied) when calling the GetObject operation:
User: arn:aws:sts::111122223333:assumed-role/rol-mercadofresco-tienda/i-0abc123def4567890
is not authorized to perform: s3:GetObject
on resource: "arn:aws:s3:::mercadofresco-informes-analitica/ventas/2026-07.csv"
because no identity-based policy allows the s3:GetObject actionRespon: (a) quin principal està fent la crida exactament i com ho saps?; (b) el problema és una
denegació explícita o implícita?; (c) quina és la solució correcta i quina seria la solució
còmoda i equivocada?; (d) canviaria alguna cosa si el bucket tingués una política de bucket que
concedeix s3:GetObject a arn:aws:iam::111122223333:root?
Exercici 3: resoldre una avaluació de polítiques
La Sara pertany al grup mercadofresco-analitica. Sobre ella actuen aquestes quatre polítiques:
- Política del grup:
Allowdes3:GetObjectsobrearn:aws:s3:::mercadofresco-informes-analitica/*. - Política inline de l'usuari:
Allowdes3:*sobre*. - Política d'MFA obligatori (la de l'apartat corresponent), i la Sara ha iniciat sessió amb MFA.
- Política de bucket de
mercadofresco-copias-basedatos:Denydes3:*a tot principal elaws:PrincipalTag/Departamentodel qual no siguitecnologia. La Sara té l'etiquetaDepartamento=analitica.
Determina si la Sara pot fer cadascuna d'aquestes tres operacions i per què:
- (a)
s3:GetObjectsobremercadofresco-informes-analitica/ventas/2026-07.csv - (b)
s3:GetObjectsobremercadofresco-copias-basedatos/dump-2026-07-01.sql - (c)
s3:DeleteObjectsobremercadofresco-catalogo-fotos/productos/tomate-rama.jpg
Solucions
Solució 1
Política de confiança (el principal és el servei Lambda, igual que a rol-lambda-miniaturas):
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": { "Service": "lambda.amazonaws.com" },
"Action": "sts:AssumeRole"
}
]
}Política de permisos:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "Registros",
"Effect": "Allow",
"Action": ["logs:CreateLogStream", "logs:PutLogEvents"],
"Resource": "arn:aws:logs:eu-west-1:111122223333:log-group:/aws/lambda/mercadofresco-informe-ventas:*"
},
{
"Sid": "LeerExportaciones",
"Effect": "Allow",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::mercadofresco-copias-basedatos/exportaciones/*"
},
{
"Sid": "ListarSoloEsePrefijo",
"Effect": "Allow",
"Action": "s3:ListBucket",
"Resource": "arn:aws:s3:::mercadofresco-copias-basedatos",
"Condition": { "StringLike": { "s3:prefix": "exportaciones/*" } }
},
{
"Sid": "EscribirElInforme",
"Effect": "Allow",
"Action": "s3:PutObject",
"Resource": "arn:aws:s3:::mercadofresco-informes-analitica/ventas/*"
},
{
"Sid": "AvisarAlTerminar",
"Effect": "Allow",
"Action": "sns:Publish",
"Resource": "arn:aws:sns:eu-west-1:111122223333:alertas-mercadofresco"
}
]
}Justificació dels ARN:
- El grup de registres porta
:*final per abastar els fluxos, i s'hi nomena la funció concreta: aquesta Lambda no pot escriure als registres de les altres. - La lectura és
GetObjectsobreexportaciones/*únicament. No hi ha accés a la resta demercadofresco-copias-basedatos, que conté les còpies completes de la base de dades. ListBucketva sobre l'ARN sense/*i acotat pers3:prefix.- L'escriptura és només
PutObjectsobreventas/*: no pot esborrar informes anteriors. - SNS porta l'ARN del tema exacte, no
*. - No hi ha
logs:CreateLogGroup(es crea a part) ni cap acció de KMS: si els buckets estiguessin xifrades ambalias/mercadofresco-datoscaldria afegirkms:Decryptikms:GenerateDataKeyambkms:ViaService, com veurem a 04-02.
Solució 2
(a) El principal és una sessió de rol assumit: l'ARN comença per arn:aws:sts:: i té la
forma assumed-role/<rol>/<nom-de-sessió>. Per a EC2 el nom de sessió és l'identificador de la
instància, i-0abc123def4567890. És a dir: el codi s'està executant a la instància fent servir
rol-mercadofresco-tienda, exactament com ha de ser. No hi ha claus d'accés pel mig.
(b) Implícita. Ho diu l'última línia: because no identity-based policy allows. Si fos
explícita, el missatge diria with an explicit deny in....
(c) La solució correcta és preguntar-se si la botiga ha de llegir informes d'analítica.
Gairebé segur que no: aquell bucket és de la Sara. El que cal arreglar és l'script, no la política.
Si de debò calgués, s'afegiria una declaració amb s3:GetObject sobre el prefix mínim necessari. La
solució còmoda i equivocada és adjuntar AmazonS3ReadOnlyAccess al rol, que concediria lectura sobre
tots els buckets del compte, incloses les còpies de la base de dades amb dades personals de
clients.
(d) Sí, canviaria. Amb principal i recurs al mateix compte, n'hi ha prou que una de
les dues polítiques concedeixi el permís. Una política de bucket que concedeix a
arn:aws:iam::111122223333:root delega en les polítiques d'identitat del compte... però aquell
root en una política de recurs vol dir «el compte», i per si sol no concedeix: encara
cal que la política d'identitat ho permeti. Si en canvi la política de bucket nomenés explícitament
arn:aws:iam::111122223333:role/rol-mercadofresco-tienda amb Allow de s3:GetObject, aleshores sí
que funcionaria sense tocar la política d'identitat. És una distinció subtil i és la que més confon a
la pràctica.
Solució 3
(a) Permesa. La política del grup concedeix s3:GetObject sobre aquell bucket, la política
inline també, no hi ha cap Deny aplicable (la política 4 només afecta
mercadofresco-copias-basedatos) i la condició d'MFA es compleix perquè la Sara va iniciar sessió
amb MFA.
(b) Denegada. La política de bucket conté un Deny explícit per a principals el Departamento
dels quals no sigui tecnologia, i el de la Sara és analitica. Una denegació explícita guanya
sempre, en qualsevol política, incloses les de recurs. Tant li fa que la política inline li
concedeixi s3:* sobre *.
(c) Permesa, i aquest és justament el problema. La política inline del punt 2 concedeix s3:*
sobre *, cosa que inclou s3:DeleteObject sobre el catàleg de fotos. La Sara, que és analista de
negoci, pot esborrar les fotos de producte de la botiga. La política inline anul·la per complet
l'acurat privilegi mínim del grup: cal eliminar-la. Aquest és el patró real pel qual els comptes es
degraden amb el temps: algú afegeix un permís ampli «només per provar» i ningú no el retira. Un límit
de permisos sobre l'usuari sara hauria evitat el problema, perquè el permís efectiu és la
intersecció del límit i de les polítiques.
Conclusió
IAM ha deixat de ser la part que es configura a base de clics fins que alguna cosa funciona. Ara saps
que autenticació i autorització són dues coses diferents i que el tipus d'error et diu quina ha
fallat; saps llegir un ARN camp a camp, inclosa la raresa d'S3 sense regió ni compte i la
diferència entre l'ARN del bucket i el dels seus objectes, que és l'origen de la meitat dels
AccessDenied del món.
Coneixes els sis tipus de política i, sobretot, la lògica d'avaluació: denegació explícita per damunt de permís explícit, i tots dos per damunt de la denegació implícita que ho nega tot per defecte. Saps que al mateix compte n'hi ha prou que una política concedeixi, que entre comptes calen totes dues, i que KMS és l'excepció que obliga sempre a la política de recurs, cosa que importarà molt d'aquí a una lliçó.
Has entès per què una EC2 o una Lambda mai no han de portar claus d'accés: els rols i
sts:AssumeRole lliuren credencials temporals que es renoven soles, no es desen en cap fitxer i no
es poden filtrar a Git. I has formalitzat per fi els dos rols que arrossegàvem des del mòdul 2:
rol-mercadofresco-tienda, amb la política pol-mercadofresco-tienda —lectura de productos/,
escriptura xifrada a miniaturas/, mètriques acotades al seu propi espai de noms i un Deny que
exigeix TLS— i rol-lambda-miniaturas, amb kms:ViaService perquè la seva clau només serveixi a
través d'S3. Saps que a la instància cal donar-li un perfil d'instància, no el rol directament.
En el pla humà, MercadoFresco ja té els seus grups: mercadofresco-administracion per a la
Marta, mercadofresco-desarrollo per al Luis i mercadofresco-analitica per a la Sara,
aquesta última amb només lectura sobre mercadofresco-informes-analitica i confinada a eu-west-1;
tots ells amb MFA obligatori per condició aws:MultiFactorAuthPresent i amb el BoolIfExists
que evita el fals sentit de seguretat. I tens el mètode que de debò produeix privilegi mínim:
començar sense permisos, executar, llegir l'AccessDenied —que et diu acció, recurs i motiu
exactes— i afegir només el que falta, ajudant-te del simulador de polítiques, de --dry-run, de
l'informe de credencials i d'IAM Access Analyzer, que arriba a escriure la política a partir
de l'activitat real registrada a CloudTrail (05-03).
Queda un cap per lligar, i és gros. A la política de la botiga hem exigit que les miniatures es pugin
amb s3:x-amz-server-side-encryption: aws:kms, i a la de la Lambda hem concedit kms:Decrypt i
kms:GenerateDataKey sobre una clau identificada per un UUID. Però encara ningú no ha decidit qui
administra aquesta clau, qui la pot fer servir, cada quant rota ni què passa exactament quan S3
xifra un objecte de 4 MB. La clau alias/mercadofresco-datos continua sent un nom sense política.
A la lliçó 04-02, «AWS Key Management Service (KMS)», la construirem de debò: veurem el
xifratge de sobre que explica per què les teves dades no viatgen mai a KMS, la diferència real
entre SSE-S3, SSE-KMS i SSE-C, la política de clau completa per a alias/mercadofresco-datos amb
la Marta com a administradora i rol-mercadofresco-tienda com a usuari, i xifrarem per fi el bucket
mercadofresco-copias-basedatos, els volums d'EBS i la instància mercadofresco-pedidos, amb dades
personals de clients espanyols i el RGPD mirant per damunt de l'espatlla.
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
