Best practices per la sicurezza su OpenShift

OpenShift è una piattaforma di containerizzazione che si basa su Kubernetes, ma con alcune differenze significative rispetto a Docker. Se stai pensando di migrare i tuoi container da Docker a OpenShift, ci sono alcune best practices da considerare:
- Immagini compatibili: Assicurati che le tue immagini siano compatibili con OpenShift. OpenShift supporta immagini OCI e Docker, ma è importante verificare che non ci siano dipendenze specifiche di Docker che potrebbero causare problemi.
- UID arbitrari: OpenShift consente l’esecuzione di container con UID arbitrari, il che significa che puoi eseguire i tuoi container come un utente non root. Questo è importante per la sicurezza e per evitare conflitti di permessi.
- Configurazione del SecurityContext: Configura il
SecurityContext
per i tuoi pod e container. Questo ti permette di specificare le politiche di sicurezza, come l’uso di un utente non root, la gestione dei volumi e altre impostazioni di sicurezza. - ServiceAccount e ImagePullSecret: Utilizza i
ServiceAccount
per gestire le autorizzazioni dei tuoi container e configura gliImagePullSecret
per accedere a registri privati. Questo è importante per garantire che i tuoi container possano essere eseguiti correttamente in OpenShift. - Persistenza dei dati: Se i tuoi container gestiscono dati persistenti, considera l’uso di
PersistentVolumeClaim
(PVC) per gestire i volumi in modo più efficiente. OpenShift gestisce i volumi in modo diverso rispetto a Docker, quindi è importante adattare la tua configurazione di conseguenza. - Sicurezza e best practices: OpenShift ha un modello di sicurezza più rigoroso rispetto a Docker, quindi è importante seguire le best practices per garantire che i tuoi container siano sicuri. Questo include l’uso di
SecurityContextConstraints
(SCC) e la configurazione corretta delle autorizzazioni.
Esempio di Dockerfile per OpenShift
Immagina di avere un’applicazione Node.js che vuoi eseguire su OpenShift. Ecco un esempio di Dockerfile che segue le best practices:
FROM node:20-alpine
USER 1001
WORKDIR /app
COPY package*.json ./
RUN npm install --production
COPY ../../draft .
EXPOSE 3000
CMD ["node", "server.js"]
Questo Dockerfile:
- Utilizza un’immagine base di Node.js.
- Esegue il container come utente non root (UID 1001).
- Imposta la directory di lavoro su
/app
. - Copia i file di configurazione e installa le dipendenze.
- Espone la porta 3000 per l’applicazione.
- Esegue il server Node.js grazie all’istruzione
CMD
.
Una volta preparato il Dockerfile, puoi eseguire la build dell’immagine ed eseguirne il deploy su OpenShift utilizzando i comandi oc
o tramite l’interfaccia web di OpenShift a partire dal registry dell’immagine che hai utilizzato.
Se hai dubbi su come portare la tua immagine su un registry privato, dai un’occhiata a questo articolo dove spieghiamo come portare la tua applicazione su Kubernetes in Vite.js da zero utilizzando Quay.io o Docker Hub.
Su OpenShift è possibile utilizzare i template per semplificare il processo di deploy. Ecco un esempio di template che puoi utilizzare:
apiVersion: template.openshift.io/v1
kind: Template
metadata:
name: my-node-app
objects:
- apiVersion: v1
kind: Service
metadata:
name: my-node-app
spec:
ports:
- port: 3000
targetPort: 3000
selector:
app: my-node-app
- apiVersion: apps/v1
kind: Deployment
metadata:
name: my-node-app
spec:
replicas: 1
selector:
matchLabels:
app: my-node-app
template:
metadata:
labels:
app: my-node-app
spec:
containers:
- name: my-node-app
image: myregistry/my-node-app:latest
ports:
- containerPort: 3000
securityContext:
runAsUser: 1001
In questo esempio, il template definisce un servizio e un deployment per l’applicazione Node.js. Il deployment specifica l’immagine da utilizzare e le impostazioni di sicurezza per eseguire il container come utente non root. In particolare, l’istruzione runAsUser: 1001
garantisce che il container venga eseguito con un UID non privilegiato, migliorando la sicurezza dell’applicazione.
Per aggiungere uno strato di sicurezza ulteriore, è possibile configurare i SecurityContextConstraints
(SCC) per limitare le azioni che i container possono eseguire. Ad esempio, puoi creare un SCC personalizzato che consente solo l’esecuzione di container con un UID specifico e limita l’accesso a determinate risorse del sistema, oppure utilizzare la direttiva allowPrivilegeEscalation: false
per impedire l’escalation dei privilegi all’interno del container.
Un altro esempio è l’uso della direttiva readOnlyRootFilesystem: true
, che rende il filesystem di root del container in sola lettura, impedendo modifiche non autorizzate ai file di sistema. Questo è particolarmente utile per applicazioni che non necessitano di scrivere sul filesystem di root e contribuisce a ridurre la superficie di attacco.
Esiste poi anche la direttiva runAsNonRoot
che, se impostata su true
, garantisce che il container venga eseguito come un utente non root, migliorando ulteriormente la sicurezza.
Infine, la direttiva capabilities
può essere utilizzata per limitare le capacità del container, ad esempio rimuovendo capacità come NET_ADMIN
o SYS_ADMIN
, che potrebbero essere utilizzate per compromettere la sicurezza del sistema, così come l’uso di seccompProfile
per applicare profili di sicurezza specifici che limitano le chiamate di sistema disponibili al container.
Per capacità nel contesto di un sistema Unix-based si intende un insieme di privilegi che possono essere assegnati a un processo. Queste capacità consentono di eseguire operazioni specifiche senza dover eseguire il processo come root, migliorando la sicurezza e la granularità dei permessi. Alcuni esempi sono
CAP_NET_ADMIN
per la gestione della rete,CAP_SYS_ADMIN
per operazioni di amministrazione del sistema, eCAP_DAC_OVERRIDE
per ignorare le regole di accesso ai file. In OpenShift, puoi configurare le capacità dei container tramite ilSecurityContext
, specificando quali capacità devono essere aggiunte o rimosse dal container.
L’esempio precedente rivisto è il seguente:
apiVersion: template.openshift.io/v1
kind: Template
metadata:
name: my-node-app
objects:
- apiVersion: v1
kind: Service
metadata:
name: my-node-app
spec:
ports:
- port: 3000
targetPort: 3000
selector:
app: my-node-app
- apiVersion: apps/v1
kind: Deployment
metadata:
name: my-node-app
spec:
replicas: 1
selector:
matchLabels:
app: my-node-app
template:
metadata:
labels:
app: my-node-app
spec:
containers:
- name: my-node-app
image: myregistry/my-node-app:latest
ports:
- containerPort: 3000
securityContext:
allowPrivilegeEscalation: false
capabilities:
drop:
- ALL # Rimuove tutte le capabilities, lasciando solo quelle di base
readOnlyRootFilesystem: true
runAsNonRoot: true
runAsUser: 1001
Considerazioni finali
Per quanto OpenShift sia uno dei prodotti di punta di Red Hat, è importante tenere a mente alcune considerazioni quando si lavora con i container: il fatto che la soluzione che mettete in esecuzione sia compatibile con OpenShift non implica necessariamente che sia sicura o conforme alle best practices di sicurezza. Ecco perché è fondamentale seguire le best practices di sicurezza e configurazione per garantire che i tuoi container siano sicuri e funzionino correttamente in OpenShift, tra cui quelle descritte in questo articolo.
Se poi vuoi approfondire ulteriormente come migrare da Docker a OpenShift, ti consiglio di dare un’occhiata a questo articolo dove descriviamo in dettaglio le best practices per la migrazione e l’ottimizzazione dei container su OpenShift.
Risorse utili
- Docker - per cominciare bene con Docker e Kubernetes
- Kubernetes - Guida per gestire e orchestrare i container
- Documentazione ufficiale di OpenShift