Feat v0.6.7 native mtls (#42)
All checks were successful
CI/CD / detect-changes (push) Successful in 4s
CI/CD / test-frontend (push) Successful in 6s
CI/CD / test-go-pg (push) Successful in 2m37s
CI/CD / test-sqlite (push) Successful in 2m48s
CI/CD / build-and-deploy (push) Successful in 52s

Co-authored-by: Jeffrey Smith <jasafpro@gmail.com>
Co-committed-by: Jeffrey Smith <jasafpro@gmail.com>
This commit was merged in pull request #42.
This commit is contained in:
2026-03-31 18:36:12 +00:00
committed by xcaliber
parent 7915d84c8b
commit fb5284f667
14 changed files with 1138 additions and 209 deletions

View File

@@ -1,6 +1,6 @@
# Switchboard Core — Roadmap
## Current: v0.6.6Final Hardening
## Current: v0.6.7Native mTLS
Self-hosted extensible platform. Auth, identity, packages, Starlark sandbox,
storage, realtime, and ops are kernel primitives. Everything else is an extension.
@@ -131,13 +131,13 @@ End-to-end mutual TLS without a reverse proxy. Targets systemd+podman deployment
| Step | Status | Description |
|------|--------|-------------|
| `TLS_MODE` config | | Three values: `none` (default, plain HTTP) · `server` (TLS, no client cert) · `mtls` (mutual TLS, client cert required). Independent of `AUTH_MODE`. |
| TLS server mode | | Go binary calls `ListenAndServeTLS` directly. `TLS_CERT`, `TLS_KEY`, `TLS_CA` path config. TLS 1.3 minimum, no fallback. `server/config/tls.go` loader + validation. |
| `MTLSNativeProvider` | | Reads `r.TLS.PeerCertificates[0]` — no header trust. `Subject.CommonName` → username. `sha256(Raw)``external_id`. Auto-provisions `auth_source=mtls`. Rename existing `mtls.go``mtls_proxy.go`. Extract shared helpers to `mtls_helpers.go`. |
| Node-to-node mTLS | | Cluster heartbeat over mTLS. Node presents its cert when dialing peers. Single cert per node (ServerAuth + ClientAuth EKU). SAN includes hostname + IP. Peer verification against cluster CA pool. |
| `armature-ca.sh` | | Shell wrapper around openssl. Three commands: `init` (CA keypair) · `issue-node --name <n> --san <addrs>` (365d) · `issue-user --cn <name>` (90d). All output PEM. |
| Unit tests | | Ephemeral CA via `crypto/x509`. Fabricated `PeerCertificates`. Valid cert → user provisioned · no TLS → `ErrNoCert` · empty certs → `ErrNoCert` · same fingerprint → idempotent. |
| Integration tests | | Real TLS listener on localhost. No cert / wrong CA / expired → TLS handshake rejected. Valid cert → 200, user created. Node-to-node: two in-process servers, distinct certs, same CA. Wrong-CA node rejected. |
| `TLS_MODE` config | | Three values: `none` (default, plain HTTP) · `server` (TLS, no client cert) · `mtls` (mutual TLS, client cert required). Independent of `AUTH_MODE`. |
| TLS server mode | | Go binary calls `ListenAndServeTLS` directly. `TLS_CERT`, `TLS_KEY`, `TLS_CA` path config. TLS 1.3 minimum, no fallback. `server/config/tls.go` loader + validation. |
| `MTLSNativeProvider` | | Reads `r.TLS.PeerCertificates[0]` — no header trust. `Subject.CommonName` → username. `sha256(Raw)``external_id`. Auto-provisions `auth_source=mtls`. Renamed existing `mtls.go``mtls_proxy.go`. Shared helpers in `mtls_helpers.go`. |
| Node-to-node mTLS | | `BuildPeerTLSConfig()` constructs outbound TLS config with node cert + CA pool. Forward-looking — cluster registry is DB-backed (PG LISTEN/NOTIFY), no HTTP peer calls yet. |
| `switchboard-ca.sh` | | Shell wrapper around openssl. Three commands: `init` (CA keypair) · `issue-node --name <n> --san <addrs>` (365d) · `issue-user --cn <name>` (90d). All output PEM. |
| Unit tests | | Ephemeral CA via `crypto/x509`. Fabricated `PeerCertificates`. Valid cert → user provisioned · no TLS → `ErrNoCert` · empty certs → `ErrNoCert` · no CN → `ErrInvalidCreds`. |
| Integration tests | | Real TLS listener on localhost. No cert / wrong CA / expired → TLS handshake rejected. Valid cert → 200. Peer certificate CN + email visibility verified. |
### v0.6.6 — Final Hardening