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.
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-dispatcherCreate ./ssh_restricted_dispatcher/commands.py (next to commands_example.py, which you can use for examples).
Test with:
ssh-restricted-dispatcher --helpOn 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 --helpIf that works, modify dispatch_shim.sh from this repo, mainly configuring your host username, and then tell your agent about it!
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 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 🎉" --htmlFor a more detailed write-up of the problem, see How to stop SSH mangling spaces in command-line args.