Categories
Development Linux

Man In The Middle (ZScaler)

Version 2 aus dem Beispiel Reverse Proxy mit OneLogin soll zu Testzwecken in der Cloud zum Laufen gebracht werden.

Bei dem PoC handelt es sich um einen Apache HTTP-Server mit simpler Public Page und OneLogin Authentifizierung für den Zugriff auf die Private Page: der ShowHeaders Anwendung in einem Apache Tomcat-Server. Beide Server laufen in Docker Containern, die über Docker Compose gestartet werden.

Cloud Server einrichten

Auf dem Server muss Docker etc. eingerichtet werden:

sudo su

yum install docker -y
systemctl umask docker
systemctl start docker
docker version

curl -L https://github.com/docker/compose/releases/download/v2.2.3/docker-compose-`uname -s`-`uname -m` -o /usr/bin/docker-compose
chmod +x /usr/bin/docker-compose
docker-compose --version
# # Output:
# Docker Compose version v2.2.3

yum install git -y
mkdir /app
cd /app
git clone https://github.com/DerIngo/DockerOneLoginApacheSample.git

vim /app/DockerOneLoginApacheSample/version2/reverseproxy/conf/reverseproxy.conf

Fehler

Auf dem lokalen Entwicklerlaptop läuft der PoC und entsprechend soll er auch in der Cloud gestartet werden:

sudo su
cd /app/DockerOneLoginApacheSample/version2
docker-compose up

Zuerst sieht alles wie gewohnt aus, bis Maven ShowHeaders bauen soll:

root@showheaders:/usr/local/tomcat/ShowHeaders# mvn package
[INFO] Scanning for projects...
[INFO]
[INFO] ------------------------< deringo:ShowHeaders >-------------------------
[INFO] Building ShowHeaders 0.0.1-SNAPSHOT
[INFO] --------------------------------[ war ]---------------------------------
Downloading from central: https://repo.maven.apache.org/maven2/org/apache/tomcat/tomcat-catalina/8.5.53/tomcat-catalina-8.5.53.pom
[INFO] ------------------------------------------------------------------------
[INFO] BUILD FAILURE
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  0.956 s
[INFO] Finished at: 2022-06-07T12:28:46Z
[INFO] ------------------------------------------------------------------------
[ERROR] Failed to execute goal on project ShowHeaders: Could not resolve dependencies for project deringo:ShowHeaders:war:0.0.1-SNAPSHOT: Failed to collect dependencies at org.apache.tomcat:tomcat-catalina:jar:8.5.53: Failed to read artifact descriptor for org.apache.tomcat:tomcat-catalina:jar:8.5.53: Could not transfer artifact org.apache.tomcat:tomcat-catalina:pom:8.5.53 from/to central (https://repo.maven.apache.org/maven2): Transfer failed for https://repo.maven.apache.org/maven2/org/apache/tomcat/tomcat-catalina/8.5.53/tomcat-catalina-8.5.53.pom: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target -> [Help 1]
[ERROR]
[ERROR] To see the full stack trace of the errors, re-run Maven with the -e switch.
[ERROR] Re-run Maven using the -X switch to enable full debug logging.
[ERROR]
[ERROR] For more information about the errors and possible solutions, please read the following articles:
[ERROR] [Help 1] http://cwiki.apache.org/confluence/display/MAVEN/DependencyResolutionException

Es kann eine Dependency nicht aufgelöst werden und im Text findet sich dieser Satz: "PKIX path building failed". Also irgendwas mit Zertifikaten, wie mir die Erfahrung sagt.

Nach einer Recherche ist klar: In dem Cloud Setup wird der ausgehende Traffic durch einen Proxy geleitet und dessen Zertifikate sind nicht in den Standard-Zertifikaten enthalten.

Simple Lösung: Validierung aushebeln

Die schnellste Lösung: Einfach auf jedwede Validierung verzichten.

Aus Stackoverflow:

You can disable SSL certificate checking by adding one or more of these command line parameters:

  • -Dmaven.wagon.http.ssl.insecure=true - enable use of relaxed SSL check for user generated certificates.
  • -Dmaven.wagon.http.ssl.allowall=true - enable match of the server's X.509 certificate with hostname. If disabled, a browser like check will be used.
  • -Dmaven.wagon.http.ssl.ignore.validity.dates=true - ignore issues with certificate dates.

Official documentation: http://maven.apache.org/wagon/wagon-providers/wagon-http/

Here's the oneliner for an easy copy-and-paste:

-Dmaven.wagon.http.ssl.insecure=true -Dmaven.wagon.http.ssl.allowall=true -Dmaven.wagon.http.ssl.ignore.validity.dates=true

Umsetzung

In dem Dockerfile von ShowHeaders wird der Maven Aufruf um obige Parameter ergänzt:

WORKDIR ShowHeaders
# aus
    RUN mvn package
# wird
    RUN mvn package -Dmaven.wagon.http.ssl.insecure=true -Dmaven.wagon.http.ssl.allowall=true -Dmaven.wagon.http.ssl.ignore.validity.dates=true

und schon kann das Image wieder erfolgreich gebaut werden.

Der gleiche Fehler

Die Images werden gebaut, die Container starten und die Public Page wird korrekt angezeigt auf http://localhost:80/public

Aber: die Private Page auf http://localhost:80/private wird nicht angezeigt, bzw. wird nicht mal das OneLogin Login angezeigt.

Das Problem findet sich in der OpenID Konfiguration für OneLogin:

OIDCProviderMetadataURL https://deringo-dev.onelogin.com/oidc/2/.well-known/openid-configuration

Per HTTPS soll die Außenwelt erreicht werden, doch auch hier ist wieder der Proxy dazwischen.

Der Aufruf mittels http wird direkt auf https umgeleitet.

Die Validierung kann hier nicht so einfach ausgehebelt werden, falls überhaupt.

Lösung: Zertifikat(e) hinzufügen

Lösungsansatz: Zertifikatskette herunterladen und dem Truststore hinzufügen.

Der erste Ansatz, wie an die Zertifikate gelangt werden kann fand sich auf StackExchange:

openssl s_client -showcerts -servername deringo-dev.onelogin.com -connect deringo-dev.onelogin.com:443 | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' > mycert.pem

Das funktioniert prinzipiell, dauert aber sehr lange. Außerdem wird in einem ausführlichen Kommentar darauf hingewiesen, dass die Intermediate Certificates so nicht gezogen würden.

Eine ausführliche Anleitung findet sich auf Baeldung, deren Ansatz ist wesentlich schneller (von mir nicht auf Intermediate C. getestet):

openssl s_client -connect deringo-dev.onelogin.com:443 -showcerts  mycert.pem

Den Inhalt der heruntergeladenen Zertifikatsdatei in Plain Text anzeigen lassen:

openssl x509 -in mycert.pem -noout -text

Auf der Seite sind noch viele weitere Beispiele.

Die Zertifikate importieren:

cp mycert.pem /usr/local/share/ca-certificates/mycert.crt
update-ca-certificates

Umsetzung

Das Dockerfile des Reverse Proxies:

FROM httpd:2.4
RUN apt update && apt install -y \
        libapache2-mod-auth-openidc \
        ca-certificates
RUN openssl s_client -connect deringo-dev.onelogin.com:443 -showcerts  mycert.pem && \
    cp mycert.pem /usr/local/share/ca-certificates/mycert.crt && \
    update-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"]

Anschließend das Image neu bauen:

docker-compose build reverseproxy

Test

Leider kann momentan nur auf der Konsole getestet werden:

curl localhost/private/

Aber der Docker Output sieht nicht gut aus: "SSL certificate problem: unable to get local issuer certificate":

version2-reverseproxy-1  | [Tue Jun 07 13:48:25.115383 2022] [auth_openidc:error] [pid 9:tid 140428228966144] [client 172.21.0.1:57644] oidc_util_http_call: curl_easy_perform() failed on: https://deringo-dev.onelogin.com/oidc/2/.well-known/openid-configuration (SSL certificate problem: unable to get local issuer certificate)
version2-reverseproxy-1  | [Tue Jun 07 13:48:25.115513 2022] [auth_openidc:error] [pid 9:tid 140428228966144] [client 172.21.0.1:57644] oidc_provider_static_config: could not retrieve metadata from url: https://deringo-dev.onelogin.com/oidc/2/.well-known/openid-configurationversion2-reverseproxy-1  | 172.21.0.1 - - [07/Jun/2022:13:48:25 +0000] "GET /private/ HTTP/1.1" 500 531
version2-reverseproxy-1  | 172.21.0.1 - - [07/Jun/2022:13:48:27 +0000] "GET /private/ HTTP/1.1" 302 478

Allerdings, nach einem Neustart sieht es doch gut aus in der Konsole:

 curl localhost/private/


302 Found

Found

The document has moved here.

Anschließend wieder gestoppt, Reverse Proxy Image neu bauen lassen, Container wieder gestartet: Beim ersten curl kommt der "SSL certificate problem: unable to get local issuer certificate"-Fehler, bei weiteren curls der "The document has moved"-Hinweis.

Schauen wir mal, wie das im Container aussieht:

Vorbereitung:

# frisch starten:
docker-compose down -v
docker-compose build reverseproxy
docker-compose up
# vor dem ersten Aufruf: Ab in den Container:
docker exec -it version2-reverseproxy-1 bash

Im Container:

# Im Container:
apt install curl

# 1. Aufruf
curl https://deringo-dev.onelogin.com/oidc/2/.well-known/openid-configuration
curl: (60) SSL certificate problem: unable to get local issuer certificate
More details here: https://curl.se/docs/sslcerts.html

curl failed to verify the legitimacy of the server and therefore could not
establish a secure connection to it. To learn more about this situation and
how to fix it, please visit the web page mentioned above.

# 2. Aufruf
curl https://deringo-dev.onelogin.com/oidc/2/.well-known/openid-configuration
curl: (60) SSL certificate problem: unable to get local issuer certificate
More details here: https://curl.se/docs/sslcerts.html

curl failed to verify the legitimacy of the server and therefore could not
establish a secure connection to it. To learn more about this situation and
how to fix it, please visit the web page mentioned above.

# 3. Aufruf
curl https://deringo-dev.onelogin.com/oidc/2/.well-known/openid-configuration
{"acr_values_supported":["onelogin:nist:level:1:re-auth"],"authorization_endpoint":"https://deringo-dev.onelogin.com/oidc/2/auth","claims_parameter_suppor[...]

Ich habe es noch ein paar mal ausprobiert, ungefähr jedes zweite bis dritte Mal hat es funktioniert.

Offensichtlich ist da noch ein LoadBalancer zwischen. 🤦‍♂️🤦‍♂️🤦‍♂️

Recherche

Im Container:

apt install iputils-ping

ping deringo-dev.onelogin.com

Mehrmaliges pingen gab zwei verschiedene IPs zurück.

apt install nmap

nmap deringo-dev.onelogin.com
Starting Nmap 7.80 ( https://nmap.org ) at 2022-06-07 14:18 UTC
Nmap scan report for deringo-dev.onelogin.com (52.29.255.229)
Host is up (0.0052s latency).
Other addresses for deringo-dev.onelogin.com (not scanned): 52.29.255.230
rDNS record for 52.29.255.229: ec2-52-29-255-229.eu-central-1.compute.amazonaws.com
Not shown: 994 filtered ports
PORT     STATE SERVICE
21/tcp   open  ftp
53/tcp   open  domain
80/tcp   open  http
443/tcp  open  https
554/tcp  open  rtsp
1723/tcp open  pptp

Aha! Es gibt zwei IPs zu dem Domainnamen und anscheinend ist OneLogin in den AWS.

Nochmal ein anderer Test; Wie ist das denn, wenn ich nicht im Container, sondern direkt vom Host curle:

curl https://deringo-dev.onelogin.com/oidc/2/.well-known/openid-configuration

Und siehe da: Auf dem Host funktioniert jeder Aufruf!

Mal schauen, ob wir die Certs finden:

locate *.crt
unter Anderen:
/etc/pki/ca-trust/source/anchors/ZscalerCloudChain.crt

Ab in den Container damit:

docker cp /etc/pki/ca-trust/source/anchors/ZscalerCloudChain.crt version2-reverseproxy-1:/tmp

im Container hinzufügen & testen:

cp /tmp/ZscalerCloudChain.crt /usr/local/share/ca-certificates/ZscalerCloudChain.crt
update-ca-certificates

curl https://deringo-dev.onelogin.com/oidc/2/.well-known/openid-configuration

Jeder Aufruf funktioniert, diesmal sieht es wirklich gut aus.

Umsetzung

Das Dockerfile des Reverse Proxies:

FROM httpd:2.4
RUN apt update && apt install -y \
        libapache2-mod-auth-openidc \
        ca-certificates
COPY /etc/pki/ca-trust/source/anchors/ZscalerCloudChain.crt /usr/local/share/ca-certificates/ZscalerCloudChain.crt
RUN update-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"]

Anschließend das Image neu bauen:

docker-compose build reverseproxy

Ich fasse es nicht:

Step 3/6 : COPY /etc/pki/ca-trust/source/anchors/ZscalerCloudChain.crt /usr/local/share/ca-certificates/ZscalerCloudChain.crt
1 error occurred:
        * Status: COPY failed: file not found in build context or excluded by .dockerignore: stat etc/pki/ca-trust/source/anchors/ZscalerCloudChain.crt: filedoes not exist, Code: 1

"you can only use files in your Dockerfile that are within the build context. Usually, this is ., so the Dockerfile's directory. " Copy file with absolute path to Docker Container using a Dockerfile - Stack Overflow

Nächster Versuch:

FROM httpd:2.4
RUN apt update && apt install -y \
        libapache2-mod-auth-openidc \
        ca-certificates
COPY ./ZscalerCloudChain.crt /usr/local/share/ca-certificates/ZscalerCloudChain.crt
RUN update-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"]
cp /etc/pki/ca-trust/source/anchors/ZscalerCloudChain.crt /app/DockerOneLoginApacheSample/version2/reverseproxy/ZscalerCloudChain.crt
docker-compose build reverseproxy
docker-compose up

Test

curl localhost/private/

Keine Fehler im Log! 🎉

Test im Browser mit Login

Steht noch aus, aktuell ist der Server nur über Console zu erreichen.

UPDATE: Inzwischen wurde der Zugriff per Browser auf den Server freigeschaltet (bzw. eine fehlerhafte Netzwerkkonfiguration gefixt) und die Public Seiten sind zugänglich.

Bei Aufruf der Private Seiten kommt allerdings eine Fehlermeldung:

Die Lösung wird im nächsten Post erarbeitet.

2 replies on “Man In The Middle (ZScaler)”

Leave a Reply

Your email address will not be published. Required fields are marked *