feat(cli,transport): Let's Encrypt outer-cert support on TLS-443/QUIC

Server admins can now point the outer TLS layer at a real CA-signed cert
(e.g. Let's Encrypt fullchain.pem) so the on-wire HTTPS camouflage is
indistinguishable from a normal CA-trusted HTTPS server. The inner Aura
mutual-auth handshake still uses the Aura CA (necessarily — that's where
the PQ mutual auth lives).

- aura-cli config: optional [server.outer_cert] {cert_path, key_path}.
  Both fields together (or neither); resolve() reads PEMs and returns
  (cert, key) tuple. Absent section -> falls back to reusing the Aura
  server cert (v2 behavior, fully back-compat).
- aura-transport: additive MultiServer::bind_with_outer and
  TcpServer::bind_with_outer that accept an optional separate outer cert.
  Old MultiServer::bind / TcpServer::bind preserved as thin wrappers
  (back-compat: existing callers untouched). AuraServer::bind already
  took outer cert separately.
- UDP transport doesn't have outer TLS, so outer cert is irrelevant
  there — only QUIC + TCP layers benefit.
- 4 new tests (parsing, back-compat, partial-section validation, two-CA
  loopback verifying inner peer_id is the inner CN). Workspace: 257 tests
  passed (+4), clippy -D warnings clean, fmt clean.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
xah30
2026-05-27 19:35:22 +03:00
parent fe618b839d
commit f26ed7fce0
6 changed files with 555 additions and 19 deletions
+15
View File
@@ -28,6 +28,21 @@ ca_cert = "~/.aura/ca.crt"
cert = "~/.aura/server.crt"
key = "~/.aura/server.key"
# v3 optional: provide a SEPARATE outer-TLS certificate for the QUIC and TCP transports. When set,
# a passive observer on :443 sees a CA-trusted handshake (e.g. Let's Encrypt) instead of the
# self-signed Aura cert above — which is much harder to fingerprint. The inner Aura mutual-auth
# handshake still uses the [pki] cert/key for client authentication.
#
# Both fields MUST be provided together. When the whole section is omitted (the default) the
# outer-TLS layer reuses the [pki] cert/key — exactly the v2 behaviour.
#
# Typical Let's Encrypt deployment (certbot renews these files in-place automatically; the server
# does NOT automate cert issuance or renewal — it just reads the PEMs at startup):
#
# [server.outer_cert]
# cert_path = "/etc/letsencrypt/live/vpn.example.com/fullchain.pem"
# key_path = "/etc/letsencrypt/live/vpn.example.com/privkey.pem"
[tunnel]
# Address pool / TUN network. v2 reads the active pool config from [server.pool] below; this value
# is kept as the v1-compatible fallback (used when [server.pool] is omitted entirely) and as the