Access Git Servers Without an SSH Key
Give the agent working git access to GitHub, GitLab, and other git hosts from inside the sandbox without provisioning SSH keys, but by using the secret mechanism.
Common steps
Rewriting SSH remotes to HTTPS
All approaches in this recipe authenticate over HTTPS. A repository cloned over SSH keeps an git@host:owner/repo remote, which git would try over SSH; each example includes a snippet that rewrites SSH access to HTTPS access.
Network access
Sandboxes need to be able to reach the git server hosts. Under the default public profile, the public git hosts are already reachable. Under non-public profiles, add each host so it is both allowed and resolvable:
# .agents-sandbox/config.yaml
network:
profile: none
egress-allow:
- github.com
- api.github.com
- gitlab.com
Secrets
Secrets should not be stored in the project config, but in the project-specific user config. Determine the path and create the directory if it’s missing:
asb config paths|grep "per-project user config"
# output e.g. " per-project user config /home/user/.config/agents-sandbox/example-project-123456789abcde (missing)"
mkdir -p /home/user/.config/agents-sandbox/example-project-123456789abcde
For every approach in this recipe, edit secrets under the respective path, e.g. /home/user/.config/agents-sandbox/example-project-123456789abcde/env.secret.yaml.
GitHub
GitHub uses the gh CLI. Install it via a custom Dockerfile and initialize it via a startup hook.
When using the managed base image (Debian), add this snippet to your project Dockerfile (for custom base images, adapt the snippet):
# .agents-sandbox/Dockerfile
ARG GH_VERSION=2.103.3
RUN curl -fsSL "https://github.com/cli/cli/releases/download/v${GH_VERSION}/gh_${GH_VERSION}_linux_$(dpkg --print-architecture).deb" -o /tmp/gh.deb \
&& dpkg -i /tmp/gh.deb \
&& rm -f /tmp/gh.deb
Configure the startup hook:
# .agents-sandbox/config.yaml
home:
.github/github-auth.sh:
source: github-auth.sh
hook: startup
#!/bin/sh
# .agents-sandbox/github-auth.sh
if [ -n "${GITHUB_PAT:-}" ]; then
printf '%s\n' "$GITHUB_PAT" | gh auth login --with-token
gh auth setup-git
git config --global url."https://github.com/".insteadOf "git@github.com:"
git config --global --add url."https://github.com/".insteadOf "ssh://git@github.com/"
fi
Define the token as secret. The name and the two hosts are both significant:
# in project-specific user-config directory
GITHUB_PAT:
value: ghp_xxxxxxxx
hosts: [ github.com, api.github.com ]
header_fields: [ authorization ]
Warning: Do not name the secret
GITHUB_TOKENorGH_TOKEN. When either of those is set,gh auth login --with-tokenrefuses to store a token and the hook fails.
gh auth login validates the token against the API, which is why api.github.com has to be in the hosts list
alongside github.com. gh auth setup-git then configures git to ask gh for the credential, and gh returns the
placeholder.
A nice side-effect of this setup is that agents can use the cli to broadly interact with github, e.g. open/merge/close PRs.
GitLab
Configure git’s credential store directly and write the placeholder into ~/.git-credentials from a startup hook:
# .agents-sandbox/config.yaml
home:
.gitlab/gitlab-auth.sh:
source: gitlab-auth.sh
hook: startup
#!/bin/sh
# .agents-sandbox/gitlab-auth.sh
if [ -n "${GITLAB_TOKEN:-}" ]; then
git config --global credential.helper store
umask 077
printf 'https://oauth2:%s@gitlab.com\n' "$GITLAB_TOKEN" > "$HOME/.git-credentials"
git config --global url."https://gitlab.com/".insteadOf "git@gitlab.com:"
git config --global --add url."https://gitlab.com/".insteadOf "ssh://git@gitlab.com/"
fi
Then, configure the secret with a GitLab token:
# in project-specific user-config directory
GITLAB_TOKEN:
value: glpat-xxxxxxxx
hosts: [ gitlab.com ]
header_fields: [ authorization ]
GitLab accepts a personal, project, or group access token as the password with any username; oauth2 is the
convention. The token needs the read_repository scope, and write_repository for pushing.
Optional: enable GitLab API access
The credential store covers only git operations. To enable agents to call the GitLab REST API, widen the token’s
header scope to include GitLab’s PRIVATE-TOKEN header:
# in project-specific user-config directory
GITLAB_TOKEN:
value: glpat-xxxxxxxx
hosts: [ gitlab.com ]
header_fields: [ authorization, private-token ]
The API shares the git host (gitlab.com/api/v4), so no extra hosts or egress entry is needed, but the token now
also needs the api scope. Because header scoping is baked into the VM at creation, this change recreates the
sandbox once.
The agent can then authenticate either way:
curl --fail --header "PRIVATE-TOKEN: $GITLAB_TOKEN" "https://gitlab.com/api/v4/user"
curl --fail --header "Authorization: Bearer $GITLAB_TOKEN" "https://gitlab.com/api/v4/user"
Optional: add the glab CLI
glab wraps the same API in merge-request, issue, and CI commands (glab mr,
glab issue, glab ci), the counterpart of the GitHub gh route. It depends on the API access from the previous
step: glab sends the token in the PRIVATE-TOKEN header, so with header_fields: [ authorization ] alone its
requests carry an unsubstituted placeholder and fail.
Install it the same way as gh: on the managed base image (Debian), add this snippet to your project Dockerfile (for
custom base images, adapt the snippet to install glab from its release tarball or another package):
# .agents-sandbox/Dockerfile
ARG GLAB_VERSION=1.130.0
RUN curl -fsSL "https://gitlab.com/gitlab-org/cli/-/releases/v${GLAB_VERSION}/downloads/glab_${GLAB_VERSION}_linux_$(dpkg --print-architecture).deb" -o /tmp/glab.deb \
&& dpkg -i /tmp/glab.deb \
&& rm -f /tmp/glab.deb
No startup hook is needed: glab reads the GITLAB_TOKEN environment variable and sends it to the host it resolves
from the repository’s Git remote (gitlab.com by default). For a self-managed instance, add its host to the secret’s
hosts and set it in the project env file:
# .agents-sandbox/env
GITLAB_HOST=gitlab.example.com
glab api <path> also calls endpoints the CLI doesn’t wrap.
Other HTTPS git hosts
The credential store works for any HTTPS git host: one line per host in ~/.git-credentials, each carrying that
host’s own secret placeholder. Use this instead of the gh route for GitHub, not alongside it.
# .agents-sandbox/config.yaml
home:
.github/git-auth.sh:
source: git-auth.sh
hook: startup
Here’s an example hook that you can adapt to your git host:
#!/bin/sh
# .agents-sandbox/git-auth.sh
git config --global credential.helper store
umask 077
: > "$HOME/.git-credentials"
if [ -n "${GITLAB_TOKEN:-}" ]; then
printf 'https://oauth2:%s@gitlab.com\n' "$GITLAB_TOKEN" >> "$HOME/.git-credentials"
git config --global url."https://gitlab.com/".insteadOf "git@gitlab.com:"
git config --global --add url."https://gitlab.com/".insteadOf "ssh://git@gitlab.com/" # with port: ssh://git@gitlab<.com:23/
fi
if [ -n "${GITHUB_PAT:-}" ]; then
printf 'https://x-access-token:%s@github.com\n' "$GITHUB_PAT" >> "$HOME/.git-credentials"
git config --global url."https://github.com/".insteadOf "git@github.com:"
git config --global --add url."https://github.com/".insteadOf "ssh://git@github.com/" # with port: ssh://git@github.com:23/
fi
The username in each line is ignored by most hosts; any non-empty value works. Give each host its own secret so the proxy only ever substitutes that host’s token.
Then, define the necessary secrets:
# in project-specific user-config directory
GITLAB_TOKEN:
value: glpat-xxxxxxxx
hosts: [ gitlab.com ]
header_fields: [ authorization ]
GITHUB_PAT:
value: ghp_xxxxxxxx
hosts: [ github.com, api.github.com ]
header_fields: [ authorization ]
Send a raw header instead
If a host needs the credential in a specific header rather than basic auth, set it per URL (in your hook):
git config --global http."https://gitlab.com/".extraHeader 'Authorization: Bearer $MSB_GITLAB_TOKEN'
This avoids the credentials file, but the server has to accept the Bearer scheme. GitLab’s and GitHub’s git
endpoints expect basic auth, which the credential store sends, so prefer the credential store unless the host
documents otherwise.