Architecture updates based on gaps discovered during live deployment testing: - ADR-023: HTTP/2 client-facing support via ALPN-based protocol detection. The spec previously said HTTP/2 was out of scope, but the deployment revealed that modern browsers negotiate HTTP/2 via ALPN. The proxy now correctly detects the negotiated ALPN protocol and uses the appropriate HTTP server builder (http2::Builder for h2, auto::Builder for http/1.1). Upstream connections remain HTTP/1.1. Host resolution now falls back to URI host for HTTP/2 :authority pseudo-headers. - ADR-024: ANSI-disabled logging. All tracing-subscriber layers now use with_ansi(false) to prevent ANSI escape codes in log output, which broke fail2ban regex matching in Docker deployments. Also documents the fail2ban regex anchor fix (^RATE_LIMIT → RATE_LIMIT). Bug fixes found by architecture review: - Fix missing ALPN protocols in manual TLS mode. build_manual_server_config and build_multi_domain_server_config did not set alpn_protocols, meaning manual TLS mode could not support HTTP/2. Added h2 and http/1.1 ALPN entries to both functions (acme-tls/1 only in ACME mode). - Fix missing with_ansi(false) in JSON log format. The init_json function with file output did not disable ANSI on stdout or file layers, which would break fail2ban in production JSON logging mode. Other spec updates: - All document statuses updated from draft to reviewed - proxy.md: documented Server header removal, upstream HTTPS client, two-phase timeout enforcement, HTTP/2 host resolution, connect timeout - tls.md: documented ALPN configuration differing by mode (ACME vs manual) - overview.md: added HTTP/2 client-facing support to scope, updated crate deps (hyper-rustls, rustls-native-certs, hyper-util), clarified out-of-scope - config.md: fixed http_port type (u16→u32) to match implementation, added ANSI-disabled note for LoggingConfig - operations.md: documented ANSI-disabled logging, fail2ban regex anchor - open-questions.md: updated OQ-09 resolution (connect timeout fully implemented), OQ-10 (C2 bug is fixed)
2.1 KiB
ADR-024: ANSI-Disabled Logging for Container Deployments
Status
Accepted
Context
During deployment, the proxy's log output contained ANSI escape codes (color
codes) because tracing-subscriber's default fmt::layer() enables ANSI
output when connected to a terminal. In a Docker container, docker logs
captures stdout/stderr, and the log file written to
/var/log/reverse-proxy/access.log is also a plain text file.
ANSI escape codes in logs cause two problems:
- fail2ban regex failure: The fail2ban filter regex expects plain text with
a
RATE_LIMITprefix. ANSI codes embedded in the log line before the prefix break pattern matching, causing fail2ban to miss rate limit events entirely. - Docker log readability:
docker logsoutput is cluttered with escape sequences when not running in a terminal that supports them.
Decision
All tracing-subscriber fmt layers now use with_ansi(false):
- File layer: Always plain text, no ANSI codes
- Stdout layer: Always plain text, no ANSI codes
- JSON layer: Always plain text (JSON format doesn't benefit from colors)
This applies to both text and JSON log formats, in both file and stdout destinations.
Additionally, the fail2ban regex was corrected: the ^ anchor was removed from
the failregex pattern because log lines have a timestamp/level prefix before
the RATE_LIMIT keyword. The corrected pattern matches RATE_LIMIT anywhere
in the line rather than only at the start.
Consequences
Positive:
- fail2ban regex matching works reliably in all environments
- Log output is clean and parseable regardless of environment
- No behavioral difference between Docker, systemd, and terminal environments
Negative:
- Loss of color-coding in terminal output during development (acceptable
trade-off for reliability; developers can use
RUST_LOGfiltering instead)
References
- operations.md — logging and fail2ban integration
- ADR-007 — custom structured log format
- ADR-020 — container deployment model