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:
@@ -64,16 +64,24 @@ class CredProxyPlan:
|
||||
|
||||
The slug + routes_path + upstreams + token_env_map fields are
|
||||
filled at prepare time (host-side, side-effect-free on docker).
|
||||
The network fields are populated by the backend's launch step
|
||||
via `dataclasses.replace` once those networks exist. Empty
|
||||
defaults are sentinels meaning "not yet set"; `.start` validates
|
||||
that they are populated.
|
||||
The network + pipelock fields are populated by the backend's
|
||||
launch step via `dataclasses.replace` once those resources
|
||||
exist. Empty defaults are sentinels meaning "not yet set";
|
||||
`.start` validates that they are populated.
|
||||
|
||||
`token_env_map` is `{<token_env in container>: <TokenRef on host>}`.
|
||||
The backend's start step reads `os.environ[TokenRef]` and forwards
|
||||
the value into the cred-proxy container's environ under
|
||||
`token_env`. The plan itself never holds token values — secrets
|
||||
never land in a dataclass that might be logged."""
|
||||
never land in a dataclass that might be logged.
|
||||
|
||||
`pipelock_ca_host_path` is the host path of the per-bottle CA
|
||||
pipelock will present on bumped TLS handshakes; the cred-proxy
|
||||
image's entrypoint runs `update-ca-certificates` over it so the
|
||||
proxy's HTTPS client trusts pipelock's CA. `pipelock_proxy_url`
|
||||
is the URL cred-proxy sets as `HTTPS_PROXY` in its environ so
|
||||
outbound HTTPS traverses pipelock — making pipelock's body
|
||||
scanner part of the cred-proxy egress path."""
|
||||
|
||||
slug: str
|
||||
routes_path: Path
|
||||
@@ -81,6 +89,8 @@ class CredProxyPlan:
|
||||
token_env_map: dict[str, str]
|
||||
internal_network: str = ""
|
||||
egress_network: str = ""
|
||||
pipelock_ca_host_path: Path = Path()
|
||||
pipelock_proxy_url: str = ""
|
||||
|
||||
|
||||
# Hardcoded upstream URLs for the non-gitea Kinds. Gitea's URL is per-
|
||||
|
||||
Reference in New Issue
Block a user