Per-route HTTP-level blocking policies for L7 routes. Two rule types:
block_user_agent (substring match against User-Agent, returns 403)
and require_header (named header must be present, returns 403).
Config: L7Policy struct with type/value fields, added as L7Policies
slice on Route. Validated in config (type enum, non-empty value,
warning if set on L4 routes).
DB: Migration 4 creates l7_policies table with route_id FK (cascade
delete), type CHECK constraint, UNIQUE(route_id, type, value). New
l7policies.go with ListL7Policies, CreateL7Policy, DeleteL7Policy,
GetRouteID. Seed updated to persist policies from config.
L7 middleware: PolicyMiddleware in internal/l7/policy.go evaluates
rules in order, returns 403 on first match, no-op if empty. Composed
into the handler chain between context injection and reverse proxy.
Server: L7PolicyRule type on RouteInfo with AddL7Policy/RemoveL7Policy
mutation methods on ListenerState. handleL7 threads policies into
l7.RouteConfig. Startup loads policies per L7 route from DB.
Proto: L7Policy message, repeated l7_policies on Route. Three new
RPCs: ListL7Policies, AddL7Policy, RemoveL7Policy. All follow the
write-through pattern.
Client: L7Policy type, ListL7Policies/AddL7Policy/RemoveL7Policy
methods. CLI: mcproxyctl policies list/add/remove subcommands.
Tests: 6 PolicyMiddleware unit tests (no policies, UA match/no-match,
header present/absent, multiple rules). 4 DB tests (CRUD, cascade,
duplicate, GetRouteID). 3 gRPC tests (add+list, remove, validation).
2 end-to-end L7 tests (UA block, required header with allow/deny).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Configurable maximum concurrent connections per listener. When the
limit is reached, new connections are closed immediately after accept.
0 means unlimited (default, preserving existing behavior).
Config: Listener gains max_connections field, validated non-negative.
DB: Migration 3 adds listeners.max_connections column.
UpdateListenerMaxConns method for runtime changes via gRPC.
CreateListener updated to persist max_connections on seed.
Server: ListenerState/ListenerData gain MaxConnections. Limit checked
in serve() after Accept but before handleConn — if ActiveConnections
>= MaxConnections, connection is closed and the accept loop continues.
SetMaxConnections method for runtime updates.
Proto: SetListenerMaxConnections RPC added. ListenerStatus gains
max_connections field. Generated code regenerated.
gRPC server: SetListenerMaxConnections implements write-through
(DB first, then in-memory update). GetStatus includes max_connections.
Client: SetListenerMaxConnections method, MaxConnections in
ListenerStatus.
Tests: DB CRUD and UpdateListenerMaxConns, server connection limit
enforcement (accept 2, reject 3rd, close one, accept again), gRPC
SetListenerMaxConnections round-trip with DB persistence, not-found
error handling.
Also updates PROJECT_PLAN.md with phases 6-8 and PROGRESS.md with
tracking for the new features.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
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>
Introduces a new command-line tool for managing mc-proxy via the gRPC
admin API over Unix socket. Commands include route and firewall rule
CRUD operations, health checks, and status queries.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Remove TCP listener support from gRPC server; Unix socket is now the
only transport for the admin API (access controlled via filesystem
permissions)
- Add standard gRPC health check service (grpc.health.v1.Health)
- Implement MCPROXY_* environment variable overrides for config
- Create client/mcproxy package with full API coverage and tests
- Update ARCHITECTURE.md and dev config (srv/mc-proxy.toml)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Rate limiting: per-source-IP connection rate limiter in the firewall layer
with configurable limit and sliding window. Blocklisted IPs are rejected
before rate limit evaluation to avoid wasting quota. Unix socket: the gRPC
admin API can now listen on a Unix domain socket (no TLS required), secured
by file permissions (0600), as a simpler alternative for local-only access.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Rename proto/gen directories from mc-proxy to mc_proxy for valid protobuf
package naming. Add CLI status subcommand for querying running instance
health via gRPC. Add systemd backup service/timer and backup pruning
script. Add buf.yaml and proto-lint Makefile target. Add shutdown_timeout
config field.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Database (internal/db) stores listeners, routes, and firewall rules with
WAL mode, foreign keys, and idempotent migrations. First run seeds from
TOML config; subsequent runs load from DB as source of truth.
gRPC admin API now writes to the database before updating in-memory state
(write-through cache pattern). Adds snapshot command for VACUUM INTO
backups. Refactors firewall.New to accept raw rule slices instead of
config struct for flexibility.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>