fix(cred_proxy): close git-push bypass + route through pipelock (PRD 0010)
Three coupled fixes that close a documented bypass of git-gate's gitleaks pre-receive hook: 1. cred-proxy refuses git smart-HTTP push at runtime. Any path ending in /git-receive-pack or /info/refs?service=git-receive-pack returns 403 with a pointer at the bottle.git SSH path. Fetch (upload-pack) is still allowed — the bypass we're closing is push, where gitleaks is the load-bearing scanner. Hard guarantee. 2. The provisioner suppresses the cred-proxy `~/.gitconfig` insteadOf rewrite for any host already declared in bottle.git. git-gate is the canonical git path there; we don't write a competing rule that would let `git clone https://<host>/...` succeed in ways that confuse on push. Defense in depth — (1) is the hard guarantee. 3. cred-proxy routes its outbound HTTPS through pipelock. The sidecar's environ now sets HTTPS_PROXY=<pipelock-url>, and the image's entrypoint runs `update-ca-certificates` over the per-bottle pipelock CA (docker cp'd into /usr/local/share/ca-certificates/pipelock.crt before start) so the proxy's HTTPS client trusts pipelock's bumped certs. Consequence: pipelock's allowlist + body scanner now sit in the cred-proxy egress path the same way they sit in front of direct agent traffic. The cred-proxy upstream hosts (api.github.com, github.com, gitea hosts, registry.npmjs.org) come OFF pipelock's passthrough_domains. Only api.anthropic.com remains on passthrough (LLM body content legitimately trips DLP). PRD 0010 updated to reflect all three. Tests adjusted: the "cred-proxy hosts go on passthrough" assertion in test_pipelock_allowlist flips to "they don't", a new TestIsGitPushRequest exercises the smart-HTTP refusal predicate, and the gitconfig renderer tests cover the per-host suppression matrix.
This commit is contained in:
+22
-10
@@ -100,16 +100,28 @@ def pipelock_effective_allowlist(bottle: Bottle) -> list[str]:
|
||||
|
||||
def pipelock_effective_tls_passthrough(bottle: Bottle) -> list[str]:
|
||||
"""Hostnames pipelock should pass through (no TLS MITM, no body
|
||||
scan). Default carries the LLM API endpoint (its request bodies
|
||||
legitimately trip DLP); cred-proxy upstream hosts are added so
|
||||
cred-proxy's HTTPS client (which trusts only the real CA bundle)
|
||||
can complete the upstream handshake."""
|
||||
seen: dict[str, None] = {}
|
||||
for h in DEFAULT_TLS_PASSTHROUGH:
|
||||
seen.setdefault(h, None)
|
||||
for h in pipelock_token_hosts(bottle):
|
||||
seen.setdefault(h, None)
|
||||
return sorted(seen.keys())
|
||||
scan). Default carries the LLM API endpoint — its request bodies
|
||||
are user-authored conversation text that legitimately trips DLP
|
||||
scanners (notably pipelock's BIP-39 seed-phrase detector). Every
|
||||
other allowlisted host is MITM'd by pipelock's per-bottle CA so
|
||||
its body scanner sees the cleartext.
|
||||
|
||||
cred-proxy upstream hosts (github, gitea, npm) are deliberately
|
||||
NOT auto-added here. cred-proxy's HTTPS client trusts pipelock's
|
||||
CA at runtime (folded into its trust store via docker cp +
|
||||
update-ca-certificates), so pipelock can MITM the cred-proxy →
|
||||
upstream leg and body-scan it the same way it body-scans the
|
||||
agent's direct HTTPS traffic. Without this, an agent that pushed
|
||||
a secret via cred-proxy's /gh-git/ path would have no body
|
||||
scanner in front of it. The PRD's earlier reasoning that
|
||||
cred-proxy hosts needed passthrough was a workaround for the
|
||||
cert-trust gap that no longer exists.
|
||||
|
||||
`bottle` is kept on the signature for forward-compat (a future
|
||||
knob might let a manifest opt a host into passthrough); today
|
||||
the returned list is independent of the bottle."""
|
||||
del bottle # not consulted; see docstring.
|
||||
return sorted(DEFAULT_TLS_PASSTHROUGH)
|
||||
|
||||
|
||||
def pipelock_allowlist_summary(bottle: Bottle) -> str:
|
||||
|
||||
Reference in New Issue
Block a user