Come proteggere un Ingress Nginx su Kubernetes con Basic Auth

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.

Pagina di Login Basic Auth su Chrome

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.

Wildcard DNS che punta al mio firewall sulla rete interna

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!