Swap Werkzeug for Caddy as the protocol layer for http and https decoy services. Flask keeps owning app logic (fake_app, custom_body, headers, syslog) on 127.0.0.1:8080; Caddy terminates h1/h2/h2c/h3 on the wire with real-world TLS/QUIC fingerprints. - Add `multi_enum` FieldType to ServiceConfigField + _coerce - Add `http_versions` field to HTTPService (h1/h2c) and HTTPSService (h1/h2/h3); selecting h3 emits UDP/443 port mapping in compose - Rewrite both Dockerfiles with multi-stage Caddy binary copy + setcap for port binding as the logrelay user - Entrypoints parse HTTP_VERSIONS JSON, render a Caddyfile, start Flask in background, wait for it, then exec Caddy - https/server.py drops direct TLS handling; Caddy owns the cert - Add ProxyFix to both server.py so Flask sees real attacker IPs - Frontend: multi_enum checkbox-group renderer in ServiceConfigFields; FormValue union extended to string[]; compactPayload skips [] - Fix stale test_smtp_relay_schema_matches_smtp: relay schema is a superset of smtp, not equal; update assertions accordingly
76 lines
2.2 KiB
Python
76 lines
2.2 KiB
Python
"""Caddyfile protocol token generation for http and https entrypoints."""
|
|
import json
|
|
|
|
|
|
def _http_protocols(versions_json: str | None) -> str:
|
|
"""Mirrors the Python inline logic in templates/http/entrypoint.sh."""
|
|
versions = json.loads(versions_json or '["http/1.1"]')
|
|
tokens = []
|
|
if "http/1.1" in versions:
|
|
tokens.append("h1")
|
|
if "http/2" in versions:
|
|
tokens.append("h2c")
|
|
return " ".join(tokens) if tokens else "h1"
|
|
|
|
|
|
def _https_protocols(versions_json: str | None) -> str:
|
|
"""Mirrors the Python inline logic in templates/https/entrypoint.sh."""
|
|
versions = json.loads(versions_json or '["http/1.1"]')
|
|
tokens = []
|
|
if "http/1.1" in versions:
|
|
tokens.append("h1")
|
|
if "http/2" in versions:
|
|
tokens.append("h2")
|
|
if "http/3" in versions:
|
|
tokens.append("h3")
|
|
return " ".join(tokens) if tokens else "h1"
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# HTTP (cleartext) protocol token tests
|
|
# ---------------------------------------------------------------------------
|
|
|
|
def test_http_h1_only():
|
|
assert _http_protocols('["http/1.1"]') == "h1"
|
|
|
|
|
|
def test_http_h1_and_h2c():
|
|
assert _http_protocols('["http/1.1", "http/2"]') == "h1 h2c"
|
|
|
|
|
|
def test_http_h2c_only():
|
|
assert _http_protocols('["http/2"]') == "h2c"
|
|
|
|
|
|
def test_http_default_fallback():
|
|
assert _http_protocols(None) == "h1"
|
|
|
|
|
|
def test_http_empty_versions_fallback():
|
|
# Should not happen (coercion rejects empty list) but guard the fallback.
|
|
assert _http_protocols("[]") == "h1"
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# HTTPS (TLS) protocol token tests
|
|
# ---------------------------------------------------------------------------
|
|
|
|
def test_https_h1_only():
|
|
assert _https_protocols('["http/1.1"]') == "h1"
|
|
|
|
|
|
def test_https_h1_and_h2():
|
|
assert _https_protocols('["http/1.1", "http/2"]') == "h1 h2"
|
|
|
|
|
|
def test_https_all_three():
|
|
assert _https_protocols('["http/1.1", "http/2", "http/3"]') == "h1 h2 h3"
|
|
|
|
|
|
def test_https_h3_only():
|
|
assert _https_protocols('["http/3"]') == "h3"
|
|
|
|
|
|
def test_https_default_fallback():
|
|
assert _https_protocols(None) == "h1"
|