Implements mTLS for securing agent-to-agent communication in the Hermes
fleet. Fixes#806.
Changes:
- scripts/gen_fleet_ca.sh: generate a self-signed Fleet CA (4096-bit RSA,
10-year validity) that signs all agent certificates
- scripts/gen_agent_cert.sh: generate per-agent certs (Timmy, Allegro,
Ezra) signed by the fleet CA with SAN entries and clientAuth/serverAuth
extended key usage
- agent/mtls.py: new module providing:
- build_server_ssl_context() — TLS_SERVER context with CERT_REQUIRED,
enforces client cert against Fleet CA
- build_client_ssl_context() — TLS_CLIENT context for outbound A2A calls
- MTLSMiddleware — ASGI middleware that rejects unauthenticated requests
to A2A routes (/.well-known/agent-card*, /api/agent-card, /a2a/) with
HTTP 403 when mTLS is enabled
- is_mtls_configured() — checks HERMES_MTLS_CERT/KEY/CA env vars
- hermes_cli/web_server.py: wire MTLSMiddleware into the FastAPI app;
pass SSL context to uvicorn when HERMES_MTLS_* env vars are set so
the server runs TLS with mandatory client cert verification
- ansible/roles/hermes_mtls/: Ansible role to distribute Fleet CA cert,
agent cert, and agent key to fleet nodes; writes an env file with
HERMES_MTLS_* vars and restarts the hermes-gateway service
- ansible/fleet_mtls.yml: fleet-wide playbook referencing the role for
Timmy, Allegro, and Ezra nodes
- tests/test_mtls.py: 15 tests covering is_mtls_configured, SSL context
creation with real cryptography-generated certs, and MTLSMiddleware
(unauthorized agent rejected → 403, authorized agent accepted → 200)
mTLS is opt-in: set HERMES_MTLS_CERT, HERMES_MTLS_KEY, and HERMES_MTLS_CA
to enable. When unset, the server behaves exactly as before.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>