Dove gira il job: ospitato da GitHub o macchina tua
Il primo bivio da amministratore non è tecnico ma di responsabilità. Un GitHub-hosted runner è una macchina virtuale nuova per ogni job, fornita e mantenuta da GitHub: patch del sistema operativo, aggiornamento del software preinstallato e ciclo di vita della macchina non sono affari tuoi. L’unica eccezione documentata riguarda i runner a CPU singola, che girano dentro un container su una VM condivisa anziché su una VM dedicata. Le immagini disponibili coprono Ubuntu Linux, Windows e macOS, e sui piani Team ed Enterprise Cloud si aggiungono i larger runners, con più core o con GPU.
Un self-hosted runner ribalta lo schema: ottieni il controllo su hardware, sistema operativo e toolchain, ma diventi tu il responsabile della manutenzione e della sicurezza della macchina. Il punto che fa davvero differenza in un audit è che il self-hosted non è progettato per partire da un’istanza pulita a ogni esecuzione: file, cache, credenziali residue e modifiche lasciate da un job possono sopravvivere al job successivo. Da qui la raccomandazione esplicita della documentazione di usare i self-hosted runner solo con repository privati, perché un fork di un repository pubblico può far eseguire codice arbitrario sulla tua macchina semplicemente aprendo una pull request. Se non puoi evitarlo, le contromisure sono le workflow execution protections nella sezione Policies delle impostazioni Actions, l’approvazione manuale delle esecuzioni provenienti da fork da parte di chi ha accesso in scrittura, e i runner ephemeral, registrati con l’opzione --ephemeral, che si de-registrano dopo un solo job e ricreano l’ambiente da zero.
Sul piano di rete, il self-hosted runner apre solo connessioni HTTPS in uscita sulla porta 443 e interroga GitHub in polling: non serve alcuna porta in ingresso, e questo è spesso l’argomento decisivo con la sicurezza di rete. Sui quattro scenari di deployment il modello non cambia per GHEC con account personali, GHEC con Enterprise Managed Users o GHEC con data residency, che poggia su EMU: i runner ospitati sono un servizio del cloud. Su GHES, invece, ciò che l’istanza può offrire dipende dalla versione e dalla configurazione, quindi verifica la documentazione della release che hai in produzione prima di promettere runner ospitati a un team.
Il runner group è il confine di sicurezza
I runner group raccolgono insiemi di runner e disegnano attorno a essi un perimetro di autorizzazione. Si governano a livello di organizzazione, da Settings > Actions > Runner groups > New runner group, e a livello di enterprise, da Policies > Actions > Runner groups > New runner group. Valgono sia per i self-hosted sia per i larger runner ospitati da GitHub: anche questi ultimi si distribuiscono ai repository attraverso i gruppi.
Due vincoli sono candidati naturali a diventare domande d’esame. Il primo: un runner appartiene a un solo gruppo alla volta, e se alla registrazione non ne indichi uno finisce nel gruppo Default. Spostarlo si fa dalla scheda del runner, con il selettore Runner group e la voce di spostamento verso un altro gruppo. Il secondo: per rimuovere un gruppo devi prima spostare o rimuovere tutti i runner che contiene, e solo a quel punto la voce Remove group porta a termine l’operazione. Chi si aspetta che i runner “ricadano” automaticamente nel gruppo Default sbaglia: la piattaforma non li ricolloca al posto tuo.
Chi può usare il gruppo
Creato il gruppo, la domanda successiva è chi lo raggiunge. Su un gruppo di organizzazione il controllo è Repository access: la documentazione dei self-hosted indica come comportamento predefinito che soltanto i repository privati possano usare i runner del gruppo, con possibilità di sovrascrivere questa impostazione, mentre passando a Selected repositories scegli i singoli repository con l’icona a ingranaggio. Su un gruppo di enterprise il controllo è Organization access, con Selected organizations per elencare le organizzazioni ammesse; l’organizzazione, a sua volta, decide quali propri repository possono usarlo. Attenzione alla trappola: quando un gruppo è condiviso dall’enterprise, l’amministratore dell’organizzazione non può sovrascrivere l’impostazione ereditata.
C’è poi un livello più fine, Workflow access con Selected workflows, che limita l’uso del gruppo a workflow specifici indicati con percorso completo e riferimento fissato, nella forma octo-org/octo-repo/.github/workflows/build.yml@refs/tags/v2. Solo i job definiti direttamente nei workflow selezionati ottengono accesso. Non dare per scontati i default di accesso: variano tra le pagine dedicate ai self-hosted e quelle dedicate ai larger runner, quindi la risposta buona in produzione è aprire il gruppo e leggere che cosa dice la console.
Le label instradano, il gruppo autorizza
Ogni self-hosted runner nasce con label predefinite che descrivono se stesso, il sistema operativo e l’architettura, alle quali aggiungi label personalizzate dalla scheda del runner, con l’icona a ingranaggio e il campo di ricerca o creazione, oppure alla registrazione:
./config.sh --url <REPOSITORY_URL> --token <REGISTRATION_TOKEN> --labels gpu,x64,linux
Le label non distinguono maiuscole e minuscole, non si cancellano a mano e quelle rimaste inutilizzate decadono da sole; su un runner già configurato si aggiungono dalla UI o via REST API, non rilanciando lo script di configurazione. Il workflow le usa per scegliere la macchina, eventualmente insieme al gruppo:
runs-on:
group: org/my-group
labels: [ self-hosted, label-1 ]
Un gruppo di enterprise si indirizza con il prefisso corrispondente, ent/my-group. La confusione da evitare, e che l’esame ama, è proprio questa: la label è instradamento, non autorizzazione. Chi scrive un workflow può inventarsi qualsiasi label, ma non raggiungerà mai un gruppo che non gli è stato concesso; e togliere una label a un runner non protegge nulla, perché il vero controllo resta l’accesso al gruppo.