Come proteggere un Ingress Nginx su Kubernetes con Basic Auth

Ci sono scenari in cui desideriamo esporre servizi al di fuori del nostro cluster Kubernetes che non hanno un sistema di autenticazione o autorizzazione integrato. In questi casi particolari, è possibile abilitare l’autenticazione di base (Basic Auth) sull’Ingress con pochi passaggi.
È importante notare che questo tutorial è specifico per i cluster con l'Ingress Controller di Nginx. Altri ingress controller potrebbero supportare la stessa funzionalità, ma ovviamente richiedono passaggi di configurazione differenti.
Crea un file auth
Il primo passaggio è creare un file auth
usando htpasswd
.
# Installa htpasswd
sudo apt update
sudo apt install apache2-utils
# Crea un auth file con htpasswd
htpasswd -c auth nome-utente
Sostituisci nome-utente
con il nome utente che desideri utilizzare. Il programma ti chiederà di fornire la password desiderata e di confermarla. Controlla il contenuto del file auth
per assicurarti che questo passaggio sia stato completato con successo.
cat auth
# Output d'esempio
your-username:$apr1$UwVKS.DO$MMwjWgydmSroyzBw5U8fC1
Tieni in mente che il nome del file non è personalizzabile: deve essere auth
.
Crea un Segreto su Kubernetes
Il secondo passaggio consiste nell'incapsulare il file auth
in un segreto di Kubernetes. Assicurati di creare il segreto nello stesso namespace in cui intendi creare l'Ingress, sostituendo il-tuo-namespace
con il valore appropriato.
kubectl -n il-tuo-namespace create secret generic basic-auth --from-file auth
Crea l'Ingress
Se hai già un Ingress definito, dovrai semplicemente aggiungere le tre annotazioni evidenziate nel codice in esempio. Altrimenti, puoi copiare l'intera definizione dell'Ingress e modificarla per adattarla al tuo caso specifico.
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
nginx.ingress.kubernetes.io/proxy-body-size: 10m
#
# Annotazioni necessarie per configurare Basic Auth
#
# Il messaggio di prompt
nginx.ingress.kubernetes.io/auth-realm: Autenticazione: Fornisci note utente e password.
# Il nome del segreto Kubernetes che incapsula l'auth file.
nginx.ingress.kubernetes.io/auth-secret: basic-auth
# Lo schema d'autenticazione
nginx.ingress.kubernetes.io/auth-type: basic
name: il-tuo-ingress
namespace: il-tuo-namespace
spec:
ingressClassName: nginx
rules:
- host: il-tuo-dominio
http:
paths:
- backend:
service:
name: il-tuo-servizio-senza-autenticazione
port:
number: 80
path: /
pathType: ImplementationSpecific
tls:
- hosts:
- il-tuo-dominio
secretName: il-tuo-dominio-tls
L'esempio di Ingress sopra utilizza un certificato TLS autogenerato da cert-manager. Ho diversi articoli sul mio blog in Inglese che spiegano come configurarlo correttamente con Let's Encrypt e Cloudflare.
Test
Abbiamo finito! Apri il browser e prova ad accedere al tuo servizio. Se tutto è stato configurato correttamente, dovrebbe apparire un popup simile a questo.

Considerazioni sulla sicurezza
Come puoi vedere, è stato estremamente semplice abilitare l'autenticazione per un servizio non protetto utilizzando Basic Auth e il nostro Nginx Ingress Controller. Tuttavia, è importante essere consapevoli che, dal punto di vista della sicurezza, questa configurazione è vulnerabile ad attacchi brute force.
Per questo motivo, dobbiamo proteggere ulteriormente l'Ingress con alcuni passaggi aggiuntivi. Vediamo alcune opzioni.
Opzione 1: Whitelist IP sorgenti
La mia opzione preferita è consentire il traffico solo da IP fidati. Nel contesto del mio home-lab, consento solo alla mia rete privata di accedere a servizi come Grafana, Prometheus e Longhorn.
Il modo in cui lo faccio prevede due semplici passaggi.
Per prima cosa, definisco i CIDRs delle mie reti private nella risorsa Ingress aggiungendo la seguente annotazione.
# Sostituisci i CIDRs con quelli della tua rete privata
nginx.ingress.kubernetes.io/whitelist-source-range: "192.168.1.0/24, 192.168.68.0/24"
Successivamente, creo record DNS per i domini con restrizioni, indirizzandoli verso IP privati interni. Sebbene l'approccio più sicuro sarebbe utilizzare un server DNS interno per gestire questi record, trovo più conveniente usare Cloudflare. Anche se ho un server DNS interno, gestire i record su Cloudflare semplifica la configurazione e mi consente di generare certificati TLS validi per i miei servizi senza un'autorità di certificazione interna fidata. E, se te lo stai chiedendo, sì—Cloudflare consente l'uso di IP privati.

Opzione 2: Nginx Rate Limiting
Un'altra opzione è utilizzare il 'rate limiting' di nginx. Ci sono diverse annotazioni che possiamo aggiungere alla definizione del nostro Ingress, documentate qui. Un approccio semplice è limitare il numero massimo di richieste consentite ogni minuto per indirizzo IP sorgente.
# 20 richieste al minuto
# questo è moltiplicato per il limit-burst-multiplier (5 di default)
nginx.ingress.kubernetes.io/limit-rpm: "20"
Questa opzione non mi piace molto perché non previene completamente gli attacchi di forza bruta da attori esterni, ma si limita solo a rallentarli.
Opzione 3: Web Application Firewall
È anche possibile instradare tutto il traffico attraverso un Web Application Firewall (WAF), come ad esempio Cloudflare. Tuttavia, tieni presente che potrebbe essere incompatibile con la whitelist degli IP sorgente.
Conclusione
Tutto qui!
Spero che questo articolo ti sia stato utile. Se hai problemi, non esitare a utilizzare la sezione commenti e cercherò di aiutarti!