Extend the config, database schema, and server internals to support per-route L4/L7 mode selection and PROXY protocol fields. This is the foundation for L7 HTTP/2 reverse proxying and multi-hop PROXY protocol support described in the updated ARCHITECTURE.md. Config: Listener gains ProxyProtocol; Route gains Mode, TLSCert, TLSKey, BackendTLS, SendProxyProtocol. L7 routes validated at load time (cert/key pair must exist and parse). Mode defaults to "l4". DB: Migration v2 adds columns to listeners and routes tables. CRUD and seeding updated to persist all new fields. Server: RouteInfo replaces bare backend string in route lookup. handleConn dispatches on route.Mode (L7 path stubbed with error). ListenerState and ListenerData carry ProxyProtocol flag. All existing L4 tests pass unchanged. New tests cover migration v2, L7 field persistence, config validation for mode/cert/key, and proxy_protocol flag round-tripping. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
3.8 KiB
3.8 KiB
CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Project Overview
mc-proxy is a TLS proxy and router for Metacircular Dynamics services. It operates in two per-route modes: L4 passthrough (reads SNI, proxies raw TCP without terminating TLS) and L7 terminating (terminates TLS, reverse proxies HTTP/2 and HTTP/1.1 traffic including gRPC). A global firewall (IP, CIDR, GeoIP, rate limiting) is evaluated before routing. PROXY protocol support enables multi-hop deployments preserving real client IPs. See ARCHITECTURE.md for full design.
Build Commands
make all # vet → lint → test → build
make mc-proxy # build the binary with version injection
make build # compile all packages
make test # run all tests
make vet # go vet
make lint # golangci-lint
make proto # regenerate gRPC code from proto definitions
make devserver # build and run locally with srv/mc-proxy.toml
Run a single test:
go test ./internal/sni -run TestExtract
Architecture
- Module path:
git.wntrmute.dev/kyle/mc-proxy - Go with CGO_ENABLED=0, statically linked, Alpine containers
- Dual mode, per-route — L4 (passthrough) and L7 (TLS-terminating HTTP/2 reverse proxy) coexist on the same listener
- PROXY protocol — listeners accept v1/v2; routes send v2. Enables edge→origin deployments over Tailscale
- gRPC admin API — manages routes and firewall rules at runtime; Unix socket only; optional (disabled if
[grpc]section omitted from config) - No auth on proxy listeners — this is pre-auth infrastructure; services behind it handle their own MCIAS auth
- SQLite database — persists listeners, routes, and firewall rules; pure-Go driver (
modernc.org/sqlite); seeded from TOML on first run, DB is source of truth thereafter - Write-through pattern — gRPC mutations write to DB first, then update in-memory state
- Config: TOML via
go-toml/v2, runtime data in/srv/mc-proxy/ - Testing: stdlib
testingonly,t.TempDir()for isolation - Linting: golangci-lint v2 with
.golangci.yaml
Package Structure
internal/config/— TOML config loading and validationinternal/db/— SQLite database: migrations, CRUD for listeners/routes/firewall rules, seeding, snapshotsinternal/sni/— TLS ClientHello parser; extracts SNI hostname without consuming bytesinternal/firewall/— global blocklist evaluation (IP, CIDR, GeoIP via MaxMind GeoLite2); rate limiting; thread-safe mutations and GeoIP reloadinternal/proxy/— L4 bidirectional TCP relay with half-close propagation and idle timeoutinternal/proxyproto/— PROXY protocol v1/v2 parser and v2 writerinternal/l7/— L7 TLS termination,prefixConn, HTTP/2 reverse proxy with h2c backend transportinternal/server/— orchestrates listeners → PROXY protocol → firewall → SNI → route → L4/L7 dispatch; per-listener state with connection trackinginternal/grpcserver/— gRPC admin API: route/firewall CRUD, status, write-through to DBproto/mc_proxy/v1/— protobuf definitions;gen/mc_proxy/v1/has generated code
Signals
SIGINT/SIGTERM— graceful shutdown (drain in-flight connections up toshutdown_timeout)SIGHUP— reload GeoIP database without restart
Critical Rules
- L4 routes never terminate TLS and never modify the byte stream.
- L7 routes terminate TLS at the proxy and reverse proxy HTTP/2 (including gRPC) to backends.
- Firewall rules are always evaluated before any routing decision.
- PROXY protocol is only parsed on explicitly enabled listeners.
- SNI matching is exact and case-insensitive.
- Blocked connections get a TCP RST — no error messages, no TLS alerts.
- Database writes must succeed before in-memory state is updated (write-through).