Corso
Ho visto innumerevoli immagini Docker esposte per errore, con API key hardcoded, password di database e token di autenticazione incorporati nei layer. Queste violazioni di sicurezza spesso si verificano durante il processo di build, quando gli sviluppatori hanno bisogno di un accesso temporaneo a risorse private ma, senza volerlo, inseriscono le credenziali nell’immagine finale.
Il problema è che gli approcci tradizionali alla gestione delle credenziali durante le build, come variabili d’ambiente o build arg, non sono pensati per un uso effimero. Lasciano tracce permanenti nei metadati o nei layer dell’immagine, creando vulnerabilità che persistono a lungo dopo il completamento della build. Nasce quindi un dilemma: come autenticarsi a risorse private durante le build senza compromettere la sicurezza?
I Docker build secrets risolvono questa sfida critica offrendo un meccanismo per usare dati sensibili durante le build delle immagini senza lasciare tracce nell’artefatto finale.
In questo tutorial ti guiderò nell’implementazione efficace dei build secrets, dai concetti di base all’integrazione avanzata in CI/CD, per garantire che le tue build rimangano sicure.
Se sei alle prime armi con Docker, ti consiglio di seguire uno dei nostri corsi, come Introduzione a Docker, Containerizzazione e virtualizzazione con Docker e Kubernetes o Docker intermedio.
Cosa sono i Docker Build Secrets?
Partiamo capendo cosa rende i build secrets diversi da altri approcci alla gestione delle credenziali. I Docker build secrets sono informazioni sensibili effimere rese disponibili durante il processo di build ma mai archiviate nei layer o nel filesystem dell’immagine. Esistono solo in memoria durante specifici step di build e scompaiono al termine di quegli step.
La necessità dei build secrets nasce dai workflow moderni di sviluppo applicativo.
Spesso devi autenticarti a repository di pacchetti privati, clonare repository Git privati, scaricare software con licenza o accedere ad API interne durante le build.
Senza build secrets, gli sviluppatori ricorrono a workaround rischiosi, come inserire credenziali hardcoded nei Dockerfile, passare secret come build arg (che persistono nei metadati dell’immagine) o creare immagini base troppo permissive con credenziali integrate.
I rischi di una gestione inadeguata dei secret sono notevoli. Credenziali esposte possono portare ad accessi non autorizzati ai sistemi di produzione, violazioni dei dati, non conformità e gravi danni reputazionali. Scenari comuni di leakage includono API key per i package manager, chiavi SSH per operazioni Git, token di autenticazione per servizi interni e credenziali di database per migrazioni in fase di build.
Docker BuildKit: le fondamenta dei build secrets sicuri
Prima di passare all’implementazione, devi conoscere BuildKit, il motore di build moderno che rende possibili i secret sicuri. BuildKit è il builder di nuova generazione che ha sostituito il motore classico, offrendo notevoli miglioramenti in performance, caching e sicurezza.
BuildKit usa un modello di esecuzione basato su grafo che traccia le dipendenze tra gli step di build. Questa architettura consente a BuildKit di montare i secret come file system temporanei che esistono solo durante specifiche istruzioni RUN, assicurando che non entrino mai a far parte dei layer dell’immagine.
A differenza delle build Docker classiche, in cui tutto nel contesto di build entra nella cache di build, BuildKit tratta i secret come risorse speciali che bypassano completamente la cache dei layer.
Abilitare BuildKit è semplice. Con Docker 23.0 e versioni successive, BuildKit è il builder predefinito. Per versioni precedenti, imposta la variabile d’ambiente DOCKER_BUILDKIT=1 prima di eseguire i comandi di build:
export DOCKER_BUILDKIT=1
docker build -t myapp .
In alternativa, abilitalo in modo permanente configurando il demone Docker o usando Docker Buildx, che utilizza sempre BuildKit. La differenza chiave tra build classiche e build con BuildKit è che nelle build classiche i secret possono persistere nei layer intermedi se non gestiti con attenzione, mentre BuildKit garantisce che i secret siano effimeri e non vengano mai scritti su disco come parte dell’immagine.
Con le fondamenta sicure di BuildKit in atto, vediamo i diversi tipi di secret che abilita e come implementarli in modo efficace.
Tipi e implementazione dei Docker Build Secrets
BuildKit supporta tre meccanismi principali per gestire i secret durante le build, ciascuno pensato per casi d’uso specifici. Capire quando usare ogni tipo garantisce sicurezza e funzionalità ottimali.
Secret mount: dati sensibili generici
I secret mount offrono l’approccio più flessibile per gestire dati sensibili durante le build. Consentono di montare i secret come file in percorsi specifici durante le istruzioni RUN, rendendo disponibili le credenziali ai comandi di build senza persisterle nei layer.
Il processo prevede due step: passare il secret al comando di build e montarlo nel Dockerfile. Per prima cosa, crea un file di secret in locale o usa una variabile d’ambiente. Poi referenzialo durante la build:
docker build --secret id=api_key,src=./api_key.txt -t myapp .
Nel Dockerfile, monta il secret durante l’istruzione RUN che ne ha bisogno:
RUN --mount=type=secret,id=api_key \
API_KEY=$(cat /run/secrets/api_key) && \
curl -H "Authorization: Bearer $API_KEY" https://api.example.com/package > /app/data.json
Per impostazione predefinita, i secret sono montati in /run/secrets/<id>, ma puoi specificare destinazioni personalizzate usando il parametro target.
Le best practice includono montare i secret solo nelle RUN che ne hanno bisogno, non copiare mai i secret nel filesystem dell’immagine e usare build multi-stage per separare gli stage che richiedono secret dall’immagine finale.
La sorgente può essere un percorso file, oppure usa env per passare secret da variabili d’ambiente: --secret id=token,env=API_TOKEN.
Per montare un secret come variabile d’ambiente invece che come file, usa l’opzione env:
RUN --mount=type=secret,id=db_user,env=DB_USER \
--mount=type=secret,id=db_password,env=DB_PASSWORD \
./setup-database.sh
Puoi usare insieme le opzioni target e env per montare un secret sia come file sia come variabile d’ambiente.
Le best practice includono montare i secret solo nelle RUN specifiche che li richiedono, non copiare mai i secret nel filesystem dell’immagine e usare build multi-stage per separare gli stage che richiedono secret dalle immagini finali.
SSH mount: accesso sicuro a risorse private
Gli SSH mount gestiscono specificamente l’autenticazione basata su SSH, principalmente per accedere a repository Git privati durante le build. Invece di copiare le chiavi SSH nell’immagine (un incubo di sicurezza), gli SSH mount forniscono accesso temporaneo al tuo agent SSH durante la build.
Per usare gli SSH mount, assicurati che l’agent SSH sia in esecuzione in locale con le chiavi richieste caricate. Poi referenzia l’SSH mount nel tuo Dockerfile:
RUN --mount=type=ssh \
git clone git@github.com:myorg/private-repo.git /app
Esegui la build con il forwarding SSH abilitato:
docker build --ssh default -t myapp .
Per host multipli con chiavi diverse, puoi specificare SSH mount con nome:
RUN --mount=type=ssh,id=github \
git clone git@github.com:myorg/repo.git
Poi fornisci chiavi specifiche durante la build:
docker build --ssh github=$HOME/.ssh/github_key -t myapp .
Scoprirai che questo approccio mantiene la sicurezza senza esporre mai le chiavi private nell’immagine, consentendo al contempo l’accesso autenticato ai repository privati durante la build.
Autenticazione Git per contesti remoti
Quando la tua build Docker deve accedere a repository Git privati, sia come contesto di build stesso sia durante il fetch delle dipendenze, ti serve un modo per autenticarti senza incorporare credenziali. Ciò è particolarmente comune quando si effettua la build da un URL di repository privato o si usano istruzioni ADD per recuperare codice privato.
BuildKit fornisce due secret predefiniti per l’autenticazione Git: GIT_AUTH_TOKEN e GIT_AUTH_HEADER. Sono secret "pre-flight" che autenticano il builder prima che qualsiasi istruzione del Dockerfile venga eseguita, proteggendo il fetch iniziale del repository.
Il pattern più comune usa GIT_AUTH_TOKEN per l’autenticazione HTTPS basata su token:
GIT_AUTH_TOKEN=$(cat ~/.github-token) docker build \
--secret id=GIT_AUTH_TOKEN \
https://github.com/myorg/private-repo.git
Questo funziona senza problemi con le istruzioni ADD che recuperano repository privati:
FROM alpine
ADD https://github.com/myorg/private-configs.git /configs
Per ambienti CI/CD effimeri, inietta i token dallo store di secret della tua piattaforma:
- name: Build from private repo
env:
GIT_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
docker build --secret id=GIT_AUTH_TOKEN https://github.com/myorg/app.git
Il secret GIT_AUTH_HEADER offre un’alternativa per schemi di autenticazione personalizzati quando l’autenticazione standard tramite token non è sufficiente.
Come usare i Docker Build Secrets
Ora che abbiamo visto i tipi di build secrets, esploriamo come implementarli efficacemente in scenari reali.
Creare e strutturare i secret per la build
Una corretta preparazione dei secret è cruciale per la sicurezza. Archivia i secret in file fuori dal contesto di build Docker, assicurati che siano aggiunti a .gitignore e .dockerignore e usa permessi restrittivi sui file (600 o 400).
Per più secret, crea una directory dedicata:
mkdir -p .secrets
echo "my-api-key" > .secrets/api_key
chmod 600 .secrets/*
Poi referenziali durante le build:
docker build \
--secret id=api_key,src=.secrets/api_key \
--secret id=db_password,src=.secrets/db_password \
-t myapp .
Per i secret basati su variabili d’ambiente, comuni nelle CI/CD:
docker build \
--secret id=api_key,env=API_KEY \
--secret id=db_pass,env=DB_PASSWORD \
-t myapp .
Integrazione con build multi-stage
Le build multi-stage sono potenti se combinate con i build secrets. Usa i secret negli stage iniziali per recuperare le dipendenze, poi copia solo gli artefatti necessari nello stage finale:
# Build stage - secrets used here
FROM python:3.11 AS builder
WORKDIR /app
COPY requirements.txt .
RUN --mount=type=secret,id=api_key \
API_KEY=$(cat /run/secrets/api_key) && \
curl -H "Authorization: Bearer $API_KEY" https://api.company.com/data.json -o data.json && \
pip install -r requirements.txt
# Final stage - no secrets present
FROM python:3.11-slim
WORKDIR /app
COPY --from=builder /usr/local/lib/python3.11/site-packages /usr/local/lib/python3.11/site-packages
COPY --from=builder /app/data.json ./data.json
COPY . .
CMD ["python", "app.py"]
Questo garantisce che i secret esistano solo durante lo stage builder e non compaiano mai nell’immagine finale.
Le build multi-stage gestiscono elegantemente un singolo secret, ma le applicazioni reali spesso richiedono maggiore complessità.
Vediamo come gestire scenari che coinvolgono più secret o requisiti di build articolati
Gestire più secret e scenari complessi
Le build complesse spesso richiedono più secret nella stessa istruzione RUN. BuildKit supporta il montaggio simultaneo di più secret:
RUN --mount=type=secret,id=api_key \
--mount=type=secret,id=db_password \
API_KEY=$(cat /run/secrets/api_key) && \
DB_PASS=$(cat /run/secrets/db_password) && \
./configure.sh
Per scenari che richiedono secret in più comandi, combina i comandi in una singola RUN per minimizzare i mount dei secret mantenendo al contempo i confini di sicurezza.
Docker Build Secrets: best practice di sicurezza
Un’implementazione corretta dei build secrets richiede il rispetto di pratiche di sicurezza consolidate. Ti condivido i pattern che ho trovato più efficaci per mantenere la sicurezza durante l’intero processo di build.
Prevenire l’esposizione e le perdite di secret
Dopo aver creato un’immagine, verifica che i secret non siano trapelati ispezionando i layer dell’immagine:
docker history myapp:latest
docker save myapp:latest | tar -x
Esamina i layer estratti per rilevare eventuali dati sensibili. Inoltre, distingui tra secret di build (temporanei, usati durante le build) e secret di runtime (necessari all’esecuzione dei container). Non usare mai i build secrets per credenziali di runtime. Usa invece Docker secrets, Kubernetes secrets o variabili d’ambiente iniettate a runtime.
Aggiungi sempre i file di secret a .gitignore:
# .gitignore
.secrets/
*.key
E a .dockerignore per impedirne l’inclusione accidentale nei contesti di build:
# .dockerignore
.secrets/
*.key
.env
Gestire i secret negli ambienti CI/CD
Le piattaforme CI/CD offrono una gestione nativa dei secret che si integra perfettamente con i Docker build secrets. In GitHub Actions:
- name: Build Docker image
env:
API_KEY: ${{ secrets.API_KEY }}
run: |
docker build \
--secret id=api_key,env=API_KEY \
-t myapp .
Il principio chiave è sfruttare gli store di secret forniti dalla piattaforma invece di archiviare i secret nei repository. I secret della CI/CD sono iniettati come variabili d’ambiente, che BuildKit può consumare direttamente tramite il tipo di sorgente env.
Permessi dei secret e controllo degli accessi
Quando esegui la build come utente non root, assicurati che i file di secret abbiano permessi di lettura adeguati. Se nel Dockerfile usi USER per passare a un utente non root, monta i secret in posizioni accessibili a quell’utente:
FROM python:3.11-slim
RUN useradd -m appuser
USER appuser
RUN --mount=type=secret,id=token,uid=1000 \
cat /run/secrets/token > /dev/null
Per una conformità di sicurezza più rigorosa, implementa strategie di rotazione dei secret. Usa token a vita breve quando possibile, implementa rotazioni automatiche nelle pipeline CI/CD e mantieni log di audit sull’uso dei secret.
Valuta secret con date di scadenza e processi di rinnovo automatico per minimizzare le finestre di esposizione.
Finora ci siamo concentrati su build a singolo container, ma le applicazioni moderne spesso sono composte da più servizi. Vediamo come Docker Compose estende le funzionalità dei build secrets alle architetture multi-servizio.
Integrare i Build Secrets con Docker Compose
Docker Compose semplifica la gestione dei build secrets nelle applicazioni multi-servizio. Nel tuo docker-compose.yml, definisci i secret a livello top e referenziali nelle configurazioni di build dei servizi:
services:
app:
build:
context: .
secrets:
- api_key
- db_password
secrets:
api_key:
file: ./.secrets/api_key
db_password:
environment: DB_PASSWORD
Poi nel Dockerfile, monta i secret come di consueto:
RUN --mount=type=secret,id=api_key \
--mount=type=secret,id=db_password \
./setup.sh
Esegui la build con Compose:
docker-compose build
Questo pattern scala bene per applicazioni con più servizi che richiedono secret differenti, mantenendo una gestione centralizzata dei secret e preservando i confini di sicurezza tra i servizi.
Man mano che implementi i build secrets nei tuoi progetti, inevitabilmente incontrerai problemi. Affrontiamo le sfide più comuni e le relative soluzioni per aiutarti nel troubleshooting.
Problemi comuni e troubleshooting
Anche con un’implementazione corretta, potresti incontrare difficoltà. Ecco i problemi più comuni e le soluzioni.
Errori di sintassi e problemi nelle dichiarazioni di mount
La sintassi di --mount è rigorosa. Errori comuni includono virgole mancanti tra i parametri, nomi di parametro errati e tipi di mount sbagliati. La sintassi corretta è:
RUN --mount=type=secret,id=secret_name,target=/path \
command
Se le build falliscono con "secret not found", verifica che BuildKit sia abilitato e che l’ID nel Dockerfile corrisponda al --secret id nel comando di build.
Secret non disponibili o non trovati
Quando i secret non sono accessibili, verifica che il file sorgente esista e sia leggibile:
cat .secrets/api_key
docker build --secret id=api_key,src=.secrets/api_key .
Per le variabili d’ambiente, conferma che siano impostate:
echo $API_KEY
docker build --secret id=api_key,env=API_KEY .
Confusione tra variabili d’ambiente e file
Con i build secrets, i secret sono sempre montati come file per impostazione predefinita in /run/secrets/<id>. Per usarli come variabili d’ambiente, leggi il contenuto del file:
RUN --mount=type=secret,id=token \
export TOKEN=$(cat /run/secrets/token) && \
curl -H "Authorization: Bearer $TOKEN" https://api.example.com
Non usare mai i build argument (ARG) per i secret, perché persistono nei metadati dell’immagine.
Problemi di persistenza e caching dei layer
I secret non persistono tra istruzioni RUN per progettazione. Se più RUN necessitano dello stesso secret, combinale oppure rimonta il secret:
RUN --mount=type=secret,id=token \
command1
RUN --mount=type=secret,id=token \
command2
BuildKit garantisce che i secret non entrino mai nella cache dei layer.
Una volta padroneggiati i fondamenti e il troubleshooting più comune, potresti incontrare scenari che richiedono approcci più sofisticati. Esploriamo casi d’uso avanzati e edge case oltre le implementazioni standard.
Uso avanzato dei Docker Build Secrets
Per scenari di deployment complessi, dovrai gestire considerazioni aggiuntive oltre all’implementazione di base.
Secret in pipeline CI/CD complesse
Le pipeline CI/CD multi-stage spesso richiedono secret in diversi stage. Implementa secret specifici per stage invece di condividere le stesse credenziali. Usa le funzionalità di scoping dei secret fornite dalla piattaforma CI/CD, limitando quali stage possono accedere a quali secret. Automatizza la pulizia dei secret dopo le build per ridurre la finestra di esposizione.
Secret con builder non Docker
Builder alternativi come Buildah e Kaniko hanno supporti differenti per la sintassi dei secret di BuildKit. Buildah supporta un montaggio simile dei secret tramite --secret flags. Kaniko, pensato per ambienti Kubernetes, usa meccanismi diversi, in genere i secret di Kubernetes montati come volumi. Quando usi builder non Docker, consulta la loro documentazione specifica, poiché le implementazioni differiscono in modo significativo.
Secret e conformità alla sicurezza
I build secrets sono allineati ai framework di conformità perché forniscono accesso alle credenziali effimero e tracciabile.
Per il GDPR, assicurati che i secret non facciano trapelare informazioni personali nei layer. I requisiti SOC2 traggono vantaggio da trail di audit dei secret: registra quando i secret sono usati e da chi. Mantieni i registri degli accessi ai secret per gli audit di conformità.
Rotazione e aggiornamento dei secret
Stabilisci processi di rotazione dei secret senza interrompere le build. Usa file di secret versionati o convenzioni di naming per le variabili d’ambiente che consentano il passaggio tra versioni.
Implementa rotazioni automatiche nelle pipeline CI/CD in cui i secret vengono aggiornati dagli store centrali. Per i token con scadenza, monitora l’expiration e automatizza i processi di rinnovo.
Conclusione: best practice e raccomandazioni
I Docker build secrets rappresentano un miglioramento fondamentale della sicurezza per lo sviluppo di applicazioni containerizzate. In questo tutorial, abbiamo visto come implementare i secret in modo sicuro, dai mount di base all’integrazione complessa in CI/CD.
I punti chiave sono semplici: usa sempre i meccanismi di montaggio dei secret di BuildKit invece dei build arg o di credenziali hardcoded, implementa build multi-stage per isolare gli stage che usano secret dalle immagini finali, sfrutta la gestione dei secret della piattaforma CI/CD per workflow automatizzati e controlla regolarmente le immagini per verificare che i secret non siano finiti nei layer.
Per le organizzazioni che adottano queste pratiche, inizia con una policy di gestione dei secret che definisca quali secret richiedono accesso in fase di build, stabilisci piani di rotazione automatica, implementa test completi per verificare che i secret non persistano nelle immagini e forma i team di sviluppo sui corretti pattern di gestione dei secret. Considerando i build secrets come un componente di sicurezza critico, ridurrai significativamente la superficie di attacco e costruirai applicazioni containerizzate più sicure.
Per continuare a imparare, ti consiglio di consultare le seguenti risorse:
Docker Build Secrets: FAQ
Cosa sono i Docker build secrets?
I Docker build secrets sono informazioni sensibili effimere rese disponibili durante il processo di build ma mai archiviate nei layer dell’immagine; esistono solo in memoria durante specifici step di build.
Quali tipi di build secrets supporta Docker?
Docker supporta tre tipi: secret mount per dati sensibili generici, SSH mount per accesso sicuro ai repository Git e secret di autenticazione Git (GIT_AUTH_TOKEN e GIT_AUTH_HEADER) per contesti remoti privati.
Come impedisco che i secret compaiano nelle mie immagini Docker?
Usa la sintassi --mount=type=secret di BuildKit nelle istruzioni RUN combinata con build multi-stage, non usare mai build arg o variabili d’ambiente per i secret e verifica regolarmente le immagini con docker history per controllare eventuali leakage.
Posso usare i Docker build secrets con Docker Compose?
Sì, Docker Compose supporta i build secrets tramite la sezione secrets nel file di compose, che consente di definirli a livello top e referenziarli nelle configurazioni di build dei servizi.
Come funzionano i build secrets nelle pipeline CI/CD?
Le piattaforme CI/CD iniettano i secret come variabili d’ambiente che possono essere passate alla build Docker usando --secret id=name,env=VARIABLE_NAME, abilitando workflow automatizzati senza archiviare credenziali nei repository.
Come fondatore di Martin Data Solutions e Data Scientist/ML & AI Engineer freelance, porto un portfolio vario che include Regression, Classification, NLP, LLM, RAG, reti neurali, metodi ensemble e computer vision.
- Ho sviluppato con successo diversi progetti ML end-to-end, includendo data cleaning, analytics, modellazione e deployment su AWS e GCP, offrendo soluzioni efficaci e scalabili.
- Ho realizzato applicazioni web interattive e scalabili con Streamlit e Gradio per casi d’uso in settori diversi.
- Ho insegnato e fatto mentoring a studenti di data science e analytics, supportandone la crescita professionale con approcci di apprendimento personalizzati.
- Ho progettato contenuti didattici per applicazioni di retrieval-augmented generation (RAG) su misura per le esigenze enterprise.
- Ho scritto articoli tecnici di grande impatto su AI & ML, trattando temi come MLOps, database vettoriali e LLM, ottenendo un notevole coinvolgimento.
In ogni progetto che affronto, applico pratiche aggiornate di ingegneria del software e DevOps, come CI/CD, linting del codice, formattazione, monitoraggio dei modelli, tracciamento degli esperimenti e gestione robusta degli errori. Mi impegno a fornire soluzioni complete, trasformando gli insight dai dati in strategie pratiche che aiutano le aziende a crescere e a sfruttare al meglio data science, machine learning e AI.


