En tancar la lliçó anterior va quedar una frase incòmoda: la xarxa de vpc-mercadofresco està ben dissenyada, però no filtra res. La ruta local que AWS posa a totes les taules fa que qualsevol instància de la VPC pugui intentar obrir una connexió al port 5432 de mercadofresco-pedidos, i la subxarxa pública accepta el que arribi per l'Internet Gateway. L' adreçament decideix per on pot anar un paquet; falta decidir si se li permet passar.

AWS ofereix dos tallafocs per a això, i són diferents en gairebé tot: els grups de seguretat, que protegeixen la interfície de xarxa de cada recurs, i les llistes de control d'accés de xarxa (NACL), que protegeixen el perímetre de cada subxarxa. No competeixen: es complementen, i un paquet que entra a la VPC els travessa tots dos, en un ordre concret que convé tenir gravat.

En aquesta lliçó la Marta configura el filtratge complet de MercadoFresco aplicant el patró que distingeix una xarxa ben feta d'una xarxa plena de rangs IP copiats a mà: referenciar grups de seguretat entre capes. En acabar, la base de dades de comandes només acceptarà connexions del grup de seguretat de la botiga, i ni tan sols la Marta des del seu portàtil hi podrà arribar directament.

Contingut

  1. Les dues capes de filtratge d'una VPC
  2. Grups de seguretat: amb estat i només amb regles de permís
  3. Anatomia d'una regla: protocol, port i origen
  4. El patró clau: referenciar grups de seguretat entre capes
  5. Les tres capes de MercadoFresco amb els seus grups de seguretat
  6. Límits i quotes dels grups de seguretat
  7. NACL: sense estat, numerades i amb regles de denegació
  8. El parany dels ports efímers
  9. Taula comparativa: grups de seguretat davant de NACL
  10. El camí complet d'un paquet
  11. Cas pràctic: obrir el port 22 només a l'oficina (i per què no fer-ho)
  12. Cas pràctic: bloquejar una IP abusiva amb una NACL
  13. Depuració: quin error veus quan falla cada cosa
  14. Confirmar-ho amb VPC Flow Logs
  15. Creació i auditoria per CLI
  16. Una altra capa diferent: les polítiques d'identitat

Les dues capes de filtratge d'una VPC

Abans de res, el mapa mental. Quan un paquet entra a la VPC des d'internet, travessa això:

flowchart LR
    NET["Internet"]
    NACL{{"NACL de la subxarxa<br/>(sense estat, perimetre)"}}
    SG{{"Grup de seguretat<br/>(amb estat, a la ENI)"}}
    ENI["Instancia<br/>(la seva ENI)"]

    NET --> NACL --> SG --> ENI
  • La NACL actua a la frontera de la subxarxa. Un paquet que va d'una instància a una altra dins de la mateixa subxarxa no la travessa.
  • El grup de seguretat actua a la ENI, és a dir, enganxat al recurs. Tot el trànsit dirigit a una instància el travessa, vingui d'on vingui.

La conseqüència pràctica és que el 95 % de la feina diària es fa amb grups de seguretat, i les NACL es reserven per a regles gruixudes de subxarxa, sobretot denegacions, que és l'única cosa que els grups de seguretat no saben fer.

Grups de seguretat: amb estat i només amb regles de permís

Un grup de seguretat és un tallafocs virtual que s'assigna a una ENI. Té tres propietats que cal entendre bé perquè expliquen gairebé tot el seu comportament:

1. Només admet regles de permís (allow). No existeix la regla «denegar». Tot allò que no està explícitament permès queda denegat per defecte. Això simplifica moltíssim el raonament —no cal pensar en l'ordre de les regles— però significa que amb un grup de seguretat no pots bloquejar una IP concreta: per a això hi ha les NACL.

2. Té estat (stateful). Si permets una connexió d'entrada, la resposta surt automàticament, encara que no hi hagi cap regla de sortida que la contempli. I a l'inrevés: si una instància inicia una connexió de sortida, la resposta entra encara que no hi hagi regla d'entrada. El grup de seguretat recorda les connexions establertes.

Això és el que fa que una instància amb aquestes regles funcioni perfectament:

Direcció Protocol Port Origen/Destinació
Entrada TCP 443 0.0.0.0/0
Sortida (cap regla especial, la de per defecte)

Un navegador es connecta des del port efímer 51234 al 443 de la instància; la resposta surt del 443 al 51234 sense necessitar cap regla, perquè el grup de seguretat sap que aquella conversa ja estava oberta.

3. Totes les regles s'avaluen en conjunt, sense ordre. Si una ENI té tres grups de seguretat assignats, s'avalua la unió de totes les seves regles. N'hi ha prou que una regla de qualsevol d'ells permeti el trànsit. No hi ha prioritats ni «la primera que coincideixi».

Per defecte, un grup de seguretat acabat de crear té:

  • Entrada: cap regla (no entra res).
  • Sortida: 0.0.0.0/0 en tots els protocols i ports (surt tot).

Anatomia d'una regla: protocol, port i origen

Cada regla té quatre camps:

Camp Valors Notes
Tipus/Protocol TCP, UDP, ICMP, o -1 (tots) A la consola s'ofereixen dreceres: «HTTPS», «PostgreSQL»
Rang de ports Un port (443) o un rang (1024-65535) ICMP no fa servir ports sinó tipus i codi
Origen (entrada) / Destinació (sortida) CIDR, un altre grup de seguretat, o prefix list El camp més important
Descripció Text lliure Opcional però decisiu per al manteniment

Els tres tipus d'origen possibles:

Tipus d'origen Exemple Quan fer-lo servir
CIDR IPv4/IPv6 0.0.0.0/0, 81.45.20.7/32 Trànsit d'internet o d'una IP externa concreta
Un altre grup de seguretat sg-mercadofresco-alb Sempre que l'origen sigui un altre recurs de la VPC
Prefix list pl-6da54004 (S3), o una de pròpia Conjunts de rangs gestionats o reutilitzables

Sobre la descripció: és opcional, però una regla sense descripció és una regla que d'aquí a un any ningú no esborrarà per por. La Marta imposa la norma que tota regla en porti una que expliqui per què existeix, no què fa: «accés del proveïdor de pagaments», no «port 443 obert».

Un detall sobre /32: és la màscara que designa una única adreça IP. 81.45.20.7/32 significa exactament aquella IP i cap més. És la manera correcta d'expressar «només aquesta màquina».

El patró clau: referenciar grups de seguretat entre capes

Aquí hi ha la idea més important de la lliçó. Compara dues maneres d'escriure la mateixa intenció, «la base de dades només accepta connexions de les instàncies de la botiga»:

Forma ingènua, amb rangs d'IP:

Entrada  TCP  5432  des de 10.0.32.0/20   # subxarxa d'app A
Entrada  TCP  5432  des de 10.0.48.0/20   # subxarxa d'app B

Forma correcta, amb referència a grup de seguretat:

Entrada  TCP  5432  des de sg-mercadofresco-tienda

Les diferències són de fons, no d'estil:

Amb rangs d'IP Amb referència a grup de seguretat
Abast real Tota la subxarxa, inclosa qualsevol màquina futura Només els recursos que porten aquell grup
En afegir una AZ o subxarxa Cal editar totes les regles No cal tocar res
En escalar l'ASG Funciona, però per accident Funciona per disseny
En llançar una instància aliena a la subxarxa Pot arribar a la base de dades No pot
Llegibilitat «10.0.32.0/20» no diu res «des del grup de la botiga» es llegeix sol

El cas que ho deixa clar: si el Luis llança una instància de proves a snet-mercadofresco-app-a amb un grup de seguretat diferent, amb la forma ingènua aquella instància pot connectar-se a la base de dades de producció, perquè és dins del rang permès. Amb la referència entre grups, no pot, perquè no porta sg-mercadofresco-tienda.

Com funciona per dins: quan una regla referencia sg-mercadofresco-tienda, AWS resol dinàmicament el conjunt d'IP privades de totes les ENI que tinguin aquell grup assignat, i el manté actualitzat en temps real. Quan l'ASG llança la instància número 3 i la número 4 el divendres a les 17:00, queden autoritzades en el mateix instant en què reben la seva IP.

Dos matisos que sorprenen:

  • La referència resol a IP privades. Si el trànsit arribés per la IP pública, la regla no coincidiria. Dins de la VPC això no passa, però explica per què cal fer servir sempre noms DNS interns.
  • Es pot referenciar el mateix grup (sg-x permet entrada des de sg-x). És el patró per a clústers els nodes dels quals parlen entre ells, com el que necessitarà ElastiCache a 06-05.

Les tres capes de MercadoFresco amb els seus grups de seguretat

flowchart TB
    USER["Usuaris d'internet"]

    subgraph VPC["vpc-mercadofresco"]
        subgraph PUB["Subxarxes publiques (10.0.0.0/20, 10.0.16.0/20)"]
            ALB["Balancejador de la botiga<br/><b>sg-mercadofresco-alb</b><br/>entrada 80 i 443 des de 0.0.0.0/0"]
        end
        subgraph APP["Subxarxes d'aplicacio (10.0.32.0/20, 10.0.48.0/20)"]
            EC2["Instancies de l'ASG<br/><b>sg-mercadofresco-tienda</b><br/>entrada 443 des de sg-mercadofresco-alb"]
        end
        subgraph DAT["Subxarxes de dades (10.0.64.0/20, 10.0.80.0/20)"]
            RDS[("mercadofresco-pedidos<br/><b>sg-mercadofresco-basedatos</b><br/>entrada 5432 des de sg-mercadofresco-tienda")]
            EFS["efs-mercadofresco-fotos<br/><b>sg-efs-mercadofresco</b><br/>entrada 2049 des de sg-mercadofresco-tienda"]
        end
    end

    USER -->|"443"| ALB
    ALB -->|"443"| EC2
    EC2 -->|"5432"| RDS
    EC2 -->|"2049 NFS"| EFS

La cadena de referències és la part elegant: ningú no escriu ni un sol rang IP privat. Només el grup del balancejador, que és la porta d'entrada, esmenta 0.0.0.0/0.

Les regles completes, grup per grup:

sg-mercadofresco-alb — la porta d'entrada

Dir. Protocol Port Origen/Destinació Descripció
Entrada TCP 443 0.0.0.0/0 Trànsit HTTPS públic de la botiga
Entrada TCP 80 0.0.0.0/0 Només per redirigir a HTTPS (vegeu 03-03)
Sortida TCP 443 sg-mercadofresco-tienda Reenviament a les instàncies de l'ASG

sg-mercadofresco-tienda — la capa d'aplicació

Dir. Protocol Port Origen/Destinació Descripció
Entrada TCP 443 sg-mercadofresco-alb Només des del balancejador
Sortida TCP 5432 sg-mercadofresco-basedatos Consultes a la base de comandes
Sortida TCP 2049 sg-efs-mercadofresco Muntatge NFS del sistema de fitxers
Sortida TCP 443 0.0.0.0/0 API d'AWS, passarel·la de pagament, actualitzacions

Fixa't que no hi ha regla d'entrada per al port 22. Ni una IP d'oficina, ni res. Hi tornem al cas pràctic.

sg-mercadofresco-basedatos — la capa de dades

Dir. Protocol Port Origen/Destinació Descripció
Entrada TCP 5432 sg-mercadofresco-tienda Consultes de l'aplicació
Entrada TCP 5432 sg-lambda-mercadofresco Funció mercadofresco-estado-pedido
Sortida (cap) Una base de dades no necessita sortir

Aquell «cap sortida» és deliberat: cal esborrar explícitament la regla de sortida per defecte 0.0.0.0/0. Com que el grup té estat, les respostes a les consultes surten igualment.

sg-efs-mercadofresco — el sistema de fitxers compartit

Dir. Protocol Port Origen/Destinació Descripció
Entrada TCP 2049 sg-mercadofresco-tienda NFS des de les instàncies de la botiga

Límits i quotes dels grups de seguretat

Xifres que convé tenir presents perquè s'assoleixen abans del que sembla:

Límit Valor per defecte Ampliable
Grups de seguretat per VPC 2.500
Regles d'entrada per grup 60 Sí, fins a 1.000
Regles de sortida per grup 60
Grups de seguretat per ENI 5 Sí, fins a 16
Regles × grups per ENI 1.000

La regla real és l'última: regles per grup × grups per ENI no pot passar de 1.000. I un detall comptable: una regla amb un rang de ports compta com una, però una regla amb diversos CIDR compta com una per CIDR. Un grup amb «443 des d'aquestes 40 IP» consumeix 40 regles. És exactament l'escenari per al qual existeixen les prefix lists gestionades pel client: es defineix una llista amb les 40 IP i es referencia amb una sola regla.

NACL: sense estat, numerades i amb regles de denegació

Una NACL (Network Access Control List) és un tallafocs a nivell de subxarxa. Cada subxarxa està associada a exactament una NACL; si no l'associes, fa servir la NACL per defecte de la VPC, que permet absolutament tot en els dos sentits.

Les seves propietats són gairebé les oposades a les d'un grup de seguretat:

1. Permet i denega. Cada regla és allow o deny. Això és l'única cosa que les fa imprescindibles.

2. No té estat (stateless). No recorda res. Si una petició entra per una regla d' entrada, la resposta necessita la seva pròpia regla de sortida. Aquest és l'origen del 90 % dels problemes amb NACL.

3. S'avaluen en ordre numèric, i la primera coincidència guanya. Les regles es numeren d'1 a 32.766. AWS les recorre de menor a major i s'atura en la primera que coincideix, ignorant totes les següents.

4. Existeix la regla *. Al final de tota NACL hi ha una regla no editable, numerada *, que denega tot allò que no hagi coincidit abans. És la xarxa de seguretat final.

La NACL per defecte d'una VPC nova:

Regla Tipus Protocol Port Origen Acció
100 Tot Tots Tots 0.0.0.0/0 ALLOW
* Tot Tots Tots 0.0.0.0/0 DENY

(i el mateix en sortida). És a dir: per defecte, una NACL no filtra res. Això és intencionat, i és la raó per la qual moltes arquitectures correctes deixen les NACL tal qual i confien tot el filtratge als grups de seguretat.

Consell de numeració: fes servir salts de 100 (100, 200, 300…). Quan necessitis inserir una regla entre dues, tindràs lloc. Renumerar una NACL en producció és una operació desagradable.

El parany dels ports efímers

Aquest apartat es mereix la seva pròpia secció perquè és on tothom s'estavella.

Quan un navegador es connecta a la botiga, obre la connexió des d'un port aleatori alt —un port efímer— cap al 443 del servidor. El servidor respon des del 443 cap a aquell port efímer. Com que la NACL no té estat, aquella resposta s'avalua contra les regles de sortida, i el seu port de destinació no és el 443: és el 51234, o el 62890, o el que sigui.

Per això una NACL restrictiva necessita sempre una regla de sortida que obri el rang de ports efímers. I el rang depèn del client:

Sistema del client Rang de ports efímers
Linux modern 32768–60999
Windows (des de Vista/2008) 49152–65535
Balancejadors d'AWS (ELB/NLB) 1024–65535
Lambda, contenidors 1024–65535

La recomanació pràctica d'AWS: obrir 1024–65535 en sortida, perquè no saps quin client tindràs. Sí, és un rang enorme, i és la raó per la qual les NACL són un instrument tosc: filtrar finament el trànsit de tornada és pràcticament impossible.

Una NACL funcional per a una subxarxa pública queda així:

Regla Dir. Protocol Port Origen/Destinació Acció Per què
100 Entrada TCP 443 0.0.0.0/0 ALLOW Peticions HTTPS
110 Entrada TCP 80 0.0.0.0/0 ALLOW Peticions HTTP a redirigir
120 Entrada TCP 1024-65535 0.0.0.0/0 ALLOW Respostes a connexions sortints
* Entrada Tots Tots 0.0.0.0/0 DENY Regla implícita
100 Sortida TCP 443 0.0.0.0/0 ALLOW Connexions sortints HTTPS
110 Sortida TCP 1024-65535 0.0.0.0/0 ALLOW Respostes a les peticions entrants
* Sortida Tots Tots 0.0.0.0/0 DENY Regla implícita

Quatre de les sis regles existeixen només per la manca d'estat. Compara-ho amb el grup de seguretat equivalent, que en necessitava una.

Taula comparativa: grups de seguretat davant de NACL

Criteri Grup de seguretat NACL
Nivell ENI (instància, RDS, ALB, punt d'enllaç…) Subxarxa completa
Estat Amb estat: la resposta es permet sola Sense estat: la resposta necessita regla pròpia
Tipus de regla Només permetre Permetre i denegar
Avaluació Totes les regles alhora, sense ordre Per número, la primera que coincideix guanya
Quantes se n'apliquen Diversos per ENI (fins a 5), se sumen Una per subxarxa
Per defecte No entra res, surt tot Entra tot i surt tot
Origen per grup de seguretat , la característica clau No, només CIDR
Trànsit dins de la mateixa subxarxa Sí que el filtra No el veu
Quan fer-lo servir Sempre; és l'eina principal Bloquejos gruixuts per subxarxa i denegacions
Cas d'ús típic «La base de dades només parla amb la botiga» «Aquesta IP no entra a la subxarxa pública»

Regla pràctica que resumeix el repartiment: els grups de seguretat defineixen l'arquitectura permesa; les NACL bloquegen el que cal bloquejar.

El camí complet d'un paquet

Aquest diagrama és el que cal memoritzar. Segueix una petició HTTPS d'un client fins a la instància i la seva resposta de tornada:

sequenceDiagram
    participant C as Client<br/>(203.0.113.50:51234)
    participant NE as NACL entrada<br/>subxarxa app
    participant SE as SG entrada<br/>sg-mercadofresco-tienda
    participant I as Instancia<br/>(10.0.32.15:443)
    participant SS as SG sortida<br/>(amb estat)
    participant NS as NACL sortida<br/>subxarxa app

    C->>NE: Peticio → port desti 443
    Note over NE: Regla 100: ALLOW TCP 443 ✔
    NE->>SE: Peticio
    Note over SE: Entrada 443 des de sg-alb ✔
    SE->>I: Arriba a l'aplicacio
    I->>SS: Resposta → port desti 51234
    Note over SS: Connexio ja establerta:<br/>NO s'avalua cap regla ✔
    SS->>NS: Resposta
    Note over NS: Regla 110: ALLOW TCP 1024-65535<br/>SENSE aquesta regla, la resposta MOR AQUI ✘
    NS->>C: Resposta lliurada

Quatre punts de control en total, i l'asimètric és el tercer: el grup de seguretat no avalua res a la sortida perquè recorda la connexió, mentre que la NACL la torna a examinar com si fos trànsit nou. El símptoma quan falta la regla 110 és especialment cruel: la connexió s' estableix, la petició arriba, l'aplicació la processa correctament… i el client es queda esperant fins que expira el temps d'espera.

Cas pràctic: obrir el port 22 només a l'oficina (i per què no fer-ho)

El Luis demana accés SSH a les instàncies per depurar. La resposta «correcta segons el manual» seria obrir el 22 només a la IP fixa de l'oficina:

aws ec2 authorize-security-group-ingress \
  --profile mercadofresco-dev --region eu-west-1 \
  --group-id "$SG_TIENDA" \
  --ip-permissions 'IpProtocol=tcp,FromPort=22,ToPort=22,IpRanges=[{CidrIp=81.45.20.7/32,Description="SSH des de la oficina de Barcelona"}]'

Mai 0.0.0.0/0. Un port 22 obert a internet rep intents d'accés automatitzats en qüestió de minuts.

Però per a MercadoFresco ni tan sols això és la solució correcta, per quatre raons:

  1. La IP de l'oficina canvia. El dia que l'operador la renovi, no hi entra ningú, i algú «arreglarà» el problema posant 0.0.0.0/0 a les onze de la nit.
  2. No serveix per al teletreball. La Marta des de casa té una altra IP.
  3. No queda registre de qui va entrar. SSH amb clau compartida no identifica la persona.
  4. Les instàncies de l'ASG són efímeres. Entrar per SSH a una màquina que es destruirà d'aquí a dues hores per «arreglar-la» a mà és exactament el que l'ASG intenta evitar.

L'alternativa, que ja fem servir a 02-01, és el Session Manager: l'agent de SSM instal·lat a la instància inicia una connexió sortint cap al servei d'AWS, i la sessió viatja per aquell túnel.

SSH amb port 22 obert Session Manager
Ports d'entrada necessaris 22 des d'algun origen Cap
Necessita IP pública o bastió No
Gestió de claus Fitxers .pem que es comparteixen Cap: fa servir credencials d'AWS
Qui va entrar i què va fer Sense registre fiable Registrat a CloudTrail i CloudWatch Logs
Control de permisos Tot o res Polítiques d'IAM per persona (04-01)
Funciona en subxarxa privada Només amb bastió , amb NAT o amb punts d'enllaç d'interfície

Per això sg-mercadofresco-tienda no té cap regla d'entrada per al 22, i aquella absència és una decisió de disseny, no un oblit.

Cas pràctic: bloquejar una IP abusiva amb una NACL

Un dimarts, la Sara detecta als registres que la IP 198.51.100.77 està recorrent el catàleg sencer a raó de 40 peticions per segon. No és un atac distribuït, és una sola IP raspant preus. Els grups de seguretat no serveixen: no saben denegar.

# Localitzar la NACL associada a les subxarxes publiques
NACL_PUB=$(aws ec2 describe-network-acls \
  --profile mercadofresco-dev --region eu-west-1 \
  --filters "Name=association.subnet-id,Values=$PUB_A" \
  --query 'NetworkAcls[0].NetworkAclId' --output text)

# Regla 50: numero BAIX perque s'avalui ABANS que l'ALLOW general de la 100
aws ec2 create-network-acl-entry \
  --profile mercadofresco-dev --region eu-west-1 \
  --network-acl-id "$NACL_PUB" \
  --rule-number 50 \
  --protocol -1 \
  --rule-action deny \
  --ingress \
  --cidr-block 198.51.100.77/32

El número 50 és tota la lliçó: si haguéssim fet servir el 150, la regla 100 (ALLOW general) s' avaluaria primer, coincidiria, i la 150 no es llegiria mai. La primera coincidència guanya.

Per retirar-la quan l'abús cessi:

aws ec2 delete-network-acl-entry --profile mercadofresco-dev --region eu-west-1 \
  --network-acl-id "$NACL_PUB" --rule-number 50 --ingress

Dos advertiments honestos:

  • Bloquejar IP a mà no escala. Si demà en són 300, això és inviable i caldrà l' eina adequada: AWS WAF, que s'estudia a 04-05, amb les seves regles de límit de taxa. La NACL és la resposta d'urgència, no la solució.
  • La NACL bloqueja en entrar a la subxarxa. Si el trànsit arriba a través de CloudFront (lliçó 03-04) o d'un balancejador, la IP que veu la NACL és la de l'intermediari, no la de l'atacant, i bloquejar-la deixaria fora tothom.

Depuració: quin error veus quan falla cada cosa

Els símptomes són diferents i saber llegir-los estalvia hores:

Símptoma Causa més probable Com confirmar-ho
Temps d'espera esgotat (connection timed out, la petició es queda penjada) Grup de seguretat o NACL: el paquet es descarta en silenci Flow Logs amb REJECT, o cap entrada en absolut
Connexió rebutjada (connection refused, resposta immediata) La xarxa funciona: no hi ha res escoltant en aquell port ss -lntp a la instància; el servei està caigut
Es connecta i es penja a mitges NACL sense regla de ports efímers a la sortida Flow Logs: ACCEPT d'entrada i REJECT de sortida
Es resol a una IP pública des de dins enableDnsHostnames o PubliclyAccessible dig des de la instància (vist a 03-01)
Funciona des d'una instància i no des d'una altra La segona no porta el grup de seguretat referenciat describe-instances i comparar SecurityGroups
Accés denegat (AccessDenied) de l'API d'AWS No és la xarxa: és una política d'IAM CloudTrail; s'estudia a 04-01

La distinció entre les dues primeres files és la més útil de tota la taula: un tallafocs descarta el paquet en silenci i el client espera; un servei caigut respon immediatament rebutjant. Si telnet mercadofresco-pedidos... 5432 retorna «connection refused» a l'instant, el problema no és al grup de seguretat.

Confirmar-ho amb VPC Flow Logs

Els Flow Logs que vam activar a 03-01 són l'única manera de tenir-ne certesa. Recordant el format:

2 111122223333 eni-0a1b2c3d 10.0.32.15 10.0.64.30 44321 5432 6 12 3800 1738500000 1738500060 REJECT OK

Aquí 10.0.32.15 (instància de la botiga) va intentar arribar a 10.0.64.30 (la base de dades) al port 5432, i el resultat va ser REJECT. Això confirma que el bloqueig passa a la capa de xarxa.

Com distingir quina de les dues capes ha bloquejat:

Què veus als Flow Logs Interpretació
Només un REJECT d'entrada, cap resposta Grup de seguretat de destinació: va descartar la petició
ACCEPT d'entrada i REJECT de sortida a la resposta NACL: falta la regla de ports efímers
Cap entrada en absolut El paquet ni hi va arribar: problema de rutes (03-01) o de DNS
ACCEPT en tots dos sentits però l'aplicació falla La xarxa està bé: mira l'aplicació

Aquella tercera fila és especialment valuosa: l'absència de registres és informació. L'anàlisi amb CloudWatch Logs Insights i les consultes sobre aquestes dades s'estudien a 05-01.

Creació i auditoria per CLI

Crear els grups amb referències creuades

Hi ha un problema d'ou i gallina: sg-mercadofresco-tienda referencia sg-mercadofresco-alb i a l'inrevés. La solució és crear primer els grups buits i després les regles.

crear_sg() {
  local nom=$1 desc=$2 component=$3
  aws ec2 create-security-group \
    --profile mercadofresco-dev --region eu-west-1 \
    --group-name "$nom" --description "$desc" --vpc-id "$VPC_ID" \
    --tag-specifications "ResourceType=security-group,Tags=[
        {Key=Name,Value=$nom},{Key=Proyecto,Value=mercadofresco},
        {Key=Entorno,Value=produccion},{Key=Componente,Value=$component},
        {Key=Propietario,Value=marta},{Key=CentroCoste,Value=operaciones}]" \
    --query 'GroupId' --output text
}

SG_ALB=$(crear_sg   sg-mercadofresco-alb        "Balancejador public de la botiga" tienda)
SG_TIENDA=$(crear_sg sg-mercadofresco-tienda    "Instancies EC2 de la botiga"      tienda)
SG_BD=$(crear_sg    sg-mercadofresco-basedatos  "RDS PostgreSQL de comandes"       pedidos)
SG_EFS=$(crear_sg   sg-efs-mercadofresco        "Sistema de fitxers de fotos"      catalogo)

Ara les regles. --ip-permissions en format llarg permet incloure la descripció, cosa que la forma curta --port / --cidr no admet:

# ALB: entrada des d'internet
aws ec2 authorize-security-group-ingress --profile mercadofresco-dev --region eu-west-1 \
  --group-id "$SG_ALB" --ip-permissions \
  'IpProtocol=tcp,FromPort=443,ToPort=443,IpRanges=[{CidrIp=0.0.0.0/0,Description="HTTPS public de la botiga"}]' \
  'IpProtocol=tcp,FromPort=80,ToPort=80,IpRanges=[{CidrIp=0.0.0.0/0,Description="HTTP per redirigir a HTTPS"}]'

# Botiga: entrada NOMES des del grup del balancejador (UserIdGroupPairs, no IpRanges)
aws ec2 authorize-security-group-ingress --profile mercadofresco-dev --region eu-west-1 \
  --group-id "$SG_TIENDA" --ip-permissions \
  "IpProtocol=tcp,FromPort=443,ToPort=443,UserIdGroupPairs=[{GroupId=$SG_ALB,Description=\"Transit del balancejador\"}]"

# Base de dades: entrada NOMES des del grup de la botiga
aws ec2 authorize-security-group-ingress --profile mercadofresco-dev --region eu-west-1 \
  --group-id "$SG_BD" --ip-permissions \
  "IpProtocol=tcp,FromPort=5432,ToPort=5432,UserIdGroupPairs=[{GroupId=$SG_TIENDA,Description=\"Consultes de la botiga\"}]"

# EFS: NFS des de la botiga
aws ec2 authorize-security-group-ingress --profile mercadofresco-dev --region eu-west-1 \
  --group-id "$SG_EFS" --ip-permissions \
  "IpProtocol=tcp,FromPort=2049,ToPort=2049,UserIdGroupPairs=[{GroupId=$SG_TIENDA,Description=\"Muntatge NFS de la botiga\"}]"

# Treure la sortida total de la base de dades: no necessita iniciar res
aws ec2 revoke-security-group-egress --profile mercadofresco-dev --region eu-west-1 \
  --group-id "$SG_BD" --ip-permissions 'IpProtocol=-1,IpRanges=[{CidrIp=0.0.0.0/0}]'

UserIdGroupPairs és la clau del patró: és el camp que expressa «des d'aquest altre grup de seguretat» en lloc d'un rang d'IP.

Auditoria: trobar regles perilloses

Aquesta comanda és la que la Marta executa cada dilluns. Busca qualsevol grup de seguretat del compte amb un port d'administració o de base de dades obert a tot internet:

aws ec2 describe-security-groups --profile mercadofresco-dev --region eu-west-1 \
  --query 'SecurityGroups[?IpPermissions[?
      (FromPort==`22` || FromPort==`3389` || FromPort==`3306` || FromPort==`5432` || FromPort==`6379`)
      && IpRanges[?CidrIp==`0.0.0.0/0`]]].{
        Grup:GroupName, Id:GroupId, VPC:VpcId,
        Ports:IpPermissions[?IpRanges[?CidrIp==`0.0.0.0/0`]].FromPort}' \
  --output table

Desglossament de l'expressió JMESPath, que és densa però val la pena entendre:

  • SecurityGroups[? ... ] filtra la llista de grups.
  • IpPermissions[? ... ] mira dins de les regles d'entrada de cada grup.
  • La condició combina dues coses amb &&: que el port sigui un dels sensibles (22 SSH, 3389 RDP, 3306 MySQL, 5432 PostgreSQL, 6379 Redis) i que entre els seus rangs hi hagi un 0.0.0.0/0.
  • Els accents greus (`) marquen literals numèrics i de cadena a JMESPath, com vam veure a 01-05.

Si això retorna alguna cosa, hi ha un problema per arreglar avui. I un complement útil, buscar grups orfes que ningú no fa servir:

# Grups que no estan assignats a cap interficie de xarxa
comm -23 \
  <(aws ec2 describe-security-groups --profile mercadofresco-dev --region eu-west-1 \
      --query 'SecurityGroups[].GroupId' --output text | tr '\t' '\n' | sort) \
  <(aws ec2 describe-network-interfaces --profile mercadofresco-dev --region eu-west-1 \
      --query 'NetworkInterfaces[].Groups[].GroupId' --output text | tr '\t' '\n' | sort -u)

I la comprovació des de Python, útil per integrar-la en un pipeline (mòdul 8):

import boto3

ec2 = boto3.client("ec2", region_name="eu-west-1")
PORTS_SENSIBLES = {22, 3389, 3306, 5432, 6379, 27017}

troballes = []
paginador = ec2.get_paginator("describe_security_groups")

for pagina in paginador.paginate():
    for grup in pagina["SecurityGroups"]:
        for regla in grup.get("IpPermissions", []):
            # Una regla amb IpProtocol == "-1" no porta FromPort: són TOTS els ports
            inici = regla.get("FromPort", 0)
            fi = regla.get("ToPort", 65535)
            obert_a_internet = any(
                r["CidrIp"] == "0.0.0.0/0" for r in regla.get("IpRanges", [])
            )
            if not obert_a_internet:
                continue
            afectats = {p for p in PORTS_SENSIBLES if inici <= p <= fi}
            if afectats:
                troballes.append({
                    "grup": grup["GroupName"],
                    "id": grup["GroupId"],
                    "ports": sorted(afectats),
                })

for t in troballes:
    print(f"PERILL  {t['grup']:35} {t['id']:22} ports {t['ports']}")

print(f"\n{len(troballes)} regles perilloses trobades")

Detall important del codi: es comprova el rang complet (inici <= p <= fi), no només el port inicial. Una regla «TCP 1-65535 des de 0.0.0.0/0» no té FromPort == 22, però inclou el 22 i és igual de perillosa. La comanda CLI anterior, més simple, se li escaparia.

Una altra capa diferent: les polítiques d'identitat

Convé tancar desfent una confusió freqüent. Els grups de seguretat i les NACL controlen paquets de xarxa: qui pot obrir una connexió TCP a quin port. No tenen cap opinió sobre qui és el que fa la petició ni sobre quina acció sol·licita.

Quan la instància de la botiga demana un objecte de mercadofresco-catalogo-fotos, hi ha dues preguntes independents:

  1. Hi ha camí i està permès el paquet? → rutes (03-01), grup de seguretat, NACL, punt d'enllaç de VPC.
  2. Té permís aquella identitat per fer s3:GetObject sobre aquell objecte?polítiques d' IAM, el rol rol-mercadofresco-tienda, i les polítiques de bucket. Això és matèria de la lliçó 04-01, i un AccessDenied d'allà no s'arregla mai tocant un grup de seguretat.

Distingir aquestes dues capes estalvia sessions senceres de depuració en la direcció equivocada. I la protecció davant d'atacs a nivell d'aplicació —injeccions, bots, denegació de servei— és una tercera capa diferent encara, la de Shield (04-04) i WAF (04-05).

Errors Habituals i Consells

Fer servir rangs d'IP on hi hauria d'haver referències entre grups. Funciona, i per això es propaga. Però obre la porta a qualsevol recurs futur d'aquelles subxarxes i obliga a editar regles cada vegada que canvia la xarxa. Si l'origen és un recurs de la VPC, la resposta és sempre UserIdGroupPairs.

Obrir el 22 o el 3389 a 0.0.0.0/0 «un moment, per provar». Els escàners automàtics troben el port en minuts. Si de debò cal, /32 d'una IP concreta; millor, Session Manager.

Oblidar els ports efímers a la NACL de sortida. El símptoma —la connexió s'estableix i després es penja— no apunta gens a la NACL. Si toques una NACL, obre 1024-65535 en sortida.

Numerar malament les regles de NACL. Un DENY amb número més alt que un ALLOW que ja coincideix és una regla morta. Les denegacions van amb números baixos. I numera de 100 en 100 per poder inserir.

Creure que una NACL filtra el trànsit entre dues instàncies de la mateixa subxarxa. No ho fa: la NACL només actua a la frontera de la subxarxa. Aquell trànsit només el veuen els grups de seguretat.

Deixar la sortida 0.0.0.0/0 al grup de la base de dades. No és un risc immediat, però si la base de dades es veu compromesa, aquella regla és la seva via per treure dades. Treu-la: l'estat del grup de seguretat fa que les respostes surtin igual.

Confondre «temps d'espera esgotat» amb «connexió rebutjada». El primer és un tallafocs, el segon és un servei caigut. Diagnosticar en la direcció equivocada costa hores.

No posar descripció a les regles. Una regla sense explicació és permanent per por. Exigeix una descripció a cada regla i revisa la llista cada trimestre.

Consell d'or: dibuixa el diagrama de les capes amb les fletxes dels grups de seguretat abans d'escriure ni una sola regla. Si la fletxa no és al dibuix, la regla no hauria d'existir.

Exercicis

Exercici 1: dissenyar els grups de seguretat d'un tauler intern

MercadoFresco necessita un tauler d'administració intern en instàncies pròpies, que la Sara farà servir des de l'oficina. El tauler ha de: rebre trànsit del balancejador al 443, consultar la rèplica de lectura mercadofresco-pedidos-lectura al 5432, i escriure informes a mercadofresco-informes-analitica. No ha de poder arribar a la base de dades primària.

Dissenya el grup sg-mercadofresco-panel i els canvis necessaris als grups existents, respectant el patró de referències.

Exercici 2: corregir una NACL trencada

Un company ha configurat aquesta NACL a la subxarxa d'aplicació i la botiga ha deixat de respondre:

Regla Dir. Protocol Port Origen Acció
100 Entrada TCP 443 10.0.0.0/16 ALLOW
* Entrada Tots Tots 0.0.0.0/0 DENY
100 Sortida TCP 5432 10.0.64.0/20 ALLOW
200 Sortida TCP 443 0.0.0.0/0 ALLOW
* Sortida Tots Tots 0.0.0.0/0 DENY

Identifica tots els problemes, explica el símptoma exacte de cadascun i escriu la NACL corregida.

Exercici 3: auditar i corregir un grup de seguretat heretat

Escriu un script de shell que, per a un grup de seguretat donat, imprimeixi un informe llegible de totes les seves regles d'entrada distingint el tipus d'origen, marqui les perilloses, i generi les comandes revoke-security-group-ingress necessàries per eliminar-les sense executar-les.

Solucions

Solució 1

Grup nou sg-mercadofresco-panel:

Dir. Protocol Port Origen/Destinació Descripció
Entrada TCP 443 sg-mercadofresco-alb Tauler servit a través del balancejador
Sortida TCP 5432 sg-mercadofresco-lectura Consultes a la rèplica de lectura
Sortida TCP 443 0.0.0.0/0 S3 i API d'AWS

Canvis als grups existents: cal un grup nou per a la rèplica, sg-mercadofresco-lectura, separat de sg-mercadofresco-basedatos. Aquest és el nucli de l'exercici: si la rèplica compartís grup amb la primària, autoritzar el tauler a la rèplica l'autoritzaria també a la primària, incomplint el requisit.

sg-mercadofresco-lectura Protocol Port Origen
Entrada TCP 5432 sg-mercadofresco-panel
Entrada TCP 5432 sg-mercadofresco-tienda

I sg-mercadofresco-basedatos no es toca: continua acceptant només des de sg-mercadofresco-tienda i la Lambda, per la qual cosa el tauler no pot arribar a la primària.

Sobre l'accés de la Sara des de l'oficina: no s'obre cap port per a ella. Entra pel mateix balancejador que els clients, i la restricció a l'oficina es fa per autenticació de l' aplicació, o amb WAF filtrant per IP a /admin (04-05). Afegir una regla amb la IP de l'oficina al grup del tauler seria tornar al problema de les IP canviants.

Sobre l'accés a mercadofresco-informes-analitica: no requereix cap regla nova més enllà del 443 de sortida, i si existeix el punt d'enllaç de passarel·la de S3 (03-01), ni tan sols passa pel NAT. El permís per escriure al bucket és cosa del rol d'IAM (04-01), no del tallafocs.

Solució 2

Hi ha tres problemes:

Problema 1 — falta el rang de ports efímers a la sortida. Les peticions HTTPS entren per la regla 100 i arriben a l'aplicació, però la resposta va dirigida al port efímer del client (per exemple, 51234). En sortida, la 100 només cobreix el 5432 i la 200 només el 443, així que la resposta cau al * DENY. Símptoma: la connexió s'estableix, el servidor processa la petició i el client es queda penjat fins que expira el temps d'espera. Als Flow Logs: ACCEPT d'entrada i REJECT de sortida.

Problema 2 — falta el rang de ports efímers a l'entrada. Quan la instància consulta RDS (sortint pel 5432, permès per la regla 100 de sortida), la resposta de RDS arriba a un port efímer de la instància. En entrada només està permès el 443, així que la resposta es denega. Símptoma: les consultes a la base de dades es pengen i acaben en temps d'espera esgotat. El mateix passa amb les crides HTTPS sortints a S3 o a la passarel·la de pagament.

Problema 3 — l'origen de l'entrada és massa restrictiu en un sentit i poc precís en un altre. 10.0.0.0/16 cobreix la VPC sencera, cosa que és correcta si el trànsit només ve del balancejador, però a més permet que qualsevol subxarxa de la VPC entri pel 443, cosa que la NACL no hauria de decidir. No trenca res, però és una regla mal plantejada: el filtratge fi d'origen és feina del grup de seguretat.

NACL corregida:

Regla Dir. Protocol Port Origen/Destinació Acció Per què
100 Entrada TCP 443 10.0.0.0/16 ALLOW Peticions del balancejador
200 Entrada TCP 1024-65535 0.0.0.0/0 ALLOW Respostes a consultes sortints
* Entrada Tots Tots 0.0.0.0/0 DENY Implícita
100 Sortida TCP 5432 10.0.64.0/20 ALLOW Consultes a RDS
200 Sortida TCP 443 0.0.0.0/0 ALLOW S3, API d'AWS, pagaments
300 Sortida TCP 1024-65535 0.0.0.0/0 ALLOW Respostes al balancejador
* Sortida Tots Tots 0.0.0.0/0 DENY Implícita

Moralitat de l'exercici: si el filtratge fi ja el fan els grups de seguretat, aquesta NACL podria perfectament deixar-se en l'ALLOW total per defecte sense perdre seguretat real, i s'hauria evitat la incidència.

Solució 3

#!/usr/bin/env bash
# auditar-sg.sh — informe de regles d'entrada d'un grup de seguretat
set -euo pipefail

GRUP="${1:?Us: $0 <sg-id>}"
PERFIL="mercadofresco-dev"
REGIO="eu-west-1"
PERILLOSOS="22 3389 3306 5432 6379 27017"

echo "=== Regles d'entrada de $GRUP ==="

REGLES=$(aws ec2 describe-security-groups --profile "$PERFIL" --region "$REGIO" \
  --group-ids "$GRUP" --query 'SecurityGroups[0].IpPermissions' --output json)

# Recorrem les regles amb jq, generant una linia per combinacio regla-origen
echo "$REGLES" | jq -r '
  .[] |
  . as $r |
  (($r.IpRanges // [])[]      | "CIDR|\($r.IpProtocol)|\($r.FromPort // 0)|\($r.ToPort // 65535)|\(.CidrIp)|\(.Description // "-")"),
  (($r.UserIdGroupPairs // [])[] | "SG  |\($r.IpProtocol)|\($r.FromPort // 0)|\($r.ToPort // 65535)|\(.GroupId)|\(.Description // "-")"),
  (($r.PrefixListIds // [])[]   | "PL  |\($r.IpProtocol)|\($r.FromPort // 0)|\($r.ToPort // 65535)|\(.PrefixListId)|\(.Description // "-")")
' | while IFS='|' read -r tipus proto inici fi origen desc; do

    marca="  "
    if [[ "$origen" == "0.0.0.0/0" ]]; then
      for p in $PERILLOSOS; do
        if (( inici <= p && p <= fi )); then marca="!!"; break; fi
      done
    fi

    printf "%s %-4s %-5s %6s-%-6s %-22s %s\n" \
      "$marca" "$tipus" "$proto" "$inici" "$fi" "$origen" "$desc"

    if [[ "$marca" == "!!" ]]; then
      echo "     → aws ec2 revoke-security-group-ingress --profile $PERFIL --region $REGIO \\"
      echo "         --group-id $GRUP --protocol $proto --port $inici-$fi --cidr $origen"
    fi
done

echo
echo "Les línies marcades amb !! exposen un port sensible a tot internet."
echo "Revisa les comandes suggerides abans d'executar-les."

Punts de disseny de l'script: distingeix els tres tipus d'origen (CIDR, SG, PL) perquè una referència a un altre grup mai no és perillosa per si mateixa; comprova el rang complet de ports en lloc de només l'inicial; fa servir // 0 i // 65535 a jq per al cas IpProtocol: "-1", on FromPort i ToPort no existeixen; i no executa res, només imprimeix les comandes, perquè revocar regles a cegues en producció és pitjor que la vulnerabilitat.

Conclusió

MercadoFresco ja té tallafocs. Saps que els grups de seguretat actuen a la ENI, tenen estat —la resposta a una connexió permesa surt sola, sense regla—, només permeten i s'avaluen en conjunt sense ordre, i que per defecte no deixen entrar res i ho deixen sortir tot. Coneixes l' anatomia d'una regla i, sobretot, domines el patró que defineix una xarxa ben construïda: referenciar uns grups de seguretat des d'uns altres amb UserIdGroupPairs en lloc d'escriure rangs d'IP. Gràcies a això, sg-mercadofresco-basedatos accepta el 5432 només des de sg-mercadofresco-tienda, i les instàncies que l'ASG llança el divendres a les 17:00 queden autoritzades en el mateix instant en què arrenquen, sense editar res.

Saps que les NACL són el contrari en gairebé tot: actuen a la frontera de la subxarxa, no tenen estat, s'avaluen per número aturant-se en la primera coincidència, acaben sempre en la regla * de denegació, i són l'única de les dues capes capaç de denegar. Has entès per què això les obliga a obrir el rang de ports efímers 1024-65535 en el sentit de tornada, i per què el símptoma d'oblidar-ho —la connexió s'estableix i es penja— és tan difícil d'atribuir. Has recorregut el camí complet d'un paquet pels quatre punts de control i saps quin d' ells és asimètric i per què.

A la pràctica has decidit no obrir el port 22 ni tan sols a la IP de l'oficina, amb quatre raons concretes i el Session Manager com a alternativa; has bloquejat una IP abusiva amb una regla DENY numerada 50, entenent que el número baix és el que la fa efectiva, i sabent que aquesta és una resposta d'urgència i no una solució que escali —per a això hi ha WAF, a 04-05. Saps distingir un temps d'espera esgotat (tallafocs) d'una connexió rebutjada (servei caigut), i confirmar quina de les dues capes ha bloquejat un paquet llegint l'ACCEPT/REJECT dels VPC Flow Logs. I has automatitzat l'auditoria setmanal que busca ports sensibles oberts a 0.0.0.0/0, tant en JMESPath com en boto3 comprovant el rang complet de ports. Finalment, tens clar que les polítiques d'identitat són una capa diferent: un AccessDenied d'IAM no s'arregla mai tocant un grup de seguretat, i això és matèria del mòdul 4.

Amb la xarxa dissenyada i filtrada, ja podem posar la peça que MercadoFresco porta esperant des de la primera lliçó. El grup d'Auto Scaling asg-mercadofresco-tienda sap llançar quatre instàncies el divendres a la tarda, però res no reparteix el trànsit entre elles: avui encara pengen d'una única IP. A la lliçó 03-03, «Elastic Load Balancing», muntarem el balancejador d'aplicació a les subxarxes públiques amb el grup sg-mercadofresco-alb que acabem de crear, el connectarem a l'ASG perquè registri i doni de baixa les instàncies automàticament, configurarem comprovacions d'estat que no tombin l'aplicació en lloc de salvar-la, i acabarem de resoldre el problema 1: les caigudes dels divendres.

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