Dead: provision SSH config for bottles #150

Closed
opened 2026-06-02 13:31:39 -04:00 by didericis-claude · 7 comments
Collaborator

Problem

git.remotes.*.ExtraHosts currently behaves like a hosts override for git-gate reachability. That is reasonable for cases where the gate itself needs /etc/hosts style resolution for an upstream.

However, the Gitea remote case needs a different capability: the bottle should be able to carry operator-approved SSH client config into the agent/git environment, so SSH host aliases work the same way they do on the host without changing hosts/DNS behavior for the sidecar, egress route, or shell/API traffic.

Today claude-dev.md uses this shape:

git:
  remotes:
    gitea.dideric.is:
      Name: bot-bottle
      Upstream: ssh://git@gitea.dideric.is:30009/didericis/bot-bottle.git
      ExtraHosts:
        gitea.dideric.is: 100.78.141.42

egress:
  routes:
    - host: gitea.dideric.is
      auth:
        scheme: token
        token_ref: BOT_BOTTLE_CLAUDE_GITEA_TOKEN

That makes git work, but it also means the extra host mapping can affect non-git traffic. In a bottle shell, Gitea API access can be blocked because gitea.dideric.is resolves to the internal IP:

HTTP/1.1 403 Forbidden
blocked: SSRF blocked: gitea.dideric.is resolves to internal IP 100.78.141.42

Requested Feature

Add top-level bottle SSH config support, separate from git.remotes, so the agent/git environment can use explicit SSH host stanzas without forcing those host mappings into egress DNS or hosts config.

Example intended shape:

git:
  remotes:
    gitea.dideric.is:
      Name: bot-bottle
      Upstream: ssh://git@gitea.dideric.is:30009/didericis/bot-bottle.git

ssh:
  config:
    - Host: gitea
      Hostname: 100.78.141.42
      Port: 30009
      User: git
      IdentityFile: ~/.ssh/gitea-delos-2.pem
    - Host: gitea.dideric.is
      Hostname: 100.78.141.42
      Port: 30009
      User: git
      IdentityFile: ~/.ssh/gitea-delos-2.pem

IdentityFile values are host-side paths. bot-bottle should stage or mount the referenced key material using its existing key-handling path, then render generated SSH config with the corresponding in-bottle path. The private key contents must not be duplicated or inlined into the manifest or generated logs.

Expected Behavior

  • Keep ExtraHosts as the explicit hosts override mechanism for git-gate reachability.
  • Add ssh.config as a top-level bottle capability for SSH client stanzas.
  • Git remotes using SSH host aliases, such as git@gitea:didericis/bot-bottle.git, work inside the bottle because SSH sees the provisioned Host gitea stanza.
  • Gitea HTTP/API access from the shell continues through the declared egress route and is not forced to an internal IP by SSH config.
  • SSH config provisioning does not alter Docker extra_hosts, sidecar hosts config, agent hosts config, or egress route DNS.

Acceptance Criteria

  • Manifest parser accepts top-level ssh.config entries with at least Host, Hostname, Port, User, and IdentityFile.
  • Provisioning renders valid SSH config inside the bottle/git environment and rewrites IdentityFile to the staged in-bottle key path.
  • Referenced private key contents are never printed, logged, committed, or inlined into generated config outside the intended staged key file.
  • ssh.config entries are not emitted into Docker extra_hosts, sidecar hosts config, agent hosts config, or egress route config.
  • Tests cover that ssh.config changes generated SSH config and enables alias-style git remotes without changing hosts config.
  • Documentation distinguishes ssh.config, git insteadOf rewrites, and ExtraHosts.
## Problem `git.remotes.*.ExtraHosts` currently behaves like a hosts override for git-gate reachability. That is reasonable for cases where the gate itself needs `/etc/hosts` style resolution for an upstream. However, the Gitea remote case needs a different capability: the bottle should be able to carry operator-approved SSH client config into the agent/git environment, so SSH host aliases work the same way they do on the host without changing hosts/DNS behavior for the sidecar, egress route, or shell/API traffic. Today `claude-dev.md` uses this shape: ```yaml git: remotes: gitea.dideric.is: Name: bot-bottle Upstream: ssh://git@gitea.dideric.is:30009/didericis/bot-bottle.git ExtraHosts: gitea.dideric.is: 100.78.141.42 egress: routes: - host: gitea.dideric.is auth: scheme: token token_ref: BOT_BOTTLE_CLAUDE_GITEA_TOKEN ``` That makes git work, but it also means the extra host mapping can affect non-git traffic. In a bottle shell, Gitea API access can be blocked because `gitea.dideric.is` resolves to the internal IP: ```text HTTP/1.1 403 Forbidden blocked: SSRF blocked: gitea.dideric.is resolves to internal IP 100.78.141.42 ``` ## Requested Feature Add top-level bottle SSH config support, separate from `git.remotes`, so the agent/git environment can use explicit SSH host stanzas without forcing those host mappings into egress DNS or hosts config. Example intended shape: ```yaml git: remotes: gitea.dideric.is: Name: bot-bottle Upstream: ssh://git@gitea.dideric.is:30009/didericis/bot-bottle.git ssh: config: - Host: gitea Hostname: 100.78.141.42 Port: 30009 User: git IdentityFile: ~/.ssh/gitea-delos-2.pem - Host: gitea.dideric.is Hostname: 100.78.141.42 Port: 30009 User: git IdentityFile: ~/.ssh/gitea-delos-2.pem ``` `IdentityFile` values are host-side paths. bot-bottle should stage or mount the referenced key material using its existing key-handling path, then render generated SSH config with the corresponding in-bottle path. The private key contents must not be duplicated or inlined into the manifest or generated logs. ## Expected Behavior - Keep `ExtraHosts` as the explicit hosts override mechanism for git-gate reachability. - Add `ssh.config` as a top-level bottle capability for SSH client stanzas. - Git remotes using SSH host aliases, such as `git@gitea:didericis/bot-bottle.git`, work inside the bottle because SSH sees the provisioned `Host gitea` stanza. - Gitea HTTP/API access from the shell continues through the declared egress route and is not forced to an internal IP by SSH config. - SSH config provisioning does not alter Docker `extra_hosts`, sidecar hosts config, agent hosts config, or egress route DNS. ## Acceptance Criteria - Manifest parser accepts top-level `ssh.config` entries with at least `Host`, `Hostname`, `Port`, `User`, and `IdentityFile`. - Provisioning renders valid SSH config inside the bottle/git environment and rewrites `IdentityFile` to the staged in-bottle key path. - Referenced private key contents are never printed, logged, committed, or inlined into generated config outside the intended staged key file. - `ssh.config` entries are not emitted into Docker `extra_hosts`, sidecar hosts config, agent hosts config, or egress route config. - Tests cover that `ssh.config` changes generated SSH config and enables alias-style git remotes without changing hosts config. - Documentation distinguishes `ssh.config`, git `insteadOf` rewrites, and `ExtraHosts`.
didericis added the Kind/Feature label 2026-06-02 13:31:47 -04:00
Author
Collaborator

Correction/reference from local SSH config:

The desired alias source is not ~/.gitconfig; it is the host SSH config. Locally there are SSH stanzas for both gitea and gitea.dideric.is that resolve to the same upstream endpoint:

Host gitea
  Hostname 100.78.141.42
  Port 30009
  User git

Host gitea.dideric.is
  Hostname 100.78.141.42
  Port 30009
  User git

So the useful gitconfig-only alias is the alternate SSH remote spelling that relies on that host alias, for example:

git:
  remotes:
    gitea.dideric.is:
      Name: bot-bottle
      Upstream: ssh://git@gitea.dideric.is:30009/didericis/bot-bottle.git
      Aliases:
        - ssh://git@gitea/didericis/bot-bottle.git
        - git@gitea:didericis/bot-bottle.git

Those aliases should become additional insteadOf entries only. They should not create /etc/hosts, Docker extra_hosts, sidecar hosts, agent hosts, or egress DNS changes.

Correction/reference from local SSH config: The desired alias source is not `~/.gitconfig`; it is the host SSH config. Locally there are SSH stanzas for both `gitea` and `gitea.dideric.is` that resolve to the same upstream endpoint: ```text Host gitea Hostname 100.78.141.42 Port 30009 User git Host gitea.dideric.is Hostname 100.78.141.42 Port 30009 User git ``` So the useful gitconfig-only alias is the alternate SSH remote spelling that relies on that host alias, for example: ```yaml git: remotes: gitea.dideric.is: Name: bot-bottle Upstream: ssh://git@gitea.dideric.is:30009/didericis/bot-bottle.git Aliases: - ssh://git@gitea/didericis/bot-bottle.git - git@gitea:didericis/bot-bottle.git ``` Those aliases should become additional `insteadOf` entries only. They should not create `/etc/hosts`, Docker `extra_hosts`, sidecar hosts, agent hosts, or egress DNS changes.
Owner

Ok, actually I think this direction is wrong... I think we just want to be able to specify an ssh config to give the agent. Let's update the issue to reflect that.

Ok, actually I think this direction is wrong... I think we just want to be able to specify an ssh config to give the agent. Let's update the issue to reflect that.
Author
Collaborator

Agreed. Revised direction: this should not be a gitconfig Aliases feature. The real need is to let a bottle provide SSH config entries to the agent/git-gate environment, so ordinary SSH remote spellings like git@gitea:didericis/bot-bottle.git resolve the same way they do on the host.

That means the issue should be reframed around an agent-provisioned SSH config, for example a manifest key that can declare stanzas equivalent to:

Host gitea
  Hostname 100.78.141.42
  Port 30009
  User git
  IdentityFile ~/.ssh/gitea-delos-2.pem

Host gitea.dideric.is
  Hostname 100.78.141.42
  Port 30009
  User git
  IdentityFile ~/.ssh/gitea-delos-2.pem

Acceptance criteria should move away from gitconfig alias generation and instead cover:

  • manifest support for SSH config stanzas or an SSH-config-like remote field;
  • provisioning that config only where git/SSH needs it;
  • no accidental coupling to egress route DNS or HTTP/API behavior;
  • git remotes using host aliases such as gitea work inside the bottle;
  • docs clarify the difference between SSH host config, git insteadOf, and ExtraHosts.

ExtraHosts can stay as the explicit hosts override mechanism. This feature is about carrying the operator-approved SSH host config into the bottle so git remotes do not need a fake gitconfig-only alias layer.

Agreed. Revised direction: this should not be a gitconfig `Aliases` feature. The real need is to let a bottle provide SSH config entries to the agent/git-gate environment, so ordinary SSH remote spellings like `git@gitea:didericis/bot-bottle.git` resolve the same way they do on the host. That means the issue should be reframed around an agent-provisioned SSH config, for example a manifest key that can declare stanzas equivalent to: ```sshconfig Host gitea Hostname 100.78.141.42 Port 30009 User git IdentityFile ~/.ssh/gitea-delos-2.pem Host gitea.dideric.is Hostname 100.78.141.42 Port 30009 User git IdentityFile ~/.ssh/gitea-delos-2.pem ``` Acceptance criteria should move away from gitconfig alias generation and instead cover: - manifest support for SSH config stanzas or an SSH-config-like remote field; - provisioning that config only where git/SSH needs it; - no accidental coupling to egress route DNS or HTTP/API behavior; - git remotes using host aliases such as `gitea` work inside the bottle; - docs clarify the difference between SSH host config, git `insteadOf`, and `ExtraHosts`. `ExtraHosts` can stay as the explicit hosts override mechanism. This feature is about carrying the operator-approved SSH host config into the bottle so git remotes do not need a fake gitconfig-only alias layer.
Author
Collaborator

Requested Feature: provision SSH config when a remote has an SSH key

When a git remote declares an SSH identity/key, bot-bottle should be able to provision the matching SSH client config for that remote instead of relying on gitconfig-only aliases.

Example shape, naming TBD:

git:
  remotes:
    gitea.dideric.is:
      Name: bot-bottle
      Upstream: ssh://git@gitea.dideric.is:30009/didericis/bot-bottle.git
      IdentityFile: ~/.ssh/gitea-delos-2.pem
      SshConfig:
        - Host: gitea
          Hostname: 100.78.141.42
          Port: 30009
          User: git
        - Host: gitea.dideric.is
          Hostname: 100.78.141.42
          Port: 30009
          User: git

The provisioned SSH config should reference the already-staged remote key material, not duplicate or inline private key contents. In other words, the manifest points to the host IdentityFile, bot-bottle stages it as it already does for git-gate, and generated SSH config uses the staged path inside the bottle/gate environment.

This gives git/SSH the same host alias behavior as local ~/.ssh/config while keeping HTTP/API egress resolution independent. ExtraHosts remains available for the separate case where a container genuinely needs a hosts-file override.

## Requested Feature: provision SSH config when a remote has an SSH key When a git remote declares an SSH identity/key, bot-bottle should be able to provision the matching SSH client config for that remote instead of relying on gitconfig-only aliases. Example shape, naming TBD: ```yaml git: remotes: gitea.dideric.is: Name: bot-bottle Upstream: ssh://git@gitea.dideric.is:30009/didericis/bot-bottle.git IdentityFile: ~/.ssh/gitea-delos-2.pem SshConfig: - Host: gitea Hostname: 100.78.141.42 Port: 30009 User: git - Host: gitea.dideric.is Hostname: 100.78.141.42 Port: 30009 User: git ``` The provisioned SSH config should reference the already-staged remote key material, not duplicate or inline private key contents. In other words, the manifest points to the host `IdentityFile`, bot-bottle stages it as it already does for git-gate, and generated SSH config uses the staged path inside the bottle/gate environment. This gives git/SSH the same host alias behavior as local `~/.ssh/config` while keeping HTTP/API egress resolution independent. `ExtraHosts` remains available for the separate case where a container genuinely needs a hosts-file override.
Owner

No, think it should be like this:

git:
  remotes:
    gitea.dideric.is:
      Name: bot-bottle
      Upstream: ssh://git@gitea.dideric.is:30009/didericis/bot-bottle.git
ssh:
  config:
    - Host: gitea
      Hostname: 100.78.141.42
      Port: 30009
      User: git
      IdentityFile: ~/.ssh/gitea-delos-2.pem
    - Host: gitea.dideric.is
      Hostname: 100.78.141.42
      Port: 30009
      User: git
      IdentityFile: ~/.ssh/gitea-delos-2.pem
No, think it should be like this: ```yaml git: remotes: gitea.dideric.is: Name: bot-bottle Upstream: ssh://git@gitea.dideric.is:30009/didericis/bot-bottle.git ssh: config: - Host: gitea Hostname: 100.78.141.42 Port: 30009 User: git IdentityFile: ~/.ssh/gitea-delos-2.pem - Host: gitea.dideric.is Hostname: 100.78.141.42 Port: 30009 User: git IdentityFile: ~/.ssh/gitea-delos-2.pem ```
Collaborator

Yes, this is the cleaner shape. SSH config should be a top-level bottle capability, not nested under a specific git remote.

That gives us a clearer split:

git:
  remotes:
    gitea.dideric.is:
      Name: bot-bottle
      Upstream: ssh://git@gitea.dideric.is:30009/didericis/bot-bottle.git

ssh:
  config:
    - Host: gitea
      Hostname: 100.78.141.42
      Port: 30009
      User: git
      IdentityFile: ~/.ssh/gitea-delos-2.pem
    - Host: gitea.dideric.is
      Hostname: 100.78.141.42
      Port: 30009
      User: git
      IdentityFile: ~/.ssh/gitea-delos-2.pem

Implementation implications:

  • git.remotes continues to describe repositories and git-gate rewrites.
  • ssh.config describes SSH client behavior available inside the bottle.
  • IdentityFile values are host-side paths that bot-bottle stages/mounts safely, then rewrites to the in-bottle path in generated SSH config.
  • These SSH config entries should not alter egress route DNS, Docker extra_hosts, sidecar hosts config, or HTTP/API resolution.
  • Git operations that use SSH host aliases, such as git@gitea:didericis/bot-bottle.git, should work because SSH sees the provisioned Host gitea stanza.

This also scales beyond git: if a future workflow needs SSH config for another tool, it does not have to pretend that config belongs to one git remote.

Yes, this is the cleaner shape. SSH config should be a top-level bottle capability, not nested under a specific git remote. That gives us a clearer split: ```yaml git: remotes: gitea.dideric.is: Name: bot-bottle Upstream: ssh://git@gitea.dideric.is:30009/didericis/bot-bottle.git ssh: config: - Host: gitea Hostname: 100.78.141.42 Port: 30009 User: git IdentityFile: ~/.ssh/gitea-delos-2.pem - Host: gitea.dideric.is Hostname: 100.78.141.42 Port: 30009 User: git IdentityFile: ~/.ssh/gitea-delos-2.pem ``` Implementation implications: - `git.remotes` continues to describe repositories and git-gate rewrites. - `ssh.config` describes SSH client behavior available inside the bottle. - `IdentityFile` values are host-side paths that bot-bottle stages/mounts safely, then rewrites to the in-bottle path in generated SSH config. - These SSH config entries should not alter egress route DNS, Docker `extra_hosts`, sidecar hosts config, or HTTP/API resolution. - Git operations that use SSH host aliases, such as `git@gitea:didericis/bot-bottle.git`, should work because SSH sees the provisioned `Host gitea` stanza. This also scales beyond git: if a future workflow needs SSH config for another tool, it does not have to pretend that config belongs to one git remote.
didericis-codex changed title from feat: add gitconfig-only aliases for git remotes to feat: provision SSH config for bottles 2026-06-02 13:45:24 -04:00
didericis-codex was assigned by didericis 2026-06-02 13:50:30 -04:00
Owner

Closed, see here #151 (comment) and here #152

Closed, see here https://gitea.dideric.is/didericis/bot-bottle/pulls/151#issuecomment-1294 and here https://gitea.dideric.is/didericis/bot-bottle/issues/152
didericis-codex changed title from feat: provision SSH config for bottles to Dead: provision SSH config for bottles 2026-06-02 14:33:36 -04:00
Sign in to join this conversation.
3 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: didericis/bot-bottle#150