feat(cmd): Add authbridge-proxy and authbridge-envoy lite binaries#407
Conversation
Introduces two smaller, single-mode binaries alongside the existing
"batteries-included" cmd/authbridge:
cmd/authbridge-proxy proxy-sidecar only (no Envoy, no gRPC)
jwt-validation + token-exchange only
cmd/authbridge-envoy envoy-sidecar only (ext_proc)
jwt-validation + token-exchange only
Motivation: deployments that only need a single mode and two auth
plugins don't need to pay for gRPC + envoy go-control-plane types +
a2a/mcp/inference parsers + the ext_authz listener. Most of the
current binary's size comes from those deps.
Prerequisite refactors:
1. Three flat parser plugins (a2a-parser, mcp-parser,
inference-parser) moved from authlib/plugins/ into their own
subpackages under authlib/plugins/{a2aparser,mcpparser,
inferenceparser}/. Their init()-based registration was previously
forced into every binary that imported the plugins package
(because that's how Go module init works); subpackage layout
makes them opt-in via side-effect import like jwt-validation,
token-exchange, and token-broker already were. Shared parser
helpers moved to authlib/plugins/internal/parsercommon/ (tiny
package: JSONRPCRequest, Truncate, DebugBodyMax).
2. HTTP listeners (forwardproxy, reverseproxy) moved from
cmd/authbridge/listener/ to authlib/listener/. They're
net/http-only with no gRPC or envoy dependencies, so they belong
in the shared library where any binary can reuse them without
cross-module deps. extproc and extauthz stay under
cmd/authbridge/listener/ because their gRPC/envoy surface is
specific to the envoy-integration binaries.
Binary size measurements (darwin amd64, Go 1.26.2):
Binary Unstripped Stripped (-s -w)
authbridge (full) 25 MB 17 MB
authbridge-envoy 25 MB 17 MB
authbridge-proxy 12 MB 8.0 MB
authbridge-proxy comes in at roughly half the size of the full
binary. The drops: google.golang.org/grpc, envoyproxy/go-control-plane,
cncf/xds, protoc-gen-validate, genproto/googleapis/rpc, protobuf
runtime, and the 4 uncompiled plugins (a2a, mcp, inference,
token-broker). That dep subtree is where the size actually lives.
authbridge-envoy doesn't move the needle much on size because
ext_proc forces gRPC + envoy types in regardless. Its real value is
deployment-story simplification: one binary that only knows how to
do envoy-sidecar mode, cannot misconfigure into other modes.
Layout:
authbridge/
authlib/
listener/
forwardproxy/ (moved from cmd/authbridge/listener/)
reverseproxy/ (moved from cmd/authbridge/listener/)
plugins/
a2aparser/ (moved from a2aparser.go)
mcpparser/ (moved from mcpparser.go)
inferenceparser/ (moved from inferenceparser.go)
internal/
parsercommon/ (extracted shared helpers)
jwtvalidation/ (unchanged — already subpackaged)
tokenexchange/ (unchanged)
tokenbroker/ (unchanged)
cmd/
authbridge/ (existing full binary — unchanged behavior,
now uses side-effect imports for all plugins)
listener/
extproc/ (stays here — envoy-bound)
extauthz/ (stays here — envoy-bound)
authbridge-proxy/ (NEW — own module, authlib-only)
authbridge-envoy/ (NEW — own module, authlib + cmd/authbridge
via replace directive for extproc)
Tests:
All existing authlib tests pass under -race. The
TestBuiltinsRegistered test moved from the package-internal
registry_test.go to the external plugins_test.go so it can import
the plugin subpackages via side effect (internal tests would
cycle: plugins → plugin-subpackage → plugins).
No new tests specific to the lite binaries — they're main.go
packages and duplicate the full binary's wiring. If drift becomes
a concern, consider extracting a shared setup helper into authlib.
Follow-ups (not in this PR):
- Dockerfiles for the lite binaries (the existing
cmd/authbridge/Dockerfile still builds the full one).
- CI build for the lite binaries — will surface immediately as
go build ./... coverage expands via the go.work.
- UPX compression recipe if operators need to push sizes down
further (~5 MB for authbridge-proxy with UPX).
Assisted-By: Claude (Anthropic AI) <noreply@anthropic.com>
Signed-off-by: Hai Huang <huang195@gmail.com>
evaline-ju
left a comment
There was a problem hiding this comment.
Well-structured refactoring that cleanly separates parser plugins into opt-in subpackages and introduces two purpose-built lite binaries. The proxy binary achieves a meaningful -61% image size reduction by excluding gRPC/Envoy deps. Import paths are consistently updated, tests migrated correctly, and the parsercommon API is minimal and safe. CI is all green including DCO, CodeQL, Go CI, and Bandit.
Areas reviewed: Go (new binaries, plugin refactoring, module layout), Security (deps, hardcoded secrets)
Commits: 1 commit, signed-off: yes
CI status: all passing (17/17, 1 skipped: Spellcheck)
|
|
||
| replace github.com/kagenti/kagenti-extensions/authbridge/authlib => ../../authlib | ||
|
|
||
| replace github.com/kagenti/kagenti-extensions/authbridge/cmd/authbridge => ../authbridge |
There was a problem hiding this comment.
suggestion: The replace on cmd/authbridge creates a build-time coupling to the full binary's module (needed for extproc/extauthz listeners). The PR body explains this well, but a one-line comment in go.mod above the replace would help future maintainers understand why it exists and what breaks if cmd/authbridge is refactored.
| @@ -0,0 +1,279 @@ | |||
| // Package main is the envoy-sidecar lite binary: envoy-sidecar mode | |||
There was a problem hiding this comment.
suggestion: ~270 lines duplicated between authbridge-proxy/main.go and authbridge-envoy/main.go (initLogging, startSignalToggle, startStatServer, health check, shutdown). PR body acknowledges this — fine for now, but worth tracking so drift doesn't sneak in.
Consolidates the authbridge container images down to two combined
variants, each bundling spiffe-helper that starts conditionally on
SPIRE_ENABLED. Removes the in-pod client-registration sidecar from
this repo (operator now owns Keycloak client registration).
## Image layout (after)
| Name | Built from | Contents |
|---|---|---|
| `authbridge` (proxy-sidecar combined, default mode) | cmd/authbridge/Dockerfile.proxy | authbridge-proxy + spiffe-helper |
| `authbridge-envoy` (envoy-sidecar combined) | cmd/authbridge/Dockerfile.envoy | Envoy + authbridge (ext_proc) + spiffe-helper |
| `proxy-init` | authproxy/Dockerfile.init | iptables init for envoy-sidecar mode |
Both combined images use the same supervisor pattern:
* If SPIRE_ENABLED=true: start spiffe-helper in background, append
its PID to CRITICAL_PIDS.
* Start authbridge-proxy / Envoy + authbridge in background, append.
* `wait -n $CRITICAL_PIDS` blocks until any critical process exits.
* On SIGTERM/SIGINT: graceful shutdown, exit 0.
* On any critical-process death: kill the others, exit 1, kubelet
restarts the pod. No silent process death.
## Removals
* `authproxy/Dockerfile.authbridge` (old envoy+everything combined)
* `authproxy/entrypoint-authbridge.sh`
* `authproxy/authbridge-combined.yaml` (was the bake-in YAML for
the old combined image; operator-supplied per-agent ConfigMap
superseded it)
* `client-registration/` (entire directory: Dockerfile,
Python script, requirements, example manifests, images).
Operator now creates Keycloak clients via its
ClientRegistration controller and mounts the resulting Secret
at /shared/client-id.txt + /shared/client-secret.txt — same
paths the in-pod sidecar wrote to. Workloads opting back into
the legacy in-pod path use `kagenti.io/client-registration-inject:
"true"`; that path now sources the image from kagenti-operator.
* `spiffe-helper/Dockerfile` (standalone spiffe-helper image —
bundled into both combined images now).
* `tests/test_client_registration.py` (covered the removed Python).
* `TestAuthbridgeCombinedYAML_Loads` (asserted the deleted YAML).
## Renames
* `cmd/authbridge/Dockerfile` → `cmd/authbridge/Dockerfile.envoy`
* `cmd/authbridge/Dockerfile.light` → `cmd/authbridge/Dockerfile.proxy`
* `cmd/authbridge/entrypoint.sh` → `cmd/authbridge/entrypoint-envoy.sh`
The proxy variant uses `authbridge-proxy` (lite binary from PR rossoctl#407,
no gRPC, no envoy types) and switches base from distroless/static to
alpine. Distroless/static has no shell, which the bash supervisor
needs; alpine is +3 MB but has bash readily and matches the envoy
variant's pattern. Image size ends up around ~48 MB (vs ~12 MB for
the no-spiffe-helper variant) — spiffe-helper bundle is the bulk;
size optimization is deferred to a follow-up.
The envoy variant adds spiffe-helper to the existing UBI9-micro base
(already has bash + glibc, both required). Image size ~165 MB (vs
~141 MB without spiffe-helper).
## CI matrix
Drops 6 entries (client-registration, auth-proxy, spiffe-helper,
authbridge-unified, authbridge-light, demo-app), retains 3:
* `proxy-init` (still needed for envoy-sidecar mode iptables)
* `authbridge-envoy`
* `authbridge`
The `auth-proxy` and `demo-app` source files stay in-tree for the
standalone quickstart; just not built/published by CI.
## Out of scope (Phases B + C)
Operator + chart coordination:
* kagenti-operator must flip default mode from envoy-sidecar to
proxy-sidecar.
* Operator's image-name slot for proxy-sidecar mode (`Images.AuthBridgeLight`)
should be renamed and pointed at the new `authbridge` image.
The new image bundles spiffe-helper, so the operator should stop
injecting spiffe-helper as a separate sidecar in proxy-sidecar
mode (envoy-sidecar mode unchanged).
* `combinedSidecar` feature gate cleanup.
* kagenti chart values pin update.
These changes don't break anything in this PR because operator/chart
pin to existing image tags published by older releases of this repo.
A new release should not be cut from this branch until B + C are
ready.
## Verification
* `go build ./...` clean across authlib, cmd/authbridge,
cmd/authbridge-proxy, cmd/authbridge-envoy.
* `go test -race ./...` passes in authlib + cmd/authbridge.
* `podman build` succeeds for both Dockerfiles.
* Built image sizes (linux/amd64):
authbridge:test 47.8 MB
authbridge-envoy:test 165 MB
## Follow-ups (not blocking)
* Demo manifests in `authbridge/demos/**/k8s/` reference deleted
image names — update after Phase B + C land. Most likely require
rewriting the deployment shape (proxy-sidecar default), so
delaying until operator semantics settle is correct.
* Doc references in `authbridge/README.md`, `authbridge/docs/*`,
`authbridge/cmd/authbridge/README.md`, `LOCAL_TESTING_GUIDE.md`,
`local-build-and-test.sh` — find/replace pass.
Assisted-By: Claude (Anthropic AI) <noreply@anthropic.com>
Signed-off-by: Hai Huang <huang195@gmail.com>
Summary
Adds two single-mode, single-purpose authbridge binaries alongside
the existing "batteries-included"
cmd/authbridge:cmd/authbridge(existing)cmd/authbridge-envoy(new)cmd/authbridge-proxy(new)Motivation: deployments that need only one mode and only the two
auth plugins don't need to pay for gRPC + envoy go-control-plane +
the a2a/mcp/inference parsers + the ext_authz listener. The
majority of the current binary's footprint comes from those deps.
Binary sizes (darwin amd64, Go 1.26.2)
Built with
go buildandgo build -trimpath -ldflags="-s -w":The big win is
authbridge-proxy. Dropped from the link graph:google.golang.org/grpcenvoyproxy/go-control-planecncf/xds/goenvoyproxy/protoc-gen-validategoogle.golang.org/genproto/googleapis/rpcgoogle.golang.org/protobufauthbridge-envoyis marginal on size (ext_proc forces gRPC +envoy types back in), but adds value as a deployment-story
simplification: one binary that only does envoy-sidecar, can't
misconfigure into other modes.
Container image sizes (linux/amd64)
Real measurements from
podman buildwithCGO_ENABLED=0 -trimpath -ldflags="-s -w":authbridge:combined(existing — full, with Envoy)authbridge:light(existing — full, no Envoy)authbridge-envoy:lite(new)authbridge-proxy:lite(new)Deltas (apples-to-apples, same base image)
authbridge-proxy:litevsauthbridge:light(distroless, no Envoy)authbridge-envoy:litevsauthbridge:combined(UBI9 + Envoy)Deltas (cross-profile — if you can switch envoy-sidecar → proxy-sidecar)
authbridge-proxy:litevsauthbridge:combinedPractical impact
Go sidecar image — distroless/static base (~2 MB) plus the binary
(~9.5 MB Linux-static-stripped). Pull-time and startup on rolling
updates drop proportionally.
because Envoy (87 MB) + UBI9-micro (24 MB) dominate. The value is
mode lock-in, not size.
authbridge-proxy:litecontains zero gRPC code,zero envoy types, zero parser plugins. Whatever CVE surface those
carry is gone from that image.
Sample Dockerfiles used for the measurements are added as
follow-up work (see "Out of scope" below).
Prerequisite refactors
1. Parser plugins → subpackages
Three flat parser plugins lived in
authlib/plugins/as filesalongside
registry.go:a2aparser.gomcpparser.goinferenceparser.goTheir
init()registration was forced into every binary thatimported
authlib/plugins(because importing a package runs itsinit()regardless of what the binary actually needs). Moved eachto its own subpackage (matching the pattern
jwtvalidation/,tokenexchange/,tokenbroker/already use). All plugins are nowuniformly opt-in via side-effect import.
Shared parser helpers (
JSONRPCRequest,Truncate,DebugBodyMax)extracted to
authlib/plugins/internal/parsercommon/.internal/keeps them scoped to plugins under this tree.
2. HTTP listeners → authlib
forwardproxyandreverseproxywere undercmd/authbridge/listener/. They're net/http-only with no gRPC orenvoy dependencies, so they belong in the shared library where any
binary can reuse them. Moved to
authlib/listener/.extprocandextauthzstay undercmd/authbridge/listener/—their gRPC/envoy surface is specific to envoy-integration binaries,
and pulling them into
authlib/would drag grpc+envoy into theshared lib's go.mod.
Updated
cmd/authbridge/main.goAll 5 plugins now pulled in via side-effect imports. Preserves
existing binary behavior.
New binary layouts
Both new modules are added to
authbridge/go.workso workspacebuilds cover them.
Test plan
go test -race ./...inauthbridge/authlib— passesgo test -race ./...inauthbridge/cmd/authbridge— passesgo build ./...inauthbridge/cmd/authbridge-proxy— passesgo build ./...inauthbridge/cmd/authbridge-envoy— passesgofmt -lon touched files — clean (pre-existing drift inpipeline/session_test.go,auth.go, etc. not touched)above.
No new tests specific to the lite binaries — they are main.go
packages that duplicate the relevant branches of the full binary's
wiring. If drift becomes a concern we can extract a shared setup
helper into
authlib/.Out of scope (follow-ups)
cmd/authbridge/Dockerfilestill builds the full binary. AddingDockerfile.proxyandDockerfile.envoyis a small follow-upPR once the release/CI workflows are decided.
.github/workflows/ci.yaml— adding them should be straightforwardbut left as a separate change so this PR is code-only.
further (~5 MB for authbridge-proxy with UPX, at startup-time
cost).
cmd/authbridge-proxy/notingthe build-time tradeoffs.
Assisted-By: Claude (Anthropic AI) noreply@anthropic.com