diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 000000000..5af91d954 --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,22 @@ +## Goal + + +## Changes + + +## Testing + + +## Artifacts & Screenshots + + +## Checklist + +- [ ] PR has a clear, descriptive title +- [ ] Documentation has been updated if applicable +- [ ] No secrets or large temporary files are included in the changes + +--- + + + \ No newline at end of file diff --git a/labs/assets/homepage.jpg b/labs/assets/homepage.jpg new file mode 100644 index 000000000..d51895803 Binary files /dev/null and b/labs/assets/homepage.jpg differ diff --git a/labs/lab11/docker-compose.yml b/labs/lab11/docker-compose.yml new file mode 100644 index 000000000..ae7c74cb9 --- /dev/null +++ b/labs/lab11/docker-compose.yml @@ -0,0 +1,19 @@ +services: + juice: + image: bkimminich/juice-shop:v20.0.0 + restart: unless-stopped + expose: + - "3000" + + nginx: + image: nginx:stable-alpine + restart: unless-stopped + depends_on: + - juice + ports: + - "80:80" + - "443:443" + volumes: + - ./reverse-proxy/nginx.conf:/etc/nginx/nginx.conf:ro + - ./reverse-proxy/certs:/etc/nginx/certs:ro + - ./logs:/var/log/nginx:rw diff --git a/labs/lab11/logs/access.log b/labs/lab11/logs/access.log new file mode 100644 index 000000000..79513ef15 --- /dev/null +++ b/labs/lab11/logs/access.log @@ -0,0 +1,74 @@ +172.18.0.1 - - [17/Jul/2026:19:05:21 +0000] "GET /rest/admin/application-version HTTP/2.0" 200 20 "-" "curl/8.19.0" rt=0.008 uct=0.001 urt=0.008 +172.18.0.1 - - [17/Jul/2026:19:05:34 +0000] "HEAD / HTTP/1.1" 308 0 "-" "curl/8.19.0" rt=0.000 uct=- urt=- +172.18.0.1 - - [17/Jul/2026:19:05:35 +0000] "" 400 0 "-" "-" rt=0.000 uct=- urt=- +172.18.0.1 - - [17/Jul/2026:19:05:35 +0000] "HEAD / HTTP/2.0" 200 0 "-" "curl/8.19.0" rt=0.002 uct=0.000 urt=0.002 +172.18.0.1 - - [17/Jul/2026:19:05:41 +0000] "GET /rest/user/login HTTP/2.0" 429 162 "-" "curl/8.19.0" rt=0.000 uct=- urt=- +172.18.0.1 - - [17/Jul/2026:19:05:41 +0000] "GET /rest/user/login HTTP/2.0" 429 162 "-" "curl/8.19.0" rt=0.000 uct=- urt=- +172.18.0.1 - - [17/Jul/2026:19:05:41 +0000] "GET /rest/user/login HTTP/2.0" 429 162 "-" "curl/8.19.0" rt=0.000 uct=- urt=- +172.18.0.1 - - [17/Jul/2026:19:05:41 +0000] "GET /rest/user/login HTTP/2.0" 429 162 "-" "curl/8.19.0" rt=0.000 uct=- urt=- +172.18.0.1 - - [17/Jul/2026:19:05:41 +0000] "GET /rest/user/login HTTP/2.0" 500 3014 "-" "curl/8.19.0" rt=0.004 uct=0.000 urt=0.004 +172.18.0.1 - - [17/Jul/2026:19:05:41 +0000] "GET /rest/user/login HTTP/2.0" 429 162 "-" "curl/8.19.0" rt=0.000 uct=- urt=- +172.18.0.1 - - [17/Jul/2026:19:05:41 +0000] "GET /rest/user/login HTTP/2.0" 429 162 "-" "curl/8.19.0" rt=0.000 uct=- urt=- +172.18.0.1 - - [17/Jul/2026:19:05:41 +0000] "GET /rest/user/login HTTP/2.0" 429 162 "-" "curl/8.19.0" rt=0.000 uct=- urt=- +172.18.0.1 - - [17/Jul/2026:19:05:41 +0000] "GET /rest/user/login HTTP/2.0" 429 162 "-" "curl/8.19.0" rt=0.000 uct=- urt=- +172.18.0.1 - - [17/Jul/2026:19:05:41 +0000] "GET /rest/user/login HTTP/2.0" 429 162 "-" "curl/8.19.0" rt=0.000 uct=- urt=- +172.18.0.1 - - [17/Jul/2026:19:05:41 +0000] "GET /rest/user/login HTTP/2.0" 429 162 "-" "curl/8.19.0" rt=0.000 uct=- urt=- +172.18.0.1 - - [17/Jul/2026:19:05:41 +0000] "GET /rest/user/login HTTP/2.0" 429 162 "-" "curl/8.19.0" rt=0.000 uct=- urt=- +172.18.0.1 - - [17/Jul/2026:19:05:41 +0000] "GET /rest/user/login HTTP/2.0" 429 162 "-" "curl/8.19.0" rt=0.000 uct=- urt=- +172.18.0.1 - - [17/Jul/2026:19:05:41 +0000] "GET /rest/user/login HTTP/2.0" 429 162 "-" "curl/8.19.0" rt=0.000 uct=- urt=- +172.18.0.1 - - [17/Jul/2026:19:05:41 +0000] "GET /rest/user/login HTTP/2.0" 429 162 "-" "curl/8.19.0" rt=0.000 uct=- urt=- +172.18.0.1 - - [17/Jul/2026:19:05:41 +0000] "GET /rest/user/login HTTP/2.0" 500 3014 "-" "curl/8.19.0" rt=0.012 uct=0.000 urt=0.012 +172.18.0.1 - - [17/Jul/2026:19:05:41 +0000] "GET /rest/user/login HTTP/2.0" 429 162 "-" "curl/8.19.0" rt=0.000 uct=- urt=- +172.18.0.1 - - [17/Jul/2026:19:05:41 +0000] "GET /rest/user/login HTTP/2.0" 429 162 "-" "curl/8.19.0" rt=0.000 uct=- urt=- +172.18.0.1 - - [17/Jul/2026:19:05:41 +0000] "GET /rest/user/login HTTP/2.0" 429 162 "-" "curl/8.19.0" rt=0.000 uct=- urt=- +172.18.0.1 - - [17/Jul/2026:19:05:41 +0000] "GET /rest/user/login HTTP/2.0" 429 162 "-" "curl/8.19.0" rt=0.000 uct=- urt=- +172.18.0.1 - - [17/Jul/2026:19:05:41 +0000] "GET /rest/user/login HTTP/2.0" 429 162 "-" "curl/8.19.0" rt=0.000 uct=- urt=- +172.18.0.1 - - [17/Jul/2026:19:05:41 +0000] "GET /rest/user/login HTTP/2.0" 429 162 "-" "curl/8.19.0" rt=0.000 uct=- urt=- +172.18.0.1 - - [17/Jul/2026:19:05:41 +0000] "GET /rest/user/login HTTP/2.0" 429 162 "-" "curl/8.19.0" rt=0.000 uct=- urt=- +172.18.0.1 - - [17/Jul/2026:19:05:41 +0000] "GET /rest/user/login HTTP/2.0" 429 162 "-" "curl/8.19.0" rt=0.000 uct=- urt=- +172.18.0.1 - - [17/Jul/2026:19:05:41 +0000] "GET /rest/user/login HTTP/2.0" 429 162 "-" "curl/8.19.0" rt=0.000 uct=- urt=- +172.18.0.1 - - [17/Jul/2026:19:05:41 +0000] "GET /rest/user/login HTTP/2.0" 429 162 "-" "curl/8.19.0" rt=0.000 uct=- urt=- +172.18.0.1 - - [17/Jul/2026:19:05:41 +0000] "GET /rest/user/login HTTP/2.0" 500 3014 "-" "curl/8.19.0" rt=0.018 uct=0.000 urt=0.018 +172.18.0.1 - - [17/Jul/2026:19:05:41 +0000] "GET /rest/user/login HTTP/2.0" 429 162 "-" "curl/8.19.0" rt=0.000 uct=- urt=- +172.18.0.1 - - [17/Jul/2026:19:05:41 +0000] "GET /rest/user/login HTTP/2.0" 429 162 "-" "curl/8.19.0" rt=0.000 uct=- urt=- +172.18.0.1 - - [17/Jul/2026:19:05:41 +0000] "GET /rest/user/login HTTP/2.0" 429 162 "-" "curl/8.19.0" rt=0.000 uct=- urt=- +172.18.0.1 - - [17/Jul/2026:19:05:41 +0000] "GET /rest/user/login HTTP/2.0" 429 162 "-" "curl/8.19.0" rt=0.000 uct=- urt=- +172.18.0.1 - - [17/Jul/2026:19:05:41 +0000] "GET /rest/user/login HTTP/2.0" 429 162 "-" "curl/8.19.0" rt=0.000 uct=- urt=- +172.18.0.1 - - [17/Jul/2026:19:05:41 +0000] "GET /rest/user/login HTTP/2.0" 429 162 "-" "curl/8.19.0" rt=0.000 uct=- urt=- +172.18.0.1 - - [17/Jul/2026:19:05:41 +0000] "GET /rest/user/login HTTP/2.0" 429 162 "-" "curl/8.19.0" rt=0.000 uct=- urt=- +172.18.0.1 - - [17/Jul/2026:19:05:41 +0000] "GET /rest/user/login HTTP/2.0" 429 162 "-" "curl/8.19.0" rt=0.000 uct=- urt=- +172.18.0.1 - - [17/Jul/2026:19:05:41 +0000] "GET /rest/user/login HTTP/2.0" 429 162 "-" "curl/8.19.0" rt=0.000 uct=- urt=- +172.18.0.1 - - [17/Jul/2026:19:05:41 +0000] "GET /rest/user/login HTTP/2.0" 429 162 "-" "curl/8.19.0" rt=0.000 uct=- urt=- +172.18.0.1 - - [17/Jul/2026:19:05:41 +0000] "GET /rest/user/login HTTP/2.0" 429 162 "-" "curl/8.19.0" rt=0.000 uct=- urt=- +172.18.0.1 - - [17/Jul/2026:19:05:41 +0000] "GET /rest/user/login HTTP/2.0" 429 162 "-" "curl/8.19.0" rt=0.000 uct=- urt=- +172.18.0.1 - - [17/Jul/2026:19:05:41 +0000] "GET /rest/user/login HTTP/2.0" 429 162 "-" "curl/8.19.0" rt=0.000 uct=- urt=- +172.18.0.1 - - [17/Jul/2026:19:05:41 +0000] "GET /rest/user/login HTTP/2.0" 429 162 "-" "curl/8.19.0" rt=0.000 uct=- urt=- +172.18.0.1 - - [17/Jul/2026:19:05:41 +0000] "GET /rest/user/login HTTP/2.0" 429 162 "-" "curl/8.19.0" rt=0.000 uct=- urt=- +172.18.0.1 - - [17/Jul/2026:19:05:41 +0000] "GET /rest/user/login HTTP/2.0" 429 162 "-" "curl/8.19.0" rt=0.000 uct=- urt=- +172.18.0.1 - - [17/Jul/2026:19:05:41 +0000] "GET /rest/user/login HTTP/2.0" 500 3014 "-" "curl/8.19.0" rt=0.026 uct=0.000 urt=0.026 +172.18.0.1 - - [17/Jul/2026:19:05:41 +0000] "GET /rest/user/login HTTP/2.0" 429 162 "-" "curl/8.19.0" rt=0.000 uct=- urt=- +172.18.0.1 - - [17/Jul/2026:19:05:41 +0000] "GET /rest/user/login HTTP/2.0" 429 162 "-" "curl/8.19.0" rt=0.000 uct=- urt=- +172.18.0.1 - - [17/Jul/2026:19:05:41 +0000] "GET /rest/user/login HTTP/2.0" 429 162 "-" "curl/8.19.0" rt=0.000 uct=- urt=- +172.18.0.1 - - [17/Jul/2026:19:05:41 +0000] "GET /rest/user/login HTTP/2.0" 429 162 "-" "curl/8.19.0" rt=0.000 uct=- urt=- +172.18.0.1 - - [17/Jul/2026:19:05:41 +0000] "GET /rest/user/login HTTP/2.0" 429 162 "-" "curl/8.19.0" rt=0.000 uct=- urt=- +172.18.0.1 - - [17/Jul/2026:19:05:41 +0000] "GET /rest/user/login HTTP/2.0" 429 162 "-" "curl/8.19.0" rt=0.000 uct=- urt=- +172.18.0.1 - - [17/Jul/2026:19:05:41 +0000] "GET /rest/user/login HTTP/2.0" 429 162 "-" "curl/8.19.0" rt=0.000 uct=- urt=- +172.18.0.1 - - [17/Jul/2026:19:05:41 +0000] "GET /rest/user/login HTTP/2.0" 429 162 "-" "curl/8.19.0" rt=0.000 uct=- urt=- +172.18.0.1 - - [17/Jul/2026:19:05:41 +0000] "GET /rest/user/login HTTP/2.0" 429 162 "-" "curl/8.19.0" rt=0.000 uct=- urt=- +172.18.0.1 - - [17/Jul/2026:19:05:41 +0000] "GET /rest/user/login HTTP/2.0" 429 162 "-" "curl/8.19.0" rt=0.000 uct=- urt=- +172.18.0.1 - - [17/Jul/2026:19:05:41 +0000] "GET /rest/user/login HTTP/2.0" 429 162 "-" "curl/8.19.0" rt=0.000 uct=- urt=- +172.18.0.1 - - [17/Jul/2026:19:05:41 +0000] "GET /rest/user/login HTTP/2.0" 429 162 "-" "curl/8.19.0" rt=0.000 uct=- urt=- +172.18.0.1 - - [17/Jul/2026:19:05:41 +0000] "GET /rest/user/login HTTP/2.0" 429 162 "-" "curl/8.19.0" rt=0.000 uct=- urt=- +172.18.0.1 - - [17/Jul/2026:19:05:41 +0000] "GET /rest/user/login HTTP/2.0" 429 162 "-" "curl/8.19.0" rt=0.000 uct=- urt=- +172.18.0.1 - - [17/Jul/2026:19:05:41 +0000] "GET /rest/user/login HTTP/2.0" 500 3014 "-" "curl/8.19.0" rt=0.030 uct=0.000 urt=0.030 +172.18.0.1 - - [17/Jul/2026:19:05:41 +0000] "GET /rest/user/login HTTP/2.0" 500 3014 "-" "curl/8.19.0" rt=0.032 uct=0.000 urt=0.032 +172.18.0.1 - - [17/Jul/2026:19:05:43 +0000] "" 400 0 "-" "-" rt=0.000 uct=- urt=- +172.18.0.1 - - [17/Jul/2026:19:05:52 +0000] "GET / HTTP/1.0" 408 0 "-" "-" rt=10.008 uct=- urt=- +172.18.0.1 - - [17/Jul/2026:19:06:10 +0000] "" 400 0 "-" "-" rt=0.000 uct=- urt=- +172.18.0.1 - - [17/Jul/2026:19:06:22 +0000] "GET / HTTP/1.1" 408 0 "-" "-" rt=10.010 uct=- urt=- +172.18.0.1 - - [17/Jul/2026:19:06:47 +0000] "GET /" 408 0 "-" "-" rt=10.010 uct=- urt=- +172.18.0.1 - - [17/Jul/2026:19:08:41 +0000] "GET /rest/products/search?q='%20OR%201=1-- HTTP/2.0" 500 942 "-" "curl/8.19.0" rt=0.006 uct=0.000 urt=0.006 +172.18.0.1 - - [17/Jul/2026:19:12:36 +0000] "GET /rest/products/search?q='%20OR%201=1-- HTTP/2.0" 500 942 "-" "curl/8.19.0" rt=0.004 uct=0.001 urt=0.005 +172.18.0.4 - - [17/Jul/2026:19:16:44 +0000] "GET / HTTP/1.1" 308 164 "-" "curl/8.19.0" rt=0.000 uct=- urt=- +172.18.0.4 - - [17/Jul/2026:19:18:35 +0000] "GET / HTTP/1.1" 200 9903 "-" "curl/8.19.0" rt=0.002 uct=0.000 urt=0.002 +172.18.0.1 - - [17/Jul/2026:19:18:46 +0000] "GET /rest/products/search?q='%20OR%201=1-- HTTP/2.0" 500 942 "-" "curl/8.19.0" rt=0.004 uct=0.001 urt=0.004 diff --git a/labs/lab11/logs/error.log b/labs/lab11/logs/error.log new file mode 100644 index 000000000..872bfe401 --- /dev/null +++ b/labs/lab11/logs/error.log @@ -0,0 +1,63 @@ +2026/07/17 19:03:25 [emerg] 1#1: SSL_CTX_set_cipher_list("TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256") failed (SSL: error:0A0000B9:SSL routines::no cipher match) +2026/07/17 19:03:25 [emerg] 1#1: SSL_CTX_set_cipher_list("TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256") failed (SSL: error:0A0000B9:SSL routines::no cipher match) +2026/07/17 19:03:25 [emerg] 1#1: SSL_CTX_set_cipher_list("TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256") failed (SSL: error:0A0000B9:SSL routines::no cipher match) +2026/07/17 19:03:26 [emerg] 1#1: SSL_CTX_set_cipher_list("TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256") failed (SSL: error:0A0000B9:SSL routines::no cipher match) +2026/07/17 19:03:27 [emerg] 1#1: SSL_CTX_set_cipher_list("TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256") failed (SSL: error:0A0000B9:SSL routines::no cipher match) +2026/07/17 19:03:29 [emerg] 1#1: SSL_CTX_set_cipher_list("TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256") failed (SSL: error:0A0000B9:SSL routines::no cipher match) +2026/07/17 19:03:32 [emerg] 1#1: SSL_CTX_set_cipher_list("TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256") failed (SSL: error:0A0000B9:SSL routines::no cipher match) +2026/07/17 19:03:39 [emerg] 1#1: SSL_CTX_set_cipher_list("TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256") failed (SSL: error:0A0000B9:SSL routines::no cipher match) +2026/07/17 19:03:52 [emerg] 1#1: SSL_CTX_set_cipher_list("TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256") failed (SSL: error:0A0000B9:SSL routines::no cipher match) +2026/07/17 19:05:41 [warn] 33#33: *15 limiting requests, excess: 6.000 by zone "login", client: 172.18.0.1, server: _, request: "GET /rest/user/login HTTP/2.0", host: "localhost" +2026/07/17 19:05:41 [warn] 34#34: *17 limiting requests, excess: 6.000 by zone "login", client: 172.18.0.1, server: _, request: "GET /rest/user/login HTTP/2.0", host: "localhost" +2026/07/17 19:05:41 [warn] 39#39: *21 limiting requests, excess: 6.000 by zone "login", client: 172.18.0.1, server: _, request: "GET /rest/user/login HTTP/2.0", host: "localhost" +2026/07/17 19:05:41 [warn] 36#36: *19 limiting requests, excess: 6.000 by zone "login", client: 172.18.0.1, server: _, request: "GET /rest/user/login HTTP/2.0", host: "localhost" +2026/07/17 19:05:41 [warn] 34#34: *16 limiting requests, excess: 6.000 by zone "login", client: 172.18.0.1, server: _, request: "GET /rest/user/login HTTP/2.0", host: "localhost" +2026/07/17 19:05:41 [warn] 42#42: *30 limiting requests, excess: 6.000 by zone "login", client: 172.18.0.1, server: _, request: "GET /rest/user/login HTTP/2.0", host: "localhost" +2026/07/17 19:05:41 [warn] 41#41: *29 limiting requests, excess: 6.000 by zone "login", client: 172.18.0.1, server: _, request: "GET /rest/user/login HTTP/2.0", host: "localhost" +2026/07/17 19:05:41 [warn] 37#37: *20 limiting requests, excess: 6.000 by zone "login", client: 172.18.0.1, server: _, request: "GET /rest/user/login HTTP/2.0", host: "localhost" +2026/07/17 19:05:41 [warn] 35#35: *18 limiting requests, excess: 6.000 by zone "login", client: 172.18.0.1, server: _, request: "GET /rest/user/login HTTP/2.0", host: "localhost" +2026/07/17 19:05:41 [warn] 29#29: *11 limiting requests, excess: 6.000 by zone "login", client: 172.18.0.1, server: _, request: "GET /rest/user/login HTTP/2.0", host: "localhost" +2026/07/17 19:05:41 [warn] 38#38: *23 limiting requests, excess: 6.000 by zone "login", client: 172.18.0.1, server: _, request: "GET /rest/user/login HTTP/2.0", host: "localhost" +2026/07/17 19:05:41 [warn] 31#31: *13 limiting requests, excess: 6.000 by zone "login", client: 172.18.0.1, server: _, request: "GET /rest/user/login HTTP/2.0", host: "localhost" +2026/07/17 19:05:41 [warn] 37#37: *22 limiting requests, excess: 6.000 by zone "login", client: 172.18.0.1, server: _, request: "GET /rest/user/login HTTP/2.0", host: "localhost" +2026/07/17 19:05:41 [warn] 40#40: *28 limiting requests, excess: 6.000 by zone "login", client: 172.18.0.1, server: _, request: "GET /rest/user/login HTTP/2.0", host: "localhost" +2026/07/17 19:05:41 [warn] 31#31: *52 limiting requests, excess: 5.998 by zone "login", client: 172.18.0.1, server: _, request: "GET /rest/user/login HTTP/2.0", host: "localhost" +2026/07/17 19:05:41 [warn] 23#23: *35 limiting requests, excess: 5.998 by zone "login", client: 172.18.0.1, server: _, request: "GET /rest/user/login HTTP/2.0", host: "localhost" +2026/07/17 19:05:41 [warn] 27#27: *44 limiting requests, excess: 5.998 by zone "login", client: 172.18.0.1, server: _, request: "GET /rest/user/login HTTP/2.0", host: "localhost" +2026/07/17 19:05:41 [warn] 34#34: *57 limiting requests, excess: 5.998 by zone "login", client: 172.18.0.1, server: _, request: "GET /rest/user/login HTTP/2.0", host: "localhost" +2026/07/17 19:05:41 [warn] 30#30: *50 limiting requests, excess: 5.998 by zone "login", client: 172.18.0.1, server: _, request: "GET /rest/user/login HTTP/2.0", host: "localhost" +2026/07/17 19:05:41 [warn] 43#43: *31 limiting requests, excess: 5.998 by zone "login", client: 172.18.0.1, server: _, request: "GET /rest/user/login HTTP/2.0", host: "localhost" +2026/07/17 19:05:41 [warn] 23#23: *38 limiting requests, excess: 5.998 by zone "login", client: 172.18.0.1, server: _, request: "GET /rest/user/login HTTP/2.0", host: "localhost" +2026/07/17 19:05:41 [warn] 27#27: *46 limiting requests, excess: 5.998 by zone "login", client: 172.18.0.1, server: _, request: "GET /rest/user/login HTTP/2.0", host: "localhost" +2026/07/17 19:05:41 [warn] 44#44: *34 limiting requests, excess: 5.998 by zone "login", client: 172.18.0.1, server: _, request: "GET /rest/user/login HTTP/2.0", host: "localhost" +2026/07/17 19:05:41 [warn] 22#22: *37 limiting requests, excess: 5.998 by zone "login", client: 172.18.0.1, server: _, request: "GET /rest/user/login HTTP/2.0", host: "localhost" +2026/07/17 19:05:41 [warn] 44#44: *36 limiting requests, excess: 5.998 by zone "login", client: 172.18.0.1, server: _, request: "GET /rest/user/login HTTP/2.0", host: "localhost" +2026/07/17 19:05:41 [warn] 24#24: *39 limiting requests, excess: 5.998 by zone "login", client: 172.18.0.1, server: _, request: "GET /rest/user/login HTTP/2.0", host: "localhost" +2026/07/17 19:05:41 [warn] 33#33: *53 limiting requests, excess: 5.998 by zone "login", client: 172.18.0.1, server: _, request: "GET /rest/user/login HTTP/2.0", host: "localhost" +2026/07/17 19:05:41 [warn] 24#24: *45 limiting requests, excess: 5.998 by zone "login", client: 172.18.0.1, server: _, request: "GET /rest/user/login HTTP/2.0", host: "localhost" +2026/07/17 19:05:41 [warn] 33#33: *55 limiting requests, excess: 5.998 by zone "login", client: 172.18.0.1, server: _, request: "GET /rest/user/login HTTP/2.0", host: "localhost" +2026/07/17 19:05:41 [warn] 24#24: *40 limiting requests, excess: 5.998 by zone "login", client: 172.18.0.1, server: _, request: "GET /rest/user/login HTTP/2.0", host: "localhost" +2026/07/17 19:05:41 [warn] 25#25: *56 limiting requests, excess: 5.998 by zone "login", client: 172.18.0.1, server: _, request: "GET /rest/user/login HTTP/2.0", host: "localhost" +2026/07/17 19:05:41 [warn] 33#33: *54 limiting requests, excess: 5.998 by zone "login", client: 172.18.0.1, server: _, request: "GET /rest/user/login HTTP/2.0", host: "localhost" +2026/07/17 19:05:41 [warn] 29#29: *49 limiting requests, excess: 5.997 by zone "login", client: 172.18.0.1, server: _, request: "GET /rest/user/login HTTP/2.0", host: "localhost" +2026/07/17 19:05:41 [warn] 29#29: *47 limiting requests, excess: 5.997 by zone "login", client: 172.18.0.1, server: _, request: "GET /rest/user/login HTTP/2.0", host: "localhost" +2026/07/17 19:05:41 [warn] 29#29: *51 limiting requests, excess: 5.997 by zone "login", client: 172.18.0.1, server: _, request: "GET /rest/user/login HTTP/2.0", host: "localhost" +2026/07/17 19:05:41 [warn] 45#45: *42 limiting requests, excess: 5.997 by zone "login", client: 172.18.0.1, server: _, request: "GET /rest/user/login HTTP/2.0", host: "localhost" +2026/07/17 19:05:41 [warn] 45#45: *43 limiting requests, excess: 5.997 by zone "login", client: 172.18.0.1, server: _, request: "GET /rest/user/login HTTP/2.0", host: "localhost" +2026/07/17 19:05:41 [warn] 29#29: *48 limiting requests, excess: 5.997 by zone "login", client: 172.18.0.1, server: _, request: "GET /rest/user/login HTTP/2.0", host: "localhost" +2026/07/17 19:05:41 [warn] 45#45: *41 limiting requests, excess: 5.997 by zone "login", client: 172.18.0.1, server: _, request: "GET /rest/user/login HTTP/2.0", host: "localhost" +2026/07/17 19:05:41 [warn] 24#24: *65 limiting requests, excess: 5.997 by zone "login", client: 172.18.0.1, server: _, request: "GET /rest/user/login HTTP/2.0", host: "localhost" +2026/07/17 19:05:41 [warn] 22#22: *60 limiting requests, excess: 5.997 by zone "login", client: 172.18.0.1, server: _, request: "GET /rest/user/login HTTP/2.0", host: "localhost" +2026/07/17 19:05:41 [warn] 22#22: *59 limiting requests, excess: 5.997 by zone "login", client: 172.18.0.1, server: _, request: "GET /rest/user/login HTTP/2.0", host: "localhost" +2026/07/17 19:05:41 [warn] 25#25: *64 limiting requests, excess: 5.997 by zone "login", client: 172.18.0.1, server: _, request: "GET /rest/user/login HTTP/2.0", host: "localhost" +2026/07/17 19:05:41 [warn] 23#23: *58 limiting requests, excess: 5.997 by zone "login", client: 172.18.0.1, server: _, request: "GET /rest/user/login HTTP/2.0", host: "localhost" +2026/07/17 19:05:41 [warn] 23#23: *63 limiting requests, excess: 5.996 by zone "login", client: 172.18.0.1, server: _, request: "GET /rest/user/login HTTP/2.0", host: "localhost" +2026/07/17 19:05:41 [warn] 26#26: *66 limiting requests, excess: 5.996 by zone "login", client: 172.18.0.1, server: _, request: "GET /rest/user/login HTTP/2.0", host: "localhost" +2026/07/17 19:05:41 [warn] 23#23: *62 limiting requests, excess: 5.996 by zone "login", client: 172.18.0.1, server: _, request: "GET /rest/user/login HTTP/2.0", host: "localhost" +2026/07/17 19:05:41 [warn] 26#26: *67 limiting requests, excess: 5.996 by zone "login", client: 172.18.0.1, server: _, request: "GET /rest/user/login HTTP/2.0", host: "localhost" +2026/07/17 19:05:41 [warn] 23#23: *61 limiting requests, excess: 5.996 by zone "login", client: 172.18.0.1, server: _, request: "GET /rest/user/login HTTP/2.0", host: "localhost" +2026/07/17 19:05:41 [warn] 25#25: *71 limiting requests, excess: 5.996 by zone "login", client: 172.18.0.1, server: _, request: "GET /rest/user/login HTTP/2.0", host: "localhost" +2026/07/17 19:05:41 [warn] 24#24: *68 limiting requests, excess: 5.996 by zone "login", client: 172.18.0.1, server: _, request: "GET /rest/user/login HTTP/2.0", host: "localhost" +2026/07/17 19:05:41 [warn] 24#24: *69 limiting requests, excess: 5.996 by zone "login", client: 172.18.0.1, server: _, request: "GET /rest/user/login HTTP/2.0", host: "localhost" +2026/07/17 19:05:41 [warn] 25#25: *72 limiting requests, excess: 5.996 by zone "login", client: 172.18.0.1, server: _, request: "GET /rest/user/login HTTP/2.0", host: "localhost" +2026/07/17 19:05:41 [warn] 24#24: *70 limiting requests, excess: 5.996 by zone "login", client: 172.18.0.1, server: _, request: "GET /rest/user/login HTTP/2.0", host: "localhost" diff --git a/labs/lab11/results/cipher.txt b/labs/lab11/results/cipher.txt new file mode 100644 index 000000000..d71c8fc68 --- /dev/null +++ b/labs/lab11/results/cipher.txt @@ -0,0 +1,2 @@ +Peer Temp Key: X25519, 253 bits +New, TLSv1.3, Cipher is TLS_AES_256_GCM_SHA384 diff --git a/labs/lab11/results/headers.txt b/labs/lab11/results/headers.txt new file mode 100644 index 000000000..6b0ee8919 --- /dev/null +++ b/labs/lab11/results/headers.txt @@ -0,0 +1,19 @@ +HTTP/2 200 +server: nginx +date: Fri, 17 Jul 2026 19:05:35 GMT +content-type: text/html; charset=UTF-8 +content-length: 9903 +feature-policy: payment 'self' +x-recruiting: /#/jobs +accept-ranges: bytes +cache-control: public, max-age=0 +last-modified: Fri, 17 Jul 2026 19:03:28 GMT +etag: W/"26af-19f7176559a" +vary: Accept-Encoding +strict-transport-security: max-age=63072000; includeSubDomains; preload +x-frame-options: DENY +x-content-type-options: nosniff +referrer-policy: strict-origin-when-cross-origin +permissions-policy: camera=(), microphone=(), geolocation=() +content-security-policy-report-only: default-src 'self'; img-src 'self' data:; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline' + diff --git a/labs/lab11/results/http-redirect.txt b/labs/lab11/results/http-redirect.txt new file mode 100644 index 000000000..ae33be8a3 --- /dev/null +++ b/labs/lab11/results/http-redirect.txt @@ -0,0 +1,15 @@ +HTTP/1.1 308 Permanent Redirect +Server: nginx +Date: Fri, 17 Jul 2026 19:05:34 GMT +Content-Type: text/html +Content-Length: 164 +Connection: keep-alive +Location: https://localhost/ +X-Frame-Options: DENY +X-Content-Type-Options: nosniff +Referrer-Policy: strict-origin-when-cross-origin +Permissions-Policy: camera=(), geolocation=(), microphone=() +Cross-Origin-Opener-Policy: same-origin +Cross-Origin-Resource-Policy: same-origin +Content-Security-Policy-Report-Only: default-src 'self'; img-src 'self' data:; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline' + diff --git a/labs/lab11/results/ratelimit.txt b/labs/lab11/results/ratelimit.txt new file mode 100644 index 000000000..48f128405 --- /dev/null +++ b/labs/lab11/results/ratelimit.txt @@ -0,0 +1,2 @@ + 54 429 + 6 500 diff --git a/labs/lab11/results/timeout.txt b/labs/lab11/results/timeout.txt new file mode 100644 index 000000000..01a42b89e --- /dev/null +++ b/labs/lab11/results/timeout.txt @@ -0,0 +1,2 @@ +Connection closed after 10s +# Nginx closed the connection after 10s (client_header_timeout 10s) diff --git a/labs/lab11/results/tls13.txt b/labs/lab11/results/tls13.txt new file mode 100644 index 000000000..3d0b7eb0a --- /dev/null +++ b/labs/lab11/results/tls13.txt @@ -0,0 +1,8 @@ +Connecting to ::1 +Can't use SSL_get_servername +depth=0 CN=juice.local +verify error:num=18:self-signed certificate +CONNECTION ESTABLISHED +Protocol version: TLSv1.3 +Ciphersuite: TLS_AES_256_GCM_SHA384 +Peer certificate: CN=juice.local diff --git a/labs/lab11/reverse-proxy/certs/localhost.crt b/labs/lab11/reverse-proxy/certs/localhost.crt new file mode 100644 index 000000000..49ea9cb17 --- /dev/null +++ b/labs/lab11/reverse-proxy/certs/localhost.crt @@ -0,0 +1,30 @@ +-----BEGIN CERTIFICATE----- +MIIFDTCCAvWgAwIBAgIUHq/m7Fru9SCi+umblV1LiiyfFeEwDQYJKoZIhvcNAQEL +BQAwFjEUMBIGA1UEAwwLanVpY2UubG9jYWwwHhcNMjYwNzE3MTkwMzE4WhcNMzYw +NzE0MTkwMzE4WjAWMRQwEgYDVQQDDAtqdWljZS5sb2NhbDCCAiIwDQYJKoZIhvcN +AQEBBQADggIPADCCAgoCggIBAMdzfOTb6JzIAMOeJZLAiIvBvVjWckqvR1uxXqw0 +1vDUkjLUFuIN2FyiwXU+Hy386uMDz8b37bTSSD1GLJAoW8xa+EuOSsObyqvzXbTs +gztWe4CAW6wSQrrIo5+pPLMzlmZ0TGdKpHGhXwhHZ7Jd/z72fqoBT7PpJfMKYyK2 +OUTwlc1SLSTeP8ykogHaxWhXxYbl2Asc8FvTxQS8s3jr1mXgYDoew1JOIvJkaI86 +uFv4HprcVZs3p3Ralm9SYOgGZT+wqEWTxTdZu1tR51P0pJEQtwHFPYG8oXHA0uTS +OsyFAjqUHsYbN/1l9FyBR7S7XvRhq0YTSYZWGk3Ek761S2pwlJ9hdHrXyofiWYOs +hL/R8Cj+L/TAmhubtGWILiqxojL6TaCSDo1YzEBezappZg4A5qAQDg7mtW5VV+Vl +iZlg2Z4h8ycaWsrZ+UeAH2bWj5vp/FEG8V16nubQjGwk37tDLSpK0c+7oJUutJ+N +FYnWwF5a1u4pqoJfCe9WKrqJF/OEwiEbCsI0lb2AD3btg9SVI0JoH+n1ozA9F8WB +wOlg/4ODTpDegTY6T3BDPKD2iIv+EQ77PLmbyf0+j6qDOX+Uk/8lxv9Ivk5h2/k+ +oSfI93ePHqVEa3YJ/2MAvZc397NdALUOc2wUrrlTp120/EFwp1v55GCrxq9mPiSs +AGtnAgMBAAGjUzBRMB0GA1UdDgQWBBRsQho4REYQJVAQg8usD/y+DLNPGDAfBgNV +HSMEGDAWgBRsQho4REYQJVAQg8usD/y+DLNPGDAPBgNVHRMBAf8EBTADAQH/MA0G +CSqGSIb3DQEBCwUAA4ICAQCBehiAGkdFtbfF8q2lZiV6ezCwlYQr41R6/BB1WLLr +QJ0iTySwDCTLfkdLMMdfOhFje/QQ8NfQPq6/6eCa4rI58/dRny60as4tGwHmmDQS +jXPirxZ6WCqziRqp5ZpFXTStBi0zUKyH9+nAUQj2lI+hYtzzWcC0OCJqaXKEqjtX +JsUMjFjqjJznt9XQcRTWpdmAmKxnLOKTs5APWchTzqyzJF3W0rdoFQzTyFfzpJqg +AYp8SM2OkBtr9W2oWhYqgwpml2FWDLUCHbQVTe9Oxvqp7i//liseAIdFxrRqn3M1 +0ZwDquzNakP0cNmds45NEw4BwENcOceTYNMLz8T/VfI9lJjcAPFAvY8wTWuEOLvC +sfqYlmTVn/zwe8dSNk0AvVqFRXrczPIBj/oblsbLrJNHUvdYhrB8e2orisaC0VsP +Hn/WUSzKAid83zIXkGH4ilZivqTtyqsWaEAElulL3S0PwODrVSRwWC4gX7MiBe3u +fvsfHK4+QIcq67z0LJRkBlSE0am+MN5kUdlOQsgF+2Q4kb+FaG8DORZOgFye0oyZ +Nhfw+dSj5EFtCwp2BKKiKwaul1T5wCoNL5UGL7Aj9dC0Mfv0lxUobgLkkdrVkKBz +jZVUxfectXk2izCSELxAi0/t5n/yRMPL9v013MGMDz3i/eFNBk88lW4jTAbImpyr +rw== +-----END CERTIFICATE----- diff --git a/labs/lab11/reverse-proxy/certs/localhost.key b/labs/lab11/reverse-proxy/certs/localhost.key new file mode 100644 index 000000000..580aac9e4 --- /dev/null +++ b/labs/lab11/reverse-proxy/certs/localhost.key @@ -0,0 +1,52 @@ +-----BEGIN PRIVATE KEY----- +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 +TcgXW8kCCSHui+b0lln0bEqdBq0mIA== +-----END PRIVATE KEY----- diff --git a/labs/lab11/reverse-proxy/nginx.conf b/labs/lab11/reverse-proxy/nginx.conf new file mode 100644 index 000000000..a346d592c --- /dev/null +++ b/labs/lab11/reverse-proxy/nginx.conf @@ -0,0 +1,138 @@ +user nginx; +worker_processes auto; + +events { worker_connections 1024; } + +http { + include /etc/nginx/mime.types; + default_type application/octet-stream; + sendfile on; + keepalive_timeout 10; + server_tokens off; + gzip off; + + # Security-focused logs + log_format security '$remote_addr - $remote_user [$time_local] ' + '"$request" $status $body_bytes_sent ' + '"$http_referer" "$http_user_agent" ' + 'rt=$request_time uct=$upstream_connect_time ' + 'urt=$upstream_response_time'; + access_log /var/log/nginx/access.log security; + error_log /var/log/nginx/error.log warn; + + # Upstream app + upstream juice { + server juice:3000; + keepalive 32; + } + + # Rate limit zone for login — 10 req/min per IP, burst of 5 + limit_req_zone $binary_remote_addr zone=login:10m rate=10r/m; + limit_req_status 429; + + # Connection limit zone — max 50 concurrent connections per IP + limit_conn_zone $binary_remote_addr zone=conn:10m; + + map $http_upgrade $connection_upgrade { default upgrade; '' close; } + + # Common proxy settings + 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_http_version 1.1; + proxy_set_header Connection $connection_upgrade; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Accept-Encoding ""; + proxy_read_timeout 30s; + proxy_send_timeout 30s; + proxy_connect_timeout 5s; + proxy_hide_header X-Powered-By; + proxy_hide_header X-Frame-Options; + proxy_hide_header X-Content-Type-Options; + proxy_hide_header Referrer-Policy; + proxy_hide_header Permissions-Policy; + proxy_hide_header Cross-Origin-Opener-Policy; + proxy_hide_header Cross-Origin-Resource-Policy; + proxy_hide_header Content-Security-Policy; + proxy_hide_header Content-Security-Policy-Report-Only; + proxy_hide_header Access-Control-Allow-Origin; + + # HTTP server (redirect to HTTPS) + server { + listen 80; + listen [::]:80; + server_name _; + + add_header X-Frame-Options "DENY" always; + add_header X-Content-Type-Options "nosniff" always; + add_header Referrer-Policy "strict-origin-when-cross-origin" always; + add_header Permissions-Policy "camera=(), geolocation=(), microphone=()" always; + add_header Cross-Origin-Opener-Policy "same-origin" always; + add_header Cross-Origin-Resource-Policy "same-origin" always; + add_header Content-Security-Policy-Report-Only "default-src 'self'; img-src 'self' data:; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'" always; + + return 308 https://$host$request_uri; + } + + # HTTPS server + server { + listen 443 ssl; + listen [::]:443 ssl; + http2 on; + server_name _; + + ssl_certificate /etc/nginx/certs/localhost.crt; + ssl_certificate_key /etc/nginx/certs/localhost.key; + + # TLS 1.3 only — modern crypto + ssl_protocols TLSv1.3; + ssl_prefer_server_ciphers off; + + # Cipher hardening — TLS 1.3 suites are auto-negotiated by OpenSSL; + # ssl_ciphers uses the legacy cipher-string format, so we use + # ssl_conf_command to pin TLS 1.3 suites explicitly. + ssl_conf_command Ciphersuites TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256; + ssl_ecdh_curve X25519:secp384r1; + + # Session management + ssl_session_cache shared:SSL:10m; + ssl_session_timeout 1d; + ssl_session_tickets off; + + # OCSP stapling — no effect on self-signed cert; enabled for documentation + ssl_stapling off; + # ssl_stapling on; + # ssl_stapling_verify on; + # resolver 1.1.1.1 8.8.8.8 valid=300s; + # resolver_timeout 5s; + + # Connection limits + limit_conn conn 50; + + # Timeouts (fail-closed) + client_max_body_size 2m; + client_body_timeout 10s; + client_header_timeout 10s; + keepalive_timeout 10s; + send_timeout 10s; + + # Security headers (6 required + extras) + add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always; + add_header X-Frame-Options "DENY" always; + add_header X-Content-Type-Options "nosniff" always; + add_header Referrer-Policy "strict-origin-when-cross-origin" always; + add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always; + add_header Content-Security-Policy-Report-Only "default-src 'self'; img-src 'self' data:; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'" always; + + location = /rest/user/login { + limit_req zone=login burst=5 nodelay; + limit_req_log_level warn; + proxy_pass http://juice; + } + + location / { + proxy_pass http://juice; + } + } +} diff --git a/labs/lab11/waf/docker-compose.override.yml b/labs/lab11/waf/docker-compose.override.yml new file mode 100644 index 000000000..bcb1f45f5 --- /dev/null +++ b/labs/lab11/waf/docker-compose.override.yml @@ -0,0 +1,21 @@ +services: + waf: + image: owasp/modsecurity-crs:nginx-alpine + restart: unless-stopped + depends_on: + - nginx + ports: + - "8443:8443" + environment: + - BACKEND=https://nginx:443 + - HTTPS=on + - SSL_CERTIFICATE=/etc/nginx/conf/server.crt + - SSL_CERTIFICATE_KEY=/etc/nginx/conf/server.key + - MODSEC_RULE_ENGINE=On + - PARANOIA=1 + - ANOMALY_INBOUND=5 + - ANOMALY_OUTBOUND=4 + volumes: + - ./reverse-proxy/certs/localhost.crt:/etc/nginx/conf/server.crt:ro + - ./reverse-proxy/certs/localhost.key:/etc/nginx/conf/server.key:ro + - ./logs:/var/log/modsec:rw diff --git a/labs/submission1.md b/labs/submission1.md new file mode 100644 index 000000000..a5c0e279f --- /dev/null +++ b/labs/submission1.md @@ -0,0 +1,52 @@ +# Triage Report — OWASP Juice Shop + +## Scope & Asset + +- Asset: OWASP Juice Shop (local lab instance) +- Image: bkimminich/juice-shop:v19.0.0 +- Release link/date: +- Image digest (optional): sha256:2765a26de7647609099a338d5b7f61085d95903c8703bb70f03fcc4b12f0818d + +## Environment + +- Host OS: Arch Linux (Linux kernel version: 6.16.8) +- Docker: Docker API version: 1.51, Docker client version: 28.4.0 + +## Deployment Details + +- Run command used: `docker run -d --name juice-shop -p 127.0.0.1:3000:3000 bkimminich/juice-shop:v19.0.0` +- Access URL: +- Network exposure: 127.0.0.1 only [x] Yes [ ] No (explain if No) + +## Health Check + +- Page load: +![homepage](assets/homepage.jpg) + +- API check: first 5–10 lines from `curl -s http://127.0.0.1:3000/rest/products | head` + +```html + + + + Error: Unexpected path: /rest/products +