Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,4 @@ node_modules/
.venv/
.DS_Store
*.swp
labs/lab11/waf/waf-logs/
3 changes: 3 additions & 0 deletions labs/lab11/results/cipher.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
New, TLSv1.3, Cipher is TLS_AES_256_GCM_SHA384
Cipher : TLS_AES_256_GCM_SHA384
Cipher : TLS_AES_256_GCM_SHA384
22 changes: 22 additions & 0 deletions labs/lab11/results/headers.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
HTTP/1.1 200 OK
Server: nginx
Date: Fri, 10 Jul 2026 08:42:04 GMT
Content-Type: text/html; charset=UTF-8
Content-Length: 9903
Connection: keep-alive
Feature-Policy: payment 'self'
X-Recruiting: /#/jobs
Accept-Ranges: bytes
Cache-Control: public, max-age=0
Last-Modified: Fri, 10 Jul 2026 08:38:56 GMT
ETag: W/"26af-19f4b2e096b"
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=(), 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'

15 changes: 15 additions & 0 deletions labs/lab11/results/http-redirect.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
HTTP/1.1 308 Permanent Redirect
Server: nginx
Date: Fri, 10 Jul 2026 08:41:37 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'

2 changes: 2 additions & 0 deletions labs/lab11/results/ratelimit.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
54 429
6 500
8 changes: 8 additions & 0 deletions labs/lab11/results/timeout.txt
Original file line number Diff line number Diff line change
@@ -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
verify return:1
depth=0 CN=juice.local
verify return:1
A8330000:error:0A000126:SSL routines::unexpected eof while reading:../openssl-3.5.5/ssl/record/rec_layer_s3.c:698:
8 changes: 8 additions & 0 deletions labs/lab11/results/tls13.txt
Original file line number Diff line number Diff line change
@@ -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
9 changes: 9 additions & 0 deletions labs/lab11/results/waf-audit.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{"transaction":{"client_ip":"127.0.0.1","time_stamp":"Fri Jul 10 09:00:26 2026","server_id":"c96835f14eb6f49b91a82df9e69c29ce2a1206a4","client_port":47222,"host_ip":"127.0.0.1","host_port":8443,"unique_id":"178367402620.995940","is_interrupted":false,"request":{"method":"GET","http_version":"2.0","hostname":"localhost","uri":"/healthz","headers":{"user-agent":"healthcheck","accept":"*/*","host":"localhost:8443"}},"response":{"body":"OK","http_code":200,"headers":{"Server":"nginx\u0000","Date":"Fri, 10 Jul 2026 09:00:26 GMT","Content-Length":"2","Content-Type":"application/octet-stream","Content-Type":"text/plain","Connection":"close"}},"producer":{"modsecurity":"ModSecurity v3.0.16 (Linux)","connector":"ModSecurity-nginx v1.0.4","secrules_engine":"Enabled","components":["OWASP_CRS/3.3.10\""]},"messages":[]}}
{"transaction":{"client_ip":"127.0.0.1","time_stamp":"Fri Jul 10 09:00:56 2026","server_id":"c96835f14eb6f49b91a82df9e69c29ce2a1206a4","client_port":36772,"host_ip":"127.0.0.1","host_port":8443,"unique_id":"178367405662.529850","is_interrupted":false,"request":{"method":"GET","http_version":"2.0","hostname":"localhost","uri":"/healthz","headers":{"user-agent":"healthcheck","accept":"*/*","host":"localhost:8443"}},"response":{"body":"OK","http_code":200,"headers":{"Server":"nginx\u0000","Date":"Fri, 10 Jul 2026 09:00:56 GMT","Content-Length":"2","Content-Type":"application/octet-stream","Content-Type":"text/plain","Connection":"close"}},"producer":{"modsecurity":"ModSecurity v3.0.16 (Linux)","connector":"ModSecurity-nginx v1.0.4","secrules_engine":"Enabled","components":["OWASP_CRS/3.3.10\""]},"messages":[]}}
{"transaction":{"client_ip":"127.0.0.1","time_stamp":"Fri Jul 10 09:01:27 2026","server_id":"c96835f14eb6f49b91a82df9e69c29ce2a1206a4","client_port":47686,"host_ip":"127.0.0.1","host_port":8443,"unique_id":"178367408768.607175","is_interrupted":false,"request":{"method":"GET","http_version":"2.0","hostname":"localhost","uri":"/healthz","headers":{"user-agent":"healthcheck","accept":"*/*","host":"localhost:8443"}},"response":{"body":"OK","http_code":200,"headers":{"Server":"nginx\u0000","Date":"Fri, 10 Jul 2026 09:01:27 GMT","Content-Length":"2","Content-Type":"application/octet-stream","Content-Type":"text/plain","Connection":"close"}},"producer":{"modsecurity":"ModSecurity v3.0.16 (Linux)","connector":"ModSecurity-nginx v1.0.4","secrules_engine":"Enabled","components":["OWASP_CRS/3.3.10\""]},"messages":[]}}
{"transaction":{"client_ip":"172.20.0.1","time_stamp":"Fri Jul 10 09:01:43 2026","server_id":"c96835f14eb6f49b91a82df9e69c29ce2a1206a4","client_port":46280,"host_ip":"172.20.0.4","host_port":8080,"unique_id":"178367410380.402071","is_interrupted":true,"request":{"method":"GET","http_version":"1.1","hostname":"localhost","uri":"/rest/products/search?q='%20OR%201=1--","headers":{"Host":"localhost:8081","User-Agent":"curl/8.18.0","Accept":"*/*"}},"response":{"body":"<html>\r\n<head><title>403 Forbidden</title></head>\r\n<body>\r\n<center><h1>403 Forbidden</h1></center>\r\n<hr><center>nginx</center>\r\n</body>\r\n</html>\r\n","http_code":403,"headers":{"Server":"nginx\u0000","Date":"Fri, 10 Jul 2026 09:01:43 GMT","Content-Length":"146","Content-Type":"text/plain","Access-Control-Allow-Origin":"*","Connection":"keep-alive","Access-Control-Max-Age":"3600","Access-Control-Allow-Methods":"GET, POST, PUT, DELETE, OPTIONS","Access-Control-Allow-Headers":"*"}},"producer":{"modsecurity":"ModSecurity v3.0.16 (Linux)","connector":"ModSecurity-nginx v1.0.4","secrules_engine":"Enabled","components":["OWASP_CRS/3.3.10\""]},"messages":[{"message":"SQL Injection Attack Detected via libinjection","details":{"match":"detected SQLi using libinjection.","reference":"v28,10","ruleId":"942100","file":"/etc/modsecurity.d/owasp-crs/rules/REQUEST-942-APPLICATION-ATTACK-SQLI.conf","lineNumber":"46","data":"Matched Data: s&1c found within ARGS:q: ' OR 1=1--","severity":"2","ver":"OWASP_CRS/3.3.10","rev":"","tags":["application-multi","language-multi","platform-multi","attack-sqli","paranoia-level/1","OWASP_CRS","capec/1000/152/248/66","PCI/6.5.2"],"maturity":"0","accuracy":"0"}},{"message":"Inbound Anomaly Score Exceeded (Total Score: 5)","details":{"match":"Matched \"Operator `Ge' with parameter `5' against variable `TX:ANOMALY_SCORE' (Value: `5' )","reference":"","ruleId":"949110","file":"/etc/modsecurity.d/owasp-crs/rules/REQUEST-949-BLOCKING-EVALUATION.conf","lineNumber":"81","data":"","severity":"2","ver":"OWASP_CRS/3.3.10","rev":"","tags":["modsecurity","application-multi","language-multi","platform-multi","attack-generic"],"maturity":"0","accuracy":"0"}}]}}
{"transaction":{"client_ip":"127.0.0.1","time_stamp":"Fri Jul 10 09:01:57 2026","server_id":"c96835f14eb6f49b91a82df9e69c29ce2a1206a4","client_port":50166,"host_ip":"127.0.0.1","host_port":8443,"unique_id":"178367411746.611460","is_interrupted":false,"request":{"method":"GET","http_version":"2.0","hostname":"localhost","uri":"/healthz","headers":{"user-agent":"healthcheck","accept":"*/*","host":"localhost:8443"}},"response":{"body":"OK","http_code":200,"headers":{"Server":"nginx\u0000","Date":"Fri, 10 Jul 2026 09:01:57 GMT","Content-Length":"2","Content-Type":"application/octet-stream","Content-Type":"text/plain","Connection":"close"}},"producer":{"modsecurity":"ModSecurity v3.0.16 (Linux)","connector":"ModSecurity-nginx v1.0.4","secrules_engine":"Enabled","components":["OWASP_CRS/3.3.10\""]},"messages":[]}}
{"transaction":{"client_ip":"127.0.0.1","time_stamp":"Fri Jul 10 09:02:27 2026","server_id":"c96835f14eb6f49b91a82df9e69c29ce2a1206a4","client_port":42676,"host_ip":"127.0.0.1","host_port":8443,"unique_id":"178367414721.647263","is_interrupted":false,"request":{"method":"GET","http_version":"2.0","hostname":"localhost","uri":"/healthz","headers":{"user-agent":"healthcheck","accept":"*/*","host":"localhost:8443"}},"response":{"body":"OK","http_code":200,"headers":{"Server":"nginx\u0000","Date":"Fri, 10 Jul 2026 09:02:27 GMT","Content-Length":"2","Content-Type":"application/octet-stream","Content-Type":"text/plain","Connection":"close"}},"producer":{"modsecurity":"ModSecurity v3.0.16 (Linux)","connector":"ModSecurity-nginx v1.0.4","secrules_engine":"Enabled","components":["OWASP_CRS/3.3.10\""]},"messages":[]}}
{"transaction":{"client_ip":"127.0.0.1","time_stamp":"Fri Jul 10 09:02:57 2026","server_id":"c96835f14eb6f49b91a82df9e69c29ce2a1206a4","client_port":46892,"host_ip":"127.0.0.1","host_port":8443,"unique_id":"178367417732.279455","is_interrupted":false,"request":{"method":"GET","http_version":"2.0","hostname":"localhost","uri":"/healthz","headers":{"user-agent":"healthcheck","accept":"*/*","host":"localhost:8443"}},"response":{"body":"OK","http_code":200,"headers":{"Server":"nginx\u0000","Date":"Fri, 10 Jul 2026 09:02:57 GMT","Content-Length":"2","Content-Type":"application/octet-stream","Content-Type":"text/plain","Connection":"close"}},"producer":{"modsecurity":"ModSecurity v3.0.16 (Linux)","connector":"ModSecurity-nginx v1.0.4","secrules_engine":"Enabled","components":["OWASP_CRS/3.3.10\""]},"messages":[]}}
{"transaction":{"client_ip":"127.0.0.1","time_stamp":"Fri Jul 10 09:03:27 2026","server_id":"c96835f14eb6f49b91a82df9e69c29ce2a1206a4","client_port":46066,"host_ip":"127.0.0.1","host_port":8443,"unique_id":"17836742074.571046","is_interrupted":false,"request":{"method":"GET","http_version":"2.0","hostname":"localhost","uri":"/healthz","headers":{"user-agent":"healthcheck","accept":"*/*","host":"localhost:8443"}},"response":{"body":"OK","http_code":200,"headers":{"Server":"nginx\u0000","Date":"Fri, 10 Jul 2026 09:03:27 GMT","Content-Length":"2","Content-Type":"application/octet-stream","Content-Type":"text/plain","Connection":"close"}},"producer":{"modsecurity":"ModSecurity v3.0.16 (Linux)","connector":"ModSecurity-nginx v1.0.4","secrules_engine":"Enabled","components":["OWASP_CRS/3.3.10\""]},"messages":[]}}
{"transaction":{"client_ip":"127.0.0.1","time_stamp":"Fri Jul 10 09:03:57 2026","server_id":"c96835f14eb6f49b91a82df9e69c29ce2a1206a4","client_port":39670,"host_ip":"127.0.0.1","host_port":8443,"unique_id":"178367423788.827231","is_interrupted":false,"request":{"method":"GET","http_version":"2.0","hostname":"localhost","uri":"/healthz","headers":{"user-agent":"healthcheck","accept":"*/*","host":"localhost:8443"}},"response":{"body":"OK","http_code":200,"headers":{"Server":"nginx\u0000","Date":"Fri, 10 Jul 2026 09:03:57 GMT","Content-Length":"2","Content-Type":"application/octet-stream","Content-Type":"text/plain","Connection":"close"}},"producer":{"modsecurity":"ModSecurity v3.0.16 (Linux)","connector":"ModSecurity-nginx v1.0.4","secrules_engine":"Enabled","components":["OWASP_CRS/3.3.10\""]},"messages":[]}}
36 changes: 28 additions & 8 deletions labs/lab11/reverse-proxy/nginx.conf
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ http {
limit_req_zone $binary_remote_addr zone=login:10m rate=10r/m;
limit_req_status 429;

# Connection limit zone (Task 2)
limit_conn_zone $binary_remote_addr zone=conn:10m;

map $http_upgrade $connection_upgrade { default upgrade; '' close; }

# Common proxy settings
Expand Down Expand Up @@ -83,29 +86,42 @@ http {
http2 on;
server_name _;

# Connection limit (Task 2)
limit_conn conn 50;

# Fail-closed timeouts (client-side, Task 2)
client_body_timeout 10s;
client_header_timeout 10s;

ssl_certificate /etc/nginx/certs/localhost.crt;
ssl_certificate_key /etc/nginx/certs/localhost.key;
ssl_session_timeout 10m;
ssl_session_timeout 1d;
ssl_session_cache shared:SSL:10m;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers "TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256:EECDH+AESGCM:EDH+AESGCM";
ssl_prefer_server_ciphers on;
ssl_session_tickets off;

# TLS 1.3 only, per Task 1 requirement
ssl_protocols TLSv1.3;
ssl_conf_command Ciphersuites TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256;
ssl_ecdh_curve X25519:secp384r1;
# TLS 1.3 negotiates ciphersuites independently of this directive, but Task 1
# explicitly asks for it to be off (TLS 1.2 server-preference logic doesn't apply here).
ssl_prefer_server_ciphers off;

ssl_stapling off;
# If using a publicly-trusted certificate, you may enable OCSP stapling:
# OCSP stapling requires a publicly-trusted CA-issued cert; our self-signed
# lab cert has no OCSP responder to query, so stapling is documentation-only here.
# ssl_stapling on;
# ssl_stapling_verify on;
# resolver 1.1.1.1 8.8.8.8 valid=300s;
# resolver_timeout 5s;
# ssl_trusted_certificate /etc/ssl/certs/ca-certificates.crt;

client_max_body_size 2m;
client_body_timeout 10s;
client_header_timeout 10s;
keepalive_timeout 10s;
send_timeout 10s;

# Security headers (include HSTS here only)
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
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;
Expand All @@ -118,10 +134,14 @@ http {
limit_req zone=login burst=5 nodelay;
limit_req_log_level warn;
proxy_pass http://juice;
proxy_connect_timeout 5s;
proxy_read_timeout 30s;
}

location / {
proxy_pass http://juice;
proxy_connect_timeout 5s;
proxy_read_timeout 30s;
}
}
}
16 changes: 16 additions & 0 deletions labs/lab11/waf/docker-compose.override.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
services:
waf:
image: owasp/modsecurity-crs:nginx-alpine
restart: unless-stopped
depends_on:
- juice
environment:
BACKEND: "http://juice:3000"
PARANOIA: "1"
MODSEC_RULE_ENGINE: "On"
MODSEC_AUDIT_ENGINE: "On"
MODSEC_AUDIT_LOG: "/var/log/modsecurity/audit.log"
ports:
- "8081:8080"
volumes:
- ./waf-logs:/var/log/modsecurity:rw
Loading