Reverse-Proxy mit Nginx: Mehrere Server hinter einer IP per Subdomain ansprechen

Zuhause und in vielen KMUs dürfte die Situation wohl ähnlich sein: Man besitzt einen Internetanschluss mit einer festen oder einer dynamischen IP, der per DynDNS-Dienst über einen Domainnamen erreichbar ist. Durch Portforwarding im Router kann man einzelne Geräte (z.B. Webserver) ins öffentliche Netz bringen, jeden Port jedoch nur einmal benutzen. Man müsste also für zwei Webserver zwei unterschiedliche Ports verwenden und diese beim Aufruf auch so mit angeben. Durch einen Reverse-Proxy kann man jedoch kurze sprechende Adressen verwenden, um die verschiedene Server im internen Netz per Subdomain erreichbar zu machen. In etwa so:

  • blog.indibit.de -> 192.168.236.12 (WordPress auf Server 1)
  • wiki.indibit.de -> 192.168.236.23 (Mediawiki auf Server 2)

Beide Server sind über den Standard-Port 80, bzw. 443 erreichbar sein und können durch die aufgerufene Adresse (Subdomain) blog.indibit.de, bzw. wiki.indibit.de unterschieden werden.

Funktionsweise des Reverse-Proxy

Funktionsweise des Reverse-Proxy

Nur zur Info: das Ganze ist nur ein Beispiel. Beide Server und beide Subdomains sind frei erfunden 😉

Voraussetzungen

Auf das Thema DynDNS möchte ich an dieser Stelle nicht weiter eingehen. Es gibt unsagbar viele Dienste und Möglichkeiten, das umzusetzen. Damit das Konzept so aufgeht, sollte man möglichst einen Domain-Anbieter nutzen, der einen eigenen DynDNS-Dienst bereitstellt. Ich setze nachfolgend voraus, dass die zu verwendenen Subdomains, in meinem Fall blog.indibit.de und wiki.indibit.de, zuverlässig auf den Internetanschluss zeigen, hinter dem sich die Server befinden.

Weiterhin setze ich voraus, dass es bereits ein Linux-System gibt, auf dem wir gleich Nginx als Reverse-Proxy installieren.

Und ich setze voraus, dass das Portforwarding grundsätzlich klappt, ihr wisst, was das ist und wie ihr das in eurem Router einrichtet. Könnt ihr auch gleich machen – die Ports 80 und 443 auf den Linux-Server weiterleiten, auf dem wir Nginx installieren und der damit zum Reverse-Proxy wird.

Für einen Test wäre es sinnvoll, wenn die Server, die sich hinter dem Reverse-Proxy befinden, schon funktionsfähig wären und deren Webseiten im lokalen Netzwerk erreichbar sind.

Nginx installieren

Als Grundsystem benutze ich eine Virtuelle Maschine mit Ubuntu 20.04 LTS, die eine feste IP (192.168.236.3) im internen Netz zugeordnet bekommen hat. Auf der Kommandozeile setzen wir folgende Befehle ab:

System auf den aktuellen Stand bringen:
$ sudo apt update
$ sudo apt upgrade

Nginx installieren:
$ sudo apt install nginx nginx-extras

Nach Abschluss der Installation sollte der Webserver nun online sein, was sich einfach überprüfen lässt, indem man die IP-Adresse in den Browser eintippt. Es zeigt sich die Standardseite von Nginx:

Nginx Standardseite

Welcome to Nginx!

Reverse-Proxy konfigurieren

Wir befinden uns wieder auf der Kommandozeile. Nginx soll in unserem fall nicht als Webserver fungieren, sondern als Reverse-Proxy, daher schalten wir die Standardseite ab…

$ sudo unlink /etc/nginx/sites-enabled/default

… und erzeugen eine neue Konfiguration

$ cd /etc/nginx/sites-available
$ sudo nano reverse-proxy.conf

Hier definieren wir die beiden Server in sogenannten Server-Blocks und sagen Nginx, was er machen soll.

server {
        server_name blog.indibit.de;
        location / {
                proxy_pass      http://192.168.236.12;
        }
}

server {
        server_name wiki.indibit.de;
        location / {
                proxy_pass      http://192.168.236.23;
        }
}

Wir verlassen den Editor und speichern die Änderungen. Anschließend aktivieren wir die Konfiguration,…

$ sudo ln -s /etc/nginx/sites-available/reverse-proxy.conf /etc/nginx/sites-enabled/reverse-proxy.conf

…schauen, ob sie ok ist…

$ sudo nginx -t

…und wenn dem so ist, schalten wir den Reverse-Proxy scharf:

$ sudo nginx -s reload

Es passiert nun folgendes:

Der Besucher gibt in seinem Browser eine der beiden Adressen (blog.indibit.de oder wiki.indibit.de) ein. Über den DNS-Server seines Internetanbieters und den DynDNS-Eintrag bei meinem Domainanbieter landet diese Anfrage nun an meinem Internetanschluss. Mein Router leitet diese Anfrage (Port 80, da Webbrowser) an den Reverse-Proxy weiter. Der Reverse-Proxy wertet nun aus, welche Adresse der Besucher im Browser eingegeben hat und leitet diese Anfrage an den entsprechenden internen Server weiter. Hat der Besucher also wiki.indibit.de eingegeben, so wird die Anfrage an 192.168.236.23 weitergeleitet. Hat er hingegen blog.indibit.de eingegeben, wird die Anfrage an 192.168.236.12 weitergeleitet.

Im Prinzip war es das schon. Wenn man keinen Fehler gemacht hat, funktioniert das System sofort.

Erweiterte Konfiguration

Mit der momentanen Konfiguration funktioniert die Weiterleitung zwar, allerdings können die beiden angeschlossenen Server nicht erkennen, wer darauf zugreift und wie dessen IP-Adresse lautet. Das ist im eigentlich nicht weiter tragisch, aber eventuell benötigt man diese Informationen ja. Wenn diese Informationen am Zielsystem ankommen soll, muss die Konfiguration entsprechend erweitert werden:

server {
    server_name blog.indibit.de;
    location / {
        proxy_pass      http://192.168.236.12;
                proxy_set_header Host $host;
                proxy_set_header X-Real-IP $remote_addr;
                proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
    client_max_body_size 0;
}

server {
    server_name wiki.indibit.de;
    location / {
        proxy_pass      http://192.168.236.23;
                proxy_set_header Host $host;
                proxy_set_header X-Real-IP $remote_addr;
                proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
    client_max_body_size 0;
}

Bei Umgebungen, in denen größere Dateien übertragen werden sollen, zum Beispiel Nextcloud-Installationen, ist außerdem der Parameter client_max_body_size 0; sinnvoll. Dieser hebt die standardmäßige Dateigrößenbeschränkung auf. Bleibt dieser Wert unverändert, können Dateien mit einer maximalen Größe, von 1 MByte hochgeladen werden. Der Wert kann nach folgendem Schema gesetzt werden: 1K, 1M, 1G, 0.

Auch diese Änderung wird erst auf entsprechenden Befehl hin übernommen:

$ sudo nginx -s reload

SSL-Verschlüsselung nutzen (und erzwingen)

Eine verschlüsselte Verbindung ist ein nicht ganz unwichtiger Aspekt. Sie ersetzt zwar keine sicheren Passwörter, dennoch werden Bösewichte daran gehindert, den Datenstrom ohne weiteres mitzulesen. Ein echter Vorteil des Reverse-Proxys ist, dass man mit ihm auch Verbindungen zur Servern absichern kann, die eine Verschlüsselung nicht selbst unterstützen (zum Beispiel der Loxone Miniserver Gen. 1).

Mit Let’s Encrypt gibt es eine kostenfreie Zertifizierungsstelle und certbot nimmt einem die Arbeit fast vollständig ab.

Die Zertfikate von Let’s Encrypt besitzen eine relativ kurze Gültigkeitsdauer (derzeit 90 Tage) und müssen regelmäßig erneuert werden. Auch diese Arbeit nimmt uns certbot ab, indem er täglich per Cron-Job prüft, wielange die Zertifikate noch gültig sind und diese 30 Tage vor Ablauf erneuert. Der Cron-Job wird automatisch eingerichtet.

Also beginnen wir mit der Installation auf der Kommandozeile des Reverse-Proxy-Servers:

$ sudo apt install certbot python3-certbot-nginx

Anschließend führen wir certbot aus:

$ sudo certbot --nginx

Achtung!
Es ist wichtig, dass der Reverse-Proxy soweit läuft und beide internen Server über ihre jeweils zugewiesene Subdomain erreichbar sind, da die Zertifizierung sonst fehlschlägt.

Der Ablauf sieht in etwa so aus:

Saving debug log to /var/log/letsencrypt/letsencrypt.log
Plugins selected: Authenticator nginx, Installer nginx
Enter email address (used for urgent renewal and security notices) (Enter 'c' to
cancel):

Hier hinterlegen wir eine Email-Adresse, über die wir für wichtige Informationen erreichbar sind und drücken Enter.

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Please read the Terms of Service at
https://letsencrypt.org/documents/LE-SA-v1.2-November-15-2017.pdf. You must
agree in order to register with the ACME server at
https://acme-v02.api.letsencrypt.org/directory
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
(A)gree/(C)ancel:

Nutzungsbedingungen mit A akzeptieren.

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Would you be willing to share your email address with the Electronic Frontier
Foundation, a founding partner of the Let's Encrypt project and the non-profit
organization that develops Certbot? We'd like to send you email about our work
encrypting the web, EFF news, campaigns, and ways to support digital freedom.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
(Y)es/(N)o:

Für den Newsletter anmelden (Y) oder lieber keine unnötigen Mails erhalten (N)?

Which names would you like to activate HTTPS for?
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1: blog.indibit.de
2: wiki.indibit.de
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Select the appropriate numbers separated by commas and/or spaces, or leave input
blank to select all options shown (Enter 'c' to cancel):

Da wir certbot mit dem Parameter --nginx aufgerufen haben, erkennt er entsprechend der Konfiguration die beiden Server, die erreichbar sein sollen. Mit dem einfachen Betätigen der Enter-Taste wählen wir beide aus.

Obtaining a new certificate
Performing the following challenges:
http-01 challenge for smarthome.sebastiankoehler.de
http-01 challenge for testserver.sebastiankoehler.de
Waiting for verification...
Cleaning up challenges
Deploying Certificate to VirtualHost /etc/nginx/sites-enabled/reverse-proxy.conf
Deploying Certificate to VirtualHost /etc/nginx/sites-enabled/reverse-proxy.conf

Please choose whether or not to redirect HTTP traffic to HTTPS, removing HTTP access.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1: No redirect - Make no further changes to the webserver configuration.
2: Redirect - Make all requests redirect to secure HTTPS access. Choose this for
new sites, or if you're confident your site works on HTTPS. You can undo this
change by editing your web server's configuration.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Select the appropriate number [1-2] then [enter] (press 'c' to cancel):

In diesem Schritt bietet uns certbot nun an, Nginx so zu konfigurieren, dass eine verschlüsselte Verbindung erzwungen wird. Die Reverse-Proxy-Konfiguration wird in diesem Fall automatisch angepasst und man muss sich um nichts mehr kümmern.

1 keine Änderung vornehmen
2 alle HTTP-Anfragen auf HTTPS umzuleiten

Redirecting all traffic on port 80 to ssl in /etc/nginx/sites-enabled/reverse-proxy.conf
Redirecting all traffic on port 80 to ssl in /etc/nginx/sites-enabled/reverse-proxy.conf

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Congratulations! You have successfully enabled
https://blog.indibit.de and https://wiki.indibit.de

You should test your configuration at:
https://www.ssllabs.com/ssltest/analyze.html?d=blog.indibit.de
https://www.ssllabs.com/ssltest/analyze.html?d=wiki.indibit.de
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

IMPORTANT NOTES:
 - Congratulations! Your certificate and chain have been saved at:
   /etc/letsencrypt/live/blog.indibit.de/fullchain.pem
   Your key file has been saved at:
   /etc/letsencrypt/live/blog.indibit.de/privkey.pem
   Your cert will expire on 2020-11-02. To obtain a new or tweaked
   version of this certificate in the future, simply run certbot again
   with the "certonly" option. To non-interactively renew *all* of
   your certificates, run "certbot renew"
 - If you like Certbot, please consider supporting our work by:

   Donating to ISRG / Let's Encrypt:   https://letsencrypt.org/donate
   Donating to EFF:                    https://eff.org/donate-le

Noch einmal die neue Konfiguration übernehmen:

$ sudo nginx -s reload

Von nun an sollten alle Verbindungen verschlüsselt ablaufen, was man auch mit einem Blick in die automatisch angepasste reverse-proxy.conf erahnen kann.

server {
        server_name blog.indibit.de;

        location / {
                proxy_pass      http://192.168.236.12;
                proxy_set_header Host $host;
                proxy_set_header X-Real-IP $remote_addr;
                proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        }
        client_max_body_size 0;

        listen 443 ssl; # managed by Certbot
        ssl_certificate /etc/letsencrypt/live/blog.indibit.de/fullchain.pem; # managed by Certbot
        ssl_certificate_key /etc/letsencrypt/live/blog.indibit.de/privkey.pem; # managed by Certbot
        include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
        ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
}

server {
        server_name wiki.indibit.de;

        location / {
                proxy_pass      http://192.168.236.23;
                proxy_set_header Host $host;
                proxy_set_header X-Real-IP $remote_addr;
                proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        }
        client_max_body_size 0;

        listen 443 ssl; # managed by Certbot
        ssl_certificate /etc/letsencrypt/live/blog.indibit.de/fullchain.pem; # managed by Certbot
        ssl_certificate_key /etc/letsencrypt/live/blog.indibit.de/privkey.pem; # managed by Certbot
        include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
        ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
}

server {
        if ($host = blog.indibit.de) {
                return 301 https://$host$request_uri;
        } # managed by Certbot

        listen 80;
        server_name blog.indibit.de;
        return 404; # managed by Certbot
}

server {
        if ($host = wiki.indibit.de) {
                return 301 https://$host$request_uri;
        } # managed by Certbot

        listen 80;
        server_name wiki.indibit.de;
        return 404; # managed by Certbot
}

Es wurden nicht nur die SSL-Parameter hinzugefügt, sondern auch zwei zusätzliche Server-Blocks, die alle http-Anfragen mit einer 301-Weiterleitung (permanent verschoben) auf https umleiten. Sollte ein Client sich nicht weiterleiten lassen, bekommt er den Fehler 404 (nicht gefunden) angezeigt.

Renew der Zertifikate

Bleibt eigentlich nur noch eins: prüfen, ob sich die Zertifikate erneuern lassen. Ich hatte ja oben schon geschrieben, dass certbot das alles alleine macht, da er täglich per Cron-Job dazu ermuntert wird. Dennoch kann ein Test nicht schaden.

Im echten Leben würde man dazu folgenden Befehl verwenden:

$ sudo certbot renew

Da die Zertifikate aber noch brandneu sind wird dieser Befehl mit einer entsprechenden Meldung einfach abgebrochen. Um das dennoch zu testen, steht der Parameter --dry-run zur Verfügung. Der gesamte Befehl sieht dann also so aus:

$ sudo certbot renew --dry-run

Wenn alles glatt läuft erhält man folgende Ausgabe:

Saving debug log to /var/log/letsencrypt/letsencrypt.log

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Processing /etc/letsencrypt/renewal/blog.indibit.de.conf
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Cert not due for renewal, but simulating renewal for dry run
Plugins selected: Authenticator nginx, Installer nginx
Renewing an existing certificate
Performing the following challenges:
http-01 challenge for blog.indibit.de
http-01 challenge for wiki.indibit.de
Waiting for verification...
Cleaning up challenges

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
new certificate deployed with reload of nginx server; fullchain is
/etc/letsencrypt/live/blog.indibit.de/fullchain.pem
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
** DRY RUN: simulating 'certbot renew' close to cert expiry
**          (The test certificates below have not been saved.)

Congratulations, all renewals succeeded. The following certs have been renewed:
  /etc/letsencrypt/live/blog.indibit.de/fullchain.pem (success)
** DRY RUN: simulating 'certbot renew' close to cert expiry
**          (The test certificates above have not been saved.)
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

Im Prinzip war das schon alles, was wichtig ist. Aber es geht natürlich noch mehr.

Mehr Struktur bei mehreren Servern

Wenn man nur zwei Server nutzt ist das Ganze noch übersichtlich. Hat man jedoch mehrere Server, oder will sich von vornherein offen halten, später noch etwas zu skalieren, dann kann man die komplette Konfiguration auch modular aufbauen. Vom Grunde her ändert sich dabei garnicht soviel, man teilt die Konfiguration einfach so auf, dass man eine Datei je Server nutzt. Je nach Art des nachgeschalteten Webservers kann die Reihenfolge, wie die Konfiguration geladen wird, entscheidend sein, daher kann die Dateibezeichnung wichtig werden (siehe Abschnitt zum Exchange-Server).

In unserem Beispiel würde das so aussehen:

1. Konfigurationsdatei: /etc/nginx/sites-available/S10_blog.conf mit folgendem Inhalt:

server {
        server_name blog.indibit.de;

        location / {
                proxy_pass      http://192.168.236.12;
                proxy_set_header Host $host;
                proxy_set_header X-Real-IP $remote_addr;
                proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        }
        client_max_body_size 0;

        listen 443 ssl; # managed by Certbot
        ssl_certificate /etc/letsencrypt/live/blog.indibit.de/fullchain.pem; # managed by Certbot
        ssl_certificate_key /etc/letsencrypt/live/blog.indibit.de/privkey.pem; # managed by Certbot
        include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
        ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
}

server {
        if ($host = blog.indibit.de) {
                return 301 https://$host$request_uri;
        } # managed by Certbot

        listen 80;
        server_name blog.indibit.de;
        return 404; # managed by Certbot
}

2. Konfigurationsdatei: /etc/nginx/sites-available/S20_wiki.conf mit folgendem Inhalt:

server {
        server_name wiki.indibit.de;

        location / {
                proxy_pass      http://192.168.236.23;
                proxy_set_header Host $host;
                proxy_set_header X-Real-IP $remote_addr;
                proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        }
        client_max_body_size 0;

        listen 443 ssl; # managed by Certbot
        ssl_certificate /etc/letsencrypt/live/blog.indibit.de/fullchain.pem; # managed by Certbot
        ssl_certificate_key /etc/letsencrypt/live/blog.indibit.de/privkey.pem; # managed by Certbot
        include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
        ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
}

server {
        if ($host = wiki.indibit.de) {
                return 301 https://$host$request_uri;
        } # managed by Certbot

        listen 80;
        server_name wiki.indibit.de;
        return 404; # managed by Certbot
}

Einfach die alte Konfiguration deaktivieren…

$ sudo unlink /etc/nginx/sites-enabled/reverse-proxy.conf

…die beiden neuen Konfigurationsdateien aktivieren…

$ sudo ln -s /etc/nginx/sites-available/S10_blog.conf /etc/nginx/sites-enabled/S10_blog.conf
$ sudo ln -s /etc/nginx/sites-available/S20_wiki.conf /etc/nginx/sites-enabled/S20_wiki.conf

…und die Änderungen übernehmen:

$ sudo nginx -s reload

Nun läuft alles wie vorher, allerdings lassen sich nun auf einfache Art und Weise beliebig Server hinzufügen oder entfernen. Die alte Konfigurationsdatei kann gelöscht werden.

Microsoft Exchange-Server 2016 hinter einem Reverse-Proxy

Alles zuvor beschriebene wird mit den meisten Netzwerkgeräten, die einen Webserver integriert haben, funktionieren. Microsoft Exchange ist jedoch ein Sonderling und muss gesondert berücksichtigt werden, wenn dieser hinter dem Reverse-Proxy arbeiten soll. Bevor ihr das hier Beschriebene ausprobiert stellt zuvor sicher, dass der Exchange-Server zu 100% zuverlässig läuft, wenn die Ports direkt zu ihm weitergeleitet sind.

Folgendes gilt es zu berücksichtigen:

  1. Ein Exchange-Server mag keine Let’s Encrypt-Zertifikate und lässt sich damit auch nicht ohne Fehlermeldung betreiben. Da ich voraussetze, dass der Exchange 100% zuverlässig läuft, besitzt er auch schon ein Zertifikat, das wir zusätzlich für den Reverse-Proxy nutzen.
  2. Die Reverse-Proxy-Konfiguration für den Exchange-Server muss vor allen anderen geladen werden, damit sie einwandfrei funktioniert.
  3. Im IIS-Manager muss Standardauthentifizierung für EWS und mapi aktiviert werden.

Folgende Parameter gelten in meiner Umgebung:

  • Der Exchange-Server soll über owa.mycompany.de und autodiscover.mycompany.de von außen erreichbar sein
  • Die Windows-Server-Domäne heißt ad.mycompany.de
  • Der interne DNS-Name des Exchange-Servers lautet srv-exc1.ad.mycompany.de

Zertifikat installieren

In meinem Fall liegt das Zertifikat des Exchange-Servers als ca-bundle vor und besteht aus 3 Dateien (die Dateinamen können bei euch natürlich anders lauten):

  • cert_exc.ca-bundle
  • cert_exc.crt
  • key.txt

Diese Dateien werden per WinSCP ins Benutzer-Home-Verzeichnis auf dem Reverse-Proxy-Server kopiert. Dann geht es per SSH auf der Kommandozeile weiter: das Zertifikat anpassen und im Zertifikatsspeicher ablegen:

$ sudo cat cert_exc.crt cert_exc.ca-bundle >> /etc/ssl/certs/_exchange-cert.crt
$ sudo cp key.txt /etc/ssl/certs/_exchange-key.txt

Reverse-Proxy-Konfiguration

Wir erzeugen die Konfigurationsdatei /etc/nginx/sites-available/S01_exchange.conf mit folgendem Inhalt:

server {
	listen       80;
	server_name owa.mycompany.de autodiscover.mycompany.de;

	# Redirect any HTTP request to HTTPS
	return 301 https://$server_name$request_uri;

	error_log  /var/log/nginx/exchange-error.log;
	access_log /var/log/nginx/exchange-access.log;
}
 
server {
	listen			443 ssl;
	server_name		owa.mycompany.de autodiscover.mycompany.de;

	# Enable SSL
	ssl_certificate		/etc/ssl/certs/_exchange-cert.crt;
	ssl_certificate_key	/etc/ssl/certs/_exchange-key.txt;
	ssl_session_timeout	5m;

	# Set global proxy settings
	proxy_read_timeout	360;
	proxy_http_version	1.1;
	proxy_buffering		off;
	proxy_request_buffering	off;

	proxy_pass_request_headers	on;

	proxy_pass_header	Date;
	proxy_pass_header	Server;
	proxy_pass_header	Authorization;

	proxy_set_header	Host $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	Accept-Encoding "";
	proxy_set_header	Connection "Keep-Alive";

	more_set_input_headers	'Authorization: $http_authorization';
	more_set_headers	-s 401 'WWW-Authenticate: Basic realm="srv-exc1.ad.mycompany.de"';

	client_max_body_size	0;
 
	location /owa		{ proxy_pass https://srv-exc1.ad.mycompany.de/owa; }
	location /OWA		{ proxy_pass https://srv-exc1.ad.mycompany.de/owa; }
	location /EWS		{ proxy_pass https://srv-exc1.ad.mycompany.de/EWS; }
	location /ews		{ proxy_pass https://srv-exc1.ad.mycompany.de/EWS; }
	location /Microsoft-Server-ActiveSync	{ proxy_pass https://srv-exc1.ad.mycompany.de/Microsoft-Server-ActiveSync; }
	location /mapi		{ proxy_pass https://srv-exc1.ad.mycompany.de/mapi; }
	location /MAPI		{ proxy_pass https://srv-exc1.ad.mycompany.de/mapi; }
	location /rpc		{ proxy_pass https://srv-exc1.ad.mycompany.de/Rpc; }
	location /RPC		{ proxy_pass https://srv-exc1.ad.mycompany.de/Rpc; }
	location /oab		{ proxy_pass https://srv-exc1.ad.mycompany.de/OAB; }
	location /OAB		{ proxy_pass https://srv-exc1.ad.mycompany.de/OAB; }
	location /autodiscover	{ proxy_pass https://srv-exc1.ad.mycompany.de/Autodiscover; }
	location /Autodiscover	{ proxy_pass https://srv-exc1.ad.mycompany.de/Autodiscover; }

	error_log		/var/log/nginx/exchange-ssl-error.log;
	access_log		/var/log/nginx/exchange-ssl-access.log;
}

Anschließend wieder die Konfiguration aktivieren und Nginx neu laden:

$ sudo ln -s /etc/nginx/sites-available/S01_exchange.conf /etc/nginx/sites-enabled/S01_exchange.conf
$ sudo nginx -s reload

Internetinformationsdiense (ISS) konfigurieren

Bleibt noch die Konfiguration der Authentifizierung. Leider kann die kostenfreie Variante von Nginx nicht mit NTLM umgehen, also müssen wir dem Exchange-Server mitteilen, dass er auch Standard-Authentifizierung akzeptieren soll. Dafür starten wir den Internetinformationsdienste (IIS)-Manager, navigieren links im Baum nach SRV-EXC1 -> Sites -> Default Web Site -> EWS, wählen dort Authentifizierung

ISS für Reverse-Proxy konfigurieren

ISS für Reverse-Proxy konfigurieren

…und aktivieren die Standardauthentifizierung:

ISS für Reverse-Proxy konfigurieren (EWS)

ISS für Reverse-Proxy konfigurieren (EWS)

Das Gleiche machen wir nochmal für mapi:

ISS für Reverse-Proxy konfigurieren (MAPI)

ISS für Reverse-Proxy konfigurieren (MAPI)

Die Änderungen sind sofort aktiv, ein Server-Neustart oder Ähnliches ist nicht nötig. Der Exchange-Server sollte nun auch hinter dem Reverse-Proxy erreichbar sein, inkl. Zugriff über Outlook und auf „Automatische Antworten“.

Einschränkungen

Obwohl das ganze im praktischen Einsatz sehr gut funktioniert, gibt es in bestimmten Konstellationen dennoch eine kleine Einschränkung: Wird eine VPN-Verbindung ins Firmennetzwerk hergestellt und Outlook erst danach gestartet, wird die Windows-Authentifizierung NTLM genutzt.

Reverse-Proxy: Outlook-Verbindung über NTLM

Reverse-Proxy: Outlook-Verbindung über NTLM

Wenn die VPN-Verbindung nun getrennt wird, wird auch die Verbindung zu Outlook getrennt, da der Weg über Windows-Authentifizierung nicht mehr zur Verfügung steht. Je nach sonstiger Konfiguration von Outlook wird die Verbindung eventuell nicht von alleine aufgebaut und es erscheint ein Fenster, in dem das Passwort für den Zugang eingegeben werden soll. Selbst, wenn man das Passwort 100 mal eingibt, wird sich Outlook nicht verbinden. In diesem Fall muss man Outlook beenden und erneut starten, damit die Verbindung per Standard-Authentifizierung hergestellt wird.

Reverse-Proxy: Outlook-Verbindung über Standard-Authentifizierung

Reverse-Proxy: Outlook-Verbindung über Standard-Authentifizierung

Der andere Weg, erst Outlook starten und anschließend VPN, funktioniert ohne Unterbrechung, da die Standard-Authentifizierung sowohl über VPN, als auch das Internet zur Verfügung steht.

Es gibt sicherlich Wege, wie man das beheben kann. Zum Beispiel generell nur Standard-Authentifizierung verwenden, lokal, wie über VPN, wie von extern. Oder Outlook zu einem erneuten Verbindungsversuch überreden. Ich habe jedoch bisher noch keinen ausprobiert, da das ganze für mich kaum eine Einschränkung bedeutet und somit kein Bedarf vorhanden ist.

Sebastian

Hinterlasse eine Antwort

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.