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

  1. Autenticació i autorització: dues preguntes diferents
  2. El vocabulari d'IAM
  3. Anatomia d'un ARN
  4. Usuaris, grups i el problema de les claus d'accés
  5. Rols: identitats que ningú no posseeix
  6. sts:AssumeRole i la relació de confiança
  7. Els sis tipus de política
  8. Anatomia d'una política JSON
  9. Comodins, variables de política i condicions
  10. La lògica d'avaluació de polítiques
  11. Formalitzar rol-mercadofresco-tienda
  12. Formalitzar rol-lambda-miniaturas
  13. Perfils d'instància
  14. Privilegi mínim a la pràctica: llegir els AccessDenied
  15. IAM Access Analyzer i la generació de polítiques
  16. Les persones de MercadoFresco: usuaris i grups
  17. MFA obligatori per condició
  18. Rotació de claus i informe de credencials
  19. IAM Identity Center i federació
  20. Eines de diagnòstic
  21. 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:

arn:partition:service:region:account-id:resource-type/resource-id
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/50dc6c495c0c9188

Quatre 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. Escriure arn: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-fotos serveix per a s3:ListBucket; arn:aws:s3:::mercadofresco-catalogo-fotos/* serveix per a s3:GetObject. Confondre'ls produeix l'AccessDenied més comú del món.
  • IAM és global: regió buida. I les polítiques gestionades per AWS fan servir aws al 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:AssumeRole en nom teu.
  • Action: sts:AssumeRole: l'acció especial que emet credencials temporals. N'hi ha variants: sts:AssumeRoleWithWebIdentity (Cognito, OIDC) i sts: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 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 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 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 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:

  1. Tot està denegat per defecte (denegació implícita).
  2. Un Allow explícit en qualsevol política aplicable ho permet.
  3. Un Deny explí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 Deny perdut 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 Deny d'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:

  1. Llegir les fotos de mercadofresco-catalogo-fotos/productos/.
  2. Escriure les miniatures generades a miniaturas/.
  3. 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és s3:GetObject, i només sota dos prefixos. La botiga no pot llegir res fora de productos/ i miniaturas/, ni tan sols dins del mateix bucket. Tampoc no pot esborrar ni llegir versions antigues.
  • ListarSoloLosPrefijosNecesarios: s3:ListBucket actua sobre el bucket, no sobre els objectes, per això l'ARN va sense /*. La condició s3:prefix impedeix que algú llisti el bucket sencera i descobreixi quins altres prefixos existeixen. És una fuita d'informació subtil però real.
  • EscribirMiniaturasCifradas: s3:PutObject limitat a miniaturas/, 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 amb alias/mercadofresco-datos a 04-02.
  • PublicarMetricasDeNegocio: cloudwatch:PutMetricData no admet ARN de recurs, cal posar-hi "Resource": "*". Però s'acota amb cloudwatch: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: un Deny de reforç. Recorda la regla: un Deny guanya sempre, fins i tot davant dels Allow de 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:CreateLogGroup no 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:ViaService limita l'ús de la clau a peticions que arribin a través d'S3. Si algú roba les credencials de la funció, no pot cridar kms:Decrypt directament 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-dev

Si 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-tienda

Privilegi 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 action

Aquest 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-dev

Advertiment 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-dev

A 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-dev

La 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 inclou us-east-1 perquè 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.
  • NotAction aquí sí que és correcte perquè va amb Deny: «denega tot excepte el necessari per configurar l'MFA».
  • BoolIfExists en lloc de Bool. Si la clau no existeix al context —cosa que passa en certes crides de servei—, Bool faria que la condició no coincidís i el Deny no s'aplicaria. BoolIfExists tracta l'absència com a false, é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-dev

L'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.csv

Les 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 sso i aws 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?»:

aws sts get-caller-identity --profile mercadofresco-dev
{
    "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-dev

Per 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 action

Respon: (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:

  1. Política del grup: Allow de s3:GetObject sobre arn:aws:s3:::mercadofresco-informes-analitica/*.
  2. Política inline de l'usuari: Allow de s3:* sobre *.
  3. Política d'MFA obligatori (la de l'apartat corresponent), i la Sara ha iniciat sessió amb MFA.
  4. Política de bucket de mercadofresco-copias-basedatos: Deny de s3:* a tot principal el aws:PrincipalTag/Departamento del qual no sigui tecnologia. La Sara té l'etiqueta Departamento=analitica.

Determina si la Sara pot fer cadascuna d'aquestes tres operacions i per què:

  • (a) s3:GetObject sobre mercadofresco-informes-analitica/ventas/2026-07.csv
  • (b) s3:GetObject sobre mercadofresco-copias-basedatos/dump-2026-07-01.sql
  • (c) s3:DeleteObject sobre mercadofresco-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 GetObject sobre exportaciones/* únicament. No hi ha accés a la resta de mercadofresco-copias-basedatos, que conté les còpies completes de la base de dades.
  • ListBucket va sobre l'ARN sense /* i acotat per s3:prefix.
  • L'escriptura és només PutObject sobre ventas/*: 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 amb alias/mercadofresco-datos caldria afegir kms:Decrypt i kms:GenerateDataKey amb kms: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

Mòdul 2: Serveis principals d'AWS

Mòdul 3: Xarxes i lliurament de contingut

Mòdul 4: Seguretat i identitat

Mòdul 5: Monitoratge i gestió

Mòdul 6: Bases de dades

Mòdul 7: Integració d'aplicacions

Mòdul 8: Eines per a desenvolupadors

Mòdul 9: Infraestructura com a codi i govern de comptes

Mòdul 10: Contenidors a AWS

Mòdul 11: Millors pràctiques i gestió de costos

© Copyright 2026. Tots els drets reservats