Intro
Per la mia nuova attivita’ da freelancer come DevOps Engineer, ho pensato di sviluppare un sito web utilizzando Flask come backend e un template (il cui nome e’ “Simone - Personal Portfolio Template”), acquistato da ThemeForest come frontend.
Niente di troppo complicato; HTML, CSS e Javascript fanno il 90% del lavoro, e il restante 10% e’ composto dal backend in Flask che si occupa di gestire il funzionamento interno come il contact form con Captcha, il routing delle pagine, l’endpoint per il livenessProbe di Kubernetes e tutta quella parte per i motori di ricerca (robots.txt e sitemap).
E’ da un paio di mesi che sto hostando questo sito web lazzarotto.dev sul mio homelab tramite Kubernetes sul mio cluster di 3 nodi K3S, con due repliche per HA (decisamente overkill) e Longhorn come storage (in realta’ usato solo per i file di log di Flask).
Fino alla settimana scorsa utilizzavo l’immagine piu’ comune per un sito web in Flask, ovvero python:3.14-slim; si tratta di un’immagine Docker basata sulla famosissima distro Linux Debian Trixie.
Immagine Non-Root e Distroless
Il sito web funzionava bene con l’immagine Docker menzionata in precedenza, ma era da parecchio tempo che su Reddit (e non solo) sentivo parlare di queste immagini distroless e senza utente “root”, che permettevano di avere un livello di sicurezza maggiore rispetto alle classiche immagini Docker.
E per questo motivo, oltre al fatto che volevo “mettere le mani in pasta” e imparare, ho deciso di convertire il mio Dockerfile per avere un’immagine Docker del mio sito web root-less e distro-less.
Ho pertanto utilizzato comunque l’immagine python:3.14-slim come builder nel mio Dockerfile, ma, ho usato gcr.io/distroless/python3-debian13:nonroot come release.
Quindi, immagine costruita, fatto il deploy su Kubernetes con la nuova immagine e il pod partiva, ma dopo qualche secondo andava in crash. Guardando tra i log del pod, scoprivo che loguru (la libreria per il logging) non riusciva a scrivere il file di log nella cartella che avrebbe dovuto essere “montata” dal PVC di Longhorn.
Troubleshooting su un Container Distroless
Se avete mai lavorato con un container distroless, sicuramente saprete quanto sia complicato fare troubleshooting di un’app in un container del genere. Per chi non lo sapesse, un’immagine distroless e’ un’immagine Docker in cui manca qualsiasi programma basilare di Linux, tra cui: apt/yum, ls, tar, sh o bash. E quindi e’ impossibile usare docker exec o kubectl exec per entrare nel container e controllare lo stato del mountpoint in questione.
A questo punto, il mio pensiero era “ho cambiato due cose: distro e utente che fa girare l’app”. E non avevo idea di quale delle due modifiche effettuate sull’immagine avesse introdotto il problema della scrittura dei log.
Cio’ che mi rimaneva da fare era ricreare l’immagine Docker, comunque senza utente “root”, ma con una distro di base in modo da avere accesso quantomeno a ls, touch, etc.
Tengo a sottolineare che tutto cio’ avveniva mentre il mio sito web era offline perche’ non ho un ambiente di test nel mio homelab (non ancora, almeeno).
Una volta fatto il deploy del pod con la nuova immagine di “debug”, riuscivo a loggarmi nel pod (anzi, nel container del pod) e a rendermi conto che la cartella /app/logs non era di proprieta’ dell’utente corrente (se ricordo dovrebbe essere un utente con UID 1000), ma bensi’ di proprieta’ di root.
Come Perdere Tempo Chiedendo a Gemini la Soluzione
Mi sono rivolto inizialmente a Gemini 2.5 Flash (lo so, avrei dovuto usare il modello Pro, ma pensavo che la soluzione fosse semplice) per trovare la soluzione al problema dei permessi della cartella logs.
Le sue risposte sembravano veramente buone ed era sempre molto self confident, come succede sempre con le sue allucinazioni, ça va sans dire. Ci sono stato dietro per mezz’ora circa, ma alla fine mi sono arreso e mi sono rimesso a cercare con Google, in maniera “vecchio stile”.
La Soluzione
La soluzione e’ arrivata tramite una issue del repo Github del progetto Longhorn che mi ha salvato.
In breve, il problema nasce dal fatto che Longhorn, quando “monta” il PVC nel container, imposta l’utente e il gruppo owner a root, e questo fa in modo che l’utente non-root della mia immagine non possa accedere a tale PVC.
Ci ho messo comunque un po’ di tempo a mettere assieme i pezzi, lo devo ammettere, ma alla fine ho scelto la soluzione numero 7 (Case 7) che era la migliore per il mio caso d’uso perche’ non mi richiedeva di creare StorageClass custom o stravolgere i manifest, ma di agire direttamente sul securityContext del mio Deployment.
In pratica, si tratta di dire a Kubernetes: ‘Ehi, quando monti questo volume, per favore assicurati che appartenga a un gruppo a cui il mio utente applicativo può scrivere’. Semplice ed efficace.
Conclusioni e Manifest
Ecco qua, un intero pomeriggio di debug per aggiungere tre righe di YAML al mio securityContext. Ma al di là della soluzione tecnica, questa esperienza mi ha ricordato due cose importanti.
Primo: le immagini minimali e non-root sono fantastiche per la sicurezza, ma possono rendere il debug un vero incubo se non si è preparati. Secondo: mai sottovalutare il potere di una ricerca “vecchio stile” su GitHub issues quando l’AI ti manda fuori strada.
Adesso il mio sito è di nuovo online, più sicuro di prima, e io ho imparato una lezione preziosa sui permessi dello storage in Kubernetes.
Aggiungo qui sotto la parte utile del mio manifest che ho usato per il deployment dell’app.
apiVersion: apps/v1
kind: Deployment
metadata:
name: lazzarotto-dev
namespace: dmz
labels:
app: lazzarotto-dev
spec:
replicas: 2
selector:
matchLabels:
app: lazzarotto-dev
revisionHistoryLimit: 2
template:
metadata:
labels:
app: lazzarotto-dev
spec:
securityContext:
runAsGroup: 1000
runAsNonRoot: true
runAsUser: 1000
fsGroup: 2000
containers:
- name: lazzarotto-dev
image: git.mlazzarotto.it/marco/lazzarotto_dev:20260205-113032
imagePullPolicy: Always
[...]