feat: serve the caller's ssh-agent to an identity #49

Merged
claudiomaradonna merged 2 commits from feat/host-ssh-agent into master 2026-10-17 14:56:54 +02:00
Collaborator

Why

Some operators keep their SSH keys only in an agent, such as a
hardware token or a password manager's agent, so they have no private
key file to copy into an identity's key. The only SSH route today is
that key, and SSH_AUTH_SOCK is filtered out of every env file. A
--mount of the socket plus IdentityAgent gets ssh working, but
ssh-keygen -Y sign doesn't find the agent, so SSH-signed commits
fail.

What changed

  • An identity whose files set AZKABAN_SSH_AGENT=host launches with
    the caller's $SSH_AUTH_SOCK mounted at /ssh-agent and
    SSH_AUTH_SOCK=/ssh-agent set in the container. No per-launch agent
    starts, and the value of the caller's variable never reaches the
    container.
  • The launch stops before the engine starts when the variable is unset
    or empty, isn't an absolute path to a socket, or holds a :; when the
    identity also has a key; or when the value isn't host.
  • The identity line names the socket, for example
    claude: identity: me: host ssh-agent /run/user/1000/….
  • On macOS the agent isn't served, as with key. One line says so and
    the launch goes on without it.
  • docs/bot-accounts.md gains "Your own agent". The README lists the
    control and updates the security model, caller environment and macOS
    notes, and the env example lists the line.

Left out: a --ssh-agent flag and an identity agent file. The control
is per identity, so bot identities never get your agent, and the
workspace .env can't turn it on, because it already rejects every
AZKABAN_ name.

How to verify

On a Linux host with an agent running:

mkdir -p -m 700 ~/.azkaban/identities/me
printf 'AZKABAN_SSH_AGENT=host\n' >>~/.azkaban/identities/me/env
claude . --identity me --dry-run

Expect identity: me: host ssh-agent <socket> on stderr and
-v <socket>:/ssh-agent -e SSH_AUTH_SOCK=/ssh-agent in the printed
command. Running SSH_AUTH_SOCK= claude . --identity me instead stops
with AZKABAN_SSH_AGENT=host needs SSH_AUTH_SOCK, which is not set.

Checks run on the tip: make lint, .agents/hooks/shellcheck.sh repo
(41 files clean) and make test (0 failures) all pass. Each folded
commit also passed make lint && make test. The new tests fail on
master's code at their behavior assertions. Not run: make test-e2e,
which needs real rootless Podman, so no real agent socket has been
mounted.

Considerations

  • A session running as such an identity can use every key in your
    agent, and can remove keys or lock the agent, for as long as it runs.
    The docs say to keep such an identity for work you watch.
  • A leftover --mount …:/ssh-agent beside the control isn't caught
    before launch, and Podman refuses the duplicate destination. The docs
    say to drop such a mount.
  • A review note that a comment in _azk_resolve_host_agent is
    unevenly wrapped was set aside as low.
## Why Some operators keep their SSH keys only in an agent, such as a hardware token or a password manager's agent, so they have no private key file to copy into an identity's `key`. The only SSH route today is that `key`, and `SSH_AUTH_SOCK` is filtered out of every env file. A `--mount` of the socket plus `IdentityAgent` gets `ssh` working, but `ssh-keygen -Y sign` doesn't find the agent, so SSH-signed commits fail. ## What changed - An identity whose files set `AZKABAN_SSH_AGENT=host` launches with the caller's `$SSH_AUTH_SOCK` mounted at `/ssh-agent` and `SSH_AUTH_SOCK=/ssh-agent` set in the container. No per-launch agent starts, and the value of the caller's variable never reaches the container. - The launch stops before the engine starts when the variable is unset or empty, isn't an absolute path to a socket, or holds a `:`; when the identity also has a `key`; or when the value isn't `host`. - The identity line names the socket, for example `claude: identity: me: host ssh-agent /run/user/1000/…`. - On macOS the agent isn't served, as with `key`. One line says so and the launch goes on without it. - `docs/bot-accounts.md` gains "Your own agent". The README lists the control and updates the security model, caller environment and macOS notes, and the env example lists the line. Left out: a `--ssh-agent` flag and an identity `agent` file. The control is per identity, so bot identities never get your agent, and the workspace `.env` can't turn it on, because it already rejects every `AZKABAN_` name. ## How to verify On a Linux host with an agent running: ```bash mkdir -p -m 700 ~/.azkaban/identities/me printf 'AZKABAN_SSH_AGENT=host\n' >>~/.azkaban/identities/me/env claude . --identity me --dry-run ``` Expect `identity: me: host ssh-agent <socket>` on stderr and `-v <socket>:/ssh-agent -e SSH_AUTH_SOCK=/ssh-agent` in the printed command. Running `SSH_AUTH_SOCK= claude . --identity me` instead stops with `AZKABAN_SSH_AGENT=host needs SSH_AUTH_SOCK, which is not set`. Checks run on the tip: `make lint`, `.agents/hooks/shellcheck.sh repo` (41 files clean) and `make test` (0 failures) all pass. Each folded commit also passed `make lint && make test`. The new tests fail on master's code at their behavior assertions. Not run: `make test-e2e`, which needs real rootless Podman, so no real agent socket has been mounted. ## Considerations - A session running as such an identity can use every key in your agent, and can remove keys or lock the agent, for as long as it runs. The docs say to keep such an identity for work you watch. - A leftover `--mount …:/ssh-agent` beside the control isn't caught before launch, and Podman refuses the duplicate destination. The docs say to drop such a mount. - A review note that a comment in `_azk_resolve_host_agent` is unevenly wrapped was set aside as low.
AZKABAN_SSH_AGENT=host in an identity's files mounts the caller's
SSH_AUTH_SOCK at /ssh-agent and sets SSH_AUTH_SOCK there, in place of
the per-launch agent a key file gets, so keys that live only in an
agent can push and sign. The launch stops when the socket is missing
or the identity also has a key; on macOS the agent is not served.
claudiomaradonna deleted branch feat/host-ssh-agent 2026-10-17 14:57:15 +02:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
claudiomaradonna/azkaban!49
No description provided.