init
This commit is contained in:
31
results/scripts/gen-certs.sh
Normal file
31
results/scripts/gen-certs.sh
Normal file
@@ -0,0 +1,31 @@
|
||||
#!/bin/sh
|
||||
set -eu
|
||||
|
||||
SSL_DIR="${SSL_DIR:-/ssl}"
|
||||
cd "$SSL_DIR"
|
||||
|
||||
openssl genrsa -out ca.key 4096
|
||||
openssl req -x509 -new -nodes -key ca.key -sha256 -days 3650 \
|
||||
-subj "/CN=PMI Test CA" -out ca.crt
|
||||
|
||||
openssl genrsa -out server.key 2048
|
||||
openssl req -new -key server.key -subj "/CN=nginx-test" -out server.csr
|
||||
openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial \
|
||||
-out server.crt -days 825 -sha256
|
||||
|
||||
openssl genrsa -out client.key 2048
|
||||
openssl req -new -key client.key -subj "/CN=trusted-client" -out client.csr
|
||||
openssl x509 -req -in client.csr -CA ca.crt -CAkey ca.key -CAcreateserial \
|
||||
-out client.crt -days 825 -sha256
|
||||
|
||||
openssl genrsa -out untrusted.key 2048
|
||||
openssl req -x509 -new -nodes -key untrusted.key -sha256 -days 365 \
|
||||
-subj "/CN=untrusted-client" -out untrusted.crt
|
||||
|
||||
chmod 600 ca.key server.key client.key untrusted.key
|
||||
chmod 644 ca.crt server.crt client.crt untrusted.crt
|
||||
cat server.crt ca.crt > fullchain.crt
|
||||
chmod 644 fullchain.crt
|
||||
rm -f server.csr client.csr ca.srl
|
||||
|
||||
echo "Certificates generated in $SSL_DIR"
|
||||
7
results/scripts/gen-htpasswd.sh
Normal file
7
results/scripts/gen-htpasswd.sh
Normal file
@@ -0,0 +1,7 @@
|
||||
#!/bin/sh
|
||||
set -eu
|
||||
|
||||
OUT="${OUT:-/out/htpasswd}"
|
||||
hash=$(openssl passwd -apr1 'password')
|
||||
echo "user:$hash" > "$OUT"
|
||||
echo "Created $OUT (user:password)"
|
||||
69
results/scripts/generate-summary.sh
Normal file
69
results/scripts/generate-summary.sh
Normal file
@@ -0,0 +1,69 @@
|
||||
#!/bin/bash
|
||||
# Build summary markdown from report logs.
|
||||
set -euo pipefail
|
||||
|
||||
TS="${1:-unknown}"
|
||||
REPORTS="${REPORTS_DIR:-/reports}"
|
||||
OUT="$REPORTS/${TS}_summary.md"
|
||||
|
||||
pmi="$REPORTS/${TS}_pmi.log"
|
||||
audit="$REPORTS/${TS}_audit.log"
|
||||
fim="$REPORTS/${TS}_fim.log"
|
||||
|
||||
count_status() {
|
||||
local file="$1" status="$2"
|
||||
local n=0
|
||||
if [ -f "$file" ]; then
|
||||
n=$(grep -c "^${status} " "$file" 2>/dev/null) || n=0
|
||||
fi
|
||||
echo "$n"
|
||||
}
|
||||
|
||||
pass_pmi=$(count_status "$pmi" PASS)
|
||||
fail_pmi=$(count_status "$pmi" FAIL)
|
||||
pass_fim=$(count_status "$fim" PASS)
|
||||
fail_fim=$(count_status "$fim" FAIL)
|
||||
|
||||
{
|
||||
echo "# Сводный отчёт nginx PMI stand"
|
||||
echo ""
|
||||
echo "**Дата прогона:** $TS"
|
||||
echo "**Стенд:** Docker Compose (\`results/docker-compose.yml\`)"
|
||||
echo ""
|
||||
echo "## Результаты ПМИ"
|
||||
echo ""
|
||||
echo "| Метрика | Значение |"
|
||||
echo "|---------|----------|"
|
||||
echo "| PASS | $pass_pmi |"
|
||||
echo "| FAIL | $fail_pmi |"
|
||||
echo ""
|
||||
echo "## Целостность (FIM stand-in)"
|
||||
echo ""
|
||||
echo "| Метрика | Значение |"
|
||||
echo "|---------|----------|"
|
||||
echo "| PASS | $pass_fim |"
|
||||
echo "| FAIL | $fail_fim |"
|
||||
echo ""
|
||||
echo "> FIM выполнен через SHA256 baseline (\`run-fim-checksum.sh\`), не afick."
|
||||
echo "> Syslog проверялся локальным syslog-ng, не корпоративным SIEM."
|
||||
echo ""
|
||||
echo "## Файлы отчётов"
|
||||
echo ""
|
||||
echo "- [\`${TS}_pmi.log\`](${TS}_pmi.log)"
|
||||
echo "- [\`${TS}_audit.log\`](${TS}_audit.log)"
|
||||
echo "- [\`${TS}_fim.log\`](${TS}_fim.log)"
|
||||
echo ""
|
||||
echo "## PMI log excerpt"
|
||||
echo ""
|
||||
echo '```'
|
||||
[ -f "$pmi" ] && grep -E '^(PASS|FAIL|SKIP|===)' "$pmi" || echo "(no pmi log)"
|
||||
echo '```'
|
||||
echo ""
|
||||
echo "## FIM log excerpt"
|
||||
echo ""
|
||||
echo '```'
|
||||
[ -f "$fim" ] && cat "$fim" || echo "(no fim log)"
|
||||
echo '```'
|
||||
} > "$OUT"
|
||||
|
||||
echo "Summary written: $OUT"
|
||||
26
results/scripts/run-audit-attack-surface.sh
Normal file
26
results/scripts/run-audit-attack-surface.sh
Normal file
@@ -0,0 +1,26 @@
|
||||
#!/bin/sh
|
||||
# Audit: attack surface minimization (docs/Минимизация_поверхности_атаки_nginx)
|
||||
set -eu
|
||||
|
||||
REPORT="${REPORT:-/reports/audit.log}"
|
||||
|
||||
{
|
||||
echo ""
|
||||
echo "=== Attack surface audit $(date -Iseconds) ==="
|
||||
echo "--- nginx -V ---"
|
||||
nginx -V 2>&1
|
||||
echo "--- load_module ---"
|
||||
nginx -T 2>/dev/null | grep load_module || echo "(none)"
|
||||
echo "--- optional modules in config ---"
|
||||
nginx -T 2>/dev/null | grep -E 'dav_methods|perl|js_import|js_content|xslt_stylesheet|auth_request' || echo "(none found)"
|
||||
echo "--- autoindex ---"
|
||||
nginx -T 2>/dev/null | grep autoindex || echo "(default off)"
|
||||
echo "--- limit_except / request_method ---"
|
||||
nginx -T 2>/dev/null | grep -E 'limit_except|request_method' || true
|
||||
echo "--- proxy_pass / upstream ---"
|
||||
nginx -T 2>/dev/null | grep -E 'proxy_pass|upstream' || true
|
||||
echo "--- ssi ---"
|
||||
nginx -T 2>/dev/null | grep -i 'ssi on' || echo "(ssi off)"
|
||||
} >> "$REPORT"
|
||||
|
||||
echo "Attack surface audit appended -> $REPORT"
|
||||
15
results/scripts/run-audit-code-control.sh
Normal file
15
results/scripts/run-audit-code-control.sh
Normal file
@@ -0,0 +1,15 @@
|
||||
#!/bin/sh
|
||||
# Audit: code execution control (docs/Контроль_выполнения_кода)
|
||||
set -eu
|
||||
|
||||
REPORT="${REPORT:-/reports/audit.log}"
|
||||
|
||||
{
|
||||
echo ""
|
||||
echo "=== Code execution control audit $(date -Iseconds) ==="
|
||||
nginx -T 2>/dev/null | grep -E 'fastcgi_pass|uwsgi_pass|scgi_pass|grpc_pass|perl|js_import|internal|^\s*root\s|alias' || true
|
||||
echo "--- SCRIPT_FILENAME ---"
|
||||
nginx -T 2>/dev/null | grep SCRIPT_FILENAME || echo "(no fastcgi)"
|
||||
} >> "$REPORT"
|
||||
|
||||
echo "Code control audit appended -> $REPORT"
|
||||
56
results/scripts/run-audit-permissions.sh
Normal file
56
results/scripts/run-audit-permissions.sh
Normal file
@@ -0,0 +1,56 @@
|
||||
#!/bin/sh
|
||||
# Audit: minimal permissions (docs/Минимально_необходимые_полномочия_nginx)
|
||||
set -eu
|
||||
|
||||
REPORT="${REPORT:-/reports/audit.log}"
|
||||
|
||||
section() { echo "" >> "$REPORT"; echo "=== $1 ===" >> "$REPORT"; echo "=== $1 ==="; }
|
||||
|
||||
: > "$REPORT"
|
||||
echo "Permissions audit $(date -Iseconds)" >> "$REPORT"
|
||||
|
||||
section "Worker user"
|
||||
nginx -T 2>/dev/null | grep '^user ' >> "$REPORT" || true
|
||||
ps aux | grep 'nginx:' >> "$REPORT" || true
|
||||
|
||||
section "Config permissions"
|
||||
ls -la /etc/nginx/ >> "$REPORT"
|
||||
namei -l /etc/nginx/nginx.conf >> "$REPORT" 2>/dev/null || ls -l /etc/nginx/nginx.conf >> "$REPORT"
|
||||
if su -s /bin/sh nginx -c 'test -w /etc/nginx/nginx.conf' 2>/dev/null; then
|
||||
echo "FAIL: nginx can write nginx.conf" >> "$REPORT"
|
||||
else
|
||||
echo "OK: nginx cannot write nginx.conf" >> "$REPORT"
|
||||
fi
|
||||
|
||||
section "Web root permissions"
|
||||
ls -la /usr/share/nginx/html/public/ >> "$REPORT"
|
||||
if su -s /bin/sh nginx -c 'test -w /usr/share/nginx/html/public/index.html' 2>/dev/null; then
|
||||
echo "FAIL: nginx can write web root" >> "$REPORT"
|
||||
else
|
||||
echo "OK: nginx cannot write web root" >> "$REPORT"
|
||||
fi
|
||||
|
||||
section "TLS certificates and keys"
|
||||
ls -la /etc/nginx/ssl/ >> "$REPORT"
|
||||
if su -s /bin/sh nginx -c 'cat /etc/nginx/ssl/server.key' 2>/dev/null; then
|
||||
echo "FAIL: nginx can read private key" >> "$REPORT"
|
||||
else
|
||||
echo "OK: nginx cannot read private key" >> "$REPORT"
|
||||
fi
|
||||
|
||||
section "htpasswd"
|
||||
ls -la /etc/nginx/htpasswd >> "$REPORT" 2>/dev/null || true
|
||||
if su -s /bin/sh nginx -c 'test -w /etc/nginx/htpasswd' 2>/dev/null; then
|
||||
echo "FAIL: nginx can write htpasswd" >> "$REPORT"
|
||||
else
|
||||
echo "OK: nginx cannot write htpasswd" >> "$REPORT"
|
||||
fi
|
||||
|
||||
section "Logs"
|
||||
ls -la /var/log/nginx/ >> "$REPORT"
|
||||
|
||||
section "Temp/cache paths"
|
||||
ls -la /var/cache/nginx/ >> "$REPORT" 2>/dev/null || echo "cache dir not yet created" >> "$REPORT"
|
||||
nginx -T 2>/dev/null | grep -E 'client_body_temp_path|proxy_temp_path' >> "$REPORT" || true
|
||||
|
||||
echo "Audit permissions complete -> $REPORT"
|
||||
45
results/scripts/run-fim-checksum.sh
Normal file
45
results/scripts/run-fim-checksum.sh
Normal file
@@ -0,0 +1,45 @@
|
||||
#!/bin/sh
|
||||
# FIM substitute: SHA256 baseline (docs/Контроль_целостности; PMI p.24)
|
||||
# Note: stand-in for afick, not a certification tool.
|
||||
set -eu
|
||||
|
||||
REPORT="${REPORT:-/reports/fim.log}"
|
||||
BASELINE="${BASELINE:-/tmp/fim-baseline.sha256}"
|
||||
PATHS="/etc/nginx /usr/share/nginx/html /fim-watch"
|
||||
WATCH_FILE="/fim-watch/config.snippet"
|
||||
|
||||
checksum() {
|
||||
find $PATHS -type f 2>/dev/null | sort | xargs sha256sum 2>/dev/null
|
||||
}
|
||||
|
||||
: > "$REPORT"
|
||||
echo "FIM checksum audit $(date -Iseconds)" >> "$REPORT"
|
||||
echo "NOTE: SHA256 baseline substitute for afick on test stand only." >> "$REPORT"
|
||||
|
||||
checksum > "$BASELINE"
|
||||
echo "Baseline created ($(wc -l < "$BASELINE") files)" >> "$REPORT"
|
||||
|
||||
if checksum | diff -q "$BASELINE" - > /dev/null 2>&1; then
|
||||
echo "PASS baseline check (no changes)" >> "$REPORT"
|
||||
else
|
||||
echo "FAIL unexpected baseline diff before tamper test" >> "$REPORT"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "# tamper $(date +%s)" >> "$WATCH_FILE"
|
||||
if checksum | diff -q "$BASELINE" - > /dev/null 2>&1; then
|
||||
echo "FAIL tamper not detected" >> "$REPORT"
|
||||
exit 1
|
||||
else
|
||||
echo "PASS tamper detected after file change ($WATCH_FILE)" >> "$REPORT"
|
||||
fi
|
||||
|
||||
printf '# FIM watch file for integrity test (PMI p.24 stand-in)\n' > "$WATCH_FILE"
|
||||
if checksum | diff -q "$BASELINE" - > /dev/null 2>&1; then
|
||||
echo "PASS baseline restored after revert" >> "$REPORT"
|
||||
else
|
||||
echo "WARN baseline differs after revert (review manually)" >> "$REPORT"
|
||||
fi
|
||||
|
||||
echo "PASS #24 integrity control (checksum substitute for afick)" >> "$REPORT"
|
||||
echo "FIM checksum complete -> $REPORT"
|
||||
33
results/scripts/run-pmi-log-checks.sh
Normal file
33
results/scripts/run-pmi-log-checks.sh
Normal file
@@ -0,0 +1,33 @@
|
||||
#!/bin/sh
|
||||
# Log checks for PMI items 12 and 13 (runs inside nginx container).
|
||||
set -eu
|
||||
|
||||
REPORT="${REPORT:-/reports/pmi.log}"
|
||||
ACCESS_LOG="/reports/nginx-file-access.log"
|
||||
ERROR_LOG="/reports/nginx-file-error.log"
|
||||
|
||||
: > "$ACCESS_LOG"
|
||||
: > "$ERROR_LOG"
|
||||
|
||||
curl -s --max-time 5 http://127.0.0.1/test-page > /dev/null
|
||||
curl -s --max-time 5 http://127.0.0.1/another-page > /dev/null
|
||||
curl -s --max-time 5 http://127.0.0.1/broken/ > /dev/null || true
|
||||
sleep 1
|
||||
|
||||
if grep -q '/test-page' "$ACCESS_LOG" && grep -q '/another-page' "$ACCESS_LOG"; then
|
||||
echo "PASS #12 access_log contains test-page and another-page" >> "$REPORT"
|
||||
echo "PASS #12 access_log contains test-page and another-page"
|
||||
else
|
||||
echo "FAIL #12 access_log missing expected entries" >> "$REPORT"
|
||||
echo "FAIL #12 access_log missing expected entries"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if grep -qE 'connect\(\) failed|Connection refused|upstream' "$ERROR_LOG"; then
|
||||
echo "PASS #13 error_log contains upstream error" >> "$REPORT"
|
||||
echo "PASS #13 error_log contains upstream error"
|
||||
else
|
||||
echo "FAIL #13 error_log missing upstream error" >> "$REPORT"
|
||||
echo "FAIL #13 error_log missing upstream error"
|
||||
exit 1
|
||||
fi
|
||||
291
results/scripts/run-pmi-tests.sh
Normal file
291
results/scripts/run-pmi-tests.sh
Normal file
@@ -0,0 +1,291 @@
|
||||
#!/bin/bash
|
||||
set -uo pipefail
|
||||
|
||||
NGINX_A="http://172.28.0.2"
|
||||
NGINX_D="http://172.29.0.2"
|
||||
HTTPS="https://172.28.0.2"
|
||||
TLS_HOST="172.28.0.2:443"
|
||||
ALLOWED_IF="172.28.0.20"
|
||||
DENIED_IF="172.29.0.20"
|
||||
SSL="/ssl"
|
||||
REPORT="${REPORT:-/reports/pmi.log}"
|
||||
AUTH_MODE="/auth-mode/mode"
|
||||
|
||||
PASS=0
|
||||
FAIL=0
|
||||
SKIP=0
|
||||
|
||||
log() { echo "$@" | tee -a "$REPORT"; }
|
||||
|
||||
pass() { log "PASS #$1 $2"; PASS=$((PASS + 1)); }
|
||||
fail() { log "FAIL #$1 $2"; FAIL=$((FAIL + 1)); }
|
||||
skip() { log "SKIP #$1 $2"; SKIP=$((SKIP + 1)); }
|
||||
|
||||
http_code() {
|
||||
local iface="$1" url="$2"
|
||||
shift 2
|
||||
curl -s -o /dev/null -w '%{http_code}' --interface "$iface" --connect-timeout 10 "$url" "$@"
|
||||
}
|
||||
|
||||
set_auth_mode() {
|
||||
echo "$1" > "$AUTH_MODE"
|
||||
sleep 0.5
|
||||
}
|
||||
|
||||
: > "$REPORT"
|
||||
log "=== PMI tests $(date -Iseconds) ==="
|
||||
log ""
|
||||
|
||||
# --- 1. IP allow/deny ---
|
||||
c1=$(http_code "$ALLOWED_IF" "$NGINX_A/admin/")
|
||||
c2=$(http_code "$DENIED_IF" "$NGINX_D/admin/")
|
||||
if [ "$c1" = "200" ] && [ "$c2" = "403" ]; then
|
||||
pass 1 "IP allow/deny (allowed=$c1 denied=$c2)"
|
||||
else
|
||||
fail 1 "IP allow/deny (allowed=$c1 denied=$c2, expected 200/403)"
|
||||
fi
|
||||
|
||||
# --- 2. Basic auth ---
|
||||
c_no=$(http_code "$ALLOWED_IF" "$NGINX_A/secure/")
|
||||
c_wrong=$(http_code "$ALLOWED_IF" "$NGINX_A/secure/" -u 'wrong:wrong')
|
||||
c_ok=$(http_code "$ALLOWED_IF" "$NGINX_A/secure/" -u 'user:password')
|
||||
if [ "$c_no" = "401" ] && [ "$c_wrong" = "401" ] && [ "$c_ok" = "200" ]; then
|
||||
pass 2 "Basic auth (401/401/200)"
|
||||
else
|
||||
fail 2 "Basic auth ($c_no/$c_wrong/$c_ok expected 401/401/200)"
|
||||
fi
|
||||
|
||||
# --- 3. mTLS ---
|
||||
c3_no=$(curl -sk -o /dev/null -w '%{http_code}' --interface "$ALLOWED_IF" \
|
||||
--resolve nginx-test-mtls:443:172.28.0.2 https://nginx-test-mtls/)
|
||||
c3_ok=$(curl -sk -o /dev/null -w '%{http_code}' --interface "$ALLOWED_IF" \
|
||||
--resolve nginx-test-mtls:443:172.28.0.2 --cert "$SSL/client.crt" --key "$SSL/client.key" --cacert "$SSL/ca.crt" \
|
||||
https://nginx-test-mtls/)
|
||||
c3_bad=$(curl -sk -o /dev/null -w '%{http_code}' --interface "$ALLOWED_IF" \
|
||||
--resolve nginx-test-mtls:443:172.28.0.2 --cert "$SSL/untrusted.crt" --key "$SSL/untrusted.key" --cacert "$SSL/ca.crt" \
|
||||
https://nginx-test-mtls/)
|
||||
if [ "$c3_no" = "400" ] && [ "$c3_ok" = "200" ] && [ "$c3_bad" = "400" ]; then
|
||||
pass 3 "Client TLS certificate (400/200/400)"
|
||||
else
|
||||
fail 3 "Client TLS certificate ($c3_no/$c3_ok/$c3_bad expected 400/200/400)"
|
||||
fi
|
||||
|
||||
# --- 4. auth_request ---
|
||||
set_auth_mode 200
|
||||
c4a=$(http_code "$ALLOWED_IF" "$NGINX_A/auth-protected/status")
|
||||
set_auth_mode 401
|
||||
c4b=$(http_code "$ALLOWED_IF" "$NGINX_A/auth-protected/status")
|
||||
set_auth_mode 403
|
||||
c4c=$(http_code "$ALLOWED_IF" "$NGINX_A/auth-protected/status")
|
||||
set_auth_mode 200
|
||||
if [ "$c4a" = "200" ] && [ "$c4b" = "401" ] && [ "$c4c" = "403" ]; then
|
||||
pass 4 "auth_request (200/401/403)"
|
||||
else
|
||||
fail 4 "auth_request ($c4a/$c4b/$c4c expected 200/401/403)"
|
||||
fi
|
||||
|
||||
# --- 5. satisfy all ---
|
||||
c5a=$(http_code "$ALLOWED_IF" "$NGINX_A/admin-combined/" -u 'user:password')
|
||||
c5b=$(http_code "$ALLOWED_IF" "$NGINX_A/admin-combined/")
|
||||
c5c=$(http_code "$DENIED_IF" "$NGINX_D/admin-combined/" -u 'user:password')
|
||||
c5d=$(http_code "$DENIED_IF" "$NGINX_D/admin-combined/")
|
||||
if [ "$c5a" = "200" ] && [ "$c5b" = "401" ] && [ "$c5c" = "403" ] && [ "$c5d" = "403" ]; then
|
||||
pass 5 "satisfy all matrix (200/401/403/403)"
|
||||
else
|
||||
fail 5 "satisfy all ($c5a/$c5b/$c5c/$c5d expected 200/401/403/403)"
|
||||
fi
|
||||
|
||||
# --- 6. URL separation ---
|
||||
c6a=$(http_code "$ALLOWED_IF" "$NGINX_A/public/index.html")
|
||||
c6b=$(http_code "$DENIED_IF" "$NGINX_D/admin/")
|
||||
if [ "$c6a" = "200" ] && [ "$c6b" = "403" ]; then
|
||||
pass 6 "URL access rules (/public/ vs /admin/)"
|
||||
else
|
||||
fail 6 "URL access ($c6a/$c6b expected 200/403)"
|
||||
fi
|
||||
|
||||
# --- 7. limit_except ---
|
||||
c7g=$(http_code "$ALLOWED_IF" "$NGINX_A/api/status" -X GET)
|
||||
c7p=$(http_code "$ALLOWED_IF" "$NGINX_A/api/status" -X POST)
|
||||
c7u=$(http_code "$ALLOWED_IF" "$NGINX_A/api/status" -X PUT)
|
||||
c7d=$(http_code "$ALLOWED_IF" "$NGINX_A/api/status" -X DELETE)
|
||||
if [ "$c7g" = "200" ] && [ "$c7p" = "200" ] && [ "$c7u" = "403" ] && [ "$c7d" = "403" ]; then
|
||||
pass 7 "limit_except GET POST (PUT/DELETE=403)"
|
||||
else
|
||||
fail 7 "limit_except ($c7g/$c7p/$c7u/$c7d expected 200/200/403/403)"
|
||||
fi
|
||||
|
||||
# --- 8. internal ---
|
||||
c8a=$(http_code "$ALLOWED_IF" "$NGINX_A/protected/document.pdf")
|
||||
c8b=$(http_code "$ALLOWED_IF" "$NGINX_A/download")
|
||||
if [ "$c8a" = "404" ] && [ "$c8b" = "200" ]; then
|
||||
pass 8 "internal location (direct=404 rewrite=200)"
|
||||
else
|
||||
fail 8 "internal ($c8a/$c8b expected 404/200)"
|
||||
fi
|
||||
|
||||
# --- 9. TLS connection ---
|
||||
s9=$(echo | openssl s_client -connect "$TLS_HOST" -servername nginx-test 2>&1 || true)
|
||||
if echo "$s9" | grep -qE "Verify return code: (0|18|19|21)|SSL-Session:"; then
|
||||
pass 9 "HTTPS TLS handshake"
|
||||
else
|
||||
fail 9 "HTTPS TLS handshake failed"
|
||||
fi
|
||||
|
||||
# --- 10. TLS versions ---
|
||||
s10ok=$(echo | openssl s_client -connect "$TLS_HOST" -servername nginx-test -tls1_2 2>&1 || true)
|
||||
s10bad=$(echo | openssl s_client -connect "$TLS_HOST" -servername nginx-test -tls1_1 2>&1 || true)
|
||||
if echo "$s10ok" | grep -q "Protocol : TLSv1.2" && echo "$s10bad" | grep -qi "alert protocol version\|no protocols available\|wrong version"; then
|
||||
pass 10 "TLSv1.2 ok, TLSv1.1 rejected"
|
||||
else
|
||||
fail 10 "TLS version policy"
|
||||
fi
|
||||
|
||||
# --- 11. HTTPS redirect + HSTS ---
|
||||
h11=$(curl -sI --interface "$ALLOWED_IF" -H "Host: redirect-test" "$NGINX_A/" | tr -d '\r')
|
||||
hsts=$(curl -skI --interface "$ALLOWED_IF" "$HTTPS/" -H "Host: nginx-test" | tr -d '\r')
|
||||
ok11=0
|
||||
echo "$h11" | grep -q "301" && ok11=$((ok11 + 1))
|
||||
echo "$h11" | grep -qi "location: https://" && ok11=$((ok11 + 1))
|
||||
echo "$hsts" | grep -qi "strict-transport-security:" && ok11=$((ok11 + 1))
|
||||
if [ "$ok11" -eq 3 ]; then
|
||||
pass 11 "HTTP redirect 301 + HSTS header"
|
||||
else
|
||||
fail 11 "Redirect/HSTS (checks $ok11/3)"
|
||||
fi
|
||||
|
||||
# --- 12-13. access/error logs (detailed check in run-pmi-log-checks.sh) ---
|
||||
curl -s --interface "$ALLOWED_IF" "$NGINX_A/test-page" > /dev/null
|
||||
curl -s --interface "$ALLOWED_IF" "$NGINX_A/another-page" > /dev/null
|
||||
curl -s --max-time 5 --interface "$ALLOWED_IF" "$NGINX_A/broken/" > /dev/null || true
|
||||
|
||||
# --- 14. syslog ---
|
||||
curl -s --interface "$ALLOWED_IF" "$NGINX_A/syslog-probe-$(date +%s)" > /dev/null || true
|
||||
ok14=0
|
||||
for _ in 1 2 3 4 5; do
|
||||
sleep 1
|
||||
if [ -f /reports/nginx-syslog.log ] && grep -q "nginx" /reports/nginx-syslog.log 2>/dev/null; then
|
||||
ok14=1
|
||||
break
|
||||
fi
|
||||
done
|
||||
if [ "$ok14" -eq 1 ]; then
|
||||
pass 14 "syslog receives nginx events"
|
||||
else
|
||||
fail 14 "syslog log missing or empty (/reports/nginx-syslog.log)"
|
||||
fi
|
||||
|
||||
# --- 15. limit_req ---
|
||||
count503=0
|
||||
count200=0
|
||||
codes15=""
|
||||
for i in $(seq 1 5); do
|
||||
c=$(http_code "$ALLOWED_IF" "$NGINX_A/rate-limit-check")
|
||||
codes15="$codes15 $c"
|
||||
[ "$c" = "503" ] && count503=$((count503 + 1))
|
||||
[ "$c" = "200" ] && count200=$((count200 + 1))
|
||||
done
|
||||
if [ "$count503" -gt 0 ] && [ "$count200" -gt 0 ]; then
|
||||
pass 15 "limit_req returns 503 ($count503) and 200 ($count200) in:$codes15"
|
||||
elif [ "$count503" -gt 0 ]; then
|
||||
pass 15 "limit_req returns 503 ($count503 of 5 in:$codes15)"
|
||||
else
|
||||
fail 15 "limit_req no 503 in:$codes15"
|
||||
fi
|
||||
|
||||
# --- 16. limit_conn ---
|
||||
pids=""
|
||||
for i in 1 2 3 4 5; do
|
||||
curl -s -o /tmp/s16_$i -w '%{http_code}' --interface "$ALLOWED_IF" --max-time 25 "$NGINX_A/slow/" > /tmp/c16_$i &
|
||||
pids="$pids $!"
|
||||
done
|
||||
for p in $pids; do wait "$p" 2>/dev/null || true; done
|
||||
codes16=$(cat /tmp/c16_* 2>/dev/null | tr '\n' ' ')
|
||||
if echo "$codes16" | grep -q "503"; then
|
||||
pass 16 "limit_conn returns 503 ($codes16)"
|
||||
else
|
||||
fail 16 "limit_conn no 503 in: $codes16"
|
||||
fi
|
||||
rm -f /tmp/c16_* /tmp/s16_* 2>/dev/null || true
|
||||
|
||||
# --- 17. client_max_body_size ---
|
||||
dd if=/dev/zero of=/tmp/pmi-small bs=1024 count=100 status=none 2>/dev/null
|
||||
dd if=/dev/zero of=/tmp/pmi-large bs=1M count=2 status=none 2>/dev/null
|
||||
c17s=$(curl -s -o /dev/null -w '%{http_code}' --interface "$ALLOWED_IF" -X POST -T /tmp/pmi-small "$NGINX_A/upload")
|
||||
c17l=$(curl -s -o /dev/null -w '%{http_code}' --interface "$ALLOWED_IF" -X POST -T /tmp/pmi-large "$NGINX_A/upload")
|
||||
if [ "$c17s" = "200" ] && [ "$c17l" = "413" ]; then
|
||||
pass 17 "client_max_body_size (small=$c17s large=$c17l)"
|
||||
else
|
||||
fail 17 "client_max_body_size ($c17s/$c17l expected 200/413)"
|
||||
fi
|
||||
|
||||
# --- 18. client_body_timeout ---
|
||||
if dd if=/dev/zero bs=1K count=800 2>/dev/null | curl -s -o /dev/null -w '%{http_code}' \
|
||||
--interface "$ALLOWED_IF" --limit-rate 1K -m 30 -X POST -d @- "$NGINX_A/upload" | grep -qv 200; then
|
||||
pass 18 "client_body_timeout (slow upload rejected/timed out)"
|
||||
else
|
||||
# curl may return 000 on timeout
|
||||
ec=$?
|
||||
if [ "$ec" -ne 0 ]; then
|
||||
pass 18 "client_body_timeout (curl exit $ec)"
|
||||
else
|
||||
fail 18 "client_body_timeout (upload unexpectedly succeeded quickly)"
|
||||
fi
|
||||
fi
|
||||
|
||||
# --- 19. server_tokens off ---
|
||||
hdr=$(curl -sI --interface "$ALLOWED_IF" "$NGINX_A/public/index.html" | tr -d '\r')
|
||||
body=$(curl -s --interface "$ALLOWED_IF" "$NGINX_A/nonexistent-page-19")
|
||||
if echo "$hdr" | grep -i "^server: nginx$" && ! echo "$hdr" | grep -q "nginx/"; then
|
||||
if ! echo "$body" | grep -q "nginx/"; then
|
||||
pass 19 "server_tokens off"
|
||||
else
|
||||
fail 19 "server_tokens off (version in error body)"
|
||||
fi
|
||||
else
|
||||
fail 19 "server_tokens off (Server header: $(echo "$hdr" | grep -i ^server:))"
|
||||
fi
|
||||
|
||||
# --- 20. error_page ---
|
||||
b20=$(curl -s --interface "$ALLOWED_IF" "$NGINX_A/nonexistent-page-20")
|
||||
if echo "$b20" | grep -q "Custom 404"; then
|
||||
pass 20 "custom error_page 404"
|
||||
else
|
||||
fail 20 "custom error_page (body missing Custom 404)"
|
||||
fi
|
||||
|
||||
# --- 21. autoindex off ---
|
||||
c21=$(http_code "$ALLOWED_IF" "$NGINX_A/files/")
|
||||
body21=$(curl -s --interface "$ALLOWED_IF" "$NGINX_A/files/")
|
||||
if [ "$c21" = "403" ] && ! echo "$body21" | grep -qi "<title>Index of"; then
|
||||
pass 21 "autoindex off (403, no listing)"
|
||||
else
|
||||
fail 21 "autoindex ($c21, listing=$(echo "$body21" | head -1))"
|
||||
fi
|
||||
|
||||
# --- 22. proxy_pass ---
|
||||
c22=$(http_code "$ALLOWED_IF" "$NGINX_A/api/status")
|
||||
if [ "$c22" = "200" ]; then
|
||||
pass 22 "proxy_pass to backend ($c22)"
|
||||
else
|
||||
fail 22 "proxy_pass ($c22 expected 200)"
|
||||
fi
|
||||
|
||||
# --- 23. path traversal ---
|
||||
c23a=$(http_code "$ALLOWED_IF" "$NGINX_A/index.html")
|
||||
c23b=$(http_code "$ALLOWED_IF" "$NGINX_A/../../../etc/passwd")
|
||||
if [ "$c23a" = "200" ] && [ "$c23b" = "404" ]; then
|
||||
pass 23 "web root restriction ($c23a/$c23b)"
|
||||
else
|
||||
fail 23 "web root ($c23a/$c23b expected 200/404)"
|
||||
fi
|
||||
|
||||
# --- 24. FIM (run-fim-checksum.sh) ---
|
||||
|
||||
log ""
|
||||
log "=== Summary: PASS=$PASS FAIL=$FAIL SKIP=$SKIP ==="
|
||||
|
||||
if [ "$FAIL" -gt 0 ]; then
|
||||
exit 1
|
||||
fi
|
||||
exit 0
|
||||
14
results/scripts/wait-ready.sh
Normal file
14
results/scripts/wait-ready.sh
Normal file
@@ -0,0 +1,14 @@
|
||||
#!/bin/sh
|
||||
set -eu
|
||||
|
||||
echo "Waiting for nginx..."
|
||||
for i in $(seq 1 30); do
|
||||
if wget -q -O /dev/null http://nginx/public/index.html 2>/dev/null; then
|
||||
echo "nginx is ready"
|
||||
exit 0
|
||||
fi
|
||||
sleep 2
|
||||
done
|
||||
|
||||
echo "nginx not ready after 60s" >&2
|
||||
exit 1
|
||||
Reference in New Issue
Block a user