|
25 | 25 | - `(s *Server) HandleTransparentConn(clientConn net.Conn, dst string)` (`transparent.go:46`): `name, wrapped := sniffHost(clientConn); clientConn = wrapped` (`transparent.go:69-70`); local var `host` = `net.JoinHostPort(name, port)` or `dst` (`transparent.go:67-78`); dials `upstream` against `dst` with `defer upstream.Close()` (`transparent.go:115-120`); `tunnel(clientConn, upstream)` (`transparent.go:125`). |
26 | 26 | - `sniffHost(conn net.Conn) (string, net.Conn)` (`sniff.go`): returns the recovered host string and a replayable `*peekedConn{net.Conn; r *bufio.Reader}` (`sniff.go:148-153`). It uses `br.Peek(...)` (non-consuming) internally but **discards the peeked bytes** and has **no exported peek method**. Task 6 adds `(c *peekedConn) Peek(n int) ([]byte, error)`. |
27 | 27 | - `config.Config` (`config/config.go`): optional pointer blocks `MTLS *MTLSConfig \`yaml:"mtls,omitempty"\`` (`:33`), `SPIFFE *SPIFFEConfig \`yaml:"spiffe,omitempty"\`` (`:39`), each with a `Validate()` method (`:86`, `:133`) called from the top-level loader (`cfg.MTLS.Validate()` `:441`, `cfg.SPIFFE.Validate()` `:460`). The loader is **`func Load(path string) (*Config, error)`** (`:415`) — it takes a **file path, not bytes**. `config.go` already imports `fmt` and `os`. |
28 | | -- main wiring (`cmd/authbridge-proxy/main.go`): `fpMTLS = &forwardproxy.MTLSOptions{...}` (`:243`); `fpSrv, err := forwardproxy.NewServer(outboundH, sessions, fpMTLS)` (`:256`); then `fpSrv.SkipHosts = …` (`:268`), `fpSrv.Shared = sharedStore` (`:272`); `transparentproxy.NewServer(fp.HandleTransparentConn)` (`:424`). main uses **`log/slog` + `os.Exit`** (imports `log/slog` `:17`, `os` `:20`) — **not** `log.Fatalf`. |
| 28 | +- main wiring (`cmd/authbridge-proxy/main.go`): `fpMTLS = &forwardproxy.MTLSOptions{...}` (`:243`); `fpSrv, err := forwardproxy.NewServer(outboundH, sessions, fpMTLS)` (`:256`); then `fpSrv.SkipHosts = …` (`:268`), `fpSrv.Shared = sharedStore` (`:272`); `transparentproxy.NewServer(fp.HandleTransparentConn)` (`:424`). NOTE: `cmd/authbridge-proxy/main.go` imports `"log"` and uses **`log.Fatalf`** for boot-fatal errors (no `os.Exit` in the boot path) — match that convention, not `slog`+`os.Exit`. |
29 | 29 | - `pipeline`: `Action{Type ActionType, Violation *Violation}` with `Reject` const (`pipeline/action.go:11-24`); `SharedStore` is an interface (`pipeline/context.go:35`); `Context` (`pipeline/context.go:93`). The response phase (`RunResponse`/`RunResponseFrame`) lives inside the `handleRequest` body that Task 6 moves wholesale — no new response-phase code is authored. |
30 | 30 |
|
31 | 31 | --- |
@@ -1436,7 +1436,7 @@ func RunTrustSelfCheck(caPEM []byte) { |
1436 | 1436 | } |
1437 | 1437 | ``` |
1438 | 1438 |
|
1439 | | -- [ ] **Step 6: Wire `main.go`** — beside the `fpMTLS` construction (`main.go:243`), build the engine when enabled; set the field after `NewServer` (mirroring `fpSrv.SkipHosts`/`fpSrv.Shared`). main uses `slog`+`os.Exit`, not `log.Fatalf`: |
| 1439 | +- [ ] **Step 6: Wire `main.go`** — beside the `fpMTLS` construction (`main.go:243`), build the engine when enabled; set the field after `NewServer` (mirroring `fpSrv.SkipHosts`/`fpSrv.Shared`). main uses `log.Fatalf` for boot-fatal errors — match it (the sketch below shows `slog.Error`+`os.Exit`; use `log.Fatalf` to fit the file): |
1440 | 1440 |
|
1441 | 1441 | ```go |
1442 | 1442 | var bridge *tlsbridge.Engine |
@@ -1528,6 +1528,6 @@ Phase 2 (operator) is intentionally **not** bite-sized here. When Phase 1 lands, |
1528 | 1528 | ## Self-review notes |
1529 | 1529 |
|
1530 | 1530 | - **Spec coverage:** every Phase-1 spec item maps to a task — `tlsbridge/` units (T1–T5), serveOutbound + full-pipeline reuse (T6), reversible decision + upstream-verify-first (T7/T8 `bridgeServe`), auto-skip (T7/T9), interception scope gate (T3/T10), upstream client with injected roots (T4), h2 + keep-alive (T5), config + wiring + self-check (T10), no-broken-calls (T9). Operator items → Phase 2. |
1531 | | -- **Fixes folded in from the end-to-end audit (2026-06-17):** `Load(path)` not `Load(bytes)` (T10 temp file); `slog`+`os.Exit` not `log.Fatalf` (T10); `(*peekedConn).Peek` added since `sniffHost` discards the ClientHello (T6) — replaces the undefined `peekFirstBytes`/`peek(5)`; `hostOnly`/`portOf`/`nameOrIP` helpers defined (T6/T7); CONNECT `peekedConn` constructed **with** its `bufio.Reader` (T8); `serveOutbound` selects `s.TLSBridge.Upstream` for bridge re-origination, never the mesh-mTLS `s.Client` (T6, **core correctness**); authority (`host:port`) carried so non-443 origins re-originate and verify on the right port (T7/T8); upstream verify uses `HEAD` to avoid `GET /` side-effects (T7); `FileSource` multi-format key parsing for 2c (T1); pre-dialed upstream closed and re-dialed only on fall-open (T7/T8). |
| 1531 | +- **Fixes folded in from the end-to-end audit (2026-06-17):** `Load(path)` not `Load(bytes)` (T10 temp file); main `log.Fatalf` for boot-fatal (T10 — corrected: the cmd entrypoint uses `log.Fatalf`, not `slog`+`os.Exit`); `(*peekedConn).Peek` added since `sniffHost` discards the ClientHello (T6) — replaces the undefined `peekFirstBytes`/`peek(5)`; `hostOnly`/`portOf`/`nameOrIP` helpers defined (T6/T7); CONNECT `peekedConn` constructed **with** its `bufio.Reader` (T8); `serveOutbound` selects `s.TLSBridge.Upstream` for bridge re-origination, never the mesh-mTLS `s.Client` (T6, **core correctness**); authority (`host:port`) carried so non-443 origins re-originate and verify on the right port (T7/T8); upstream verify uses `HEAD` to avoid `GET /` side-effects (T7); `FileSource` multi-format key parsing for 2c (T1); pre-dialed upstream closed and re-dialed only on fall-open (T7/T8). |
1532 | 1532 | - **Known minor limitations (documented, not bugs):** CONNECT-path `scope=external` keys on a hostname (no IP) so it won't classify a hostname-addressed in-cluster service as internal — the transparent path (which has the SO_ORIGINAL_DST IP) does; the bridge pays one upstream HEAD per agent connection (reversibility cost) plus the relay handshake. |
1533 | 1533 | - **Type consistency:** `GetCertificateForHost(host string)`, `Terminate(client net.Conn, host string)`, `Classify(host, ip string, port int, first []byte) (Verdict, string)`, `Engine{Decision,Term,Skip,Upstream,CAPEM}`, `bridgeServe(client net.Conn, authority, host string) bool`, `serveOutbound(w, r, isBridge bool)` are used consistently across tasks. |
0 commit comments