Archivi tag: http

CentOS 6: debug del proxy Squid mediante cURL

Di recente, durante uno dei miei controlli di routine presso la rete di un cliente, mi sono accorto che il proxy ivi utilizzato (Squid) mandava in timeout tutte le richieste HTTP/HTTPS dirette al sito paypal.com.

squidOra, premesso che esistono diversi modi per fare un po’ di debug sul proxy, utilizzando ad esempio dei tool sviluppati ad hoc (uno su tutti squidclient), ho preferito adoperare semplicemente cURL, fondamentalmente per 3 motivi:

1) la sua semplicità di impiego;
2) l’ottima documentazione a corredo;
3) la possibilità di visualizzare gli header HTTP di ciascuna richiesta (tracciando eventuali redirect).

Ma bando alle ciance ed ecco come ho individuato (e risolto) l’anomalia riscontrata.

Test di funzionamento

Per prima cosa ho effettuato 2 differenti richieste: la prima diretta ad http://www.paypal.com e la seconda verso http://paypal.com, avvalendomi, rispettivamente, delle flag -v (verbose), -k (per ignorare eventuali problemi di certificati SSL/TLS), -x (per definire il proxy da utilizzare) e – L (che mi permette di indicare il sito target della richiesta). Per http://www.paypal.com ho ottenuto:

root@linux-box:~# curl -v -k -x http://192.168.10.1:3128 -L http://www.paypal.com
* About to connect() to proxy 192.168.10.1 port 3128 (#0)
*   Trying 192.168.10.1... connected
> GET http://www.paypal.com HTTP/1.1
> User-Agent: curl/7.22.0 (x86_64-pc-linux-gnu) libcurl/7.22.0 OpenSSL/1.0.1 zlib/1.2.3.4 libidn/1.23 librtmp/2.3
> Host: www.paypal.com
> Accept: */*
> Proxy-Connection: Keep-Alive
>
* HTTP 1.0, assume close after body
< HTTP/1.0 302 Moved Temporarily
< Date: Tue, 21 Feb 2017 08:16:33 GMT
< Server: Apache
< Location: https://192.168.10.1/block.html
< Vary: Accept-Encoding
< Content-Length: 214
< Content-Type: text/html; charset=iso-8859-1
< X-Cache: MISS from localhost
< X-Cache-Lookup: MISS from localhost:3128
< Via: 1.0 localhost (squid/3.1.19)
* HTTP/1.0 connection set to keep alive!
< Connection: keep-alive
<
* Ignoring the response-body
* Connection #0 to host 192.168.10.1 left intact
* Issue another request to this URL: 'https://192.168.10.1/block.html'
* About to connect() to proxy 192.168.10.1 port 3128 (#1)
*   Trying 192.168.10.1... connected
* Establish HTTP proxy tunnel to 192.168.10.1:443
> CONNECT 192.168.10.1:443 HTTP/1.1
> Host: 192.168.10.1:443
> User-Agent: curl/7.22.0 (x86_64-pc-linux-gnu) libcurl/7.22.0 OpenSSL/1.0.1 zlib/1.2.3.4 libidn/1.23 librtmp/2.3
> Proxy-Connection: Keep-Alive
>
< HTTP/1.0 200 Connection established
<
* Proxy replied OK to CONNECT request
* successfully set certificate verify locations:
*   CAfile: none
  CApath: /*/*/certs
* SSLv3, TLS handshake, Client hello (1):
* SSLv3, TLS handshake, Server hello (2):
* SSLv3, TLS handshake, CERT (11):
* SSLv3, TLS handshake, Server key exchange (12):
* SSLv3, TLS handshake, Server finished (14):
* SSLv3, TLS handshake, Client key exchange (16):
* SSLv3, TLS change cipher, Client hello (1):
* SSLv3, TLS handshake, Finished (20):
* SSLv3, TLS change cipher, Client hello (1):
* SSLv3, TLS handshake, Finished (20):
* SSL connection using ECDHE-RSA-AES256-GCM-SHA384
* Server certificate:
*        subject: C=IT; ST=Some-State; L=Roma; O=Test; CN=Test; emailAddress=hidden@email.it
*        start date: 2013-02-25 15:48:44 GMT
*        expire date: 2014-02-25 15:48:44 GMT
*        issuer: C=IT; ST=Some-State; L=Roma; O=Test; CN=Test; emailAddress=hidden@email.it
*        SSL certificate verify result: self signed certificate (18), continuing anyway.
> GET /block.html HTTP/1.0
> User-Agent: curl/7.22.0 (x86_64-pc-linux-gnu) libcurl/7.22.0 OpenSSL/1.0.1 zlib/1.2.3.4 libidn/1.23 librtmp/2.3
> Host: 192.168.10.1
> Accept: */*
>
< HTTP/1.1 200 OK
< Date: Tue, 21 Feb 2017 08:16:33 GMT
< Server: Apache
< Last-Modified: Mon, 25 Feb 2013 16:13:19 GMT
< ETag: "f293-289-4d68ed1c4eeb4"
< Accept-Ranges: bytes
< Content-Length: 649
< Vary: Accept-Encoding
< Connection: close
< Content-Type: text/html
<
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Proibito</title>
</head>
<body>
<p align="center"><img src="http://192.168.10.1/images/forbidden.png" alt="forbidden" vspace="100"/></p>
<p align="center"><strong>I contenuti del sito sono stati bloccati per ragioni di sicurezza. Per maggiori informazioni contattare l'amministratore</strong></p>
</body>
</html>
* Closing connection #1
* SSLv3, TLS alert, Client hello (1):
* Closing connection #0

mentre per http://paypal.com l’output è stato il seguente:

root@linux-box:~# curl -v -k -x http://192.168.10.1:3128 -L http://paypal.com
* About to connect() to proxy 192.168.10.1 port 3128 (#0)
*   Trying 192.168.10.1... connected
> GET http://paypal.com HTTP/1.1
> User-Agent: curl/7.22.0 (x86_64-pc-linux-gnu) libcurl/7.22.0 OpenSSL/1.0.1 zlib/1.2.3.4 libidn/1.23 librtmp/2.3
> Host: paypal.com
> Accept: */*
> Proxy-Connection: Keep-Alive
>
* HTTP 1.0, assume close after body
< HTTP/1.0 302 Moved Temporarily
< Date: Tue, 21 Feb 2017 08:17:36 GMT
< Server: Apache
< Location: https://192.168.10.1/block.html
< Vary: Accept-Encoding
< Content-Length: 214
< Content-Type: text/html; charset=iso-8859-1
< X-Cache: MISS from localhost
< X-Cache-Lookup: MISS from localhost:3128
< Via: 1.0 localhost (squid/3.1.19)
* HTTP/1.0 connection set to keep alive!
< Connection: keep-alive
<
* Ignoring the response-body
* Connection #0 to host 192.168.10.1 left intact
* Issue another request to this URL: 'https://192.168.10.1/block.html'
* About to connect() to proxy 192.168.10.1 port 3128 (#1)
*   Trying 192.168.10.1... connected
* Establish HTTP proxy tunnel to 192.168.10.1:443
> CONNECT 192.168.10.1:443 HTTP/1.1
> Host: 192.168.10.1:443
> User-Agent: curl/7.22.0 (x86_64-pc-linux-gnu) libcurl/7.22.0 OpenSSL/1.0.1 zlib/1.2.3.4 libidn/1.23 librtmp/2.3
> Proxy-Connection: Keep-Alive
>
< HTTP/1.0 200 Connection established
<
* Proxy replied OK to CONNECT request
* successfully set certificate verify locations:
*   CAfile: none
  CApath: /*/*/certs
* SSLv3, TLS handshake, Client hello (1):
* SSLv3, TLS handshake, Server hello (2):
* SSLv3, TLS handshake, CERT (11):
* SSLv3, TLS handshake, Server key exchange (12):
* SSLv3, TLS handshake, Server finished (14):
* SSLv3, TLS handshake, Client key exchange (16):
* SSLv3, TLS change cipher, Client hello (1):
* SSLv3, TLS handshake, Finished (20):
* SSLv3, TLS change cipher, Client hello (1):
* SSLv3, TLS handshake, Finished (20):
* SSL connection using ECDHE-RSA-AES256-GCM-SHA384
* Server certificate:
*        subject: C=IT; ST=Some-State; L=Roma; O=Test; CN=Test; emailAddress=hidden@email.it
*        start date: 2013-02-25 15:48:44 GMT
*        expire date: 2014-02-25 15:48:44 GMT
*        issuer: C=IT; ST=Some-State; L=Roma; O=Test; CN=Test; emailAddress=hidden@email.it
*        SSL certificate verify result: self signed certificate (18), continuing anyway.
> GET /block.html HTTP/1.0
> User-Agent: curl/7.22.0 (x86_64-pc-linux-gnu) libcurl/7.22.0 OpenSSL/1.0.1 zlib/1.2.3.4 libidn/1.23 librtmp/2.3
> Host: 192.168.10.1
> Accept: */*
>
< HTTP/1.1 200 OK
< Date: Tue, 21 Feb 2017 08:17:36 GMT
< Server: Apache
< Last-Modified: Mon, 25 Feb 2013 16:13:19 GMT
< ETag: "f293-289-4d68ed1c4eeb4"
< Accept-Ranges: bytes
< Content-Length: 649
< Vary: Accept-Encoding
< Connection: close
< Content-Type: text/html
<
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Proibito</title>
</head>
<body>
<p align="center"><img src="http://192.168.10.1/images/forbidden.png" alt="forbidden" vspace="100"/></p>
<p align="center"><strong>I contenuti del sito sono stati bloccati per ragioni di sicurezza. Per maggiori informazioni contattare l'amministratore</strong></p>
</body>
</html>
* Closing connection #1
* SSLv3, TLS alert, Client hello (1):
* Closing connection #0

Individuazione della causa di malfuzionamento

In entrambi i casi il proxy mi ha reindirizzato alla pagina di notifica di squidGuard, ovvero il software che si occupa del filtraggio dei siti Web. Ciò ha fatto scattare un campanello di allarme nella mia testa, in quanto entrambi i domini dovrebbero essere già consentiti, ragion per cui ho deciso di indagare ulteriormente analizzando i log delle blacklist di squidGuard:

root@linux-box:/var/log/squid# tail -f spyware.log
2017-02-21 09:57:06 [1636] Request(default/spyware/-) http://www.paypal.com/ 192.168.10.1/proxy - GET REDIRECT

Una volta individuata la blacklist di interesse (ovvero spyware) ho focalizzato alla mia attenzione sul file domains, andando alla ricerca di tutte le stringhe contenenti paypal.com. Così facendo, una entry ha subito destato in me qualche sospetto, ovvero:

-paypal.com

che ho prontamente sostituito con:

\-paypal.com

aggiungendo semplicemente il carattere di escape (\) davanti al carattere .

Ho quindi ricompilato le blacklist, settando i dovuti permessi e ricaricando la configurazione di Squid proxy:

root@linux-box:~# squidGuard -d -C all 
root@linux-box:~# chown proxy:proxy -R /var/lib/squidguard/db
root@linux-box:~# squid3 -k reconfigure

A questo punto ho lanciato una nuova richiesta verso http://paypal.com, proprio per sincerarmi che la entry errata contenuta nel file domains fosse proprio quella modificata in precedenza:

root@linux-box:/var/lib/squidguard/db/spyware# curl -v -k -x http://192.168.10.1:3128 -L http://paypal.com/
* About to connect() to proxy 192.168.10.1 port 3128 (#0)
*   Trying 192.168.10.1... connected
> GET http://paypal.com/ HTTP/1.1
> User-Agent: curl/7.22.0 (x86_64-pc-linux-gnu) libcurl/7.22.0 OpenSSL/1.0.1 zlib/1.2.3.4 libidn/1.23 librtmp/2.3
> Host: paypal.com
> Accept: */*
> Proxy-Connection: Keep-Alive
>
* HTTP 1.0, assume close after body
< HTTP/1.0 302 Moved Temporarily
< Date: Tue, 21 Feb 2017 10:06:48 GMT
< Location: https://paypal.com/
< Server: BigIP
< Content-Length: 0
< X-Cache: MISS from localhost
< X-Cache-Lookup: MISS from localhost:3128
< Via: 1.0 localhost (squid/3.1.19)
* HTTP/1.0 connection set to keep alive!
< Connection: keep-alive
<
* Connection #0 to host 192.168.10.1 left intact
* Issue another request to this URL: 'https://paypal.com/'
* About to connect() to proxy 192.168.10.1 port 3128 (#1)
*   Trying 192.168.10.1... connected
* Establish HTTP proxy tunnel to paypal.com:443
> CONNECT paypal.com:443 HTTP/1.1
> Host: paypal.com:443
> User-Agent: curl/7.22.0 (x86_64-pc-linux-gnu) libcurl/7.22.0 OpenSSL/1.0.1 zlib/1.2.3.4 libidn/1.23 librtmp/2.3
> Proxy-Connection: Keep-Alive
>
< HTTP/1.0 200 Connection established
<
* Proxy replied OK to CONNECT request
* successfully set certificate verify locations:
*   CAfile: none
  CApath: /etc/ssl/certs
* SSLv3, TLS handshake, Client hello (1):
* SSLv3, TLS handshake, Server hello (2):
* SSLv3, TLS handshake, CERT (11):
* SSLv3, TLS handshake, Server finished (14):
* SSLv3, TLS handshake, Client key exchange (16):
* SSLv3, TLS change cipher, Client hello (1):
* SSLv3, TLS handshake, Finished (20):
* SSLv3, TLS change cipher, Client hello (1):
* SSLv3, TLS handshake, Finished (20):
* SSL connection using AES256-GCM-SHA384
* Server certificate:
*        subject: C=US; ST=California; L=San Jose; O=PayPal, Inc.; OU=PayPal Production; CN=paypal.com
*        start date: 2016-11-04 00:00:00 GMT
*        expire date: 2018-11-01 12:00:00 GMT
*        issuer: C=US; O=DigiCert Inc; OU=www.digicert.com; CN=DigiCert SHA2 High Assurance Server CA
*        SSL certificate verify ok.
> GET / HTTP/1.0
> User-Agent: curl/7.22.0 (x86_64-pc-linux-gnu) libcurl/7.22.0 OpenSSL/1.0.1 zlib/1.2.3.4 libidn/1.23 librtmp/2.3
> Host: paypal.com
> Accept: */*
>
* HTTP 1.0, assume close after body
< HTTP/1.0 301 Moved Permanently
< Location: https://www.paypal.com/
< Strict-Transport-Security: max-age=63072000
< Connection: close
< Content-Length: 0
<
* Closing connection #1
* SSLv3, TLS alert, Client hello (1):
* Issue another request to this URL: 'https://www.paypal.com/'
* About to connect() to proxy 192.168.10.1 port 3128 (#1)
*   Trying 192.168.10.1... connected
* Establish HTTP proxy tunnel to www.paypal.com:443
> CONNECT www.paypal.com:443 HTTP/1.1
> Host: www.paypal.com:443
> User-Agent: curl/7.22.0 (x86_64-pc-linux-gnu) libcurl/7.22.0 OpenSSL/1.0.1 zlib/1.2.3.4 libidn/1.23 librtmp/2.3
> Proxy-Connection: Keep-Alive
>
< HTTP/1.0 200 Connection established
<
* Proxy replied OK to CONNECT request
* successfully set certificate verify locations:
*   CAfile: none
  CApath: /etc/ssl/certs
* SSLv3, TLS handshake, Client hello (1):
* SSLv3, TLS handshake, Server hello (2):
* SSLv3, TLS handshake, CERT (11):
* SSLv3, TLS handshake, Server key exchange (12):
* SSLv3, TLS handshake, Server finished (14):
* SSLv3, TLS handshake, Client key exchange (16):
* SSLv3, TLS change cipher, Client hello (1):
* SSLv3, TLS handshake, Finished (20):
* SSLv3, TLS change cipher, Client hello (1):
* SSLv3, TLS handshake, Finished (20):
* SSL connection using ECDHE-RSA-AES128-GCM-SHA256
* Server certificate:
*        subject: 1.3.6.1.4.1.311.60.2.1.3=US; 1.3.6.1.4.1.311.60.2.1.2=Delaware; businessCategory=Private Organization; serialNumber=3014267; C=US; postalCode=95131-2021; ST=California; L=San Jose; street=2211 N 1st St; O=PayPal, Inc.; OU=CDN Support; CN=www.paypal.com
*        start date: 2016-02-02 00:00:00 GMT
*        expire date: 2017-10-30 23:59:59 GMT
*        issuer: C=US; O=Symantec Corporation; OU=Symantec Trust Network; CN=Symantec Class 3 EV SSL CA - G3
*        SSL certificate verify ok.
> GET / HTTP/1.0
> User-Agent: curl/7.22.0 (x86_64-pc-linux-gnu) libcurl/7.22.0 OpenSSL/1.0.1 zlib/1.2.3.4 libidn/1.23 librtmp/2.3
> Host: www.paypal.com
> Accept: */*
>
* HTTP 1.0, assume close after body
< HTTP/1.0 302 Moved Temporarily
< Server: Apache
< X-Recruiting: If you are reading this, maybe you should be working at PayPal instead! Check out https://www.paypal.com/us/webapps/mpp/paypal-jobs
< Paypal-Debug-Id: c075f46342370
< Cache-Control: no-cache
< X-Content-Type-Options: nosniff
< X-XSS-Protection: 1; mode=block
< X-FRAME-OPTIONS: SAMEORIGIN
< Content-Security-Policy: default-src 'self' https://*.paypal.com https://*.paypalobjects.com https://nexus.ensighten.com 'unsafe-inline'; frame-src 'self' https://*.brighttalk.com https://*.paypal.com https://*.paypalobjects.com https://www.youtube.com/embed/ https://www.paypal-donations.com https://www.paypal-donations.co.uk https://*.qa.missionfish.org https://www.youtube-nocookie.com https://www.xoom.com https://*.pub.247-inc.net/; script-src 'self' https://*.brighttalk.com https://*.paypal.com https://*.paypalobjects.com https://www.youtube.com/iframe_api https://s.ytimg.com/yts/jsbin/ https://*.t.eloqua.com https://img.en25.com/i/elqCfg.min.js https://nexus.ensighten.com/ 'unsafe-inline' 'unsafe-eval'; connect-src 'self' https://*.paypal.com https://*.paypalobjects.com https://*.salesforce.com https://*.force.com https://*.eloqua.com https://nexus.ensighten.com https://storelocator.api.where.com https://api.paypal-retaillocator.com https://nominatim.openstreetmap.org https://www.paypal-biz.com; img-src 'self' * data:; object-src 'self' https://*.paypal.com https://*.paypalobjects.com; font-src 'self' https://*.paypalobjects.com;
< HTTP_X_PP_AZ_LOCATOR: slcb.slc
< Paypal-Debug-Id: c075f46342370
< Location: /it/home
< Content-Encoding: gzip
< Cache-Control: max-age=0, no-cache, no-store, must-revalidate
< Pragma: no-cache
< Content-Type: text/plain; charset=utf-8
< DC: phx-origin-www-1.paypal.com
< Content-Length: 56
< X-EdgeConnect-MidMile-RTT: 3
< X-EdgeConnect-Origin-MEX-Latency: 198
< X-EdgeConnect-MidMile-RTT: 169
< X-EdgeConnect-Origin-MEX-Latency: 198
< Date: Tue, 21 Feb 2017 10:06:51 GMT
< Connection: close
< Set-Cookie: LANG=it_IT%3BIT; Domain=.paypal.com; Path=/; Expires=Tue, 21 Feb 2017 18:52:46 GMT; HttpOnly
< Set-Cookie: tsrce=mppnodeweb; Domain=.paypal.com; Path=/; Expires=Wed, 22 Feb 2017 10:06:50 GMT; HttpOnly; Secure
< Set-Cookie: x-pp-s=eyJ0IjoiMTQ4NzY3MTYxMTM5NCIsIm0iOiIwIn0; Domain=.paypal.com; Path=/; HttpOnly; Secure
< Set-Cookie: nsid=s%3AUhjv0IeCf2Lsd4mAbdfaZCowZwbnEoLG.VPNYMobMlVf8MPI4EqbaGJHWsctx9knCbLdeDqVeGhg; Path=/; HttpOnly; Secure
< Set-Cookie: X-PP-SILOVER=name%3DLIVE3.WEB.1%26silo_version%3D880%26app%3Dmppnodeweb%26TIME%3D991013976%26HTTP_X_PP_AZ_LOCATOR%3Dslcb.slc; Expires=Tue, 21 Feb 2017 10:36:51 GMT; domain=.paypal.com; path=/; Secure; HttpOnly
< Set-Cookie: X-PP-SILOVER=; Expires=Thu, 01 Jan 1970 00:00:01 GMT
< Set-Cookie: AKDC=phx-origin-www-1.paypal.com; expires=Tue, 21-Feb-2017 10:36:51 GMT; path=/; secure
< Set-Cookie: akavpau_ppsd=1487672211~id=cae8071e8da0e5fdb485811908c64fd0; path=/
< Strict-Transport-Security: max-age=63072000
<
* Closing connection #1
* SSLv3, TLS alert, Client hello (1):
* Issue another request to this URL: 'https://www.paypal.com/it/home'
* About to connect() to proxy 192.168.10.1 port 3128 (#1)
*   Trying 192.168.10.1... connected
* Establish HTTP proxy tunnel to www.paypal.com:443
> CONNECT www.paypal.com:443 HTTP/1.1
> Host: www.paypal.com:443
> User-Agent: curl/7.22.0 (x86_64-pc-linux-gnu) libcurl/7.22.0 OpenSSL/1.0.1 zlib/1.2.3.4 libidn/1.23 librtmp/2.3
> Proxy-Connection: Keep-Alive
>
< HTTP/1.0 200 Connection established
<
* Proxy replied OK to CONNECT request
* successfully set certificate verify locations:
*   CAfile: none
  CApath: /etc/ssl/certs
* SSL re-using session ID
* SSLv3, TLS handshake, Client hello (1):
* SSLv3, TLS handshake, Server hello (2):
* SSLv3, TLS change cipher, Client hello (1):
* SSLv3, TLS handshake, Finished (20):
* SSLv3, TLS change cipher, Client hello (1):
* SSLv3, TLS handshake, Finished (20):
* SSL connection using ECDHE-RSA-AES128-GCM-SHA256
* Server certificate:
*        subject: 1.3.6.1.4.1.311.60.2.1.3=US; 1.3.6.1.4.1.311.60.2.1.2=Delaware; businessCategory=Private Organization; serialNumber=3014267; C=US; postalCode=95131-2021; ST=California; L=San Jose; street=2211 N 1st St; O=PayPal, Inc.; OU=CDN Support; CN=www.paypal.com
*        start date: 2016-02-02 00:00:00 GMT
*        expire date: 2017-10-30 23:59:59 GMT
*        issuer: C=US; O=Symantec Corporation; OU=Symantec Trust Network; CN=Symantec Class 3 EV SSL CA - G3
*        SSL certificate verify ok.
> GET /it/home HTTP/1.0
> User-Agent: curl/7.22.0 (x86_64-pc-linux-gnu) libcurl/7.22.0 OpenSSL/1.0.1 zlib/1.2.3.4 libidn/1.23 librtmp/2.3
> Host: www.paypal.com
> Accept: */*
>
* HTTP 1.0, assume close after body
< HTTP/1.0 200 OK
< Server: Apache
< X-Recruiting: If you are reading this, maybe you should be working at PayPal instead! Check out https://www.paypal.com/us/webapps/mpp/paypal-jobs
< Paypal-Debug-Id: ef6f606ac62a4
< Cache-Control: no-cache
< X-Content-Type-Options: nosniff
< X-XSS-Protection: 1; mode=block
< X-FRAME-OPTIONS: SAMEORIGIN
< Content-Security-Policy: default-src 'self' https://*.paypal.com https://*.paypalobjects.com https://nexus.ensighten.com 'unsafe-inline'; frame-src 'self' https://*.brighttalk.com https://*.paypal.com https://*.paypalobjects.com https://www.youtube.com/embed/ https://www.paypal-donations.com https://www.paypal-donations.co.uk https://*.qa.missionfish.org https://www.youtube-nocookie.com https://www.xoom.com https://*.pub.247-inc.net/; script-src 'self' https://*.brighttalk.com https://*.paypal.com https://*.paypalobjects.com https://www.youtube.com/iframe_api https://s.ytimg.com/yts/jsbin/ https://*.t.eloqua.com https://img.en25.com/i/elqCfg.min.js https://nexus.ensighten.com/ 'unsafe-inline' 'unsafe-eval'; connect-src 'self' https://*.paypal.com https://*.paypalobjects.com https://*.salesforce.com https://*.force.com https://*.eloqua.com https://nexus.ensighten.com https://storelocator.api.where.com https://api.paypal-retaillocator.com https://nominatim.openstreetmap.org https://www.paypal-biz.com; img-src 'self' * data:; object-src 'self' https://*.paypal.com https://*.paypalobjects.com; font-src 'self' https://*.paypalobjects.com;
< ETag: W/"6646-N9seUapd2xMK7C+gFi/cTw"
< HTTP_X_PP_AZ_LOCATOR: dcg11.slc
< Paypal-Debug-Id: ef6f606ac62a4
< Cache-Control: max-age=0, no-cache, no-store, must-revalidate
< Pragma: no-cache
< Content-Type: text/html; charset=utf-8
< DC: phx-origin-www-1.paypal.com
< X-EdgeConnect-MidMile-RTT: 3
< X-EdgeConnect-Origin-MEX-Latency: 242
< X-EdgeConnect-MidMile-RTT: 175
< X-EdgeConnect-Origin-MEX-Latency: 242
< Date: Tue, 21 Feb 2017 10:06:54 GMT
< Content-Length: 26182
< Connection: close
< Set-Cookie: cookie_check=yes; Domain=.paypal.com; Path=/; Expires=Sun, 21 Feb 2027 10:06:52 GMT; HttpOnly; Secure
< Set-Cookie: LANG=it_IT%3BIT; Domain=.paypal.com; Path=/; Expires=Tue, 21 Feb 2017 18:52:48 GMT; HttpOnly
< Set-Cookie: tsrce=mppnodeweb; Domain=.paypal.com; Path=/; Expires=Wed, 22 Feb 2017 10:06:52 GMT; HttpOnly; Secure
< Set-Cookie: x-pp-s=eyJ0IjoiMTQ4NzY3MTYxMzk0NyIsIm0iOiIwIn0; Domain=.paypal.com; Path=/; HttpOnly; Secure
< Set-Cookie: nsid=s%3A74ujkGU9eWQSC2wQM8PXHRRoHjuQmucM.0ykPY%2FAkmfXl50QcbBuCSwC7lEm1YbqKWOXxw4FicY0; Path=/; HttpOnly; Secure
< Set-Cookie: X-PP-SILOVER=name%3DLIVE3.WEB.1%26silo_version%3D880%26app%3Dmppnodeweb%26TIME%3D1024568408%26HTTP_X_PP_AZ_LOCATOR%3Ddcg11.slc; Expires=Tue, 21 Feb 2017 10:36:53 GMT; domain=.paypal.com; path=/; Secure; HttpOnly
< Set-Cookie: X-PP-SILOVER=; Expires=Thu, 01 Jan 1970 00:00:01 GMT
< Set-Cookie: AKDC=phx-origin-www-1.paypal.com; expires=Tue, 21-Feb-2017 10:36:54 GMT; path=/; secure
< Set-Cookie: akavpau_ppsd=1487672214~id=56a8294899d3c18083d0709205e7d737; path=/
< Strict-Transport-Security: max-age=63072000
<
<

richiesta che è andata a buon fine, per la causa del problema è stata correttamente individuata.

Soluzione definitiva

Per risolvere definitivamente tale anomalia (legata principalmente alla cattiva gestione, da parte di squidGuard, dei domini che contengono caratteri particolari), ho deciso di agire come segue. Poichè le blackist vengono aggiornate ogni notte in modo automatico, il file domains modificato in precedenza verrebbe continuamente sovrascritto, rendendo nulla la sostituzione di -paypal.com com con \-paypal.com. Quindi, per fare in modo che squidGuard torni a consentire definitivamente l’accesso al dominio paypal.com (e relativi sottodomini), ho deciso di creare un file domains all’interno della dir /var/lib/squidguard/db/whitelist, contenente la entry paypal.com:

root@linux-box:~# cat /var/lib/squidguard/db/whitelist/domains
paypal.com

Ho quindi modificato la configurazione di squidGuard (/etc/squid/squidGuard.conf), ridefinendo la sequenza di matching dell’ACL che si occupa del filtraggio:

acl {
        default {
                pass whitelist !aggressive !drugs !gambling !hacking !porn !proxy !redirector !spyware !suspect !violence !warez !custom
                redirect http://192.168.10.1/block.html
        }

Nella fattispecie, ho fatto in modo che venissero dapprima consentiti tutti i domini in whitelist, bloccati i domini presenti nelle blacklist ed infine consentiti tutti gli altri.

Ho quindi ricompilato le blacklist di squidGuard come visto in precedenza:

root@linux-box:~# squidGuard -d -C all 
root@linux-box:~# chown proxy:proxy -R /var/lib/squidguard/db
root@linux-box:~# squid3 -k reconfigure

ed il problema è stato definitivamente risolto.

Alla prossima.

Nginx e CentOS: creare un load balancer per i Websocket di nodejs

In questo post ho riportato il codice bash (di mia stesura) necessario per eseguire l’applicativo nodejs come demone (senza avvelersi quindi di tool esterni quali nohup o screen). In soldoni, esso non fa altro che tirare su (o stoppare/riavviare) 8 istanze di nodejs (ciascuna delle quali è in ascolto su una porta TCP dedicata, dalla 9001 alla 9008), il cui compito è quello di gestire le connessioni Websocket in ingresso (tramite la libreria socket.io ed il metodo HTTP Upgrade).

Nel caso in cui vi fosse la necessità di distribuire le istanze di nodejs su più macchine (per questioni di ridondanza e di suddivisione del carico), è indispensabile avvalersi di un bilanciatore in grado di gestire la suddetta tipologia di traffico (Websocket).

A tal proposito, se si vuole optare per una soluzione “software”, non si hanno tantissime alternative e le più gettonate sono sicuramente HAProxy e Nginx. Il primo è un bilanciatore a tutti gli effetti, mentre il secondo è un server Web (a detta di molti più performante e malleabile di Apache), che però può essere configurato a mo’ di load balancer.

La scelta è ricaduta proprio su Nginx perchè si è rivelato essere molto più stabile rispetto ad HAProxy (quest’ultimo crashava inesorabilmente se le richieste di connessione provenivano dal browser Safari).

Di seguito riporto la configurazione di Nignx, supponendo che vi siano 3 frontend da bilanciare, ciascuno dei quali dotato di 8 istanze nodejs:

upstream node_app {
    ip_hash;
    server node1.domain.com:9001;
    server node1.domain.com:9002;
    server node1.domain.com:9003;
    server node1.domain.com:9004;
    server node1.domain.com:9005;
    server node1.domain.com:9006;
    server node1.domain.com:9007;
    server node1.domain.com:9008;
    server node2.domain.com:9001;
    server node2.domain.com:9002;
    server node2.domain.com:9003;
    server node2.domain.com:9004;
    server node2.domain.com:9005;
    server node2.domain.com:9006;
    server node2.domain.com:9007;
    server node2.domain.com:9008;
    server node3.domain.com:9001;
    server node3.domain.com:9002;
    server node3.domain.com:9003;
    server node3.domain.com:9004;
    server node3.domain.com:9005;
    server node3.domain.com:9006;
    server node3.domain.com:9007;
    server node3.domain.com:9008;
}

server {
    server_name lblive.domain.com;
    listen 80;
    location / {
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_http_version 1.1;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header Host $host;
        proxy_pass http://node_app;
    }
}

La configurazione è abbastanza intuitiva: nella prima parte viene definito l’elenco dei frontend e la politica di bilanciamento (stickyip_hash), mentre nella seconda parte vengono definite le regole per la gestione del traffico (soprattutto per ciò che concerne gli header HTTP).

A configurazione completata avviamo Nginx:

[root@loadbalancer ~]# service nginx start

e facciamo in modo che venga eseguito automaticemente dopo ogni riavvio:

[root@loadbalancer ~]# chkconfig nginx on

E’ tutto. Alla prossima.

Load balancer software basato su LVS e Centos 6

Premessa

Oggigiorno esistono diverse soluzioni basate su distribuzioni Linux che consentono di realizzare dei load balancer piuttosto affidabili e performanti. Personalmente ritengo che la scelta dovrebbe ricadere su due software in particolare, ovvero HAProxy oppure LVS (acronimo di Linux Virtual Server).

Nel primo caso abbiamo a che fare con un applicativo che viene eseguito nello spazio utente, con tutte le limitazioni che questo comporta, soprattutto in termini di velocità e di carico computazionale. Esso, inoltre, lavora a livello 7 ISO/OSI (application).

Nel secondo caso, invece, parliamo di un software che viene eseguito a livello del kernel, riducendo significatamente i tempi di risposta e di elaborazione. Inoltre, esso operera a livello 4 (transport) della pila ISO/OSI.

Ovviamente, da buon freak control, ho scelto LVS per realizzare il mio load balancer. Certo, molti di voi obietteranno dicendo che questa tipologia di bilanciatore non ha la stessa affidabilità, le stesse features e la stessa velocità di una soluzione hardware dedicata. La mia risposta è che se non avete intenzione di vendervi un rene per acquistare una macchina che invece di fare N, fa semplicemente N+1 (o + 2, in base alle migliaia di euro in fattura), allora dovrete accontentarvi (si fa per dire) del load balancer software, che per lo meno è più malleabile.

Installazione di LVS

L’installazione di LVS è abbastanza semplice e può essere effettuata mediante yum:

[root@lb1 ~]#  yum groupinstall "Load Balancer"

[root@lb1 ~]#  yum install httpd

[root@lb1 ~]#  chkconfig --level 2345 pulse on
[root@lb1 ~]#  chkconfig --level 2345 piranha-gui on
[root@lb1 ~]#  chkconfig --level 2345 httpd on

In particolare, httpd è il server Web in ascolto sul load balancer (in quanto il mio intento è quello di realizzare un bilanciatore di carico per le richieste HTTP/HTTPS); pulse è il cuore del servizio LVS e si occupa anche della gestione dell’alta affidabilità (HA) mediante l’uso di heartbeat; piranha-gui è un’utile interfaccia grafica (Web based) per la configurazione del nostro Linux Virtual Server.

Una volta installato il pacchetto piranha-gui, occorre impostarne la password di accesso (trattasi di semplice autentica HTTP):

[root@lb1 ~]#  piranha-passwd

Lo username sarà quello di default, ovvero piranha.

A questo punto occorre abilitare il forwarding tra le varie interfacce di rete della macchina Centos adibita a bilanciatore, lanciando semplicemente il comando:

[root@lb1 ~]#  echo 1 > /proc/sys/net/ipv4/ip_forward

Tale comando va inserito anche all’interno del file rc.local, in modo da abilitare il forwarding dopo ogni reboot della macchina.

Infine, avviamo sia il server Web che piranha-gui:

service httpd start
service piranha-gui start

Prima di entrare nel merito della configurazione di LVS è opportuno introdurre un po’ di nozioni teoriche.

Modalità di funzionamento

LVS può operare in 3 diverse modalità (mutuamente esclusive):

1) NAT;

2) Tunneling;

3) Direct routing (DC).

Nel primo caso il load balancer prenderà in carico le richieste provenienti dai client e le girerà ai frontend mediante un semplice NAT. Le risposte dei frontend verranno quindi inoltrate al load balancer stesso che si occuperà di recapitarle ai client. A rigor di logica, tale modalità di funzionamento è certamente la meno performante, in quanto il load balancer rappresenta il classico collo di bottiglia dell’infrastruttura (dato che tutto il traffico bilanciato dovrà necessariamente utilizzare LVS come crocevia).

Nel secondo caso non viene utilizzato il NAT ma un tunnel ipip creato ad hoc per incapsulare le richieste dei client prese in carico dal load balancer e successivamente inoltrarle ai frontend. In questo caso le risposte verranno instratade ai client direttamente dai frontend, senza passare necessariamente dal director (ovvero la macchina che effettua il bilanciamento nella terminologia LVS).Inoltre, a differenza della modalità NAT, affichè l’infrastruttura possa funzionare correttamente è necessario configurare appositamente anche i frontend.

Infine, la terza modalità (che tratterò nell’ambito di questo post) può essere utilizzata nel caso in cui il director ed i frontend (real server per LVS), si trovino nella stessa subnet. Anche in questo caso i frontend vanno configurati appositamente.

In definitiva, la prima modalità richiede un minor numero di configurazioni a discapito di performance ottimizzate; la seconda e la terza modalità, invece, consentono di superare il problema del collo di bottiglia, ma richiedono alcune variazioni nella configurazione dei frontend.

Modalità di bilanciamento

Le modalità di bilanciamento messe a disposizione da LVS sono molteplici. Senza scendere troppo nel dettaglio, le due che secondo me meritano maggiore attenzione sono le seguenti:

1) round robin (classica o nella variante weighted);

2) least connection (anch’essa disponibile nella forma classica o nella variante weighted).

Nel primo caso le richieste vengono inoltrate ai frontend seguendo l’algoritmo round robin (vedi qui per ulteriori dettagli). Nel caso in cui i frontend non siano tutti dotati della medesima capacità di calcolo e risorse hardware, è possibile assegnare loro un peso specifico che li contraddistinguerà. In questo caso, scegliendo la weighted round robin sarà possibile inoltrare una mole di richieste maggiore ai server più corazzati, riservando un carico inferiore a quelli meno performanti.

Per quanto riguarda, invece, la least connection, un client che instaura la connessione con un frontend continuerà ad essere servito da quel frontend (fino ad un eventuale connection timeout). La modalità weighted tiene conto del peso assegnato a ciascun frontend, ovvero valgono i medesimi ragionamenti fatti per la weighted round robin.

Configurazione di LVS

Lo schema riportato di seguito mostra l’infrastruttura creata per il bilanciamento del traffico HTTP/HTTPS.

 

netschema.PNG

Come si può notare, esistono 2 director configurati in HA (active/standby) e 2 real server, identici dal punto di vista hardware e software.

Colleghiamoci alla GUI installata in precedenza attraverso la seguente URL:

http://ipdirector:3636

dove, banalmente, ipdirector è l’indirizzo IP del server che funge da director e 3636 (TCP) è la porta su cui è in ascolto piranha-gui.

Cliccando sul tab VIRTUAL SERVERS e successivamente sul tasto ADD possiamo creare un nuovo bilanciatore (come riportato nello screenshot sottostante):

 

screen1.PNG

Cliccando sul radio button presente alla nostra sinistra selezioniamo il virtual server appena creato. A questo punto sarà sufficiente cliccare su EDIT per definirne i parametri di configurazione:

screen2.PNG

Oltre al nome del virtual server (lb_cluster), abbiamo configurato la TCP 80 come application port (HTTP), abbiamo scelto il virtual IP (10.2.66.13) a cui dovrà rispondere il load balancer ed abbiamo scelto l’interfaccia virtuale (eth0:1) su cui mettere in bind l’IP in questione (appartenente ad una classe privata, appositamente “nattato” in IP pubblico da un firewall che gestisce le connessioni in ingresso alla LAN). Da notare che alla voce quiesce server il radio button è posizionato su Yes. In questo modo, nel caso in cui venisse aggiunto un nuovo real server, la tabella di bilanciamento presente sul director verrebbe resettata e la nuova macchina verrà coinvolta immediatamente nelle operazioni di smistamento delle richieste.

Adesso cliccliamo su REAL SERVER, su ADD e dopo aver selezionato la nuova entry, clicchiamo su EDIT.

La pagina di configurazione dei real server sarà simile alla seguente:

screen3.PNG

Questa, invece, è l’overview dei real server configurati:

screen4.PNG

Selezioniamo ciascuno di essi e clicchiamo su (DE)ACTIVATE.

Torniamo alla schermata dei virtual server, selezioniamo quello appena creato ed anche in questo caso clicchiamo su (DE)ACTIVATE.

Ora sarà possibile creare un virtual server anche per le richieste HTTPS. E’ sufficiente ricopiare la configurazione utilizzata per l’HTTP, eccezion fatta per i seguenti parametri:

1) Apllication port, che deve essere la 443.

2) Persistence, settata a 320 (ovvero il timeout in secondi della connessione inizializzata dal client).

Quest’ultima è necessaria per il buon funzionamento del protocollo HTTPS, in quanto una sessione di questo tipo deve essere gestita da un solo server per volta (rispettando il principio di autenticazione della fonte e la logica su cui si basa SSL/TLS).

Anche in questo caso cliccando su DE(ACTIVATE) renderemo attivo il bilanciamento per il protocollo in questione.

Occorre, inoltre, fare una precisazione: nel caso in cui il director debba gestire anche le connessioni HTTPS, è necessario installare su di esso (ed anche sui real server), il modulo mod_ssl di Apache:

[root@lb1 ~]# yum install mod_ssl

e successivamente restartare httpd:

[root@lb1 ~]# service httpd restart

in modo che rimanga in ascolto sulla porta 443.

La corretta installazione e configurazione dei certificati sui real server esula dallo scopo del presente post.

Configurazione del cluster (HA)

Anche la configurazione del cluster active/standby (per evitare il famigerato single point of failure) può essere portata a termine mediante piranha-gui.

Nello specifico, è sufficiente cliccare sul tab REDUNDANCY ed impostare l’IP reale del director di backup:

screen5.PNG

Alla fine della fiera, il file di configurazione di LVS presente sul nodo attivo dovrà essere simile al seguente:

[root@lb1 ~]# cat /etc/sysconfig/ha/lvs.cf

serial_no = 77
primary = 10.2.66.11
service = lvs
backup_active = 1
backup = 10.2.66.12
heartbeat = 1
heartbeat_port = 539
keepalive = 6
deadtime = 10
network = direct
debug_level = NONE
monitor_links = 1
syncdaemon = 0
virtual lb_cluster {
     active = 1
     address = 10.2.66.13 eth0:1
     vip_nmask = 255.255.255.255
     port = 80
     send = "GET / HTTP/1.0rnrn"
     expect = "HTTP"
     use_regex = 0
     load_monitor = none
     scheduler = lc
     protocol = tcp
     timeout = 6
     reentry = 15
     quiesce_server = 1
     server vmtest6 {
         address = 10.2.66.7
         active = 1
         weight = 1
     }
     server vmtest7 {
         address = 10.2.66.9
         active = 1
         weight = 1
     }
}
virtual lb_cluster_https {
     active = 1
     address = 10.2.66.13 eth0:1
     vip_nmask = 255.255.255.255
     port = 443
     persistent = 320
     use_regex = 0
     load_monitor = none
     scheduler = lc
     protocol = tcp
     timeout = 6
     reentry = 15
     quiesce_server = 1
     server vmtest6 {
         address = 10.2.66.7
         active = 1
         weight = 1
     }
     server vmtest7 {
         address = 10.2.66.9
         active = 1
         weight = 1
     }
}


Come potete notare, per quanto riguarda il virtual server dedicato alle richeste HTTPS, non sono presenti meccanismi di controllo (identificati dai parametri send ed expect), in quanto non sono pienamente supportati da LVS e la loro presenza potrebbe inficiare il funzionamento del bilanciatore per il tipo di richieste in questione.

Ora copiamo la suddetta configurazione sul server di backup mediante SCP:

[root@lb1 ~]#  scp /etc/sysconfig/ha/lvs.cf root@10.2.66.12:/etc/sysconfig/ha/lvs.cf

A configurazione dei nodi ultimata, possiamo avviare pulse:

[root@lb1 ~]# service pulse start

Controlliamo che il servizio sia effettivamente funzionante e che i real server siano stati attivati correttamente:

[root@lb1 ~]# watch ipvsadm

il cui output dovrebbe essere simile al seguente:

Every 2.0s: ipvsadm                                                                                 Mon Oct 21 11:32:53 2013

IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
-> RemoteAddress:Port           Forward Weight ActiveConn InActConn
TCP  10.2.66.13:http lc
-> 10.2.66.7:http               Route   1      0          0
-> 10.2.66.9:http               Route   1      0          0
TCP  10.2.66.13:https lc persistent 320
-> 10.2.66.7:https              Route   1      0          0
-> 10.2.66.9:https              Route   1      0          0

Configurazione dei real server ed il problema delle richieste ARP

Come accennato in precedenza, la modalità Direct Connect prevede un minimo di configurazione anche sui frontend Web. Quindi, fin quando la loro configurazione non verrà ultimata, il bilanciatore non potrà funzionare.

Nello specifico, occorre mettere in bind l’IP dei due virtual server sui frontend, assegnandolo, anche in questo caso, ad un’interfaccia virtuale (eth0:1).

Lanciamo dunque il comando:

ifconfig eth0:1 10.2.66.13 netmask 255.255.255.255 up

ed inseriamo la suddetta stringa all’interno del file /etc/rc.local per rendere attiva l’interfaccia ache dopo un eventual reboot della macchina.

Con il comando:

[root@lb1 ~]# ifconfig

verifichiamo che l’interfaccia virtuale sia effettivamente attiva. L’output dovrà essere simile al seguente:

eth0:1    Link encap:Ethernet  HWaddr 00:0C:29:14:C7:79
           inet addr:10.2.66.13  Bcast:10.2.66.13  Mask:255.255.255.255
           UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1

Una volta configurata l’interfaccia virtuale occorre risolvere il problema delle richieste ARP. Infatti, poichè sia il director che i due real server rispondono al medesimo indirizzo IP virtuale, nel caso in cui venisse fatta una richiesta da un client, la riposta potrebbe provenire direttamente da uno dei due frontend, senza passare per il director. Tale comportamento è dovuto al fatto che, in una LAN, ogniqualvolta si cerca di contattare un indirizzo IP, viene inoltrata una richiesta ARP (in broadcast) richiedendo l’indirizzo MAC dell’interfaccia a cui quell’indirizzo è stato assegnato. Ovviamente, a tale richiesta potrebbe seguire la risposta di uno dei due frontend o del director, in modo totalmente randomico, inficiando il corretto funzionamento del load balancer stesso.

Per evitare ciò si può installare arptables_jf, una sorta di firewall di livello 2, in modo da bloccare le richieste ARP provenienti dai client:

[root@lb1 ~]# yum install arptables_jf

Una volta installato è necessario configurarlo nel seguente modo:

[root@lb1 ~]# arptables -A IN -d 10.2.66.13 -j DROP
[root@lb1 ~]# arptables -A OUT -s 10.2.66.13 -j mangle --mangle-ip-s 10.2.66.7

La prima entry blocca le richieste ARP in ingresso al real server, contenenti l’indirizzo IP virtuale; la seconda entry, invece, consente di rispondere alla richieste ARP con l’IP reale del frontend (10.2.66.7).

Salviamo la configurazione di arptables lanciando il comando:

[root@lb1 ~]# service arptables_jf save

e successivamente avviamo il servizio:

[root@lb1 ~]# service arptables_jf start

Facciamo anche in modo che il suddetto servizio venga avviato automaticamente ad ogni boot del sistema:

[root@lb1 ~]# chkconfig --levels 2345 arptables_jf on

Per verificare che tutto stia funzionando correttamente spegnamo momentaneamente l’interfaccia virtuale eth0:1 sul director e proviamo a pingare l’IP assegnatogli:

[root@lb1 ~]# ifdown eth0:1

[root@lb1 ~]# ping 10.2.66.13

se al ping non seguirà nessuna reply allora arptables sta funzionando come dovrebbe.

Test di funzionamento per il bilanciamento del traffico HTTP/HTTPS

Un test banale per appurare che il load balancer funziona bene consiste nel creare due pagine Web leggermente differenti tra di loro (ad esempio nel tag <title> oppure nel <body>). Una di queste pagine dovrà essere caricata sul frontend 1 e l’altra sul frontend 2.

Facendo delle richieste all’indirizzo IP pubblico del director (ad esempio 37.88.91.154), a cui corrisponde l’IP locale e virtuale 10.2.66.13, dovremmo essere reindirizzati ad uno o all’altro frontend.

Stessa prova può essere fatta per testare il protocollo HTTPS, facendo attenzione che il timeout impostato per la persistenza delle sessioni venga rispettato.

Test di funzionamento per il cluster active/standby

Per testare il funzionamento del cluster è sufficiente stoppare il servizio pulse sul nodo attivo (per intenderci, quello su cui abbiamo lanciato il comando watch ipvsadm).

Lanciando il suddetto comando sul nodo di backup dovremmo riconoscere lo stesso output visto in precedenza sull’ex nodo attivo.

Test di carico

Per i test di carico vi rimando a questo post.

Fine della guida.

Alla prossima.

Apache, virtual host ed SSL su CentOS

La cosa fantastica di Apache è che ti consente di gestire i cosiddetti virtual host, ovvero N server Web virtuali, ognuno dei quali presenta le proprie caratteristiche peculiari.

Essi risultano particolarmente comodi quando si ha la necessità di imbastire dei frontend che rispondano per alcuni domini su HTTPS e per altri su HTTP.

logo_apache22.jpg

Come al solito, la presente guida si focalizzerà sulla configurazione del server vera e propria (httpd.conf), e successivamente sulla configurazione di ciascun virtual host.

Configurazione del server

La prima modifica da effettuare nel file di configurazione di Apache riguarda l’abilitazione del protocollo HTTPS. In particolare, esso verrà gestito grazie ad uno specifico modulo, ovvero mod_ssl.so, non presente nell’installazione di default relativa ad Apache.

Ergo, sarà necessario dapprima scaricarlo mediante il comando:

[root@serverweb conf]# yum install mod_ssl

Non ho installato anche openssl in quanto sono partito dal presupposto che siate già in possesso dei certificati X.509 (nel formato Apache) relativi al vostro dominio.

A questo punto è necessario abilitare il suddetto modulo aggiungendo la seguente direttiva nel file /etc/conf/httpd.conf:

LoadModule ssl_module modules/mod_ssl.so

Successivamente, si dovrà procedere con la messa in ascolto sul server della porta TCP 443 (HTTPS).

Per fare ciò si può aggiungere la seguente stringa nel file di configurazione di Apache:

Listen 443

Infine, sempre nel file di configurazione in questione, si dovrà specificare in quale directory sono presenti le direttive relative ai virtual host:

# Include the virtual host configurations:
Include /etc/httpd/vhosts.d/*.conf

Configurazione del virtual host

Posizioniamoci nella directory oggetto dell’include e creiamo un nuovo file di configurazione per il virtual host:

[root@serverweb vhosts.d]# nano virtualhost1.conf

Il cui contenuto sarà simile al seguente:

<VirtualHost *:443>
  ServerName www.miodominio.com

  DocumentRoot /var/www/virtualhosts/www.miodominio.com/htdocs

  SSLEngine on
  SSLCertificateFile /etc/ssl/sslcert.crt
  SSLCertificateKeyFile /etc/ssl/sslkey.pem
  SSLCertificateChainFile /etc/ssl/sf_bundle.crt

  <Directory /var/www/virtual/static.betuniq.info/htdocs>
      AllowOverride All
      Order allow,deny
      Allow from all
   </Directory>

  ErrorLog /var/www/virtualhosts/www.miodominio.com/logs/ssl_error.log
  CustomLog /var/www/virtualhosts/www.miodominio.com/logs/ssl_access.log combined
  #ServerSignature Off

  Redirect 404 /favicon.ico

  <Location /favicon.ico>
   ErrorDocument 404 “No favicon”
  </Location>

</VirtualHost>

Come avrete intuito, il virtual host in questione è in ascolto sulla porta TCP 443, quindi è in grado di servire solo ed esclusivamente il traffico HTTPS:

<VirtualHost *:443>

Con ServerName specifico il dominio associato al virtual host in questione mentre con DocumentRoot indico la direcotry in cui sono presenti le pagine Web (e relativi file, come immagini, fogli di stile, ecc.).

Con SSLEngine on abilito il motore SSL e con le direttive:

SSLCertificateFile /etc/ssl/sslcert.crt
SSLCertificateKeyFile /etc/ssl/sslkey.pem
SSLCertificateChainFile /etc/ssl/sf_bundle.crt

indico rispettivamente il pathname relativo al certificato X.509 (sslcert.crt), della chiave privata RSA (sslkey.pem) e del certificato di terze parti (sf_bundle.crt).

Le altre stringe riguardano semplicemente i file di log e le politiche di overwrite sulla directory (ad esempio mediante file .htaccess).

Ora, affinchè il suddetto dominio rimanga in ascolto sia sulla 443 che sulla 80 (HTTP puro), si può creare un ulteriore file di configurazione, contenente però la direttiva:

<VirtualHost *:80>

nessuna entry associata all’SSL ed ai certificati ma la stessa DocumentRoot del vhost precedente.

Per rendere effettive le modifiche senza creare disservizio è sufficiente lanciare il comando:

[root@serverweb conf]# service httpd reload

Se tale comando non restituisce errori è un ottimo segno.

Per avere ulteriore conferma del corretto funzionamento relativo al nostro server Web, si possono fare due check mediante netstat:

[root@serverweb conf]# netstat -anp | grep :80

e

[root@serverweb conf]# netstat -anp | grep :443

Se il server risulta in Listen è molto probabile che stia funzionando correttamente. Fate comunque delle prove con un browser per esserne certi.

E’ tutto. Alla prossima.

Redirect del traffico in uscita mediante iptables

Iptables, ovvero l’interfaccia a linea di comando del celeberrimo firewall Netfilter, consente di fare dei veri e propri giochi di prestigio.

Ad esempio, potrebbe succedere che, per ragioni di sicurezza, un dato server Web non sia in ascolto sulla classica porta TCP 80, ma su una porta non standard (ad esempio la 9876). Supponiamo, inoltre, che a tale sito debbano accedere gli sviluppatori, che, di volta in volta dovranno forgiare opportune URL con l’estensione :9876.

netfilter.jpg

Per semplicifare loro la vita si può agire direttamente sul firewall/router, facendo un semplice redirect del traffico in uscita diretto alla porta 80 del server Web. Ecco la regola:

iptables -t nat -A OUTPUT -p tcp -d ipserverweb –dport 80 -j DNAT –to-destination ipserverweb:9876
Ovviamente potete lavorare anche di file hosts, aggiungendo dei record A statici che vi possano aiutare a creare le regole più facilmente, senza doversi obbligatoriamente ricordare gli indirizzi IP coinvolti a memoria.
In quest’ultimo caso la suddetta regola diverrebbe:
iptables -t nat -A OUTPUT -p tcp -d miodominio.com --dport 80 -j DNAT --to-destination miodominio.com:9876
Semplice, vero?
A presto.

Logging con Varnish Cache

In questo post ho spiegato, a grandi linee, cos’è Varnish Cache ed a cosa serve. Il problema che mi ponevo era, sostanzialmente, quello di individuare l’IP sorgente delle richieste HTTP, in modo che il log di Apache (httpd) fosse in grado di tenerne traccia.

http, header http, client ip, logging, x-forwarded-for, varnishlog, backand, varnishncsa, httpd, apache

Infatti, nel caso in cui Varnish sia in esecuzione sulla stessa macchina su cui gira Apache, l’IP sorgente delle richieste HTTP sarà sempre e comunque 127.0.0.1 (aka localhost). In particolare, sarà Varnish ad intercettare le suddette richieste sulla porta 80 e, successivamente, ad inoltrarle al server Web di backend.

Sempre nel post in questione ho mostrato una direttiva del file di configurazione di Varnish (dafault.vcl), in cui viene manipolato il dato HTTP, modificandone l’header (precisamente considerando il campo X-FORWARDED-FOR come IP sorgente della richiesta, in modo da inserirlo nel file di log). Inutile dire che tale soluzione, seppure lecita, è sconveniente per due motivi:

1) manipolare i dati HTTP risulta in qualche modo oneroso in termini computazionali;

2) tale operazione potrebbe non andare a buon fine, in quanto il campo X-FORWARDED-FOR potrebbe essere vuoto.

Cosa fare dunque? Semplice, utilizzare due validi strumenti messi a disposizione da Varnish Cache, ovvero varnishncsa e varnishlog.

Il primo ci consente di salvare in un file di log apposito le richieste HTTP che Varnish ha preso in carico. Il secondo, invece, può essere facilmente customizzato, in modo da tenere traccia solo delle informazioni che ci interessano. Personalmente, ritengo che utilizzare entrambi per loggare il traffico utente sia inutile, ecco perchè uso varnishncsa per il logging delle richieste HTTP e varnishlog per tenere traccia delle comunicazioni con i backend.

Ma vediamo adesso come utilizzare i tool in questione (che su CentOS sono semplicemente dei demoni).

Per prima cosa avviamo varnishncsa utilizzando il comando:

[user@host varnish]# service varnishncsa start

Successivamente, facciamo in modo che il suddetto demone entri in esecuzione automaticamente su diversi runlevel, mediante il comando chkconfig:

[user@host varnish]# chkconfig --level 2345 varnishncsa on

A questo punto non ci rimane che configurare varnishlog. Se lanciamo il comando:

[user@host varnish]# varnishlog help

otteniamo una lista delle flag utilizzabili. Quella che a noi interessa è -b, ovvero si vuole imporre a varnishlog di registrare solo gli eventi relativi alla comuncazione con i backend (utile in caso di troubleshooting). Ecco uno stralcio del man che indica il significato della suddetta flag:

 -b     Include log entries which result from communication with a backend server.  If neither -b nor -c  is  specified, varnishlog acts as if they both were.

Modifichiamo il demone, posizionandoci nella directory /etc/init.d e digitando:

[user@host varnish]# nano varnishlog

Nella direttiva:

DAEMON_OPTS="-a -w $logfile -D -P $pidfile"

inseriamo la flag -b. Essa diventerà:

DAEMON_OPTS="-a -b -w $logfile -D -P $pidfile"

Ora impostiamo l’esecuzione automatica del demone in questione (come già fatto per varnishncsa):

[user@host varnish]# chkconfig --level 2345 varnishlog on

Infine, avviamolo:

[user@host varnish]# service varnishlog start

Adesso Varnish Cache sarà in grado di loggare tutti gli eventi che la coinvolgono.

Alla prossima.

Ntop ed HTTPS

Da un po’ di tempo a questa parte ho deciso di rendere raggiungibili i miei server Web solo mediante il protocollo HTTPS. Una volta fatto ciò, ho riscontrato (con mio enorme dispiacere) che l’interfaccia Web di ntop (in ascolto sulla porta 3000 TCP) non era più utilizzabile.

logo-ntop-small.jpg

Proprio per questo motivo (e per non cambiare la regola di forwarding sul mio router e sui miei 2 firewall), ho deciso di effettuare qualche modifica al demone in questione.

In particolare, ho fatto in modo che ntop rimanesse in ascolto sulla porta 3000, accettando però connessioni HTTPS.

Ecco la modifica nello specifico:

start-stop-daemon --start --quiet --name $NAME --exec $DAEMON --
  -d -L -u $USER -w 0 -W 3000 -P $HOMEDIR

ovvero ho disabilitato l’ascolto su HTTP (-w 0) ed abilitato l’ascolto su HTTPS (-W 3000).

Il file da modificare è /etc/init.d/ntop.

A modifiche completate si può procedere con un riavvio del demone, digitando:

nightfly@nightbox:/etc/init.d$ sudo service ntop restart

ed abbiamo finito.

Enjoy.

Swatch: configurazione per il monitoraggio dei server Web

Di seguito riporto la configurazione del mio Swatch, in grado di tenere sotto controllo ciò che accade sul server Web (Apache) che attualmente ho in gestione:

ignore /nagios-plugins/

#HTTP Forbidden
watchfor  /403/
     echo
     mail addresses=vostro.indirizzo@email.it,subject=SWATCH HOME: Access Forbidden

#HTTP Not Found
watchfor  /404/
     echo
     mail addresses=vostro.indirizzo@email.it,subject=SWATCH HOME: Not Found

#HTTP Internal Server Error
watchfor  /500/
     echo
     mail addresses=vostro.indirizzo@email.it,subject=SWATCH HOME: Internal Server Error

#HTTP OK
watchfor  /200/
     echo
     mail addresses=vostro.indirizzo@email.it,subject=SWATCH HOME: Hit OK

apache_display.png

Poichè sul server è presente anche nagios che ogni tanto effettua dei check sull’HTTPS, ho dovuto utilizzare la entry ignore /nagios-plugins/, evitando così di ricevere un’email ad ogni controllo.

Inoltre, sul server Web è presente l’autentica .htaccess, quindi ho messo sotto monitoraggio anche le hit andate a buon fine, in modo da poter verificare quale utente ha effettuato l’accesso alle aree riservate. Ciò è stato possibile anche grazie al fatto che sul server non vi è una grande mole di richieste. In caso contrario avrei certamente evitato una configurazione del genere, pena il rischio di beccarmi un ban sullo smarthost.

Infine, non ho ritenuto opportuno controllare gli errori HTTP 401 (Unauthorized), in quanto tale funzione è svolta egregiamente da fail2ban.

A presto.

Squid e Windows Update

Recentemente ho installato da un amico un proxy basato su Squid. Ora, per bloccare il traffico diretto alle porte http, https ed http-alt ho utilizzato un firewall (Iptables), mentre per il controllo dei contenuti Web mi sono avvalso di squidGuard.

 

squid.jpg

 

Dopo aver effettuato diverse prove di navigazione ed aver constatato che tutto funzionava correttamente, lascio la mia postazione e mi dirigo a casa per godermi un po’ di meritato riposo. Peccato però che dopo qualche minuto mi chiama il mio amico lamentando l’impossibilità di scaricare gli aggiornamenti di Windows.

Uhm, mi sa che qui abbiamo a che fare con Windows Update 5… bene, vorrà dire che forzerò le workstation (4 Win-XP) ad utilizzare il proxy anche in fase di download degli aggiornamenti.

Ritorno dunque dall’amico e comincio a lanciare tale comando su ciascuna postazione di lavoro:

C:\> proxycfg -p proxy.cliente.lan:3128

Successivamente, digito il comando (come ulteriore verifica):

C:\> proxycfg

A questo punto procedo con il download degli aggiornamenti e tutto funziona come dovrebbe.

A presto.

PS: ovviamente in nessuna delle 4 macchine coinvolte veniva utilizzato IE come browser predefinito (per ragioni di sicurezza). In caso contrario avrei potuto lanciare il comando:

C:\> proxycfg -u

al posto di:

C:\> proxycfg -p proxy.cliente.lan:3128

impostando come proxy quello usato da IE.

Fail2ban: una valida contromisura agli attacchi bruteforce

Gli attacchi bruteforce rappresentano ormai una vera rogna per tutti i sys/net admin, in quanto, oltre a riempire i file di log di robaccia inutile, c’è sempre la remota possibilità che vadano a buon fine, compromettendo in todo o in parte la sicurezza dei vari dispositivi. Cosa fare dunque? La risposta è semplice: utilizzare Fail2ban. Questo semplice applicativo (scritto in Python), esamina i file di log relativi ai servizi pubblicati sul nostro sistema (ad esempio SSH, FTP, HTTP, ecc.), riuscendo a riconoscere eventuali attacchi bruteforce (quando i tentativi di login da parte di un determinato IP sorgente superano la soglia massima consentita). Ora, di documentazione su Fail2ban se ne trova davvero parecchia. Questo post, però, vuole concentrarsi non solo sulla corretta installazione/configurazione di tale applicativo, ma anche sulla creazione di un server SMTP locale, il quale, appaggiandosi ad un SMTP esterno (altrimenti detto smarthost), ci permetterà di ricevere le notifiche di ban direttamente sulla nostra casella e-mail pubblica. Per prima cosa, ovviamente, dobbiamo procedere con l’installazione di Fail2ban, digitando:

nightfly@nightbox:~$ sudo apt-get install fail2ban

Ad installazione completa procederemo con la configurazione dello stesso, editando il file jail.conf presente nella directory /etc/fail2ban:

nightfly@nightbox:~$ sudo nano /etc/fail2ban/jail.conf

Le modifiche da apportare sono le seguenti: 1) Nella sezione [DEFAULT] possiamo definire gli IP che non dovranno essere MAI bannati (per non rischiare di buttare fuori uno o più host della nostra LAN), mediante il parametro ignoreip. Ad esempio:

ignoreip = 127.0.0.1 192.168.1.0/24

Il parametro bantime, invece, specifica la durata del ban (in secondi):

bantime = 600

Il parametro maxretry indica il numero massimo di tentativi di login (per default) prima di effettuare il ban. Esso verrà ingorato nel caso in cui sia indicato un maxretry specifico per uno o più servizi in ascolto sulla nostra macchina. Infine, il parametro destmail consente di specificare l’indirizzo e-mail pubblico sul quale inoltrare le notifiche generate da Fail2ban.

destmail = vostro.indirizzo@email.it

2) nella sottosezione ACTION dobbiamo definire l’azione di default che Fail2ban dovrà intraprendere, nel nostro caso il ban dell’IP che ha generato l’attacco ed il successivo invio di una notifica via e-mail direttamente sulla nostra casella di posta. Per fare ciò associamo al parametro action il valore %(action_mw)s:

action = %(action_mw)s

Passiamo ora alla creazione delle jail (prigioni). In particolare, proprio grazie alle jail, fail2ban capirà quali servizi monitorare e proteggere dagli attacchi bruteforce. Occorre dunque abilitare una jail per ciascun servizio in ascolto sulla nostra macchina, ad esempio:

[ssh]

enabled = true
port    = ssh
filter  = sshd
logpath  = /var/log/auth.log
maxretry = 6

come potete notare, il parametro enabled è stato impostato su true, per fare in modo che la jail venga attivata per SSH. Possiamo eseguire la stessa procedura per gli altri servizi:

[apache]

enabled = true
port    = http,https
filter  = apache-auth
logpath = /var/log/apache*/*error.log
maxretry = 6

[vsftpd]

enabled  = true
port     = ftp,ftp-data,ftps,ftps-data
filter   = vsftpd
logpath  = /var/log/vsftpd.log

e così via. Riavviamo dunque Fail2ban per rendere effettive le modifiche:

nightfly@nightbox:~$ sudo /etc/init.d/fail2ban restart

Passiamo ora alla configurazione di un server SMTP locale. Personalmente uso exim4 in quanto è abbastanza sicuro, performante ed allo stesso tempo user-friendly. Per installarlo basta digitare:

nightfly@nightbox:~$ sudo apt-get install exim4

Una volta completata l’installazione del nostro MTA (Message Transfer Agent) dobbiamo configurarlo correttamente. Per fare ciò occorre lanciare il comando:

nightfly@nightbox:~$ sudo dpkg-reconfigure exim4-config

Ecco gli screenshot di configurazione:

exim0.png
exim1.png
exim2.png
exim3.png
exim4.png
exim5.png
exim6.png
exim7.png

Verifichiamo che il nostro MTA funzioni correttamente, utilizzando il comando mail:

mail vostro.indirizzo@email.it

CC:

Subject: prova

prova.

.

Il punto finale serve a far capire all’applicativo mail che abbiamo completato la stesura del messaggio di posta e che quindi può inviarlo al destinatario. Se non vi sono errori di sorta (in output o nel file /var/log/exim4/mainlog), vuol dire che il nostro MTA è configurato correttamente. Non ci resta che riavviare Fail2ban ed alla riattivazione automatica delle jail dovremo ricevere sul nostro indirizzo di posta un’apposita email di notifica. Come comportarsi in caso di attacco Ma, una volta individuati gli indirizzi IP dai quali è scaturito l’attacco, quali sono le azioni che possiamo intraprendere? Bhè, se gli attacchi sono molti e gli IP sempre gli stessi, si potrebbe inviare un’e-mail al servizio abuse relativo all’ISP usato dall’attaccante (magari allegando i file di log). Occorre precisare, però, che nel 99% dei casi l’attacker non si espone direttamente, ma usa dei proxy anonimi come front-end, quindi è molto probabile che la segnalazione all’ISP non rappresenti un’azione risolutiva. Troubleshooting Potrebbe sempre verificarsi il caso in cui venga bannato un host della nostra LAN. In tal caso la prima cosa da fare è listare le regole attive su IPTABLES ed identificare quelle relative a Fail2ban:

nightfly@nightbox:~$ sudo iptables -L

otterremo un output simile al seguente:

Chain INPUT (policy ACCEPT)
target     prot opt source               destination
fail2ban-ssh  tcp  --  anywhere             anywhere            tcp dpt:ssh

Chain FORWARD (policy ACCEPT)
target     prot opt source               destination

Chain OUTPUT (policy ACCEPT)
target     prot opt source               destination

Chain fail2ban-ssh (1 references)
target     prot opt source               destination
DROP       0    --  nightlocal.mylittlelan.org  anywhere
RETURN     0    --  anywhere             anywhere

Da quanto riportato sopra si può facilmente notare che l’host della LAN bannato è nightlocal.mylittlelan.org. Possiamo dunque rimuovere il ban in questione mediante utilizzando la sintassi:

iptables -D <nome della catena> <numero che identifica la regola della catena>

Ad esempio:

nightfly@nightbox:~$ sudo iptables -D fail2ban-ssh 1

Infine, possiamo verificare l’avvenuto sblocco dell’host locale digitando nuovamente:

nightfly@nightbox:~$ sudo iptables -L

Enjoy!