Nginx tip & tricks

Da Webmobili Wiki.

Varnish Cache server non supporta SSL perché lo sviluppatore non ha voglia di includerlo (fonte non ha voglia).

Per ovviare al problema è necessario installare nginx , un web-server/reverse-proxy in grado di catturare la chiamata https e redirigerla a Varnish.

Procedura per installazione

Sulla macchina che ha già VARNISH, installare nginx

sudo apt-get install nginx

L'installazione andrà a buon fine ma verrà mostrato un errore, nginx non riesce a partire perché Varnish è già attivo sulla porta 80. È necessario configurarlo in modo che accolga solo le richieste https.

Per prima cosa dobbiamo generare un certificato autofirmato

sudo mkdir /etc/nginx/ssl
sudo openssl req -x509 -nodes -days 3650 -newkey rsa:2048 -keyout /etc/nginx/ssl/nginx.key -out /etc/nginx/ssl/nginx.crt

Alla domanda sul common name è necessario scrivere il nome della macchina sul dominio (lo stesso di a /etc/hostname). Il certificato generato vale solo 10 anni, cambiare il parametro days a piacimento


Per quanto riguarda la configurazione generale di Nginx conviene aggiungere 3 parametri che aumentano il tempo di timeout delle pagine quando sono sotto reverse-proxy dentro alla sezione http { ... } di /etc/nginx/nginx.conf

##
# Reverse-Proxy Settings
##
proxy_connect_timeout 900;
proxy_send_timeout 900;
proxy_read_timeout 900;
send_timeout 900;



Ora apriamo la configurazione di nginx e lo istruiamo per rispondere alle richieste https

sudo nano /etc/nginx/sites-enabled/default

Il seguente è la configurazione del server di test BRUNOPROXY, il valore di server_name deve corrispondere con il common name che è stato dato al certificato.

# Default server configuration
#
server {
	# listen 80 default_server;
	# listen [::]:80 default_server;

	# SSL configuration
	#
	listen 443 ssl default_server;
	listen [::]:443 ssl default_server;


	#root /var/www/html;

	# Add index.php to the list if you are using PHP
	# index index.html index.htm index.nginx-debian.html;

	server_name POCAHONTAS;
	ssl_certificate /etc/nginx/ssl/pocahontas.crt;
	ssl_certificate_key /etc/nginx/ssl/pocahontas.key;

	location / {
		# First attempt to serve request as file, then
		# as directory, then fall back to displaying a 404.
		#try_files $uri $uri/ =404;
		proxy_pass http://127.0.0.1:80;
		proxy_set_header X-Real-IP  $remote_addr;
		proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
		proxy_set_header X-Forwarded-Proto https;
		proxy_set_header X-Forwarded-Port 443;
		proxy_set_header Host $host;
	}
}

Ora tutte le richieste HTTPS passano da nginx che a sua volta le rigira a Varnish sulla porta 80 (riga proxy_pass http://127.0.0.1:80). Vengono servite col certificato autofirmato che non deve essere usato in ambiente di produzione.

Facciamo un restart di ngix

sudo service nginx start

e per testare andare su https://IPPUBBLICO. In caso di Varnish che punta a Windows si arriverà all'IIS. Non è necessario configurare IIS con SSL, possono essere tutti normali websites.

Aggiungere domini certificati

Per creare un VirtualHost con certificato autentico aggiungere un file dentro alla cartella /etc/nginx/sites-available con la configurazione personalizzata che utilizzi i certificati giusti. Per renderlo disponibile creare un symlink dentro alla cartella /etc/nginx/sites-enabled e restartare nginx.

Esempio pratico:

  • Copiare i file del certificato (crt e key) + il certificato intermedio nella cartella /etc/nginx/ssl/. N.B. Se si dispone del file .cer basta rinominare l'estensione in .crt
  • Unire certificato e certificato intermedio in un unico file con il comando
cat nomesito.com_ssl_certificate.crt nomesito.com_ssl_certificate_intermediate.crt >> nomesito.com_ssl_certificate_bundle.crt

Ricordarsi di mettere in /etc/nginx/ssl/ i tre file

  • nomesito.com_ssl_certificate.crt
  • nomesito.com_private_key.key
  • nomesito.com_ssl_certificate_bundle.crt


Per il momento su MULAN c'è solo designbest.com .
wm4pr.com è settato ma non usato. In uso in Test su POCAHONTAS.

  • Creare il virtual host
sudo touch /etc/nginx/sites-available/nomesito.com
sudo nano /etc/nginx/sites-available/nomesito.com

e scriverci dentro qualcosa come

server {
	listen 443;
	ssl on;
	ssl_certificate /etc/nginx/ssl/nomesito.com_ssl_certificate_bundle.crt;
	ssl_certificate_key /etc/nginx/ssl/nomesito.com_ssl_certificate.key;
	
	server_name nomesito.com;

	location / {
		proxy_pass http://127.0.0.1:80;
		proxy_set_header X-Real-IP  $remote_addr;
		proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
		proxy_set_header X-Forwarded-Proto https;
		proxy_set_header X-Forwarded-Port 443;
		proxy_set_header Host $host;
	}
}


  • Pubblicare il virtual host su nginx
sudo ln -s /etc/nginx/sites-available/nomesito.com /etc/nginx/sites-enabled
sudo service nginx restart



Verificare la validità del certificato su siti come questo: SSL Certificate Checker

Forward dell'IP reale del client

Per la natura dell'infrastruttura server, l'IP reale del client viene perso dopo la chiamata a Nginx.
Il giro è
Client -> Nginx -> Varnish -> IIS
di conseguenza ad IIS arriva come client IP quello del Varnish e la geolocalizzazione va a puttane
Per sistemare il problema il dato dev'essere passato tramite le varie request all'interno di un header specializzato: X-FORWARDED-FOR.

Forward di Nginx

Essendo il primo punto di passaggio è l'unico che possiede il reale client-IP. Nella configurazione vista del paragrafo precedente si vede l'assegnazione del client-IP nel req header X-FORWARDED-FOR.

#...
location / {
  proxy_pass http://127.0.0.1:80;
  # Set di un header X-Real-IP che serve a varnish
  proxy_set_header X-Real-IP  $remote_addr;
  # Set dell'header X-Forwarded-For per conservare il real IP tramite le varie request che portano ad IIS
  proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
  proxy_set_header X-Forwarded-Proto https;
  proxy_set_header X-Forwarded-Port 443;
  proxy_set_header Host $host;
}
#...

Forward di Varnish

Nel file di configurazione /etc/varnish/default.vcl piazzare queste righe

sub vcl_recv {
  # Setta l'IP reale del client e lo forwarda
  set req.http.X-Actual-IP = regsub(req.http.X-Forwarded-For, "[, ].*$", "");
  set req.http.X-Forwarded-For = req.http.X-Actual-IP;

  #...


Redirect http to https

Per colpa della configurazione del routing il redirect verso https non può essere fatto a livello applicativo.
Infatti succederebbe la seguente cosa:

  1. La chiamata http arriva al Varnish
  2. Varnish la dirotta verso la macchina Win con IIS
  3. IIS dal suo rewriter trasforma la richiesta in https
  4. Il giro ricomincia passando questa volta da Nginx
  5. Da qui si torna a Varnish, il quale non sapendo gestire una richiesta https, dirotta una normale richiesta http alla macchina Win con IIS
  6. IIS dal suo rewriter trasforma la richiesta in https e sitorna al punto 4. generando un loop infinito



SOLUZIONE:
La gestione del redirect verso HTTPS dev'essere affidata a Varnish.
Dentro al file /etc/varnish/default.vlc aggiungere queste 2 parti di codice

sub vcl_recv {
  # ...

  # Gestione redirect Https
  if(req.http.X-Forwarded-Proto !~ "(?i)https") {
    set req.http.location = "https://" + req.http.host + req.url;
    return (synth(750, "Permanently moved"));
  }

  # altri contenuti
}

sub vcl_synth {

  # ... altri contenuti

  else if(resp.status == 750) {
    set resp.http.Location = "https://" + req.http.host + req.url;
    set resp.status = 301;
    return (deliver);
  }
}

In questo modo si evita il loop infinito. IMPORTANTE ricordarsi che dev'essere Varnish ad occuparsi del redirect https, mai l'applicativo.
Ringrazio Stephen Reese per le info di questa guida.

Leggere i LOG del web-server

Purtroppo i log non potranno più essere letti comodamente dall'IIS di Windows perché davanti a lui c'è nginx.
Il consiglio è quello di creare uno share in Samba come questo

[NginxLogs]
  comment = Cartella di condivisione dei log di nginx
  read only = yes
  locking = no
  path = /var/log/nginx
  guest ok = no

e di rendere il proprio utente facente parte del gruppo adm affinché abbia i permessi di lettura.

  sudo adduser nomeutente adm


LOG configurazione avanzata

Avendo diversi siti su Nginx bisogna dividere i log nelle cartelle
es.

/media/discodati/logs/nomesito1.com
/media/discodati/logs/nomesito2.it
/media/discodati/logs/immagini.sito2.com

assicurandosi di mettere i permessi adatti
es.

sudo chown www-data:adm /media/discodati/logs -R
sudo chmod 775 /media/discodati/logs -R


E di conseguenza configurare i singoli virtual host con la cartella giusta

server {

        # ...
	
	server_name nomesito.com;

        error_log /media/discodati/nomesito.com/error.log;
        access_log /media/discodati/nomesito.com/access.log;

        # ...
}


A questo punto tutti i log verranno riversati nei 2 file error.log e access.log.
Per fare in modo che vengano divisi e compressi (una volta che diventano vecchi) automaticamente è necessario configurare LOGROTATE.

sudo nano /etc/logrotate.d/nginx

e modificare come da esempio

/var/log/nginx/*.log /media/discodati/logs/*/*.log {
	su www-data adm
	daily
	missingok
	rotate 14
	compress
	delaycompress
	notifempty
	create 0640 www-data adm
	sharedscripts
	prerotate
		if [ -d /etc/logrotate.d/httpd-prerotate ]; then \
			run-parts /etc/logrotate.d/httpd-prerotate; \
		fi \
	endscript
	postrotate
		invoke-rc.d nginx rotate >/dev/null 2>&1
	endscript
}

Per forzare il logrotate usare

sudo logrotate -f /etc/logrotate.d/nginx


Ci togliamo dai coglioni Varnish

A causa dell'incapacità di Varnish di gestire https ci siamo rotti i coglioni e vogliamo eliminarlo a favore del solo NGINX.

L'unica cosa che ci tiene legati a Varnish è il fottuto scudo che ci difende dagli attacchi DDOS.

Questa guida mostra come NGINX può integrare lo scudo di fatto sfanculando Varnish.


  • /etc/nginx/sites-available/dbdemo47.com
upstream www.dbdemo47.com {
	server 192.168.250.20:443;
}
upstream dbdemo47.com {
	server 192.168.250.20:80;
}
limit_req_zone $binary_remote_addr zone=mylimit:10m rate=10r/s;

##
# DBDEMO47.COM
#
server {
	listen 80;

	server_name *.dbdemo47.com;

	access_log /var/log/nginx/dbdemo47.com/access.log;
	error_log /var/log/nginx/dbdemo47.com/error.log;

	location ~ ^/(it|fr|de|en) {
		limit_req zone=mylimit burst=20 nodelay;
        proxy_pass http://dbdemo47.com;
		include /etc/nginx/proxy_params;
	}

	location / {
		proxy_pass http://dbdemo47.com;
		include /etc/nginx/proxy_params;
	}
}

server {
	# SSL configuration
	#
	listen 443 ssl;
	listen [::]:443 ssl;
	#
	# Self signed certs generated by the ssl-cert package
	# Don't use them in a production server!
	#
	# include snippets/snakeoil.conf;

	#root /var/www/html;

	index index.html index.htm index.nginx-debian.html;

	server_name *.dbdemo47.com;

	ssl_certificate /etc/nginx/ssl/dbdemo47.com_ssl_certificate_bundle.crt;
	ssl_certificate_key /etc/nginx/ssl/dbdemo47.com_private_key.key;

	access_log /var/log/nginx/dbdemo47.com/access.log;
	error_log /var/log/nginx/dbdemo47.com/error.log;

	location ~ ^/(it|fr|de|en) {
		limit_req zone=mylimit burst=20 nodelay;
		proxy_ssl_server_name on;
		proxy_pass https://www.dbdemo47.com;
		include /etc/nginx/proxy_params;
	}

	location / {
		proxy_ssl_server_name on;
		proxy_pass https://www.dbdemo47.com;
		include /etc/nginx/proxy_params;
	}
}

Verificando che il contenuto di /etc/nginx/proxy_params sia

proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;