Reverse Proxy Target A2
+Use this page to validate load balancing between backend-a and backend-a2.
+diff --git a/README.md b/README.md index 9d8fc4a..ac2f5c3 100644 --- a/README.md +++ b/README.md @@ -28,23 +28,29 @@ Basisrouten nach dem Start: - `http://localhost:8080/hints.html` -> Hint Cheatsheet - `http://localhost:8080/solutions.html` -> Musterloesungen -## Voraussetzungen (WSL oder Linux) +## Voraussetzungen (Linux / macOS / Windows WSL) 1. Docker + Compose (`docker compose` oder `docker-compose`) -2. Bei Windows: Docker Desktop + WSL Integration aktiv -3. Optional fuer TLS-Challenges: `easy-rsa`, `openssl`, `wireshark` +2. Optional fuer TLS-Challenges: `easy-rsa`, `openssl`, `wireshark` + +Plattformhinweise: + +- Linux: Docker Engine oder Docker Desktop +- macOS: Docker Desktop +- Windows: Docker Desktop + WSL Integration aktiv +- macOS: falls `make` fehlt -> `xcode-select --install` ## Schnellstart +### Linux / macOS + ```bash ./scripts/bootstrap.sh ``` -### Start auf Windows +### Windows (WSL) -Empfohlen: ueber WSL starten (nicht nativ in PowerShell ohne WSL). - -Option A (WSL Terminal): +Option A (im WSL-Terminal): ```bash ./scripts/bootstrap.sh @@ -56,7 +62,7 @@ Option B (PowerShell Wrapper): ./scripts/workshop.ps1 -Action bootstrap ``` -Der PowerShell-Wrapper braucht kein `make` und ruft die Linux-Skripte direkt in WSL auf. +Der PowerShell-Wrapper braucht kein `make` und ruft Linux-Skripte direkt in WSL auf. Falls PowerShell das Script blockiert: @@ -64,12 +70,13 @@ Falls PowerShell das Script blockiert: Set-ExecutionPolicy -Scope Process Bypass ``` -Weitere Aktionen aus PowerShell: +Weitere Aktionen in PowerShell: ```powershell ./scripts/workshop.ps1 -Action redeploy ./scripts/workshop.ps1 -Action proxy-reload ./scripts/workshop.ps1 -Action reset +./scripts/workshop.ps1 -Action reset-hard ``` Wenn mehrere Distros installiert sind: @@ -78,23 +85,31 @@ Wenn mehrere Distros installiert sind: ./scripts/workshop.ps1 -Action bootstrap -Distro Ubuntu-24.04 ``` -Der Script: +Das Skript: - prueft Docker + Compose - erstellt `.env` aus `.env.example` (falls nicht vorhanden) - startet den Stack +Hinweis zu Skriptnamen: + +- `scripts/bootstrap.sh` ist der empfohlene Einstieg +- intern nutzt es `scripts/bootstrap-unix.sh` +- `scripts/bootstrap-wsl.sh` existiert nur noch als Kompatibilitaets-Wrapper + ## Neu deployen / resetten ```bash make redeploy make proxy-reload make reset +make reset-hard ``` - `redeploy`: build + restart aller Services - `proxy-reload`: nur Reverse Proxy restart - `reset`: Container/Netzwerk/Volumes aufraeumen +- `reset-hard`: wie `reset`, plus lokale Git-Aenderungen verwerfen (falls Git-Repo) Hinweis: `make redeploy` startet zusaetzlich den Reverse Proxy neu, damit Nginx-Config-Aenderungen sicher aktiv sind. diff --git a/backends/a2/index.html b/backends/a2/index.html new file mode 100644 index 0000000..7387421 --- /dev/null +++ b/backends/a2/index.html @@ -0,0 +1,47 @@ + + +
+ + +Use this page to validate load balancing between backend-a and backend-a2.
+This is a starter page for challenge 2.
+Route example: /service/c
Fokus: manuelle Proxy-Konfiguration, saubere Security-Entscheidungen, TLS von Grund auf.
+Manuelle Proxy-Konfiguration, Security-Entscheidungen und TLS von Grund auf.
Ziel: Verstehen, wie der Reverse Proxy Requests anhand des Pfads verteilt.
-Muss:
+Muss: zuerst in proxy/nginx.conf nachsehen, dann testen.
/service/a und /service/b aufrufen.location auf welchen upstream zeigt.location matched /service/a?upstream wird verwendet?Done-Check:
curl http://localhost:8080/service/a
curl http://localhost:8080/service/b
Ziel: Proxy-Setup sinnvoll erweitern, ohne bestehenden Traffic zu brechen.
-Dateien: docker-compose.yml, proxy/nginx.conf
Muss:
-backend-c in Compose anlegen.upstream backend_c und location /service/c konfigurieren.Done-Check:
+Muss: Compose-Service + Upstream + Route /service/c.
Zusatz: backends/c/index.html ist vorhanden und darf angepasst werden.
curl http://localhost:8080/service/c
curl http://localhost:8080/service/a
curl http://localhost:8080/service/b
Ziel: URL-Design vom Backend entkoppeln.
-Muss:
-/demo/a) einbauen.Done-Check:
+Muss: Alias-Route bauen, die auf Backend A fuehrt.
curl http://localhost:8080/demo/a
Ziel: Browser-Schutzmechanismen bewusst aktivieren.
-Muss:
-X-Content-Type-Options: nosniffX-Frame-Options: DENYReferrer-Policy: no-referrerDone-Check:
+Setze mindestens nosniff, DENY, no-referrer.
curl -I http://localhost:8080/
- Abgabe: kurz erklaeren, welchen Angriff jeder Header erschwert.
Ziel: Zugangskontrolle direkt im Proxy umsetzen.
-Muss:
-/internal/status einbauen.127.0.0.1 erlauben, alle anderen verbieten.Done-Check:
-curl -i http://localhost:8080/internal/status
+ Muss: /internal/status nur fuer 127.0.0.1.
Wichtig: Host-Request zeigt typischerweise 403 (Docker-Netzwerk).
+curl -i http://localhost:8080/internal/status
+./scripts/compose.sh exec -T reverse-proxy sh -lc "wget -qO- http://127.0.0.1/internal/status"
Ziel: Fehler schneller eingrenzen koennen.
-Muss:
-log_format mit Upstream-Daten erstellen.status, upstream_addr, Timing.Done-Check:
+Eigenes log_format mit Upstream-Infos einbauen.
curl http://localhost:8080/service/a
+./scripts/compose.sh logs reverse-proxy
+ Zweite Instanz von Backend A (backend-a2) einbauen und Round-Robin zeigen.
for i in $(seq 1 8); do
+ curl -s http://localhost:8080/service/a | grep -o "Target A2\|Target A"
+done
+ Mindestens einen Backend-Response-Header per proxy_hide_header ausblenden.
curl -I http://localhost:8080/service/a
+ Mit proxy/nginx.broken.conf arbeiten, Fehler finden und reparieren.
curl http://localhost:8080/service/a
+curl http://localhost:8080/service/b
./scripts/compose.sh logs reverse-proxy
Ziel: Eigene PKI und TLS-Endpoint fuer den Proxy aufbauen.
-Muss:
-localhost erstellen.443 erweitern (z. B. 8443:443).-k testen.Done-Check:
+Zertifikat fuer localhost, Port 8443:443, Root-CA importiert.
curl https://localhost:8443/service/a
Ziel: Client sauber auf TLS umleiten.
-Muss:
-/healthz darf optional auf HTTP bleiben.Done-Check:
+Voraussetzung: Challenge 7 abgeschlossen. Bestehende Config weiterverwenden.
curl -I http://localhost:8080/service/a
Ziel: TLS nicht nur aktivieren, sondern sinnvoll haerten.
-Muss:
-Strict-Transport-Security.Hinweis: Im HTTP-Basissetup ist HSTS absichtlich noch nicht aktiv.
-Done-Check:
+Voraussetzung: Challenge 7 und 8 abgeschlossen. Gleiche Config weiter erweitern.
curl -I https://localhost:8443/service/a
openssl s_client -connect localhost:8443 -servername localhost
Ziel: Nachweisbar zeigen, was bei HTTP lesbar und bei HTTPS geschuetzt ist.
-Muss:
8080, Request erzeugen, Klartext markieren.8443, TLS Handshake markieren.ClientHello, ServerHello, Certificate zeigen.8080 mitschneiden und Klartext zeigen.8443 mitschneiden.ClientHello, ServerHello, Certificate markieren.Optional: TLS Decrypt via SSLKEYLOGFILE und Unterschied vorher/nachher erklaeren.
Done-Check / Abgabe:
-Optional: TLS Decrypt mit SSLKEYLOGFILE.
Abgabe: mind. 3 Screenshots + technische Interpretation.
Check: curl http://localhost:8080/service/c
Hinweis: Stelle sicher, dass backends/c/index.html existiert (Starterdatei liegt bereits im Repo).
location = /demo/a {
- rewrite ^ /service/a break;
proxy_pass http://backend_a/;
}
Check: curl http://localhost:8080/demo/a
Check: curl -i http://localhost:8080/internal/status
Check (Host): curl -i http://localhost:8080/internal/status (typisch 403)
Check (Container-intern):
+./scripts/compose.sh exec -T reverse-proxy sh -lc "wget -qO- http://127.0.0.1/internal/status"
curl http://localhost:8080/service/a
./scripts/compose.sh logs reverse-proxy
Compose (backend-a2):
+backend-a2:
+ image: nginx:1.27-alpine
+ container_name: workshop-backend-a2
+ volumes:
+ - ./backends/a2:/usr/share/nginx/html:ro,z
+ Nginx (upstream erweitern):
+upstream backend_a {
+ server backend-a:80;
+ server backend-a2:80;
+}
+ Check:
+for i in $(seq 1 8); do
+ curl -s http://localhost:8080/service/a | grep -o "Target A2\|Target A"
+done
+ location /service/a {
+ proxy_pass http://backend_a/;
+ proxy_hide_header ETag;
+ proxy_hide_header Last-Modified;
+}
+ Check: curl -I http://localhost:8080/service/a
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
+ Check: beide Routen funktionieren wieder.
+Voraussetzung: Challenge 7 abgeschlossen (gleiches Config-File weiterverwenden).
server {
listen 80;
server_name _;
@@ -279,6 +320,7 @@ cd certs/easyrsa
Hard 9) TLS Haertung + HSTS
+ Voraussetzung: Challenge 7 und 8 abgeschlossen.
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers on;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
diff --git a/proxy/nginx.broken.conf b/proxy/nginx.broken.conf
new file mode 100644
index 0000000..f5b8d1a
--- /dev/null
+++ b/proxy/nginx.broken.conf
@@ -0,0 +1,47 @@
+events {
+ worker_connections 1024;
+}
+
+http {
+ include /etc/nginx/mime.types;
+ default_type application/octet-stream;
+ sendfile on;
+ server_tokens off;
+
+ upstream backend_a_typo {
+ server backend-a:8080;
+ }
+
+ upstream backend_b {
+ server backend-b:80;
+ }
+
+ server {
+ listen 80;
+ server_name _;
+
+ location / {
+ root /usr/share/nginx/html;
+ try_files $uri $uri/ /index.html;
+ }
+
+ location /service/a {
+ proxy_pass http://backend_a/;
+ proxy_set_header Host $host;
+ proxy_set_header X-Forwarded-Proto http;
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+ }
+
+ location /service/b {
+ proxy_pass http://backend_b;
+ proxy_set_header Host $host;
+ proxy_set_header X-Forwarded-Proto http;
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+ }
+
+ location = /healthz {
+ default_type text/plain;
+ return 200 "ok\n";
+ }
+ }
+}
diff --git a/proxy/nginx.conf b/proxy/nginx.conf
index e64d448..e519aee 100644
--- a/proxy/nginx.conf
+++ b/proxy/nginx.conf
@@ -39,7 +39,7 @@ http {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
- location /healthz {
+ location = /healthz {
default_type text/plain;
return 200 "ok\n";
}
diff --git a/scripts/bootstrap-unix.sh b/scripts/bootstrap-unix.sh
new file mode 100755
index 0000000..126ae9a
--- /dev/null
+++ b/scripts/bootstrap-unix.sh
@@ -0,0 +1,36 @@
+#!/usr/bin/env bash
+set -euo pipefail
+
+SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
+PROJECT_ROOT="$(cd -- "$SCRIPT_DIR/.." && pwd)"
+
+cd "$PROJECT_ROOT"
+
+if ! command -v docker >/dev/null 2>&1; then
+ echo "[error] docker nicht gefunden. Bitte Docker installieren (Linux/macOS) oder Docker Desktop + WSL Integration aktivieren."
+ exit 1
+fi
+
+if ! docker info >/dev/null 2>&1; then
+ echo "[error] Docker Daemon nicht erreichbar. Bitte Docker starten."
+ exit 1
+fi
+
+if ! "$SCRIPT_DIR/compose.sh" version >/dev/null 2>&1; then
+ echo "[error] Weder docker compose noch docker-compose verfuegbar."
+ exit 1
+fi
+
+if [[ ! -f .env ]]; then
+ cp .env.example .env
+ echo "[ok] .env aus .env.example erstellt"
+else
+ echo "[skip] .env existiert bereits"
+fi
+
+"$SCRIPT_DIR/compose.sh" up -d --build
+
+echo
+echo "[ok] Workshop-Stack laeuft"
+echo " Landing Page: http://localhost:8080"
+echo " Test (backend-a): curl http://localhost:8080/service/a"
diff --git a/scripts/bootstrap-wsl.sh b/scripts/bootstrap-wsl.sh
index 0e10af0..50f3674 100755
--- a/scripts/bootstrap-wsl.sh
+++ b/scripts/bootstrap-wsl.sh
@@ -2,35 +2,5 @@
set -euo pipefail
SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
-PROJECT_ROOT="$(cd -- "$SCRIPT_DIR/.." && pwd)"
-
-cd "$PROJECT_ROOT"
-
-if ! command -v docker >/dev/null 2>&1; then
- echo "[error] docker nicht gefunden. Bitte Docker installieren (Linux) oder Docker Desktop + WSL Integration aktivieren."
- exit 1
-fi
-
-if ! docker info >/dev/null 2>&1; then
- echo "[error] Docker Daemon nicht erreichbar. Bitte Docker starten."
- exit 1
-fi
-
-if ! "$SCRIPT_DIR/compose.sh" version >/dev/null 2>&1; then
- echo "[error] Weder docker compose noch docker-compose verfuegbar."
- exit 1
-fi
-
-if [[ ! -f .env ]]; then
- cp .env.example .env
- echo "[ok] .env aus .env.example erstellt"
-else
- echo "[skip] .env existiert bereits"
-fi
-
-"$SCRIPT_DIR/compose.sh" up -d --build
-
-echo
-echo "[ok] Workshop-Stack laeuft"
-echo " Landing Page: http://localhost:8080"
-echo " Test (backend-a): curl http://localhost:8080/service/a"
+echo "[warn] scripts/bootstrap-wsl.sh ist deprecated. Nutze bitte scripts/bootstrap.sh"
+"$SCRIPT_DIR/bootstrap-unix.sh"
diff --git a/scripts/bootstrap.sh b/scripts/bootstrap.sh
index 767793f..0dda692 100755
--- a/scripts/bootstrap.sh
+++ b/scripts/bootstrap.sh
@@ -2,4 +2,4 @@
set -euo pipefail
SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
-"$SCRIPT_DIR/bootstrap-wsl.sh"
+"$SCRIPT_DIR/bootstrap-unix.sh"
diff --git a/scripts/workshop.ps1 b/scripts/workshop.ps1
index 8ebcd00..b36e2aa 100644
--- a/scripts/workshop.ps1
+++ b/scripts/workshop.ps1
@@ -1,5 +1,5 @@
param(
- [ValidateSet("bootstrap", "up", "redeploy", "proxy-reload", "down", "logs", "reset")]
+ [ValidateSet("bootstrap", "up", "redeploy", "proxy-reload", "down", "logs", "reset", "reset-hard")]
[string]$Action = "bootstrap",
[string]$Distro = ""
)
@@ -25,6 +25,7 @@ switch ($Action) {
"down" { $linuxCommand = "./scripts/compose.sh down" }
"logs" { $linuxCommand = "./scripts/compose.sh logs -f" }
"reset" { $linuxCommand = "./scripts/reset-lab.sh" }
+ "reset-hard" { $linuxCommand = "./scripts/reset-lab.sh --hard" }
}
$wslArgs = @()