Skip to content

GitHub through the proxy: git and gh - #37

Merged
czpython merged 1 commit into
mainfrom
dru-454-github-through-the-proxy
Sep 7, 2026
Merged

czpython merged 1 commit into
mainfrom
dru-454-github-through-the-proxy

Conversation

@czpython

@czpython czpython commented Sep 6, 2026 •

Copy link
Copy Markdown
Owner

What changes

A box clones, pushes, and runs gh with a Druks App installation token that never enters the box.

  • github is one service with three hosts in the catalog. api.github.com and uploads.github.com, where gh puts release assets, take a bearer. github.com takes Basic with x-access-token as the user and the token as the password, since git's smart HTTP refuses a bearer. A Service is now the variable a client reads plus the Upstreams it reaches, each of which knows its host, its header, and how the credential goes on the wire. The exchange picks the upstream by the host the proxy names, and /upstreams lists every host.
  • gh gets the placeholder in GH_TOKEN. git gets it through gh: the box writes the lines gh auth setup-git writes, system wide, so git sends the placeholder as a Basic password and the proxy swaps it. An SSH remote would go around the proxy, so git@github.com: and ssh://git@github.com/ are rewritten to HTTPS.
  • One writer, environment.github, gives those lines beside trust. They replace and then add, so they can run again on the same box, as a container restart does. A test runs them twice against a real git. cloud-init runs them as root, the exe setup script with sudo -n, and the docker sandbox image in its entrypoint. That image now carries git and gh, from GitHub's own apt repository as its install guide gives it.
  • On docker-sbx a custom secret names every host of its service, with --host repeated. The github service there stays sbx's own.

Where this differs from the ticket

  • The git setup is written with git config --system, not by running gh auth setup-git. gh writes to the user's own config, and cloud-init runs as root while the caller logs in as another user. The lines are the ones gh writes, for every user of the box.
  • Both SSH forms are rewritten, git@github.com: and ssh://git@github.com/, since a pasted URL can take either.
  • uploads.github.com is a third host, since gh release upload puts assets there with the same bearer. gist.github.com is not in the catalog. A gist clone over HTTPS would reach it with the placeholder and fail with 401, which is safe and can be added when a box needs it.
  • A box with a github secret needs git and gh. The docker sandbox image gets both. The exe image has both. A cloud image without git fails its setup script on the first git config line, so the host fails with the reason instead of coming up without the swap.

Names

New names, open to change: Upstream, environment.github.

Gates

uv run ruff check, uv run ruff format --check, uv run pyright, and uv run pytest are green.

Acceptance

Run on the acceptance stack with this branch built into the API and exchange image, the proxy on the host's tailnet address, the docker sandbox image built from this branch's images/local, and a Druks App installation token for the private repository clawhaven/clawhaven-localdev as a static github secret. 13 of 13 on docker and 12 of 12 on exe, repeated after the review fixes:

  • The box became active with the placeholder in GH_TOKEN, git pointed at gh, and both SSH forms rewritten.
  • gh api read the private repository and gh pr list worked.
  • git clone over HTTPS, a push of a throwaway branch, and its delete all worked.
  • A clone through git@github.com: went over HTTPS through the rewrite.
  • The token appeared nowhere in the session, in git's credentials, or in git's system config.
  • On docker, a restart of the container brought sshd back with the same two helper values, and gh still worked. Docker gives a restarted container a new random SSH port, which is the docker provider's standing behavior and not this change.

The first docker run found one defect: gh sends Authorization: token <value>, a scheme the addon did not read, so the placeholder reached GitHub and got 401. The addon now reads the token scheme like a bearer, with a test. Both halves passed after the fix. The stack was restored afterwards, and the branch images, the token file, and the CA directory were removed from the host.

Review

One adversarial review, two findings, both applied.

  • gh release upload --clobber deletes the old asset through api.github.com, then uploads through uploads.github.com, which the catalog did not name, so the upload would have failed after the delete. The host is now the service's third upstream, with the bearer.
  • The docker entrypoint runs again at every container restart, and git config refuses to replace a key that already has two values, which would have stopped sshd after a restart. The lines now replace and then add, in the entrypoint and in the writer, with a test that runs them twice against a real git. The docker half of the acceptance now restarts the box and checks that sshd and gh come back.

@czpython
czpython force-pushed the dru-454-github-through-the-proxy branch 2 times, most recently from cbf531c to 6597add Compare September 6, 2026 15:02
@czpython
czpython force-pushed the dru-459-trust-delivery branch 4 times, most recently from 3df0ceb to e8c3b62 Compare September 7, 2026 05:19
A box must clone, push, and run gh with a Druks App installation token
that never enters the box. The proxy carries it. github is one service
with three hosts in the catalog. api.github.com and uploads.github.com,
where gh puts release assets, take a bearer. github.com takes Basic with
x-access-token as the user, since git's smart HTTP refuses a bearer. A service is now the variable a client reads and
the upstreams it reaches, each an Upstream that knows its host, its
header, and how the credential goes on the wire. The exchange picks the
upstream by the host the proxy names, and /upstreams lists every host.

gh gets the placeholder in GH_TOKEN and sends it with the token scheme,
which the proxy reads like a bearer. git gets it through gh: the box
writes the lines gh auth setup-git writes, system wide, so git sends the
placeholder as a Basic password and the proxy swaps it. An SSH remote
would go around the proxy, so git@github.com: and ssh://git@github.com/
are rewritten to HTTPS. One writer, environment.github, gives the lines
beside trust. The lines replace and then add, so they can run again on
the same box, as a container restart does. cloud-init runs them as root, the exe setup script with
sudo -n, and the docker sandbox image in its entrypoint. That image now
carries git and gh, from GitHub's own apt repository.

On docker-sbx a custom secret names every host of its service, with
--host repeated. The github service there is sbx's own.
@czpython
czpython changed the base branch from dru-459-trust-delivery to main September 7, 2026 05:23
@czpython
czpython force-pushed the dru-454-github-through-the-proxy branch from 6597add to 4a1a0a7 Compare September 7, 2026 05:23
@czpython
czpython merged commit 1e2e8a6 into main Sep 7, 2026
6 of 12 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant