diff --git a/README.md b/README.md index c2f3a91..21b6c9b 100644 --- a/README.md +++ b/README.md @@ -36,9 +36,8 @@ Basisrouten nach dem Start: Plattformhinweise: - Linux: Docker Engine oder Docker Desktop -- macOS: Docker Desktop +- macOS: Docker Desktop (falls `make` fehlt -> `xcode-select --install`) - Windows: Docker Desktop + WSL Integration aktiv -- macOS: falls `make` fehlt -> `xcode-select --install` ## Schnellstart diff --git a/challenges/README.md b/challenges/README.md index 13e2011..f54bf4c 100644 --- a/challenges/README.md +++ b/challenges/README.md @@ -161,7 +161,7 @@ curl http://localhost:8080/service/a ./scripts/compose.sh logs reverse-proxy ``` -### 6a) Load Balancing konfigurieren +### 7) Load Balancing konfigurieren **Ziel** - Kernfunktion eines Reverse Proxys praktisch zeigen. @@ -185,7 +185,7 @@ for i in $(seq 1 8); do done ``` -### 6b) Response Header Minimization +### 8) Response Header Minimization **Ziel** - Unnoetige Header aus Upstream-Responses entfernen. @@ -200,7 +200,7 @@ done **Abgrenzung zu Challenge 4** - Challenge 4 setzt aktive Schutz-Header. -- Challenge 6b entfernt unnoetige Header aus Upstream-Responses. +- Challenge 8 entfernt unnoetige Header aus Upstream-Responses. **Warum wichtig** - Weniger Response-Metadaten bedeuten weniger Angriffsoberflaeche fuer Fingerprinting und Reconnaissance. @@ -210,7 +210,7 @@ done curl -I http://localhost:8080/service/a ``` -### 6c) Debugging Challenge (kaputte Config reparieren) +### 9) Debugging Challenge (kaputte Config reparieren) **Ziel** - Fehlerdiagnose in Nginx ueben. @@ -223,6 +223,11 @@ curl -I http://localhost:8080/service/a - Mindestens 2-3 Fehler finden und fixen. - Symptome und Diagnoseweg erklaeren. +**Erwartete Fehlerarten (Beispiel aus `nginx.broken.conf`)** +- Upstream-Name passt nicht zum referenzierten Namen in `proxy_pass`. +- Falscher Upstream-Port (`8080` statt `80`). +- Fehlender Trailing Slash in `proxy_pass` bei Prefix-Location. + **Warum wichtig** - Debugging unter Druck ist Praxisalltag; diese Aufgabe trainiert systematisches Vorgehen mit Logs und Config-Tests. @@ -237,7 +242,7 @@ curl http://localhost:8080/service/b ## Hard (TLS) -### 7) HTTPS von 0 mit Easy-RSA +### 10) HTTPS von 0 mit Easy-RSA **Ziel** - Eigene CA + Server-Zertifikat fuer `localhost` erstellen. @@ -259,11 +264,11 @@ curl http://localhost:8080/service/b curl https://localhost:8443/service/a ``` -### 8) HTTP -> HTTPS Redirect +### 11) HTTP -> HTTPS Redirect **Voraussetzung** -- Challenge 7 muss abgeschlossen sein. -- Nutze deine bestehende `nginx.conf` aus Challenge 7 als Basis. +- Challenge 10 muss abgeschlossen sein. +- Nutze deine bestehende `nginx.conf` aus Challenge 10 als Basis. **Ziel** - HTTP sauber auf HTTPS umlenken. @@ -283,10 +288,10 @@ curl https://localhost:8443/service/a curl -I http://localhost:8080/service/a ``` -### 9) TLS Haertung + Chain Check + HSTS +### 12) TLS Haertung + Chain Check + HSTS **Voraussetzung** -- Challenge 7 und 8 muessen abgeschlossen sein. +- Challenge 10 und 11 muessen abgeschlossen sein. - Erweitere dieselbe `nginx.conf` weiter. **Ziel** @@ -316,7 +321,7 @@ openssl s_client -connect localhost:8443 -servername localhost ## Bonus Expert -### 10) Wireshark: HTTP vs HTTPS sauber analysieren +### 13) Wireshark: HTTP vs HTTPS sauber analysieren **Ziel** - Nachweisbar zeigen, was im Klartext sichtbar ist und was durch TLS geschuetzt wird. diff --git a/challenges/easyrsa-hints.md b/challenges/easyrsa-hints.md index 1e1088e..4bf9cff 100644 --- a/challenges/easyrsa-hints.md +++ b/challenges/easyrsa-hints.md @@ -39,15 +39,29 @@ cd certs/easyrsa ./easyrsa sign-req server localhost ``` -## 5) Dateien fuer Nginx bereitstellen +## 5) Nur Runtime-Zertifikate bereitstellen (nicht komplette PKI mounten) -Typische Dateien: +Nutze fuer den Container nur die benoetigten Laufzeitdateien: -- `pki/issued/localhost.crt` -- `pki/private/localhost.key` -- `pki/ca.crt` +- `localhost.crt` (Server-Zertifikat) +- `localhost.key` (Server-Private-Key) -Danach in `proxy/nginx.conf` TLS aktivieren und in `docker-compose.yml` Port `443` mappen. +Beispiel: + +```bash +mkdir -p ../../live +cp pki/issued/localhost.crt ../../live/ +cp pki/private/localhost.key ../../live/ +chmod 600 ../../live/localhost.key +``` + +Warum nicht die ganze PKI mounten? + +- In `pki/` liegen CA-relevante Dateien. +- Runtime-Container sollen keine unnoetigen PKI/CA-Dateien sehen. +- Prinzip: minimal noetige Secrets in Runtime. + +Danach in `proxy/nginx.conf` TLS aktivieren und in `docker-compose.yml` Port `443` + Cert-Volume mappen. ### Compose-Mindestbeispiel @@ -60,24 +74,49 @@ services: volumes: - ./proxy/nginx.conf:/etc/nginx/nginx.conf:ro,z - ./proxy/html:/usr/share/nginx/html:ro,z - - ./certs/easyrsa/pki:/etc/nginx/pki:ro,z + - ./certs/live:/etc/nginx/certs:ro,z ``` -### Nginx-Mindestbeispiel +### Nginx-Mindestbeispiel (HTTP + HTTPS) ```nginx +server { + listen 80; + server_name _; + + location = /healthz { + default_type text/plain; + return 200 "ok\n"; + } + + location / { + return 301 https://$host:8443$request_uri; + } +} + server { listen 443 ssl; server_name localhost; - ssl_certificate /etc/nginx/pki/issued/localhost.crt; - ssl_certificate_key /etc/nginx/pki/private/localhost.key; + ssl_certificate /etc/nginx/certs/localhost.crt; + ssl_certificate_key /etc/nginx/certs/localhost.key; ssl_protocols TLSv1.2 TLSv1.3; add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always; + add_header X-Content-Type-Options "nosniff" always; + add_header X-Frame-Options "DENY" always; + add_header Referrer-Policy "strict-origin-when-cross-origin" always; + add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always; + add_header Cross-Origin-Opener-Policy "same-origin" always; + add_header Cross-Origin-Resource-Policy "same-origin" always; + location /service/a { proxy_pass http://backend_a/; } + + location /service/b { + proxy_pass http://backend_b/; + } } ``` @@ -86,14 +125,14 @@ server { Fedora: ```bash -sudo cp pki/ca.crt /etc/pki/ca-trust/source/anchors/htl-workshop-root-ca.crt +sudo cp certs/easyrsa/pki/ca.crt /etc/pki/ca-trust/source/anchors/htl-workshop-root-ca.crt sudo update-ca-trust ``` Ubuntu/Debian: ```bash -sudo cp pki/ca.crt /usr/local/share/ca-certificates/htl-workshop-root-ca.crt +sudo cp certs/easyrsa/pki/ca.crt /usr/local/share/ca-certificates/htl-workshop-root-ca.crt sudo update-ca-certificates ``` @@ -114,7 +153,7 @@ curl https://localhost:8443/service/a Falls CA nicht global importiert ist: ```bash -curl --cacert pki/ca.crt https://localhost:8443/service/a +curl --cacert certs/easyrsa/pki/ca.crt https://localhost:8443/service/a ``` ## 8) Typische Fehlerbilder diff --git a/proxy/html/challenges.html b/proxy/html/challenges.html index 0b5c3c7..f8f3177 100644 --- a/proxy/html/challenges.html +++ b/proxy/html/challenges.html @@ -172,6 +172,7 @@ Solutions Backend A Backend B + Backend C (ab C2) @@ -199,7 +200,7 @@ curl http://localhost:8080/service/b -
+
Easy 2) backend-c hinzufuegen

Muss: Compose-Service + Upstream + Route /service/c.

Zusatz: backends/c/index.html ist vorhanden und darf angepasst werden.

@@ -246,7 +247,7 @@ curl http://localhost:8080/service/b
- Medium 6a) Load Balancing + Medium 7) Load Balancing

Zweite Instanz von Backend A (backend-a2) einbauen und Round-Robin zeigen.

Warum wichtig: Lastverteilung ist Kernnutzen eines Reverse Proxys fuer Skalierung und Verfuegbarkeit.

for i in $(seq 1 8); do
@@ -255,17 +256,18 @@ done
- Medium 6b) Response Header Minimization + Medium 8) Response Header Minimization

Mindestens einen Backend-Response-Header per proxy_hide_header ausblenden.

-

Abgrenzung zu #4: #4 setzt Schutz-Header, #6b entfernt unnoetige Header aus Upstream-Responses.

+

Abgrenzung zu #4: #4 setzt Schutz-Header, #8 entfernt unnoetige Header aus Upstream-Responses.

Warum wichtig: Weniger preisgegebene Metadaten erschweren Fingerprinting und zielgerichtete Angriffe.

curl -I http://localhost:8080/service/a
- Medium 6c) Debugging Challenge + Medium 9) Debugging Challenge

Mit proxy/nginx.broken.conf arbeiten, Fehler finden und reparieren.

Warum wichtig: In der Praxis geht es oft um Diagnose unter Zeitdruck, nicht nur um Greenfield-Konfiguration.

+

Typische Fehler in der kaputten Datei: Upstream-Name-Mismatch, falscher Port, fehlender Trailing Slash in proxy_pass.

curl http://localhost:8080/service/a
 curl http://localhost:8080/service/b
 ./scripts/compose.sh logs reverse-proxy
@@ -276,22 +278,22 @@ curl http://localhost:8080/service/b

Hard (TLS)

- Hard 7) HTTPS von 0 (Easy-RSA) + Hard 10) HTTPS von 0 (Easy-RSA)

Zertifikat fuer localhost, Port 8443:443, Root-CA importiert.

Warum wichtig: TLS-Grundaufbau ist Voraussetzung fuer vertrauliche und manipulationssichere Kommunikation.

curl https://localhost:8443/service/a
- Hard 8) HTTP -> HTTPS Redirect -

Voraussetzung: Challenge 7 abgeschlossen. Bestehende Config weiterverwenden.

+ Hard 11) HTTP -> HTTPS Redirect +

Voraussetzung: Challenge 10 abgeschlossen. Bestehende Config weiterverwenden.

Warum wichtig: Redirect verhindert unabsichtliche Klartext-Nutzung und erzwingt den sicheren Transport.

curl -I http://localhost:8080/service/a
- Hard 9) TLS Haertung + Chain + HSTS -

Voraussetzung: Challenge 7 und 8 abgeschlossen. Gleiche Config weiter erweitern.

+ Hard 12) TLS Haertung + Chain + HSTS +

Voraussetzung: Challenge 10 und 11 abgeschlossen. Gleiche Config weiter erweitern.

Warum wichtig: Erst Haertung + HSTS reduzieren Downgrade-Risiken und sorgen fuer dauerhaft sichere Clients.

curl -I https://localhost:8443/service/a
 openssl s_client -connect localhost:8443 -servername localhost
@@ -302,7 +304,7 @@ openssl s_client -connect localhost:8443 -servername localhost

Bonus Expert

- Expert 10) Wireshark: HTTP vs HTTPS sauber ausarbeiten + Expert 13) Wireshark: HTTP vs HTTPS sauber ausarbeiten

Warum wichtig: Sichtbarkeit auf Paketebene macht den Sicherheitsgewinn von TLS fuer alle nachvollziehbar.

  1. HTTP auf 8080 mitschneiden und Klartext zeigen.
  2. diff --git a/proxy/html/hints.html b/proxy/html/hints.html index 273adaa..c4aebcd 100644 --- a/proxy/html/hints.html +++ b/proxy/html/hints.html @@ -134,6 +134,7 @@ Solutions Backend A Backend B + Backend C (ab C2) @@ -173,6 +174,7 @@ cd certs/easyrsa ./easyrsa build-ca nopass ./easyrsa gen-req localhost nopass ./easyrsa sign-req server localhost +

    Nur Runtime-Certs mounten (z. B. certs/live), nicht die komplette PKI.

    Fedora CA Import

    diff --git a/proxy/html/index.html b/proxy/html/index.html index e1b88f9..9b63ab0 100644 --- a/proxy/html/index.html +++ b/proxy/html/index.html @@ -114,6 +114,7 @@ Solutions Backend A Backend B + Backend C (ab C2) diff --git a/proxy/html/solutions.html b/proxy/html/solutions.html index ff57923..6c2da08 100644 --- a/proxy/html/solutions.html +++ b/proxy/html/solutions.html @@ -173,6 +173,7 @@ Solutions Backend A Backend B + Backend C (ab C2) @@ -272,7 +273,7 @@ access_log /var/log/nginx/access.log workshop;
- Medium 6a) Load Balancing + Medium 7) Load Balancing

Dateien: docker-compose.yml, proxy/nginx.conf, backends/a2/index.html

Commands noetig: ja

Compose (backend-a2):

@@ -293,10 +294,10 @@ done
- Medium 6b) Response Header Minimization + Medium 8) Response Header Minimization

Dateien: proxy/nginx.conf

Commands noetig: ja

-

Abgrenzung zu #4: #4 fuegt Schutz-Header hinzu, #6b entfernt unnoetige Upstream-Metadaten.

+

Abgrenzung zu #4: #4 fuegt Schutz-Header hinzu, #8 entfernt unnoetige Upstream-Metadaten.

location /service/a {
   proxy_pass http://backend_a/;
   proxy_hide_header ETag;
@@ -306,14 +307,23 @@ done
- Medium 6c) Debugging Challenge + Medium 9) Debugging Challenge

Dateien: proxy/nginx.broken.conf, proxy/nginx.conf

Commands noetig: ja

-

Kopiere testweise proxy/nginx.broken.conf auf proxy/nginx.conf, behebe die Fehler und stelle danach die funktionierende Konfiguration wieder her.

+

Ablauf: Kopiere testweise proxy/nginx.broken.conf auf proxy/nginx.conf, behebe die Fehler und stelle danach die funktionierende Konfiguration wieder her.

cp proxy/nginx.broken.conf proxy/nginx.conf
 make proxy-reload
 ./scripts/compose.sh logs reverse-proxy
+

Konkrete Fehler und Fixes:

+
    +
  1. Upstream-Mismatch: backend_a_typo ist definiert, aber backend_a wird referenziert -> Namen angleichen.
  2. +
  3. Falscher Port: backend-a:8080 -> auf backend-a:80 korrigieren.
  4. +
  5. Pfadfehler: in /service/b fehlt der Trailing Slash bei proxy_pass -> proxy_pass http://backend_b/;.
  6. +

Check: beide Routen funktionieren wieder.

+
curl http://localhost:8080/service/a
+curl http://localhost:8080/service/b
+./scripts/compose.sh logs reverse-proxy
@@ -321,7 +331,7 @@ make proxy-reload

Hard (TLS) - Musterloesungen

- Hard 7) HTTPS mit Easy-RSA + Hard 10) HTTPS mit Easy-RSA

Dateien: docker-compose.yml, proxy/nginx.conf, certs/easyrsa/*

Commands noetig: ja

mkdir -p certs/easyrsa
@@ -337,13 +347,15 @@ cd certs/easyrsa
     - "8080:80"
     - "8443:443"
   volumes:
-    - ./certs/easyrsa/pki:/etc/nginx/pki:ro,z
+ - ./certs/live:/etc/nginx/certs:ro,z +

Wichtig: Nicht die komplette PKI in den Container mounten. Nur Runtime-Zertifikat + Key bereitstellen.

+

Nginx TLS-Pfade: ssl_certificate /etc/nginx/certs/localhost.crt; und ssl_certificate_key /etc/nginx/certs/localhost.key;

Check: curl https://localhost:8443/service/a (ohne -k nach CA-Import)

- Hard 8) HTTP -> HTTPS Redirect -

Voraussetzung: Challenge 7 abgeschlossen (gleiches Config-File weiterverwenden).

+ Hard 11) HTTP -> HTTPS Redirect +

Voraussetzung: Challenge 10 abgeschlossen (gleiches Config-File weiterverwenden).

Dateien: proxy/nginx.conf

Commands noetig: ja

server {
@@ -363,8 +375,8 @@ cd certs/easyrsa
         
- Hard 9) TLS Haertung + HSTS -

Voraussetzung: Challenge 7 und 8 abgeschlossen.

+ Hard 12) TLS Haertung + HSTS +

Voraussetzung: Challenge 10 und 11 abgeschlossen.

Dateien: proxy/nginx.conf

Commands noetig: ja

ssl_protocols TLSv1.2 TLSv1.3;
@@ -381,7 +393,7 @@ openssl s_client -connect localhost:8443 -servername localhost

Bonus Expert - Wireshark (ausformulierte Referenzloesung)

- Expert 10) HTTP vs HTTPS sauber analysieren + Expert 13) HTTP vs HTTPS sauber analysieren

Dateien: keine Pflicht-Datei; optional Wireshark Settings und Keylog-Datei

Commands noetig: ja

Schritt 1 - HTTP Capture:

@@ -394,7 +406,7 @@ openssl s_client -connect localhost:8443 -servername localhost

Schritt 2 - HTTPS Capture:

    -
  1. HTTPS muss vorher laufen (Challenge 7).
  2. +
  3. HTTPS muss vorher laufen (Challenge 10).
  4. Filter auf tcp.port == 8443 oder tls setzen.
  5. Request senden: curl https://localhost:8443/service/a.
  6. Pakete markieren: ClientHello, ServerHello, Certificate.
  7. diff --git a/proxy/nginx.tls.example.conf b/proxy/nginx.tls.example.conf index 0ab140a..50bccda 100644 --- a/proxy/nginx.tls.example.conf +++ b/proxy/nginx.tls.example.conf @@ -34,8 +34,8 @@ http { listen 443 ssl; server_name localhost; - ssl_certificate /etc/nginx/pki/issued/localhost.crt; - ssl_certificate_key /etc/nginx/pki/private/localhost.key; + ssl_certificate /etc/nginx/certs/localhost.crt; + ssl_certificate_key /etc/nginx/certs/localhost.key; ssl_protocols TLSv1.2 TLSv1.3; ssl_prefer_server_ciphers on;