SSL Termination mit stud und haproxy
| lion | disqus
stud ist ein “Scalable TLS Unwrapping Daemon” [stud Github Seite]. Was heisst, dass stud ein Service ist, der SSL bzw. TLS Verbindungen entgegen nimmt, entschlüsselt und den Inhalt anschließend weiterleitet.
Der weitere Software Stack (Load Balancer, Webserver, usw.) braucht sich dann nicht mehr um die Eigenheiten von verschlüsselten Verbindungen zu kümmern. Auch besitzt stud eine sehr gute Performance [SSL Benchmark Blog Post] [SSL Benchmark Round 2 Blog Post]
Mit stud als SSL-Terminierer kann dann auch der Load Balancer und der Webserver direkt auf die nun unverschlüsselten HTTP-Header zugreifen und diese gegebenfalls anpassen.
Im folgenden setzen wir stud zusammen mit haproxy auf. Stud kümmert sich um die SSL Verbindungen und haproxy load balanced die Verbindungen.
Installation stud
git clone https://github.com/bumptech/stud.git
cd stud
make
sudo make install
womöglich muss zu erst noch libev und openssl installiert werden. Unter debian-basierten Systemen geht das mit:
apt-get install libev-dev libssl-dev
Konfiguration stud
Zunächst generieren wir eine standard Konfigurationsdatei.
stud --default-config > stud.conf
Anschließend ändern wir frontend und backend. Das backend ist unser haproxy, den wir noch konfigurieren werden.
frontend = "[*]:443"
backend = "[127.0.0.1]:8443"
Auch praktisch ist der daemon mode
daemon = on
Jetzt brauchen wir nur noch ein Zertifikat. Zum Testen generieren wir uns ein selbst-signiertes Zertifikat. Eine Alternative wäre startssl.com.
openssl req \
-x509 -nodes -days 365 \
-newkey rsa:2048 -keyout mycert.pem -out mycert.pem
Die folgenden Fragen einfach bestmöglich beantworten, nur bei Common Name muss man besonders aufpassen. Hier ist der hostname einzutragen, zB. www.giantmonkey.de. Anschließend tragen wir dann noch unser Zertifikat in die stud.conf ein.
pem-file = "/etc/stud/mycert.pem"
Mehr Infos zu openssl kann man unter http://www.madboa.com/geek/openssl finden.
Zuletzt konfigurieren wir noch die Unterstützung für das PROXY Protokoll, damit stud haproxy alle nötigen Infos zum Request, wie zB. die Client IP-Adresse, mitteilen kann.
write-proxy = on
Installation haproxy
Wir brauchen eine aktuelle haproxy Version, die das PROXY Protokoll unterstützt. Für Debian ist diese jedoch leider nicht im Repository. Unter http://www.roedie.nl/downloads/haproxy gibt es jedoch eine aktuelle Version. Nach dem Runterladen eines .deb-Paketes kann dieses mit dpkg installiert werden.
dpkg -i haproxy_1.5.0-1~dev10-1_amd64.deb
Konfiguration haproxy
Zunächst definieren wir ein Frontend welches die Requests von stud entgegen nimmt.
frontend https
bind 127.0.0.1:8443 accept-proxy
reqadd X-Forwarded-Proto:\ https
option forwardfor
# important for reqadd and option forwardfor
option http-server-close
default_backend webapp
Viele Web Frameworks bieten die Möglichkeit Login-Seiten nur auf HTTPS geschützten Verbindungen anzuzeigen. Da stud aber bereits das HTTPS-Handling übernommen hat müssen wir dem Web Framework mitteilen, dass es sich in der Tat um eine geschützte Verbindung handelt. Dazu setzen wir den X-Forwarded-Proto Header. Die ursprüngliche IP-Adresse übertragen wir im X-Forwarded-For Header (mittels option forwardfor).
Optional können wir im haproxy jetzt noch ein zweites Frontend definieren, welches Port 80 ohne accept-proxy binded und keinen X-Forwarded-Proto Header setzt. Dann können wir unseren Server sowohl über HTTPS als auch über normales HTTP erreichen.
Dann definieren wir noch ein Backend, welches die Verbindungen per Round Robin auf die Application Server verteilt.
backend webapp
option httpchk
balance roundrobin
server webapp_1 46.4.143.210:80 check
server webapp_2 46.4.143.211:80 check
haproxy besitzt noch viele weitere Optionen, die gesetzt werden können und sollten, insbesondere timeouts, max connections, user und logging. Hier sei nur kurz auf die haproxy doku verwiesen.
Testen
Natürlich können wir unser Setup ganz normal mit einem Webbrowser testen. Wir können aber auch unser Setup mit einem RFC 5077 Client direkt testen.
git clone https://github.com/vincentbernat/rfc5077.git
cd rfc5077
Im Makefile müssen wir noch -Werror deaktivieren. Die erste Zeile im Makefile sollte dann wie folgt aussehen.
CFLAGS=-g -Wall -ansi -std=c99 -D_POSIX_SOURCE -D_BSD_SOURCE -D_GNU_SOURCE
Anschließend können wir den rfc5077-client kompilieren.
make rfc5077-client Und dann unseren Server testen. Im folgenden Beispiel habe ich das Tool auf einen unserer Server laufen lassen:
./rfc5077-client staging.zettlr.com [✔] Check arguments.
[✔] Solve staging.zettlr.com:
│ Got 1 result:
│ 46.4.143.221
[✔] Prepare tests.
[✔] Run tests without use of tickets.
[✔] Display result set:
│ IP address │ Try │ Cipher │ Reuse │ SSL Session ID │ Master key │ Ticket │ Answer
│ ───────────────────────────────┼─────┼───────────────────────┼───────┼─────────────────────┼─────────────────────┼────────┼───────────────────
│ 46.4.143.221 │ 0 │ AES256-SHA │ ✘ │ 00DD4183A681E09C33… │ D2AFEEB518FED8BBE2… │ ✘ │ HTTP/1.1 200 OK
│ 46.4.143.221 │ 1 │ AES256-SHA │ ✔ │ 00DD4183A681E09C33… │ D2AFEEB518FED8BBE2… │ ✘ │ HTTP/1.1 200 OK
│ 46.4.143.221 │ 2 │ AES256-SHA │ ✔ │ 00DD4183A681E09C33… │ D2AFEEB518FED8BBE2… │ ✘ │ HTTP/1.1 200 OK
│ 46.4.143.221 │ 3 │ AES256-SHA │ ✔ │ 00DD4183A681E09C33… │ D2AFEEB518FED8BBE2… │ ✘ │ HTTP/1.1 200 OK
│ 46.4.143.221 │ 4 │ AES256-SHA │ ✔ │ 00DD4183A681E09C33… │ D2AFEEB518FED8BBE2… │ ✘ │ HTTP/1.1 200 OK
[✔] Dump results to file.
[✔] Run tests with use of tickets.
[✔] Display result set:
│ IP address │ Try │ Cipher │ Reuse │ SSL Session ID │ Master key │ Ticket │ Answer
│ ───────────────────────────────┼─────┼───────────────────────┼───────┼─────────────────────┼─────────────────────┼────────┼───────────────────
│ 46.4.143.221 │ 0 │ AES256-SHA │ ✘ │ C6ADB3E5993CD8CAF6… │ F6874187C40198B4A9… │ ✔ │ HTTP/1.1 200 OK
│ 46.4.143.221 │ 1 │ AES256-SHA │ ✔ │ C6ADB3E5993CD8CAF6… │ F6874187C40198B4A9… │ ✔ │ HTTP/1.1 200 OK
│ 46.4.143.221 │ 2 │ AES256-SHA │ ✔ │ C6ADB3E5993CD8CAF6… │ F6874187C40198B4A9… │ ✔ │ HTTP/1.1 200 OK
│ 46.4.143.221 │ 3 │ AES256-SHA │ ✔ │ C6ADB3E5993CD8CAF6… │ F6874187C40198B4A9… │ ✔ │ HTTP/1.1 200 OK
│ 46.4.143.221 │ 4 │ AES256-SHA │ ✔ │ C6ADB3E5993CD8CAF6… │ F6874187C40198B4A9… │ ✔ │ HTTP/1.1 200 OK
[✔] Dump results to file.
Schlusswort
Wer noch mehr Performance braucht, sollte eine neue openssl Version installieren mit AES-NI Unterstützung und auch die Benutzung der Intel Accelerator Engine in Betracht ziehen. Mehr dazu unter http://vincent.bernat.im/en/blog/2011-ssl-benchmark-round2.html.
Und wenn man mehr als einen Rechner braucht um SSL Verbindungen mit stud zu terminieren, dann sollte man einen shared cache für die SSL Sessions konfigurieren. Dazu mehr in einem zukünftigen Blog-Eintrag.