Archivi categoria: Debian

Debian è il sistema operativo universale: una tre le prime distribuzioni di Linux, che continua ad essere ai massimi livelli e che non ha una azienda privata alle spalle.

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.

logrotate e postrotate

Il logrotate è un strumento molto utile che gestisce i file di log delle varie applicazioni. Ogni giorno viene eseguito dal cron e, in base alla configurazione, archivia tutti i log. Tra le cose interessanti c’è la possibilità di eseguire un comando dopo l’archiviazione del log (e prima della sua eventuale compressione). L’operazione che viene eseguita normalmente a fine archiviazione, è quella di avvisare l’applicazione di utilizzare un nuovo file di log.
Ad esempio, nel file /etc/logrotate.d/rsyslog di Debian c’è scritto:

postrotate
invoke-rc.d rsyslog rotate > /dev/null
endscript

oppure, nel file /etc/logrotate.d/apache2 c’è scritto:

postrotate
if /etc/init.d/apache2 status > /dev/null ; then \
/etc/init.d/apache2 reload > /dev/null; \
fi;
endscript

Il manuale, a proposito del postrotate, dice:
The lines between postrotate and endscript (both of which must appear on lines by themselves) are executed (using /bin/sh) after the log file is rotated. These directives may only appear inside a log file definition. Normally, the absolute path to the log file is passed as first argument to the  script. If  sharedscripts is specified, whole pattern is passed to the script. See also prerotate. See sharedscripts and nosharedscripts for error handling.

Il secondo esempio dovrebbe fare scattare un campanello d’allarme riguardo la sintassi da usare nel postrotate: non si tratta di uno script composto da vari comandi, ma di linee che vengono eseguite tramite la shell. Allora, se il comando è uno solo, come nel caso di rsyslog, non c’è problema, ma se si usano varie linee, è necessario concatenarle tramite il backslash a fine riga, e utilizzare sempre il separatore «;» tra i comandi.

Metti che sbagli a configurare il tuo apache e crei un «open proxy»

Lo so, la documentazione va letta bene e più volte, ma capita di avere fretta oppure che la documentazione sia vaga. Così mi è successo di configurare male un server apache che, dopo due anni, è stato usato come «open proxy», cioè come proxy senza nessun controllo. Questo vuol dire che chiunque, impostando come proxy l’ip del mio server, poteva accedere ad internet senza rivelare il proprio indirizzo ip.

L’errore che avevo commesso era il seguente: avevo abilitato il modulo proxy perché apache inoltrasse alcune richieste ad altri siti, nascondendoli ai client. In questo modo su un solo IP davo accesso a varie applicazioni gestite su diverse macchine virtuali. Apache riceveva la richiesta http://www.miodominio.tld/sitoA e la inoltrava all’apache sulla macchina virtuale A, se riceveva la richiesta http://www.miodominio.tld/sitoB la inoltrava all’apache sulla macchina virtuale B, eccetera.

La mia configurazione, una volta attivato il modulo proxy, era la seguente:

<IfModule mod_proxy.c>
   ProxyRequests On
   <Location "/sitoA">
      ProxyPass http://192.168.74.40/
   </Location>
   <Location "/sitoB">
      ProxyPass http://192.168.74.41:8080/
   </Location>
</IfModule>

L’errore sta nel fatto che ProxyRequests non serve ad attivare questo tipo di richieste (apache configurato come reverse proxy), ma quelle del normale proxy (configurato come forward proxy).

Evidentemente ci sono persone che scoprono quando un server web è configurato male e ne approfittano, sicché ieri pomeriggio mi è arrivato l’allarme automatico che indicava sia l’eccessivo utilizzo della banda, sia il carico anomalo in apache. Dopo aver capito cosa era successo, ed essermi documentato meglio, ho impostato il parametro ProxyRequests a Off e la cosa sarebbe stata risolta, ma…

… ma controllando i log di apache continuavo a vedere decine di richieste riferite a siti non miei, alle quali apache rispondeva 200 (HTTP OK). Come ad esempio queste due:

222.186.15.212 - - [02/Mar/2016:06:38:12 +0100] "GET http://909888.com/ HTTP/1.1" 200 272 "http://909888.com/" "Mozilla/5.0+(compatible;+Baiduspider/2.0;++http://www.baidu.com/search/spider.html)"
104.223.72.222 - - [02/Mar/2016:06:38:09 +0100] "GET http://www.heshijiuxian.net/ HTTP/1.1" 200 272 "http://www.baidu.com" "Mozilla/5.0 (compatible; Googlebot/2.1; http://www.google.com/bot.html)"

Proseguendo nella mia indagine sul server apache ho capito il problema. Il mio server gestisce sullo stesso IP siti diversi, quindi è accessibile sia come http://www.miodominio.tld/ che come http://www.altromiodominio.tld/. Per far questo ho dovuto abilitare il NameVirtualHost di apache che in pratica dice ad apache: ascolta su un IP, e quando ti arriva una richiesta, estrai l’intestazione chiamata «host» e in base a quella usa la configurazione del sito corrispondente (cercandola tra i vari VirtualHost configurati).

Non è che sia scritto proprio in caratteri cubitali, ma la documentazione dice anche, tra le righe, che se l’host non viene trovato né tra i ServerName dei vari VirtualHost e nessure tra i ServerAlias, allora viene usato il VirtualHost collegato all’hostname della macchina. Quindi tutte le richieste che si riferiscono a host non gestiti (come quelle che trovavo nei log) restituivano in realtà la pagina del sito principale di questa macchina.

Per risolvere il problema, ho creato un nuovo VirtualHost con un ServerAlias tale da farlo utilizzare per tutti questi siti. In quel VirtualHost viene restituito errore di autorizzazione, come ad esempio:

5.79.83.31 - - [02/Mar/2016:11:10:48 +0100] "GET http://pornobiz.org:81/test_url1/image.php HTTP/1.0" 403 486 "-" "Opera/9.50 (Windows NT 5.1; U; en)"
5.79.83.31 - - [02/Mar/2016:11:10:48 +0100] "CONNECT 74.125.133.138:443 HTTP/1.1" 400 0 "-" "-"

Il VirtualHost è fatto così:

<VirtualHost indirizzo-ipv4:80 [indirizzo-ipv6]:80>
  ServerName www.example.com
  ServerAlias *
  DocumentRoot /var/www/
  <Directory />
    Options FollowSymLinks
    AllowOverride None
  </Directory>
  <Directory /var/www/>
    Options Indexes FollowSymLinks MultiViews
    AllowOverride None
    Order allow,deny
    deny from all
  </Directory>
  ErrorLog /var/log/apache2/error-www.example.com.log
  LogLevel notice
  CustomLog /var/log/apache2/access-www.example.com.log combined
  ServerSignature On
</VirtualHost>

Notare che ServerAlias non contiene un hostname, ma un pattern che corrisponde a tutti gli hostname possibili, così da utilizzare questo VirtualHost per qualsiasi URL.
Ovviamente, per non fare ricadere in questo caso anche gli hostname che vengono invece gestiti dagli altri VirtualHost configurati in precedenza, è stato necessario chiamare il file di questo con un nome che fosse alfabeticamente ultimo, cioè zzz.conf. In questo modo, quando apache cerca il VirtualHost da usare, esamina tutti i file in /etc/apache2/sites-enabled nell’ordine alfabetico, e prende quest’ultimo solo se l’host della richiesta non corrisponde ad uno di quelli gestiti dagli altri VirtualHost.

smbclient e NT_STATUS_IO_TIMEOUT

A volte capita di dover trasferire file da macchine Windows a macchina Linux. In questi casi uno degli strumenti che si possono utilizzare è smbclient, che accede da Linux ad una cartella condivisa della macchina Windows e trasferisce file bidirezionalmente.

Nel caso che il trasferimento sia lento si può rilevare un timeout con il messaggio d’errore «parallel_read returned NT_STATUS_IO_TIMEOUT».

smb> get nomefile
parallel_read returned NT_STATUS_IO_TIMEOUT

Continua a leggere