Files
reverse-proxy/docs/architecture/decisions/005-tokio-rustls-direct.md
glm-5.1 8ee6284b62 Add architecture specification for Rust/axum reverse proxy
Phase 1 architecture docs covering proxy handler, TLS termination (ACME +
manual), TOML config with static/dynamic split (ArcSwap), and operations
(rate limiting, logging, health check, systemd, graceful shutdown).

Nine ADRs documenting key decisions: Rust/axum, custom proxy handler,
TOML config, rustls-acme for cert management, tokio-rustls direct,
token bucket rate limiting, custom log format for fail2ban,
static/dynamic config split, and signal handling strategy.

Includes threat landscape research documenting the nginx CVEs motivating
this project.
2026-06-11 07:25:50 +00:00

2.5 KiB

ADR-005: tokio-rustls Directly, Not axum-server

Status

Accepted

Context

We need to serve HTTPS (TLS) traffic through axum. Two approaches exist for integrating TLS with axum:

  1. axum-server: A wrapper that provides TLS support for axum via tls_rustls feature. Handles TCP binding, TLS accept, and passing TLS streams to axum. Simple API but limited control over the TLS configuration.

  2. tokio-rustls directly: Bind TCP manually, perform TLS handshake with TlsAcceptor, then serve the TLS stream to axum/hyper. More code but full control over ServerConfig, cipher suites, ALPN protocols, and cert resolvers.

The alknet project uses tokio-rustls directly and has proven this pattern for both manual and ACME certificate management.

Decision

Use tokio-rustls directly for TLS termination, with hyper serving the resulting TLS streams to axum. Do not use axum-server.

Rationale

  • ACME integration: The rustls-acme ResolvesServerCertAcme resolver needs to be set as the certificate resolver on ServerConfig via with_cert_resolver(). axum-server does not expose this level of control over the ServerConfig.
  • Cipher suite control: We may need to configure cipher suites beyond the defaults (see OQ-01). axum-server wraps the ServerConfig construction and may not expose CryptoProvider configuration. Direct tokio-rustls usage gives us full control.
  • ALPN configuration: ACME TLS-ALPN-01 challenge requires adding acme-tls/1 to the ALPN protocol list. This is only possible with direct ServerConfig access.
  • Proven pattern: alknet uses exactly this approach (TlsAcceptor wrapping tokio-rustls, with manual or ACME ServerConfig construction).
  • No abstraction cost: The code to bind TCP, accept TLS, and serve to axum/hyper is ~50 lines. axum-server saves little for our simple case.

Consequences

Positive:

  • Full control over TLS configuration
  • Direct rustls-acme integration
  • Ability to add ALPN protocols for ACME challenges
  • Proven pattern from alknet

Negative:

  • Slightly more code than axum-server (~50 lines for the TLS acceptor loop)
  • Need to manage the TCP listener and TLS accept explicitly
  • Must handle the TlsStream<TcpStream>hyper::service_fn → axum integration manually (well-documented pattern from Felix Knorr's blog and alknet)

References

  • tls.md
  • alknet transport layer (alknet-core/src/transport/tls.rs, alknet-core/src/transport/acme.rs)