Der letzte Post endete mit einer Fehlermeldung:
Analyse
Der Fehler trat auf, nachdem ich meinen PoC von meinem Entwicklungsrechner in die Cloud transferierte und dort laufen ließ und dort testen wollte.
Auf der Console des Servers sieht der Aufruf mittels "curl http://localhost/private/index.html" auch erstmal gut aus.
Der Zugriff aus dem Browser mittels IP "http://<server-ip>/private/index.html" schlägt mit obiger Fehlermeldung komplett fehl.
Lokal nachstellen
Um das Problem lokal auf dem Entwicklerlaptop nachstellen zu können, muss erstmal die lokale (Docker-)IP ermittelt werden:
ping host.docker.internal Ping wird ausgeführt für host.docker.internal [192.168.2.149] mit 32 Bytes Daten: Antwort von 192.168.2.149: Bytes=32 Zeit<1ms TTL=128 Antwort von 192.168.2.149: Bytes=32 Zeit<1ms TTL=128 Antwort von 192.168.2.149: Bytes=32 Zeit<1ms TTL=128
Der Aufruf der PRIVATE Page, nach OneLogin Login versteht sich, funktioniert über localhost:
Der Aufruf der PRIVATE Page funktioniert nicht über die IP:
Auszug aus dem Logfile des Apache HTTP Servers:
reverseproxy_1 | [Thu Jun 09 13:00:54.048377 2022] [auth_openidc:error] [pid 256:tid 140563693147904] [client 172.24.0.1:33902] oidc_authenticate_user: the URL hostname (localhost) of the configured OIDCRedirectURI does not match the URL hostname of the URL being accessed (192.168.2.149): the "state" a nd "session" cookies will not be shared between the two!, referer: http://192.168.2.149/ reverseproxy_1 | 172.24.0.1 - - [09/Jun/2022:13:00:54 +0000] "GET /private/index.html HTTP/1.1" 500 531
Der Fehler besagt also, "the URL hostname (localhost) of the configured OIDCRedirectURI does not match the URL hostname of the URL being accessed (192.168.2.149)".
OneLogin Konfigurationsanpassung
Die Redirect URIs Liste muss um die IP des Servers erweitert werden:
Allerdings ist http als Protokoll lediglich für localhost zugelassen, nicht aber für alle anderen URIs, wie zB unsere Server IP oder später der Domain Name des Servers.
Die Redirect URI muss also mit https eingetragen werden.
Daraus ergibt sich allerdings auch, dass unser Server über https erreichbar sein muss!
Apache mit HTTPS
Ein selbstsigniertes Zertifikat ist für unseren PoC vollkommen ausreichend. Eine Signierung durch zB Let's Encrypt ist nicht notwendig.
Für diesen PoC brauchen wir lediglich den Reverse Proxy, der im ersten Schritt eine Seite über http und https ausliefern kann. Später kommt dann die Authentifizierung mit OneLogin hinzu.
Schritt 1: http
Das Dockerfile des Apache aus den vorherigen PoCs uA schon mit OpenID Module:
FROM httpd:2.4 RUN apt update && apt install -y \ libapache2-mod-auth-openidc \ ca-certificates RUN cp /usr/lib/apache2/modules/mod_auth_openidc.so /usr/local/apache2/modules/ RUN mv conf/httpd.conf conf/container_httpd.conf CMD ["httpd-foreground"]
Gestartet wird mit Docker Compose:
version: '3.8' services: reverseproxy: build: ./reverseproxy hostname: reverseproxy volumes: - ./reverseproxy/public_html:/usr/local/apache2/htdocs - ${PWD}/reverseproxy/conf/reverseproxy_httpd.conf:/usr/local/apache2/conf/httpd.conf - ${PWD}/reverseproxy/conf/reverseproxy.conf:/usr/local/apache2/conf/reverseproxy.conf - ${PWD}/reverseproxy/conf/reverseproxy-ssl.conf:/usr/local/apache2/conf/reverseproxy-ssl.conf ports: - 80:80 - 443:443
Die Einbindung der Konfigurationsdateien erfolgt über reverseproxy_httpd.conf, die in den Container als httpd.conf hineinkopiert wird:
# load original configuration first Include conf/container_httpd.conf # customized configuration ServerName reverseproxy Include conf/reverseproxy.conf Include conf/reverseproxy-ssl.conf
Die Konfiguration für http erfolgt in reverseproxy.conf:
<VirtualHost *:80> ServerAdmin deringo@github.com DocumentRoot "/usr/local/apache2/htdocs" ServerName localhost </VirtualHost>
Die Konfiguration für https wird später in reverseproxy-ssl.conf erfolgen, die Datei ist im ersten Schritt leer.
Starten mittels Docker Compose:
docker-compose up
Die Seite ist sowohl über localhost, als auch über IP erreichbar:
Schritt 2: Port 443
Der Server soll über den https-Port 443 die Seiten ausliefern.
Dazu wird die http/Port 80-Konfiguration angepasst kopiert.
Wichtig ist auch das "Listen 443", das am Anfang hinzugefügt werden muss.
Listen 443 <VirtualHost *:443> ServerAdmin deringo@github.com DocumentRoot "/usr/local/apache2/htdocs" ServerName localhost </VirtualHost>
Server neu durchstarten:
docker exec -it selfsignedtest_reverseproxy_1 bash
apachectl configtest && apachectl restart
Die Seite ist über http://localhost:443 erreichbar. Richtig: http, nicht https! Verschlüsselt wird hier noch nix.
Schritt 3: https
Als ersten Teilschritt wird die reverseproxy-ssl.conf mit der Default-SSL-Konfiguration überschrieben und alle Kommentare gelöscht.
Folgende Änderungen werden gemacht:
ServerName localhost:443 ServerAdmin deringo@github.com SSLEngine off #SSLCertificateFile "/usr/local/apache2/conf/server.crt" #SSLCertificateKeyFile "/usr/local/apache2/conf/server.key"
Server neu starten, anschließend ist die Seite weiterhin unverschlüsselt über http://localhost:443 erreichbar.
Als nächster Teilschritt wird SSLEngine on gestellt und die beiden CertificateFile Einträge wieder ent-kommentiert.
Außerdem müssen die Module mod_socache_shmcb.so und mod_ssl.so geladen werden.
LoadModule socache_shmcb_module modules/mod_socache_shmcb.so LoadModule ssl_module modules/mod_ssl.so ServerName localhost:443 ServerAdmin deringo@github.com SSLEngine on SSLCertificateFile "/usr/local/apache2/conf/server.crt" SSLCertificateKeyFile "/usr/local/apache2/conf/server.key"
Ein Neustart des Servers schlägt jetzt fehl, da die CertificateFiles noch nicht existieren.
Certificate Files erzeugen lassen:
openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout /usr/local/apache2/conf/server.key -out /usr/local/apache2/conf/server.crt
Server neu durchstarten und der unverschlüsselte Aufruf wird abgewiesen:
Der verschlüsselte Aufruf über https://localhost funktioniert:
Allerdings wird die Seite als "Nicht sicher" angezeigt und es musste einmalig ein entsprechender Hinweis weggeklickt werden, da das Zertifikat nicht von einer vertrauenswürdigen Stelle signiert wurde.
Da am Ende praktisch nichts von der Standard-Konfiguration für SSL geändert wurde, wird die Konfiguration wieder vereinfacht:
LoadModule socache_shmcb_module modules/mod_socache_shmcb.so LoadModule ssl_module modules/mod_ssl.so Include conf/extra/httpd-ssl.conf
Schritt 4: OneLogin
Die OneLogin Konfiguration wird hinzugefügt:
## Default SSL LoadModule socache_shmcb_module modules/mod_socache_shmcb.so LoadModule ssl_module modules/mod_ssl.so Include conf/extra/httpd-ssl.conf ## LoadModule proxy_module modules/mod_proxy.so LoadModule xml2enc_module modules/mod_xml2enc.so LoadModule proxy_html_module modules/mod_proxy_html.so LoadModule proxy_connect_module modules/mod_proxy_connect.so LoadModule proxy_http_module modules/mod_proxy_http.so LoadModule auth_openidc_module modules/mod_auth_openidc.so <VirtualHost localhost:443> ServerAdmin deringo@github.com DocumentRoot "/usr/local/apache2/htdocs" ServerName localhost:443 ## mod_auth_openidc ## https://github.com/zmartzone/mod_auth_openidc #this is required by mod_auth_openidc OIDCCryptoPassphrase <INSERT-HERE a-random-secret> OIDCProviderMetadataURL <INSERT-HERE> OIDCClientID <INSERT-HERE> OIDCClientSecret <INSERT-HERE> # OIDCRedirectURI is a vanity URL that must point to a path protected by this module but must NOT point to any content OIDCRedirectURI https://localhost/private/redirect_uri ## OIDCScope params ## to put params including roles into header OIDCScope "openid email profile groups params" <Location /private/> AuthType openid-connect Require valid-user </Location> </VirtualHost>
Alle Seiten sind über https erreichbar.
Aber leider auch die Private Page OHNE dass vorher ein Login über OneLogin erfolgen musste.
Irgendwie klappt das nicht mit dem überschreiben/erweitern der default SSL Konfiguration. Also doch alles in eine Datei:
LoadModule proxy_module modules/mod_proxy.so LoadModule xml2enc_module modules/mod_xml2enc.so LoadModule proxy_html_module modules/mod_proxy_html.so LoadModule proxy_connect_module modules/mod_proxy_connect.so LoadModule proxy_http_module modules/mod_proxy_http.so LoadModule auth_openidc_module modules/mod_auth_openidc.so LoadModule socache_shmcb_module modules/mod_socache_shmcb.so LoadModule ssl_module modules/mod_ssl.so Listen 443 SSLCipherSuite HIGH:MEDIUM:!MD5:!RC4:!3DES SSLProxyCipherSuite HIGH:MEDIUM:!MD5:!RC4:!3DES SSLHonorCipherOrder on SSLProtocol all -SSLv3 SSLProxyProtocol all -SSLv3 SSLPassPhraseDialog builtin SSLSessionCache "shmcb:/usr/local/apache2/logs/ssl_scache(512000)" SSLSessionCacheTimeout 300 ## ## SSL Virtual Host Context ## <VirtualHost _default_:443> # General setup for the virtual host ServerName localhost:443 ServerAdmin deringo@github.com DocumentRoot "/usr/local/apache2/htdocs" ErrorLog /proc/self/fd/2 TransferLog /proc/self/fd/1 # SSL Engine Switch: # Enable/Disable SSL for this virtual host. SSLEngine on SSLCertificateFile "/usr/local/apache2/conf/server.crt" SSLCertificateKeyFile "/usr/local/apache2/conf/server.key" <FilesMatch "\.(cgi|shtml|phtml|php)$"> SSLOptions +StdEnvVars </FilesMatch> <Directory "/usr/local/apache2/cgi-bin"> SSLOptions +StdEnvVars </Directory> BrowserMatch "MSIE [2-5]" \ nokeepalive ssl-unclean-shutdown \ downgrade-1.0 force-response-1.0 CustomLog /proc/self/fd/1 \ "%t %h %{SSL_PROTOCOL}x %{SSL_CIPHER}x \"%r\" %b" ## mod_auth_openidc ## https://github.com/zmartzone/mod_auth_openidc #this is required by mod_auth_openidc OIDCCryptoPassphrase <INSERT-HERE a-random-secret> OIDCProviderMetadataURL <INSERT-HERE> OIDCClientID <INSERT-HERE> OIDCClientSecret <INSERT-HERE> # OIDCRedirectURI is a vanity URL that must point to a path protected by this module but must NOT point to any content OIDCRedirectURI https://localhost/private/redirect_uri ## OIDCScope params ## to put params including roles into header OIDCScope "openid email profile groups params" <Location /private/> AuthType openid-connect Require valid-user </Location> </VirtualHost>
So funktioniert es wie gewünscht und die Private Page wird erst nach Login angezeigt.
Allerdings nur über localhost. Nicht über die IP.
Die OIDCRedirectURI wird von https://localhost/private/redirect_uri auf https://192.168.2.149/private/redirect_uri geändert und schon funktioniert es genau anders herum: Nur über die IP, nicht über localhost.
Das ist nicht ganz so, wie ich mir das vorgestellt habe, aber für den PoC sollte es ausreichend sein.
Schritt 5: Nur https
Alles was über http rein kommt, soll über https weitergeleitet werden:
LoadModule rewrite_module modules/mod_rewrite.so <VirtualHost *:80> ServerAdmin deringo@github.com DocumentRoot "/usr/local/apache2/htdocs" ServerName localhost <IfModule mod_rewrite.c> RewriteEngine On RewriteCond %{HTTPS} off RewriteRule (.*) https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L] </IfModule> </VirtualHost>
Schritt 6: Docker
Die Generierung der Zertifikate erfolgte im laufenden Container. Dieser Schritt soll schon beim Erzeugen des Images erfolgen.
Ein Problem war, dass dies interaktiv erfolgen musste. Mit dem Parameter -subj "/" können Key & Zertifikat ohne Interaktion generiert werden. Im Subject könnten Daten eingetragen werden, dies ist aber für den PoC gar nicht erforderlich.
Einmal der Befehl vollständig, wie er im Container ausgeführt werden könnte:
openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout /usr/local/apache2/conf/server.key -out /usr/local/apache2/conf/server.crt -subj "/"
Der Befehl wird in das Dockerfile hinzugefügt.
Schritt 7: Trusted Certs
Aus den Erkenntnissen des letzten Posts folgt, dass die Möglichkeit vorgesehen werden muss, eine vertrauenswürdige Zertifikatskette einzubinden.
Dazu müssen die *.crt-Dateien in das conf-Verzeichnis kopiert werden.
FROM httpd:2.4 RUN apt update && apt install -y \ libapache2-mod-auth-openidc \ ca-certificates RUN cp /usr/lib/apache2/modules/mod_auth_openidc.so /usr/local/apache2/modules/ RUN mv conf/httpd.conf conf/container_httpd.conf RUN openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout /usr/local/apache2/conf/server.key -out /usr/local/apache2/conf/server.crt -subj "/" COPY ./conf/*.crt /usr/local/share/ca-certificates/ RUN update-ca-certificates CMD ["httpd-foreground"]
Schritt 8: ShowHeaders
Ich hatte ShowHeaders zwischendurch vermisst, so kommt es am Ende auch wieder mit rein.
Auch das ShowHeaders Image wird für Trusted Certs erweitert.
GitHub
Die Dateien zu diesem Post sind im OneLogin-GitHub-Projekt unter version7 zu finden.