Proteggere i runner CI/CD non è un espediente di sicurezza, ma un compito operativo sostenibile: i runner prelevano codice sorgente, costruiscono artefatti e spesso hanno accesso a credenziali, cache e alla rete interna. Per amministratori e operatori è cruciale ridurre la superficie di attacco in modo misurabile e disporre di percorsi chiari per verifiche e rollback. Questa guida fornisce misure concrete, passaggi di verifica e troubleshooting per l’isolamento delle credenziali, un robusto cleanup del workspace e la protezione della catena di build.
Perché i runner sono un vettore d’attacco critico
I runner sono ambienti di esecuzione per job CI/CD (build, test, packaging). Un runner compromesso può:
- esfiltrare secret (token, chiavi SSH, credenziali cloud).
- alterare artefatti o firmarli in modo manipolato.
- eseguire avvelenamento della cache (cache poisoning) e influenzare così le build successive.
- permettere pivoting nella rete interna.
Distinzione importante: trusted-builds (ad es. branch protetti o release) versus untrusted-builds (fork, PR esterni, contributi pubblici). Per gli untrusted-build vale l’assunzione operativa: il job è potenzialmente maligno.
Principi fondamentali: privilegi minimi, isolamento, tracciabilità
Attuate l’indurimento lungo tre obiettivi misurabili:
- Obiettivo di isolamento: nessun job deve poter vedere lo stato di un altro job.
- Obiettivo credenziali: i job ricevono solo le credenziali strettamente necessarie, preferibilmente a breve durata.
- Obiettivo integrità: origine e immutabilità di dipendenze e artefatti devono essere tracciabili.
Questi obiettivi possono essere operacionalizzati, testati e sottoposti ad audit.
Isolamento delle credenziali: implementazione, motivi e insidie tipiche
Isolamento delle credenziali significa: niente token statici e universali. Segmentate le identità per fase di pipeline (Build, Package, Release, Deploy) e per contesto. Utilizzate token a breve durata (TTL in minuti/ore) tramite OIDC, STS (Security Token Service) o lease di HashiCorp Vault. Le credenziali a breve durata limitano il blast radius in caso di compromissione e semplificano il revocation.
Opzioni tecniche e loro impiego
OIDC (OpenID Connect) è un protocollo per l’emissione di token a breve durata; collega i sistemi CI con Cloud-IAM o Vault senza secret persistenti. KMS/HSM (Key Management Service / Hardware Security Module) conserva le chiavi private al di fuori dei runner. Vault offre secret dinamici (es. credenziali di database su base lease). Ogni opzione impone requisiti operativi: OIDC richiede token-claim affidabili, Vault necessita di alta disponibilità e policy di accesso ben definite.
Esempio: Vault-Policy per il push di pacchetti
# Vault policy (HCL) - erlaubt Token zum Schreiben in ein internes Artefakt-Repo
path "secret/data/ci/artifacts/*" {
capabilities = ["create", "update", "read"]
}
Perché funziona: Vault emette token a durata limitata e la policy restringe i percorsi. Quando fallisce: se i runner persistono il token di Vault (ad es. nella cache) o se le policy sono definite troppo ampie.
Firma limitata da policy tramite servizio di firma
Le chiavi private di firma non devono risiedere su runner ordinari. Un servizio di firma (un servizio interno che utilizza KMS/HSM per firmare) riceve gli hash degli artefatti, verifica le policy (ad es. che la build provenga da un branch protetto) e poi firma. L’operazione di firma richiede log di audit separati e autenticazione rigorosa.
Proteggere i runner CI/CD: isolamento a livello infrastrutturale
Scegliere l’isolamento in base a rischio ed economicità:
- Host-Runner: Veloce, ma solo per job fully-trusted.
- Container-Runner: Buone pRESTazioni, ma solo con rootless/politiche di mount stringenti.
- Ephemeral VM/Instance-Runner: Isolamento massimo; per job VM nuova o snapshot. Costi più alti, sicurezza massima.
I runner effimeri sono particolarmente raccomandati per build untrusted: al termine l’istanza viene distrutta, impedendo la persistenza.
Esempio: Kubernetes-Runner come Ephemeral-Job
apiVersion: batch/v1
kind: Job
metadata:
name: runner-job-{{ .RunID }}
spec:
template:
spec:
securityContext:
runAsUser: 1000
runAsGroup: 1000
fsGroup: 1000
containers:
- name: builder
image: registry.internal/runner-image:stable
securityContext:
allowPrivilegeEscalation: false
capabilities:
drop: ["ALL"]
RESTartPolicy: Never
backoffLimit: 0
Perché aiuta: Kubernetes consente Namespaces, NetworkPolicies e Pod Security Context per limitare. Attenzione: configurazioni RBAC o di volume errate possono compromettere l’isolamento.
Pulizia sicura del workspace: robusta e resistente alle manomissioni
Una pulizia difettosa permette la persistenza. I problemi nascono da handle aperti, filesystem montati, trucchi con symlink e ACL RESTrittive. Una pulizia robusta implementa più strati di protezione: workspace dedicati, normalizzazione della ownership, –one-file-system durante la cancellazione e run di verifica con audit logging.
Linux: Cleanup avanzato incluso controllo degli handle
#!/usr/bin/env bash
set -euo pipefail
JOB_DIR="$1"
RUNNER_UID=1001
RUNNER_GID=1001
# 1) Terminare i processi che operano in JOB_DIR
fuser -k -TERM -m "$JOB_DIR" || true
sleep 1
fuser -k -KILL -m "$JOB_DIR" || true
# 2) Normalizzare ownership e permessi
chown -R "${RUNNER_UID}:${RUNNER_GID}" "$JOB_DIR" 2>/dev/null || true
chmod -R u+rwX,go-rwx "$JOB_DIR" 2>/dev/null || true
# 3) Controllare mountpoint nel jobdir
mountpoints=$(findmnt -n -o TARGET --target "$JOB_DIR" || true)
if [ -n "$mountpoints" ]; then
echo "Found mounts: $mountpoints" >&2
# Option: detach mounts safely or warn and abort
fi
# 4) Eseguire la cancellazione in modo sicuro
rm -rf --one-file-system "$JOB_DIR"
Perché fuser è necessario: handle di file aperti impediscono la cancellazione; i processi devono essere terminati correttamente. Lo script può fallire se un job ha avviato processi di sistema che lo script non può terminare — per questo logging e livelli di allerta sono importanti.
Windows: Handle, piano di reboot e interazione con antivirus
Su Windows i lock da parte di servizi e AV sono comuni. Integrare il cleanup con controlli degli handle (Sysinternals Handle/Process Explorer), retry e un percorso di reboot pianificato nel caso la cancellazione non sia possibile. I soli retry non bastano sempre per lock ostinati — documentate una procedura di rollback.
Sicurezza della catena di build: sorgenti pacchetti, cache e firma
Proteggete la catena di build isolando le sorgenti dei pacchetti, utilizzando proxy con allow-list, cache controllate e policy sugli artefatti immutabili.
Mirror interni e RESTrizione dell’egress
Oltre alle allow-list per gli endpoint dei pacchetti, i runner dovrebbero avere egress limitato alle destinazioni definite: VCS, mirror, repository artefatti, KMS/servizio di firma. Le Egress-ACL riducono le possibilità di esfiltrazione/connessioni verso infrastrutture di command-and-control. Testate le modifiche inizialmente in monitor-mode (solo logging) prima di bloccare.
Rafforzamento del repository artefatti
Impostate write-policy (non-overwrite), retention-policy e require-signed-artifact-flag quando il vostro repository le supporta. I log di audit devono mostrare chi e quando ha pubblicato un artefatto. Esempio di regola: „Le release possono essere pubblicate solo dopo la firma da parte del servizio di firma“.
Strategie di cache contro il poisoning
Separare le cache per zone di fiducia e per progetto. Evitare cache condivise scrivibili per job non attendibili. Utilizzare invalidazione TTL e hash di cache registrati per rilevare il poisoning.
Indurimento di rete e host: misure concrete
Trattate i Runner come componenti di infrastruttura critica:
- propri segmenti di rete o VLAN
- Egress-ACLs definite
- firewall host e processo di patching
- monitoraggio e log centralizzati
Esempio: regola iptables semplice per l'egress (VM-Runner)
# Erlaube nur DNS, HTTP(S) zu mirror.example und signing.example
iptables -A OUTPUT -m owner --uid-owner runner -p udp --dport 53 -j ACCEPT
iptables -A OUTPUT -m owner --uid-owner runner -p tcp -d mirror.example --dport 443 -j ACCEPT
iptables -A OUTPUT -m owner --uid-owner runner -p tcp -d signing.example --dport 443 -j ACCEPT
iptables -A OUTPUT -m owner --uid-owner runner -j DROP
Perché aiuta: anche se un job tenta di stabilire comunicazioni dannose, l'egress rimane limitato. Attenzione: DNS-over-HTTPS e altri percorsi di elusione possono aggirare questo approccio — testare e monitorare.
Testing, audit e validazione
Garantire le misure con test automatizzati e audit:
- Job di audit regolari che provano a eseguire, da un Runner, azioni non permesse (solo in una rete di test isolata!).
- Audit post-job: verificare la persistenza del workspace, handle aperti, processi attivi.
- Analisi dei log: ricerca di esfiltrazione di token o connessioni di egress anomale.
Esempio di script di test: Post-Cleanup-Validation
#!/usr/bin/env bash
# Prüft, ob ein Job-Verzeichnis nach Cleanup noch existiert
JOB_DIR="$1"
if [ -e "$JOB_DIR" ]; then
echo "CLEANUP FAILED: $JOB_DIR still exists" >&2
ls -la "$JOB_DIR" >&2
exit 2
fi
# Prüfe auf aktive Prozesse des Runner-User
if pgrep -u runner >/dev/null; then
echo "ACTIVE PROCESSES FOUND" >&2
ps -u runner -o pid,cmd
exit 3
fi
exit 0
Insidie tipiche e risoluzione dei problemi
1) Segreti inseriti per errore nel log di build
Causa: stampe di debug o mancato mascheramento. Misure: abilitare il mascheramento dei log CI, non esporre variabili sensibili in chiaro. Verificare automaticamente i log alla ricerca di pattern (API key, bearer token) con un job di scanner.
2) Uso improprio di container privilegiati
Causa: container privilegiati o inoltro del socket Docker. Misure: build senza privilegi di root (rootless), evitare mount del socket dell'host, usare Buildkit/remote daemon. Test: il job prova a creare nuovi container sull'host (solo in ambiente di test!).
3) Chiavi di firma sui Runner
Causa: praticità invece di sicurezza — i team posizionano le chiavi localmente. Misure: centralizzare i servizi di firma, conservare le chiavi solo in HSM/KMS. Contromisura: rotazione immediata e invalidamento di tutte le chiavi potenzialmente compromesse.
Strategia di rollback e di emergenza
Introdurre le modifiche in modo graduale:
- Modalità monitor: solo logging, nessun blocco.
- Chiusura graduale delle aree di Egress/policy.
- Operare in parallelo pool di Runner vecchi e nuovi; switch tramite feature-flag/route.
- Token Break-Glass per le emergenze con breve durata e audit.
Importante: documenti i passaggi di rollback e li testi regolarmente in una esecuzione di prova isolata.
Checklist pratica: implementazione in 90–180 minuti
- Inventariare i token attivi e verificare i permessi per ogni fase della pipeline.
- Migrare i job non attendibili su pool di runner separati o Ephemeral-VMs.
- Estendere gli script di cleanup: process-kill, chown, chmod, –one-file-system, Post‑Validation.
- Auditare il processo di firma e pianificare la movimentazione delle chiavi in HSM/KMS.
- Creare Egress-ACLs in Monitor-Mode e osservare il traffico.
- Audit-Job: tentativi di eseguire azioni non autorizzate dai runner (in isolamento).
Conclusione
Mettere in sicurezza i runner CI/CD significa coniugare praticabilità con sicurezza misurabile. Date priorità alla separazione delle credenziali per fase della pipeline (con token a breve vita), al cleanup garantito dei workspace (o agli Ephemeral-Runner) e alla firma tramite un servizio di firma o HSM/KMS. Completa il tutto con RESTrizioni di egress, monitoring e audit test regolari. Con un’introduzione graduale, fasi in Monitor-Mode e percorsi di rollback testati, la sicurezza RESTa gestibile e operativamente affidabile.
FAQ
Qual è l’errore principale nel mettere in sicurezza i runner CI/CD?
Un container da solo è sufficiente come isolamento per build non attendibili?
Come mi assicuro che il cleanup del workspace funzioni davvero?
Perché la chiave privata di firma non deve risiedere sul runner?
Come gestire la perdita di pRESTazioni dovuta a un isolamento più marcato?
Quali verifiche dovrebbero essere incluse in un audit-job per i runner?
Mettere in sicurezza i runner CI/CD: operatività, monitoring e integrazione
Le misure tecniche sono efficaci solo quanto il loro esercizio. Pianificate monitoring, metriche e integrazioni già all’introduzione dell’indurimento dei Runner, in modo che la sicurezza rimanga ripetibile e misurabile.
Metriche e alert importanti:
- Cleanup-Failure-Rate: percentuale di job in cui la validazione post-job è fallita. Definire una soglia e generare un allarme prima che i deficit di pulizia diventino persistenti.
- Vault-Lease‑Errors e OIDC-Token‑Failures: indicano problemi con credenziali a breve durata o claim errati.
- Connessioni Egress insolite per pool di Runner: incrementi improvvisi indicano esfiltrazione o tentativi di elusione.
- Richieste di firma per ora e errori di firma: deviazioni possono indicare pipeline compromesse o errori di policy.
Indicazioni per l’integrazione:
- IAM/IdP: Collegare il CI come client OAuth/OIDC agli Identity‑Provider esistenti (es. AD, Okta). In questo modo audit e ciclo di vita degli utenti restano gestibili in modo centrale.
- Secrets-Backends: utilizzare Vault/KMS in modo dinamico; automatizzare il rinnovo dei lease e la rotazione. Documentare le rotazioni di emergenza e testare il percorso di revoca delle chiavi.
- Repository di artefatti: integrare le signing‑policy con attestazioni (metadati di build, SBOM). Questo rende verificabile la provenienza e facilita le analisi di incidente.
Runbook e pratiche di rollback:
- Redigere un runbook conciso: passi per isolare un pool di Runner, rotazione delle chiavi, interruttore di disabilitazione per il servizio di firma e rollback su Runner in warm‑standby.
- Eseguire test di chaos regolari in una zona di test (cleanup-fail, blocco Egress) e valutare i processi operativi rispetto a SLA concreti.
Conclusione: esercizio e integrazione non sono pensieri secondari. Buone metriche, rotazione automatizzata e runbook coordinati rendono la sicurezza CI/CD robusta e gestibile nella pratica quotidiana.
Per questo argomento sono importanti anche la Supply-Chain-Security e la protezione della catena di build. Il contributo inquadra questi aspetti in modo comprensibile e mostra su cosa concentrarsi nella pratica quotidiana.