El tercer front pendent es va formular al mòdul 3 amb una frase incòmoda: "el pipeline té permisos sobre producció i no s'ha auditat". És literalment cert. cd.yml pot desplegar qualsevol imatge a prod, infra.yml pot modificar la infraestructura d'AWS, i tots dos executen accions de tercers que ningú de l'equip no ha llegit. Si algú compromet el repositori de Reservalia no necessita atacar producció: el pipeline la desplega per ell. I la lliçó anterior hi va afegir l'altra cara del mateix problema: 1.147 paquets d'uns 330 mantenidors es descarreguen, s'executen i acaben dins de la imatge. Aquesta lliçó aborda les dues meitats —la seguretat del programari que passa pel pipeline i la seguretat del pipeline mateix— i acaba amb la cadena de subministrament: SBOM, signatura i procedència. Amb un advertiment previ que convé prendre's seriosament: això ensenya a automatitzar controls, no substitueix una revisió per un professional de seguretat ni el criteri de compliment normatiu de la teva organització. Un pipeline amb sis escàners en verd no és un sistema segur: és un sistema amb sis escàners en verd.
Contingut
- El pipeline com a superfície d'atac
- Els cinc anàlisis automatitzats i què detecta cadascun
- El job
seguretatde Reservalia - La política de severitats: què trenca la build i què obre un tiquet
- Secrets: on viuen i per què OIDC era una decisió de seguretat
- Quan un secret es filtra
- Mínim privilegi en els permisos del pipeline
- Codi de tercers: fixar accions per SHA i el risc de
pull_request_target - Protecció de branques, entorns i registre d'auditoria
- Cadena de subministrament: SBOM, signatura i procedència
- Errors Comuns i Consells
- Exercicis
- Conclusió
- El pipeline com a superfície d'atac
Durant dos mòduls hem tractat el pipeline com una eina de productivitat. Vist des de fora és una altra cosa: un sistema amb accés privilegiat que executa codi de tercers i avala amb la seva reputació el que produeix.
| Propietat del pipeline | Per què interessa a un atacant |
|---|---|
Té credencials de prod |
És un camí a producció que no passa per cap tallafoc |
| Executa codi de tercers | Accions, paquets npm i els seus scripts d'instal·lació corren amb aquells permisos |
| Publica els artefactes de confiança | El que en surt ningú no ho torna a qüestionar |
| Reacciona a esdeveniments de desconeguts | Un pull request d'un fork pot disparar feina |
| Els seus logs passen per molts ulls | Un secret imprès una vegada queda a l'historial |
D'aquí les dues preguntes que estructuren la lliçó. La primera és el programari que lliurem té fallades de seguretat?, i es respon ficant anàlisis dins del pipeline: és el shift-left, avançar la detecció al moment més barat, el mateix argument del fail fast de la 04-01 aplicat a una altra classe de defecte. La segona és el pipeline mateix és segur?, i es respon amb permisos, secrets i control del codi de tercers. Un equip que només fa el primer té escàners bonics protegint un sistema que qualsevol pot segrestar.
- Els cinc anàlisis automatitzats i què detecta cadascun
| Tipus | Què analitza | Què detecta | Quan s'executa | Falsos positius |
|---|---|---|---|---|
| SAST | El teu codi font | Injecció SQL, XSS, rutes insegures, criptografia mal feta servir | A cada PR | Mitjans |
| SCA | Les teves dependències | Versions amb vulnerabilitats conegudes (CVE) | A cada PR i diàriament | Baixos, però molt soroll irrellevant |
| Detecció de secrets | Codi i historial de git | Claus, tokens i contrasenyes compromesos | Cada PR, i una vegada sobre tot l'historial | Baixos |
| Escaneig d'imatge | La imatge construïda | Vulnerabilitats del sistema base i de les capes | Després de docker build |
Alts: moltes no són explotables |
| DAST | L'aplicació en execució | Capçaleres, autenticació, configuració exposada | Nocturn, contra staging |
Mitjans |
Les diferències importen més que els noms. SAST llegeix codi sense executar-lo: troba el patró perillós però no sap si aquella ruta és assolible, i d'aquí els seus falsos positius. SCA no mira el teu codi en absolut, només compara el teu arbre de dependències amb una base de dades de vulnerabilitats; és el més barat i el que més soroll genera, perquè una CVE en una llibreria que només fas servir en proves no és un risc real. La detecció de secrets és l'única que ha de mirar l'historial complet: esborrar una clau en un commit posterior no l'elimina de git, continua estant a un git log -p de distància. L'escaneig d'imatge troba desenes de vulnerabilitats del sistema base que ni tan sols tenen pedaç disponible. I DAST és l'únic que prova el sistema real, amb la seva configuració i les seves capçaleres; per això va contra staging i al pipeline nocturn: és lent i necessita alguna cosa desplegada.
Cap no substitueix els altres, i cap no substitueix pensar. Una vulnerabilitat de lògica de negoci —"puc cancel·lar la cita d'un altre negoci canviant l'identificador de l'URL"— no la detecta cap dels cinc. Això es cobreix amb disseny, revisió i proves escrites expressament.
- El job
seguretat de Reservalia
seguretat de ReservaliaReservalia afegeix un cinquè job a ci.yml, en paral·lel amb els altres per no allargar el camí crític:
seguretat: # ~2 min · en paral·lel amb qualitat/test/build
name: Seguretat
runs-on: ubuntu-22.04
timeout-minutes: 15
permissions:
contents: read
security-events: write # 1 · pujar resultats a la pestanya Security
steps:
- uses: actions/checkout@v4
with: { fetch-depth: 0 } # 2 · gitleaks necessita l historial
- name: Secrets filtrats
uses: gitleaks/gitleaks-action@v2 # 3
env: { GITLEAKS_CONFIG: .gitleaks.toml }
- uses: github/codeql-action/init@v3 # 4 · SAST
with: { languages: javascript-typescript, config-file: .github/codeql/config.yml }
- uses: github/codeql-action/analyze@v3
- name: Dependencies vulnerables (SCA)
run: npm audit --audit-level=high --omit=dev # 5
- name: Escanejar la imatge
uses: aquasecurity/[email protected] # 6
with:
image-ref: reservalia/api:${{ github.sha }}
severity: 'CRITICAL,HIGH'
ignore-unfixed: true # 7
exit-code: '1'
trivyignores: .trivyignoresecurity-events: writepermet que les troballes apareguin a la pestanya Security del repositori en lloc de perdre's en un log. Una troballa que només existeix a la sortida d'un job és una troballa que ningú no llegirà dues vegades.fetch-depth: 0porta l'historial complet. Per defecte el checkout és superficial i gitleaks només veuria l'últim commit, amb la qual cosa un secret compromès fa vuit mesos passaria desapercebut.- gitleaks busca patrons de credencials. A
.gitleaks.tomles declaren les excepcions justificades —una clau d'exemple a la documentació, un token d'un fixture— amb un comentari que expliqui per què no són reals. - CodeQL és el SAST de GitHub. Analitza el flux de dades entre les entrades del programa i les operacions perilloses, així que detecta que un paràmetre de
req.queryacaba concatenat en una consulta SQL encara que passi per tres funcions intermèdies. npm auditamb--audit-level=highnomés falla davant del que és greu, i--omit=devexclou les eines de desenvolupament: una denegació de servei a l'executor de proves no és un risc de producció i, si bloqueja el merge, l'equip aprendrà a saltar-se la comprovació.- Trivy analitza la imatge que acaba de construir el job
build: paquets del sistema base, llibreries de l'intèrpret i dependències empaquetades. És un escaneig diferent del d'npm auditperquè cobreix el que hi ha a sota del teu codi. ignore-unfixed: trueés l'ajust que fa l'eina utilitzable: amaga les vulnerabilitats sense pedaç disponible. Sense això, la primera execució retorna desenes de troballes sobre les quals no es pot fer res, i la reacció natural de l'equip és deixar de mirar.
La primera execució sobre Reservalia dona el resultat habitual: 1 secret a l'historial —una DATABASE_URL de staging compromesa el 2024 i "esborrada" l'endemà—, 3 troballes altes de CodeQL, 9 vulnerabilitats de dependències de les quals 2 són altes, i 31 de la imatge base, 26 d'elles sense pedaç. És un quadre perfectament normal, i la reacció correcta no és bloquejar el pipeline fins a arreglar-ho tot, sinó la de l'apartat següent.
- La política de severitats: què trenca la build i què obre un tiquet
Aquí és on la majoria d'equips fracassen, sempre de la mateixa manera: s'activa tot en bloquejant, el pipeline es posa vermell per coses que ningú no pot arreglar avui, algú hi afegeix continue-on-error: true "temporalment", i sis mesos després el job de seguretat fa mig any que és en groc sense que ningú el miri. Una alerta que s'ignora és pitjor que no tenir-la, perquè a més dona sensació de cobertura.
| Severitat | Efecte | Termini | Qui |
|---|---|---|---|
| Secret detectat | Bloqueja sempre, sense excepció | Immediat: rotar la credencial | Nuria |
| Crítica amb pedaç disponible | Bloqueja el merge | Immediat | L'autor del PR |
| Alta amb pedaç, en codi nou | Bloqueja el merge | Immediat | L'autor del PR |
Alta heretada (ja era a main) |
Informa i obre tiquet | 30 dies | Torn de dependències |
| Mitjana | Informa | 90 dies o en tocar aquella zona | Torn de dependències |
| Baixa o sense pedaç disponible | Es registra, no notifica | Revisió trimestral | Marta |
Tres principis sostenen la taula. La distinció entre nou i heretat és la mateixa del quality gate de SonarQube de la 02-05: no pots exigir a un PR de vint línies que resolgui els 1.847 avisos anteriors, però sí que no n'afegeixi cap de nou; amb això el deute deixa de créixer i es redueix sol a mesura que es toca el codi. Els secrets són l'única categoria sense matisos: una credencial compromesa ho està encara que el commit s'esborri, així que no hi ha cap "ho arreglo la setmana que ve". I cada severitat té propietari i termini: una troballa sense propietari no és una tasca, és un avís.
Un matís sobre les excepcions, perquè n'hi haurà. Quan una vulnerabilitat no aplica —la ruta no és assolible, la llibreria només es fa servir en un script de desenvolupament—, es documenta amb motiu i data de caducitat:
# .trivyignore
# CVE-2024-XXXXX · openssl del sistema base. No assolible: la API no obre
# connexions TLS sortints. Revisar l 1-10-2026 o en canviar d imatge base.
CVE-2024-XXXXXUna exclusió sense data ni motiu és indistingible d'un descuit, i al cap de dos anys ningú no sap si continua sent certa.
- Secrets: on viuen i per què OIDC era una decisió de seguretat
A la 03-02 vam triar OIDC i Secrets Manager per comoditat operativa. Ara toca justificar-ho com el que era.
| On | Qui el pot llegir | Rotació | Què passa si es filtra |
|---|---|---|---|
| Variable d'entorn del repositori | Qualsevol que pugui executar un workflow | Manual | Val fins que algú la canviï |
| Secret de repositori a GitHub | Qualsevol workflow del repositori | Manual | Igual, però emmascarat als logs |
Secret d'entorn (prod) |
Només jobs amb environment: prod |
Manual | Limitat a aquell entorn |
| OIDC (sense secret) | Només qui compleixi la condició del rol | No aplica: no hi ha secret | Res per filtrar |
| Secrets Manager | Només la tasca en execució | Sense desplegar | Queda auditat a CloudTrail |
La fila que més sorprèn és la primera. Un secret de repositori sense àmbit d'entorn està disponible per a qualsevol workflow del repositori, inclòs un que algú afegeixi en un pull request: n'hi ha prou amb un workflow de tres línies que imprimeixi el valor codificat en base64 —l'emmascarament de GitHub amaga el literal, no una transformació— per endur-se'l. Per això els secrets de producció de Reservalia viuen només a l'entorn prod, que a més exigeix aprovació humana.
I per això OIDC és qualitativament diferent: no és "un secret més ben guardat", és l'absència de secret. GitHub emet un token de vida curta que afirma qui és el workflow, i AWS decideix si aquella afirmació encaixa amb la condició del rol. No hi ha res per rotar, res per filtrar i res que caduqui malament. La regla que se'n deriva: prefereix identitat federada a credencials; si no és possible, secrets d'àmbit estret; i si tampoc, almenys rota amb calendari. Sobre la rotació: un secret que no es rota mai té exposició acumulada total —va passar per totes les màquines i totes les persones que han tocat el sistema des que existeix—. Reservalia rota trimestralment el que queda (el token de la passarel·la de pagaments, la clau del proveïdor d'SMS) i assaja la rotació igual que assaja el rollback, perquè una rotació que ningú no ha provat falla justament el dia que s'ha de fer de pressa.
- Quan un secret es filtra
Aquest apartat importa perquè l'ordre dels passos determina el dany, i l'error més comú és començar pel pas equivocat.
- Rotar la credencial. Primer, i abans que res. Generar-ne una de nova, desplegar-la, invalidar la vella. Mentre la credencial continuï sent vàlida, tota la resta és cosmètic.
- Revisar què s'hi va fer. CloudTrail, logs del proveïdor, accessos anòmals. Assumir que es va fer servir, no que no.
- Després, netejar l'historial de git si escau. És el més laboriós —reescriure l'historial trenca els clons de tothom— i el menys urgent, perquè el dany ja està fet.
- Anotar-ho com a incident, amb causa i mesura preventiva.
L'error clàssic és fer el 3 primer: esborrar el commit, respirar alleujat i no rotar. Dona la sensació d'haver resolt el problema mentre la clau continua funcionant i probablement ja és al clon d'algú o en un índex automàtic. La regla, sense excepcions: un secret que ha estat en un repositori està compromès, encara que el repositori sigui privat i encara que el commit s'hagi esborrat. A Reservalia, la DATABASE_URL de staging de l'apartat 3 es tracta així: es rota la contrasenya d'RDS aquell mateix dia, es revisa CloudTrail buscant connexions des de fora de la VPC, i es decideix no reescriure l'historial —era staging, la base no era accessible des d'internet, i reescriure dos anys d'història té el seu propi cost—, deixant-ho documentat a SECURITY.md. La decisió raonada i escrita forma part de la resposta.
- Mínim privilegi en els permisos del pipeline
Tot job hauria de tenir exactament els permisos que necessita i ni un més, en dos plans: els del token de GitHub i els del rol d'AWS.
# .github/workflows/ci.yml — capcalera
permissions:
contents: read # 1 · valor per defecte per a TOT el workflow
jobs:
publicar:
permissions:
contents: read
id-token: write # 2 · nomes aquest job pot demanar el token OIDC- Declarar
permissionsa nivell de workflow és el primer que cal fer en un repositori existent. El valor per defecte d'una organització antiga pot serwrite-all, cosa que significa que un script compromès en qualsevol job pot empènyer amain, tancar issues o publicar releases. id-token: writenomés on cal, perquè és el permís que permet demanar el token OIDC i, amb ell, assumir rols d'AWS. Un job de lint no el necessita.
Al costat d'AWS regeix el mateix principi sobre els rols ja creats: AWS_ROLE_CI només necessita empènyer a un repositori d'ECR —no llegir secrets, no tocar ECS—; reservalia-deploy-prod necessita actualitzar el servei de prod i res de dev; i reservalia-terraform-plan és un rol de només lectura, diferent de reservalia-terraform-apply, que és justament el que permet executar el plan a cada pull request sense por.
La pregunta que ordena l'apartat sencer: si aquest job estigués compromès, fins on arribaria? Si la resposta és "fins a producció" i el job és el que executa les proves del PR d'un desconegut, hi ha un problema de disseny i no de configuració. Aquesta és la raó profunda que ci.yml i cd.yml siguin dos fitxers: la frontera de privilegis de la 04-01.
- Codi de tercers: fixar accions per SHA i el risc de
pull_request_target
pull_request_targetuses: actions/checkout@v4 significa "executa el que sigui que hi hagi avui a l'etiqueta v4". Una etiqueta de git és mòbil: qui controli el repositori de l'acció pot reapuntar-la a un altre commit, i el teu pipeline executarà aquell codi amb els seus permisos, sense que canviï ni una sola línia del teu repositori.
# Fragil: l etiqueta es pot moure
- uses: algu/accio-util@v3
# Robust: el SHA es immutable
- uses: algu/accio-util@8f4b7c2e1d09a6f35b2c0e7a91d4f8b6c3a2e5d1 # v3.2.1El SHA és una empremta criptogràfica del contingut i no es pot reapuntar. El comentari amb la versió llegible és imprescindible perquè el pull request de Dependabot que l'actualitzi signifiqui alguna cosa per a un humà, i amb l'entrada github-actions del dependabot.yml de la 04-02 aquells SHA es mantenen al dia automàticament. Reservalia aplica la regla per capes, que és el que és pragmàtic: accions de tercers, sempre per SHA; accions oficials de GitHub i d'AWS, per etiqueta major, acceptant el risc de manera conscient a canvi de menys soroll.
pull_request_target mereix paràgraf propi perquè és el parany més perillós de GitHub Actions. El disparador normal, pull_request, executa el workflow sense secrets quan el PR ve d'un fork: és el comportament segur per defecte. pull_request_target existeix per als casos en què un workflow necessita secrets en processar PR externs —etiquetar, comentar—, i funciona executant el workflow de la branca base però amb accés complet als secrets. La fallada apareix tan bon punt algú hi afegeix un checkout del codi del PR:
on: pull_request_target # ← amb secrets disponibles
jobs:
perillos:
steps:
- uses: actions/checkout@v4
with: { ref: '${{ github.event.pull_request.head.sha }}' } # ← codi de l atacant
- run: npm ci # ← ho executa amb secretsUn npm ci executa els scripts postinstall del package.json del PR, així que qualsevol pot obrir un pull request des d'un fork amb un postinstall que enviï tots els secrets de l'entorn a un servidor propi. Regla: no combinis pull_request_target amb un checkout del codi del PR. Si necessites totes dues coses, separa-les en dos workflows: un sense privilegis que construeix i desa un artefacte, i un altre amb privilegis que només consumeix aquell artefacte sense executar-lo. Reservalia no fa servir pull_request_target enlloc, que és la resposta més simple.
- Protecció de branques, entorns i registre d'auditoria
Les regles de la 02-07 —revisió obligatòria, checks en verd, CODEOWNERS, sense force-push, inclosos els administradors— deixen de ser higiene de procés i passen a ser controls de seguretat tan bon punt es veu el pipeline com un camí a producció: si main es pot modificar sense revisió, tota la resta sobra, perquè qui modifiqui main modifica també el workflow que desplega. Tres controls addicionals que Reservalia afegeix ara:
| Control | Què impedeix | Cost |
|---|---|---|
| Signar els commits (GPG o SSH) | Suplantar l'autoria d'un commit | Una configuració per persona |
CODEOWNERS sobre .github/ |
Que un canvi de pipeline es fusioni sense que el vegi la Nuria | Cap: ja existia |
| Aprovació per a PR de col·laboradors externs | Executar workflows de desconeguts sense mirar | Un clic per PR |
I una capacitat que es descobreix tard i val molt: el registre d'auditoria. GitHub desa qui va canviar un secret, qui va aprovar un desplegament a prod, qui va modificar una regla de protecció i qui va executar rollback.yml; AWS desa a CloudTrail cada crida a l'API amb la identitat que la va fer, inclosos els AssumeRoleWithWebIdentity d'OIDC. Val la pena mirar-los abans de necessitar-los, per dues raons: per saber que existeixen i què contenen, i per descobrir en fred coses com ara que un rol que es creia eliminat es continua fent servir cada nit.
- Cadena de subministrament: SBOM, signatura i procedència
Les tres preguntes que un equip ha de poder respondre sobre qualsevol artefacte en producció són: què conté, qui el va produir i com es va produir. Un mecanisme per a cadascuna.
SBOM (Software Bill of Materials): la llista completa de components d'un artefacte en un format estàndard (SPDX o CycloneDX). Respon què conté, i el seu valor real es veu el dia que apareix una vulnerabilitat greu en una llibreria: en lloc d'investigar durant hores, es consulta el SBOM de cada versió desplegada i en dos minuts se sap si afecta i des de quan.
# infra/scripts/generar-sbom.sh
syft "reservalia/api@${DIGEST}" -o spdx-json > "sbom-${SHA}.spdx.json" # 1
cosign attach sbom --sbom "sbom-${SHA}.spdx.json" "reservalia/api@${DIGEST}" # 2- Syft inspecciona la imatge ja construïda i ho llista tot: paquets npm, paquets del sistema base, versions i llicències. Es genera sobre l'artefacte, no sobre el codi font, perquè el que importa és el que es va desplegar de debò.
- S'adjunta a l'artefacte al registre en lloc de desar-se en un calaix: així el SBOM viatja amb la imatge i es recupera per digest anys després.
Signatura amb cosign: respon qui el va produir. En mode keyless aprofita el mateix OIDC de l'apartat 5, de manera que tampoc no hi ha clau privada per custodiar i la identitat del signant és el mateix workflow.
- uses: sigstore/cosign-installer@v3
- run: |
cosign sign --yes "${ECR}/reservalia/api@${DIGEST}"
cosign verify --certificate-identity-regexp '.*reservalia/reservalia.*' \
--certificate-oidc-issuer https://token.actions.githubusercontent.com \
"${ECR}/reservalia/api@${DIGEST}"Signar sense verificar no serveix de res: la verificació ha de passar abans de desplegar, dins de cd.yml, perquè una imatge que aparegui a ECR sense signatura vàlida no arribi mai a producció. És l'equivalent criptogràfic del smoke test: comprovar que el que desplegaràs és el que creus.
Atestacions de procedència: responen com es va produir. Són un document signat que declara de quin commit, amb quin workflow i en quin moment es va construir l'artefacte. Amb això la cadena queda tancada: del digest s'arriba al commit, del commit al pull request, i del pull request a qui el va revisar. SLSA és el marc que ordena tot això en nivells i serveix com a full de ruta:
| Nivell | Què exigeix | Reservalia |
|---|---|---|
| 1 | Build automatitzada que genera procedència | ✅ des del mòdul 2 |
| 2 | Servei de build allotjat i procedència signada | ✅ en afegir cosign |
| 3 | Build aïllada i no falsificable, font verificada | ⚠️ requereix endurir permisos i aïllar |
| 4 | Revisió de dues persones i build hermètica i reproduïble | ❌ objectiu llunyà |
El que és raonable per a un equip com el de Reservalia és arribar al nivell 2 i quedar-s'hi de manera conscient, deixant el 3 documentat com a objectiu. Pujar de nivell costa, i un nivell assolit a mitges que ningú no verifica no aporta res.
Errors Comuns i Consells
Error 1: activar tots els escàners en mode bloquejant el primer dia. El pipeline es posa vermell per coses sense solució, algú hi afegeix continue-on-error i la seguretat desapareix del procés sense que ningú ho decideixi. Error 2: no distingir troballes noves d'heretades, que és la manera de garantir que el deute no baixi mai.
Error 3: esborrar el commit del secret i no rotar la credencial. El commit és el de menys: la clau continua sent vàlida. Error 4: desar secrets de producció com a secrets de repositori en lloc de secrets d'entorn, amb la qual cosa queden a l'abast de qualsevol workflow, inclòs un d'obert en un PR.
Error 5: fer servir accions de tercers per etiqueta. Una etiqueta es pot moure; un SHA no. Error 6: combinar pull_request_target amb checkout del codi del PR, que és lliurar els secrets a qualsevol que sàpiga escriure un postinstall.
Error 7: permissions: write-all heretat de l'organització, amb la qual cosa un job de lint pot empènyer a main. Error 8: signar artefactes i no verificar la signatura abans de desplegar: una cerimònia sense efecte.
Consell 1: comença en mode informatiu i bloqueja per fases, categoria a categoria, quan el soroll estigui sota control. Consell 2: posa data i motiu a cada exclusió, o d'aquí a un any ningú no sabrà si continua sent vàlida. Consell 3: escriu un SECURITY.md amb qui cal avisar, què es fa davant d'un secret filtrat i què s'espera d'un col·laborador extern. Consell 4: demana una revisió professional abans de qualsevol certificació o compromís contractual; aquest curs et prepara per a aquella conversa, no la substitueix.
Exercicis
Exercici 1
Un equip activa Trivy amb la configuració per defecte. La primera execució retorna 214 vulnerabilitats a la imatge: 3 crítiques, 18 altes i la resta mitjanes i baixes; 180 no tenen pedaç disponible. El pipeline queda bloquejat i ningú no pot fusionar. Proposa un pla concret per a les quatre setmanes vinents.
Exercici 2
Revisa aquest workflow i enumera tots els problemes de seguretat que hi trobis, ordenats per gravetat, indicant la correcció de cadascun:
on: pull_request_target
permissions: write-all
jobs:
validar:
steps:
- uses: actions/checkout@v4
with: { ref: '${{ github.event.pull_request.head.sha }}' }
- uses: qualsevol/setup-tool@main
- run: npm ci && npm test
env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_KEY }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET }}Exercici 3
Apareix una vulnerabilitat crítica en una llibreria molt estesa. La Marta pregunta: "estem afectats, des de quan i quines versions tenim desplegades?". Explica com es respon amb el que s'ha construït en aquesta lliçó i en l'anterior, i què faltaria si Reservalia no hagués generat SBOM.
Solucions
Solució 1. L'error de partida és haver activat en bloquejant una eina el soroll de la qual no es coneixia. Pla per setmanes:
Setmana 1 — deixar de bloquejar sense deixar de mirar. Posar l'escaneig en informatiu (exit-code: '0'), activar ignore-unfixed: true i limitar a CRITICAL,HIGH. De 214 troballes es passa a unes 20 d'accionables: les 180 sense pedaç surten del soroll diari i les mitjanes i baixes queden registrades per a la revisió trimestral. L'equip torna a poder fusionar, que és la condició per poder arreglar res.
Setmana 2 — el cop de més rendiment. Actualitzar la imatge base a l'última versió de pedaç de node:20.11.0-bookworm-slim. Bona part de les vulnerabilitats del sistema base desapareix amb aquella sola línia, sense tocar codi. Reconstruir i tornar a mesurar.
Setmana 3 — les crítiques i altes restants, una a una. Per a cadascuna: té pedaç? Actualitzar. No en té però no és assolible? A .trivyignore amb motiu i data. No té pedaç i és assolible? Tiquet amb propietari i mitigació provisional.
Setmana 4 — tancar la porta. Activar el bloqueig només per a crítiques i altes amb pedaç disponible que siguin noves respecte de main, seguint la política de l'apartat 4, i programar l'escaneig complet al pipeline nocturn per detectar CVE publicades sobre codi que no ha canviat. La lliçó de fons: primer es redueix el soroll, després es bloqueja; a l'inrevés, l'eina es desactiva sola.
Solució 2. Per gravetat:
pull_request_targetamb checkout del codi del PR i execució d'npm ci(crític). Qualsevol pot obrir un PR des d'un fork amb unpostinstallque exfiltri tot l'entorn. Correcció: canviar apull_request; si de debò calen privilegis, separar en dos workflows i no executar mai el codi del PR amb secrets al davant.- Credencials estàtiques d'AWS exposades a aquell codi (crític). No caduquen i queden en mans de l'atacant indefinidament. Correcció: eliminar-les i fer servir OIDC; i en un job de validació de PR, no donar accés a AWS en absolut.
permissions: write-all(alt). El token pot empènyer amain, publicar releases i modificar el repositori. Correcció:permissions: { contents: read }a la capçalera i afegir només l'imprescindible per job.qualsevol/setup-tool@main(alt). Referenciar una branca d'un tercer és executar el que aquell repositori contingui a cada execució. Correcció: fixar per SHA amb comentari de versió.actions/checkout@v4per etiqueta (mitjà). Acceptable en una acció oficial; en un entorn estricte, també per SHA.- Falta
timeout-minutes(baix, però real): un job compromès pot minar criptomonedes durant sis hores abans que ningú ho noti.
Solució 3. Amb el que s'ha construït, la resposta surt en minuts i en tres passos. (1) Estem afectats? Es consulta el SBOM de les imatges desplegades —adjunt a cada imatge a ECR i localitzable per digest— i es busca la llibreria; com que el SBOM es va generar sobre l'artefacte, inclou les transitives i no només les declarades. En local, npm why <llibreria> dona la cadena que la introdueix. (2) Què hi ha desplegat? L'endpoint /version de cada entorn dona el commit exacte en producció, i la taula desplegaments de la 03-06 dona l'historial complet. (3) Des de quan? Creuant el primer commit el SBOM del qual conté la versió vulnerable amb la data del seu desplegament a prod s'obté la finestra d'exposició, que és la dada que caldrà per a qualsevol comunicació a clients.
Sense SBOM, els passos 1 i 3 es tornen feina manual i poc fiable: caldria reconstruir cada versió antiga —si és que les seves dependències continuen disponibles— o instal·lar a partir del lockfile de cada commit i inspeccionar-ne el resultat, amb hores de feina i cap garantia sobre el que hi havia dins de la imatge base, que el lockfile no descriu. Aquella diferència —minuts davant d'un dia, certesa davant d'estimació— és l'argument sencer a favor del SBOM, i només s'aprecia el dia que cal; per això es genera abans de necessitar-lo.
Conclusió
El pipeline ha deixat de ser un punt cec. Reservalia entén ara que el seu sistema de lliurament és infraestructura crítica: té credencials de producció, executa codi de tercers i publica els artefactes en què tothom confia. Sobre la primera meitat del problema —el programari que passa pel pipeline— té un job seguretat a ci.yml que corre en paral·lel i en dos minuts executa gitleaks sobre l'historial complet, CodeQL com a SAST, npm audit com a SCA i Trivy sobre la imatge, amb l'escaneig profund i el DAST desplaçats al pipeline nocturn. I, més important que les eines, té una política de severitats que distingeix el que és nou del que és heretat, dona propietari i termini a cada categoria, tracta els secrets com l'única urgència sense matisos i documenta cada exclusió amb motiu i caducitat: el disseny que evita que l'equip aprengui a ignorar el vermell.
Sobre la segona meitat —el pipeline mateix—, les decisions que semblaven de comoditat són ara decisions de seguretat conscients. OIDC no és un secret més ben guardat, és l'absència de secret; els secrets que queden viuen a l'entorn que els necessita i no al repositori, amb rotació trimestral assajada; els permisos són mínims i explícits, tant al token de GitHub com als rols d'AWS; les accions de tercers es fixen per SHA perquè una etiqueta moguda no canviï el que s'executa; pull_request_target no es fa servir; i les regles de protecció de branca, la signatura de commits i els registres d'auditoria de GitHub i CloudTrail tanquen el cercle. Hi ha a més un procediment escrit per al que de debò passa: rotar primer, investigar després, netejar l'historial al final. La cadena de subministrament queda tancada amb tres respostes —què conté un artefacte (SBOM amb Syft, adjuntat a la imatge), qui el va produir (signatura amb cosign, verificada abans de desplegar) i com es va produir (atestacions de procedència)—, situades al nivell 2 d'SLSA de manera deliberada. I amb l'advertiment intacte: això són controls automatitzats, no un dictamen de seguretat; per a això cal un professional que auditi el sistema real i algú que conegui les obligacions legals de la teva organització.
Amb això hi ha dos dels quatre fronts resolts, i el pipeline fa més coses que mai: cinc jobs, escaneigs, signatures i inventaris. Cosa que reobre el primer front amb més força, perquè cada control afegit costa minuts i en Diego ja ho va dir —"si la CI triga més que anar a buscar un cafè, deixo de mirar-la"—. La lliçó següent, Escalabilitat i Rendiment, mesura on se'n va exactament el temps del pipeline de Reservalia i el retalla amb les palanques ordenades per relació cost/benefici —memòria cau, paral·lelització, execució selectiva i elecció del runner—, sense perdre de vista el risc que les acompanya totes: que una optimització mal feta produeixi un verd fals, un pipeline rapidíssim que ja no comprova el que creiem que comprova.
Curs de CI/CD: Integració i Desplegament Continu
Mòdul 1: Introducció al CI/CD
- Conceptes Bàsics de CI/CD
- Beneficis del CI/CD
- Eines Populars de CI/CD
- El Projecte del Curs: l'Aplicació que Automatitzarem
- Mètriques DORA: Com es Mesura el Lliurament de Programari
Mòdul 2: Integració Contínua (CI)
- Introducció a la Integració Contínua
- Configuració d'un Entorn de CI
- Automatització de la Construcció
- Proves Automatitzades
- Qualitat de Codi i Anàlisi Estàtica
- Artefactes, Versionat i Promoció
- Integració amb el Control de Versions
Mòdul 3: Desplegament Continu (CD)
- Introducció al Desplegament Continu
- Automatització del Desplegament
- Infraestructura com a Codi i Entorns Reproduïbles
- Estratègies de Desplegament
- Feature Flags, Rollback i Recuperació davant Errors
- Monitoratge i Retroalimentació
Mòdul 4: Pràctiques Avançades de CI/CD
- Pipelines de CI/CD
- Gestió de Dependències
- Seguretat en CI/CD
- Escalabilitat i Rendiment
- Pipeline as Code: Plantilles, Reutilització i Proves del Pipeline
- Bases de Dades al Pipeline: Migracions Segures
Mòdul 5: Implementació de CI/CD en Projectes Reals
- Cas d'Estudi: Projecte Web
- Cas d'Estudi: Aplicació Mòbil
- Cas d'Estudi: Microserveis
- Cas d'Estudi: Modernitzar un Projecte Legacy
Mòdul 6: Eines i Tecnologies
- Jenkins
- GitLab CI/CD
- CircleCI
- Travis CI
- Docker i Kubernetes
- GitHub Actions a Fons
- Comparativa i Criteris per Triar Eina
Mòdul 7: Exercicis Pràctics
- Exercici 1: Configuració d'un Pipeline Bàsic
- Exercici 2: Integració de Proves Automatitzades
- Exercici 3: Desplegament en un Entorn de Producció
- Exercici 4: Monitoratge i Retroalimentació
- Exercici 5: Enfortir el Pipeline amb Seguretat i Secrets
- Projecte Final: Pipeline Complet d'Extrem a Extrem
