Come accedere a più macchine in DMZ tramite un solo indirizzo IP e un forward proxy

In ufficio abbiamo un domino Internet locale e varie macchine con alcune applicazioni web che operano in maniera federata, vale a dire che quando ci si collega alla prima si viene rimandati ad un’altra per l’autenticazione e comincia un rimpallo tra le varie macchine (sempre tramite http redirect, cioè tramite il browser sul PC client) finché non si supera sia l’autenticazione che l’autorizzazione. Questa modalità di autenticazione è chiamata federativa o anche «per attestazioni», poiché in una applicazione ci si fida di un server di autenticazione, il quale può essere federato con altri tra i quali è stabilita la fiducia. L’idea è che si arrivi ad autenticare l’utente in maniera automatica, tramite il single sign on, vale a dire che l’utente viene riconosciuto perché ha già fatto il login nel suo sistema operativo e non deve rifarlo per ogni applicazione. Il protocollo utilizzato per scambiare informazioni tra i vari siti implicati è quello SAML, mentre, per fare un po’ di nomi, le tecnologie coinvolte sono la fedlet di Java, la AD FS di Microsoft e altre meno conosciute.

Facciamo l’esempio dell’ufficio con tutte le macchine nel dominio ufficio.local. Avremo quindi dei nomi di macchine fisiche come vm1.ufficio.local o vm2.ufficio.local, e CNAME per le applicazioni, come adfs.ufficio.local e app1.ufficio.local. Le applicazioni sono tutte web, ma utilizzano le porte più disparate, la 443, 8443, la 9653 ed altre ancora, tutte rigorosamente cifrate tramite SSL con certificati emessi dalla ca.ufficio.local.

Il problema è: voglio permettere l’accesso da fuori dell’ufficio protetto dal firewall, senza utilizzare nessuna VPN che dia accesso a tutta la rete interna o che richieda di configurare il firewall in maniera potenzialmente errata.

La soluzione è stata la seguente:

  • configurare il primo firewall per inoltrare tutte le porte delle varie applicazioni web al secondo firewall,

  • configurare il secondo firewall in modo che inoltri tutte le porte delle varie applicazioni ad una sola porta di una macchina proxy sulla quale è configurato pound,

  • configurare pound perché inoltri le varie richieste alla macchina corretta nella LAN.

pound è un proxy HTTP, vale a dire che si mette in ascolto su una porta e, tramite protocollo HTTP, riceve le richieste che poi smista in base a regole sulle intestazioni della richiesta HTTP. pound è in grado di mettersi in ascolto su una porta SSL e di comunicare con il server effettivo, sempre via SSL.

Perché il certificato SSL di pound sia riconosciuto e accettato dai browser per tutti i nomi delle varie macchine, ci sono alcune strade:

  • la prima è avere un certificato per ogni macchina (in questo caso pound usa l’estensione SNI (sigla che sta per Server Name Indication: suggerimento sul nome del server) del protocollo TLS (l’attuale estensione di SSL) per capire quale servername è richiesto dal client),

  • la seconda è avere un certificato che corrisponde a tanti nomi (in questo caso di utilizza l’estensione subjectAlternativeNames del certificato),

  • la terza è avere un certificato che vale per tutto il dominio.

Utilizzo la seconda possibilità e genero un certificato e una richiesta di firma (CSR: Certificate Sign Request) usando lo strumento keytool di java. Nota: questo strumento non crea automaticamente la CSR con tutti i subjectAlternativeNames, ma li si deve specificare sia per la creazione delle chiavi iniziali, sia per la creazione della CSR.

Userò pound.ufficio.local che è il nome della macchina sulla quale ho messo pound, xi12.ufficio.local che è una delle applicazioni, erpln.ufficio.local è la seconda applicazione, adfs.ufficio.local è la terza.

keytool -genkeypair -alias pound -keyalg RSA -keysize 2048 \
-dname "CN=pound.ufficio.local, OU=demo, O=ufficio, L=Torino, C=IT" \
-ext SAN=dns:pound.ufficio.local,dns:xi12.ufficio.local,\
dns:erpln.ufficio.local,dns:adfs.ufficio.local \
-validity 3650 -keypass 'password' -keystore pound.pfx \
-storepass 'password' -storetype PKCS12

keytool -certreq -alias pound -file pound.csr \
-ext SAN=dns:pound.ufficio.local,dns:xi12.ufficio.local,\
dns:erpln.ufficio.local,dns:adfs.ufficio.local \
-keypass 'password' -keystore pound.pfx -storepass 'password' \
-storetype PKCS12

A questo punto prendo il file pound.csr, lo porto alla mia autorità di certificazione, la quale restituisce il certificato pound.cer.

Poiché pound vuole un solo file con la chiave privata e con il certificato, tutto in formato PEM, estraggo la chiave privata da file PFX iniziale e le tolgo la password di protezione, poi creo il file completo per pound:

openssl pkcs12 -in pound.pfx -nocerts -out pound.pw.key # sempre con password
openssl rsa -in pound.pw.key -out pound.nopw.key # senza password
cat pound.cer pound.nopw.key > ssl.pem
rm pound.pfx pound.csr pound.cer pound.nopw.key pound.pw.key
sudo mv ssl.pem /etc/pound/ssl.pem
sudo chown root:root /etc/pound/ssl.pem
sudo chmod go-rwx /etc/pound/ssl.pem

Infine creo il file di configurazione per pound, /etc/pound/pound.cfg:

ListenHTTPS
  Address 192.168.245.99
  Port 8442
  Cert "/etc/pound/ssl.pem"
  LogLevel 5

  ## allow PUT and DELETE also (by default only GET, POST and HEAD)?:
  xHTTP 1

  Service
      IgnoreCase 1
      URL ".*"
      HeadRequire "Host:.*xi12.ufficio.local:9543.*"
      BackEnd
        Address xi12.ufficio.local
        Port 9543
        HTTPS
      End
  End

  Service
      IgnoreCase 1
      URL "^/(bundles/|Scripts/|infor|api/|template/|Content/|CollaborationUI/|webresource/|user/|IONAPIUI/).*"
      HeadRequire "Host:.*xi12.ufficio.local.*"
      BackEnd
         Address xi12.ufficio.local
         Port 8443
         HTTPS
      End
  End

  Service
      IgnoreCase 1
      URL "^/lnui/.*"
      HeadRequire "Host:.*erpln.ufficio.local.*"
      BackEnd
        Address erpln.ufficio.local
        Port 8443
        HTTPS
      End
  End

  Service
      IgnoreCase 1
      URL "^/adfs.*"
      HeadRequire "Host:.*adfs.ufficio.local.*"
      BackEnd
        Address adfs.ufficio.local
        Port 443
        HTTPS
      End
  End
End

Ora si può avviare pound.

Come punto finale, per potersi collegare dall’esterno, si deve inserire nel proprio file /etc/hosts una riga con l’ip pubblico dell’ufficio (quello associato al firewall esterno) e tutti i nomi delle macchine e applicazioni alle quali si deve accedere.

Una volta fatta la prova, si vede che questa configurazione non funziona. Il problema che ho riscontrato è che il server adfs.ufficio.local utilizza a sua volta la SNI, ma pound non imposta l’estensione servername di openssl e quindi la SNI non funziona. A questo punto ho cercato la soluzione sul web e ho trovato che il problema è già stato sollevato da un’altra persona, alla quale è stata fornita una patch non ancora inserita nel codice di pound. La patch però funziona: difatti l’ho scaricata, poi ho fatto le modifiche suggerite nella pagina in questione, ho ricompilato il pacchetto Debian e l’ho installato e provato. Funziona. La patch è in questo thread.