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 😉

Inhalt

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

15 Comments

  1. Antworten Christoph Jander

    Moin, moin,

    danke für das ausführliche und auch sehr informative Tutorial. Zur Ergänzung/Korrektur: Wir benutzen seit ca. 2 Jahren Let’s Encrypt-Zerttifikate für den Exchange – ohne Fehlermeldungen oder anderweitige Besonderheiten. Wegen der Bequemlichkeit benutzen wir dafür Certify the web (ist als kostenfreie Variante dann auf glaube ich 10 Domains auf dem Windows-Server beschränkt).

    Cio Christoph

  2. Antworten Tilo

    Hallo Sebastian,
    Vielen Dank für die interessante Anleitung.
    Leider komme ich irgendwie nicht weiter. Bisher habe ich es nur hinbekommen, dass WordPress und Nginx-Reverseproxy nur auf einem Raspberry Pi laufen. Ich möchte aber gerne, dass der Reverseproxy auf einem Raspberry installiert ist und WordPress sowie Nextcloud auf anderen Geräten läuft. Allerdings finde ich nirgends eine funktionierende Lösung dazu. Was muss ich in der entsprechenden Config-Datei ändern, damit das funktioniert?

    Viele Grüße, Tilo

    • Antworten Sebastian

      Hi Tilo,
      Die von mir gepostete Lösung ist grundsätzlich auf getrennten Servern lauffähig. Spezielle Änderungen sind da eigentlich nicht nötig.

  3. Antworten Rüdiger

    Danke für die sehr gute Beschreibung,

    ich habe es alles ohne Probleme mit 5 Servern umgesetzt.
    Nur die automatische Aktualisierung des Letsencrypt Zertifikat funktioniert nicht.
    Ich bekomme von Letsencrypt eine Mail das mein Zertifikat abläuft, aber mein RASPI mit dem Reverse Proxy,
    24/365 am Netz, stellt kein aktualisiertes Zertifikat bereit. Mit dem Befehl crontab -e werden keine Einträge
    angezeigt. Wo wird der automatisch erzeugte Cronjob abgelegt?

  4. Antworten FelixRangel

    Hey vielen dank für diesen Artikel, hat alles wunderbar geklappt!
    Aber wie kann man den den Port ändern (ohne verschlüsselung). Damit auf 5000 weitergeleitet wird. Wenn ich in der config:

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

    die 5000 dazu mache, kommt die Fehlermeldung Bad Gateway von nginx im Browser.

    Danke für deine Antwort!

  5. Antworten joker

    Super Artikel.
    Nachdem ich mir den ganzen Tag um die Ohren gehauen habe mit Apache werde ich die Tage Mal diesen Weg versuchen.

    Allerdings habe ich noch eine Frage.

    Die Subdomains sind bei dyndns individuell festgelegt oder können frei erfunden und geändert werden?

  6. Antworten piwi

    Hi Sebastian,
    vielen Dank für diesen hilfreichen Artikel.
    Wie kann ich nachträglich nach dem Aufruf von sudo certbot –nginx noch einen server hinzufügen ?

  7. Antworten Nutzer

    Danke für diese sehr hilfreiche Anleitung, die ich leider erst nach Stunden Rumsucherei gefunden habe.
    Dank ihr funktioniert mein Reverse Proxy jetzt endlich wie gewünscht.

  8. Antworten Thomas Müller

    Hallo Sebastian,
    das ist ne schlichte gute Anleitung 🙂

    Ich bin darauf gestoßen, da ich bei meiner Konfiguration das Phänomen habe, dass die Subdomain-Blöcke nur mit listen 80 funktionieren, nicht mit 443. Die Umleitung auf https wird laut Browser vertraut hergestellt und funktioniert.

    Ich möchte es verstehen und wenn möglich auf listen 443 umstellen können.

    Hast du mir Rat?

    Viele Grüße
    Thomas

  9. Antworten hevilp

    Hey,

    bist du sicher das das funktioniert mit 2 x listen auf port 80 oder 443?
    nginx: [warn] conflicting server name „DOMAIN2“ on 0.0.0.0:80, ignored
    nginx[12673]: nginx: [warn] conflicting server name „DOMAIN1“ on 0.0.0.0:443, ignored

    ngninx versucht bei der vorgestellten kombination doch für jede domain auf port 80 und 443 bei SSL zu horchen. bei mir klappt das nicht….

  10. Antworten wing

    Hallo,

    danke für die sehr gute Anleitung. Ich habe auch Exchange 2016 laufen, nutze aber Lets Encrypt. Kann ich das dann trotzdem nutzen?
    Danke

  11. Antworten oberfragger

    Moin,

    das ist mal eine schöne, strukturierte und dockerlos Anleitung. Danke dafür. Ich kämpf mich gerade durch.

    2 kleinere Hinweise über die ich jetzt bereits gestolpert bin.

    Vor dem „sudo nginx -s reload“ muss man unbedingt den service mit „service nginx start“ starten, ansonsten gibts die Fehlermeleung: „[error]: invalid PID number „“ in „/var/run/nginx.pid““

    Und natürlich mussd ann auch noch der Router auf den ports 80, 81 und 443 auf den nginx via port-forwarding geändert werden. Daran hing ich irgendwie fest…

    • Antworten Sebastian

      „Normalerweise“ wird nginx nach der Installation automatisch gestartet, daher hatte ich es mir gespart, den Server extra zu starten.

      Das Port-Forwarding steht im Abschnitt „Voraussetzungen“, 3. Absatz 😉

      Aber freut mich, dass dir die Anleitung geholfen hat.

  12. Antworten SolarGuido

    Hallo Sebastian, Danke für die Anleitung hat leider bei mir nicht geklappt da ich den Apache Server wegen emoncms weiter benötige, habe nicht rausgefunden wieman den webserver bei Nginx deaktiviert. Denn beim starten von Nginx stellt dieser fest das der Port 80 bereits belegt ist. Ich habe NGINX erst vor ein paar Tagen entdeckt und dacht edas ist die ideale Lösung für meine Anwendungen, habe auch nicht gefunden das man den WebServer einfach deaktiviert und NGINX nur als ReverseProxy nutzen kann. Wenn es doch irgendwie geht, wäre ich für den Tipp super Dankbar.

Hinterlasse eine Antwort

Deine E-Mail-Adresse wird nicht veröffentlicht.

WordPress Cookie Hinweis von Real Cookie Banner