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.

Loved what you just read? Share it!