nginx: Reverse Proxy, Perfect Forward Secrecy
Aufmerksame Leser werden bemerkt haben dass wir uns einen kleinen Hostingcluster geleistet haben. Um die Xen-Maschinen auf diesem möglichst gut abzusichern haben wir uns entschieden diesen keine Internetverbindung zu spendieren.
Da dort hauptsächlich Webanwendungen liegen mag das zunächst kontraproduktiv wirken. Mit einem Reverse-Proxy funktioniert das aber sehr gut. Hardwaretechnisch besteht der Proxy aus zwei Maschinen in einem Active/Passive-Cluster, den man später zum Loadbalancer umrüsten könnte. Softwaretechnisch haben wir uns hier für nginx entschieden.
nginx hat hierbei drei Aufgaben:
- Auslieferung der Inhalte von den nachgelagerten Servern (Reverse Proxy)
- SSL-Verschlüsselung (inkl. Forward Secrecy)
- Ausliefern einer Fehlerseite falls der nachgelagerte Server offline ist (503 Service Unavailable)
Die Verwaltung erfolgt, wie mittlerweile gewohnt, über SaltStack:
# nginx/init.sls
nginx:
pkg:
– installed
/etc/nginx/sites-enabled/default:
file:
– managed
– source: salt://nginx/default
– user: www-data
– group: root
– mode: 664
– require:
– pkg: nginx
/var/www/maintenance/:
file.recurse:
– source: salt://nginx/maintenance/
– user: www-data
– group: www-data
– file_mode: 664
– dir_mode: 755
– require:
– pkg: nginx
/etc/nginx/nginx.conf:
file:
– managed
– source: salt://nginx/nginx.conf
– user: www-data
– group: root
– mode: 664
– require:
– pkg: nginx
/etc/ssl/certs/dhparam.pem:
file:
– managed
– source: salt://nginx/ssl/dhparam.pem
– user: root
– group: www-data
– mode: 640
/etc/ssl/certs/www.foobar.com.crt:
file:
– managed
– source: salt://nginx/ssl/www.foobar.com.crt
– user: root
– group: root
– mode: 644
/etc/ssl/private/www.foobar.com.key:
file:
– managed
– source: salt://nginx/ssl/www.foobar.com.key
– user: root
– group: www-data
– mode: 640
Zunächst wird natürlich das Paket nginx
installiert. Anschließend werden die Konfigurationsdateien, die 502-Statusseite und die nötigen SSL-Zertifikate erzeugt.
Die Datei dhparam.pem
wird für Perfect Forward Secrecy (PFS) benötigt, da es sonst passieren kann dass eine schwächere Verschlüsselung verwendet wird als eigentlich beabsichtigt/möglich. Sie wird mit folgendem Befehl erzeugt:
openssl dhparam -out dhparam.pem 2048
Die nginx.conf
selbst enthält unter anderem alle Default-Settings für alle Virtual Hosts (im Abschnitt http):
# /etc/nginx/nginx.conf
user www-data;
worker_processes 16;
worker_rlimit_nofile 2048;
error_log /var/log/nginx/error.log;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
use epoll;
accept_mutex off;
# multi_accept on;
}
http {
##
# Basic Settings
##
proxy_redirect off;
proxy_pass_header Server;
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;
proxy_set_header X-Scheme $scheme;
proxy_read_timeout 10000;
proxy_ignore_client_abort on;
proxy_connect_timeout 120s;
proxy_busy_buffers_size 16k;
proxy_headers_hash_max_size 512;
proxy_buffering on;
proxy_cache_bypass $http_pragma $http_authorization $cookie_nocache;
add_header Front-End-Https on;
open_file_cache max=10000 inactive=30s;
open_file_cache_valid 60s;
open_file_cache_min_uses 2;
open_file_cache_errors on;
client_max_body_size 500M;
client_body_buffer_size 128;
access_log off;
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 80;
types_hash_max_size 4096;
server_tokens off;
server_names_hash_bucket_size 256;
server_name_in_redirect off;
ssl_session_cache shared:SSL:256m;
ssl_session_timeout 20m;
ssl_ciphers ‘ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA:ECDHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES256-GCM-SHA384:AES128-GCM-SHA256:AES256-SHA256:AES128-SHA256:AES256-SHA:AES128-SHA:DES-CBC3-SHA:HIGH:!aNULL:!eNULL:!EXPORT:!CAMELLIA:!DES:!MD5:!PSK:!RC4’;
ssl_protocols SSLv3 TLSv1 TLSv1.1 TLSv1.2;
ssl_prefer_server_ciphers on;
# For Diffie Hellman Key Exchange:
# Create dhparam.pem: openssl dhparam -out dhparam.pem 2048
ssl_dhparam /etc/ssl/certs/dhparam.pem;
add_header Strict-Transport-Security "max-age=31536000; includeSubdomains";
include /etc/nginx/mime.types;
default_type application/octet-stream;
##
# Gzip Settings
##
gzip on;
gzip_disable "MSIE [1-6]\.";
gzip_vary on;
gzip_proxied any;
gzip_comp_level 7;
gzip_buffers 16 8k;
gzip_http_version 1.0;
gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript;
##
# Virtual Host Configs
##
include /etc/nginx/conf.d/*.conf;
include /etc/nginx/sites-enabled/*;
}
Damit die Einstellungen worker_processes 16;
und worker_rlimit_nofile 8192;
greifen müssen ggf. die max. Open-Files auf dem System erhöht werden. Die worker_processes sollten die Anzahl der CPU-Kerne nicht überschreiten.
Die ssl_ciphers
unterstützen die meisten Clients, eventuell kann es jedoch mit Java6 zu Problemen kommen, aber ich denke dies ist zu vernachlässigen. IE6 und IE8 unter Windows XP unterstützen zudem kein PFS, die SSL-Verbindung an sich ist aber problemlos möglich.
Da auf dem Proxy gzip aktiv ist sollte dieses auf den nachgelagerten Servern abgeschaltet werden.
Zu den anderen Einstellungen sollte man die nginx-Dokumentation zu Rate ziehen um möglichst sinnvolle Werte für das eigene System zu ermitteln.
Die Konfiguration der eigentlichen vHosts erfolgt dann in einer separaten Datei, da die Defaults bereits definiert sind (nginx.conf) ist die noch nötige Konfiguration sehr übersichtlich:
# /etc/nginx/sites-enabled/default
server {
listen 8.8.8.8:80;
server_name foobar.com
www.foobar.com;
rewrite ^ https://$server_name$request_uri? permanent;
}
server {
listen 8.8.8.8:443;
server_name foobar.com
www.foobar.com;
ssl on;
ssl_certificate /etc/ssl/certs/www.foobar.com.crt;
ssl_certificate_key /etc/ssl/private/www.foobar.com.key;
error_page 502 =200 /maintenance/502.html;
location /maintenance/ {
root /var/www;
}
location / {
proxy_pass http://10.0.0.1;
}
}
Der erste server
-Abschnitt sorgt für die HTTP-Unterstützung und leitet alle Anfragen auf HTTPS um. Die zugehörige HTTPS-Konfiguration erfolgt im zweiten server
-Abschnitt.
Wie bereits oben erwähnt liefert der Proxy eine 502-Fehlerseite aus, wenn der nachgelagerte Server nicht antwortet. Damit keine browser-eigene Fehlerseite ausgegeben wird sendet der Proxy statt des 502-Status den Status 200 an den Browser zurück.