Skip to content

cpbotha/ssh-restricted-dispatcher

Repository files navigation

ssh-restricted-dispatcher

This solves the problem of giving your containerised agent whitelisted access to host-side functionality.

For each command you wish to expose, write a Python function in commands.py. Setup ssh authorized_keys on the host with the restrict,command=ssh-restricted-dispatcher ssh-ed25519 AAA .... after uv tool install -e . in this folder.

In the container, dispatch_shim.sh calls ssh host.docker.internal anything-here "$@", but if you have setup the correct keys, it will call ssh-restricted-dispatcher which will enable the agent to auto-discover the commands and documentation that you've defined.

Step-by-step

Clone this repo, and dev-install the tool:

git clone https://github.com/cpbotha/ssh-restricted-dispatcher.git
cd ssh-restricted-dispatcher
uv tool install -e .
which ssh-restricted-dispatcher

Create ./ssh_restricted_dispatcher/commands.py (next to commands_example.py, which you can use for examples).

Test with:

ssh-restricted-dispatcher --help

On this host, add something like the following to your ~/.ssh/authorized_keys:

# 1. check that the command path is the same on your machine
# 2. replace the pubkey with your container's
restrict,command="$HOME/.local/bin/ssh-restricted-dispatcher" ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIHDLMzkJBMVSrti6TsR/DAt0A/ehZT27qtf73TA5aSbq agent@container

You can now test from the container with:

ssh hostuser@host.docker.internal dispatcher --help

If that works, modify dispatch_shim.sh from this repo, mainly configuring your host username, and then tell your agent about it!

Misc

Setup host.docker.internal on Linux

Either invoke docker with --add-host:

docker run --add-host host.docker.internal:host-gateway -p 8080:8080 service:latest

... or add extra_hosts to your docker-compose.yaml:

services:
  your_service:
    image: service:latest
    extra_hosts:
      - "host.docker.internal:host-gateway"
    ports:
      - "8080:8080"

See this page.

SSH argument quoting

SSH does not preserve argument boundaries when executing a remote command. When you run:

ssh host dispatcher sendmail user@example.com "subject with spaces"

SSH concatenates all arguments into a single string, sends it to the remote host, where the login shell re-tokenises it, effectively stripping your carefully placed quotes and splitting "subject with spaces" into three separate arguments.

This is a fundamental limitation of the SSH protocol (it transmits a single string, not an argument array) and is not going to be fixed upstream.

dispatch_shim.sh works around this by pre-escaping each argument with printf '%q' before passing them to SSH. The remote dispatcher's shlex.split() understands bash-style escaping, so argument boundaries are correctly reconstructed. This is transparent to the caller:

# Just works, even with spaces and special characters
cat email.html | ./dispatch_shim.sh sendmail user@example.com "My Subject 🎉" --html

For a more detailed write-up of the problem, see How to stop SSH mangling spaces in command-line args.

About

SSH-restricted dispatcher for containerised coding agents

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors