Troubleshooting
Each entry starts with what you see. Wrapper errors go to stderr as Error: <message> with exit code 1. sshd logs go to the container’s output, so docker logs <container> is the first place to look when a login fails.
Permission denied (publickey)
sshd refused the login before the wrapper ran. Check, in order:
- Username. Connect as the container’s
LOCKBOX_USER(lockboxunless the image or the run command changed it). Any other name is rejected byAllowUsers. - The key. The client’s public key must be a line in the file mounted at
/etc/lockbox/authorized_keys. Check what the container sees withdocker exec <container> cat /etc/lockbox/authorized_keys. - The mount. A file that does not exist on the host when the container starts gets mounted as an empty directory. Create
authorized_keysbeforedocker run.
Usage: ssh lockbox@host '<command> [args]'
You connected without a command, or with only whitespace. The second line lists every command you can run. This exits 1 on purpose; lockbox has no interactive session.
Error: '<name>' not allowed
The first word is neither a built-in nor a key in /etc/lockbox/allowed.json. Shell builtins and pipelines don’t exist here. cd, ls and cat are refused unless your image allows binaries with those names. Use list-files and get instead.
Error: path outside /work
A built-in’s path resolved to somewhere outside /work, through .. or a symlink. Two-path commands say source path outside /work or destination path outside /work. Use paths relative to /work. Absolute paths are fine too, but they are treated as relative to /work: /data/x means /work/data/x.
Error: no such file
get, file-hash, append-file and copy-file need an existing regular file. A directory counts as missing for them. Remember that /etc/hosts means /work/etc/hosts.
Error: parent directory does not exist
put and move-file (as destination parent directory does not exist) do not create directories. Run create-dir for the parent first.
Error: invalid command syntax: No closing quotation
The command has an unbalanced quote. The wrapper splits commands with shell quoting rules, so quote arguments that contain spaces and close every quote. Quoting happens twice when you type the command into your own shell: once for your shell and once for the wrapper.
ssh lockbox@host "put 'my file.txt'" < "my file.txt"
Invalid allowlist
Error: invalid JSON in /etc/lockbox/allowed.json: <parser message>
Error: /etc/lockbox/allowed.json must contain a JSON object
Every command fails, built-ins included, because the wrapper reads the allowlist before it does anything else. Fix the file so it is a single JSON object of "name": "/absolute/path" pairs. A missing file is fine and counts as {}.
An allowed tool can’t find its input file
Allowed binaries start in the user’s home directory, not in /work, and their arguments are passed through unchanged. Use full paths:
ssh myapp@host "ffmpeg -i /work/input.mp4 /work/output.webm"
A Python traceback instead of an error
Some cases the wrapper does not check end in a Python traceback and exit 1. The one you are most likely to hit is put onto an existing directory (IsADirectoryError). Nothing outside /work is touched; pick a file path instead.
WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED
The container got new host keys. Without a /etc/lockbox/host_keys volume, a new image build means new keys. Mount host_keys (see Configuration), then remove the old entry with ssh-keygen -R '[localhost]:2222', using your host and port.
Files in /work belong to the wrong user
The container user’s UID and GID default to 1000. Pass -e "LOCKBOX_UID=$(id -u)" -e "LOCKBOX_GID=$(id -g)" so files written to /work belong to you on the host.
The container exits right after it starts
entrypoint.sh runs with set -e, so any failing step stops the container, and a failing hook in /etc/lockbox/entrypoint.d/ is the usual cause. docker logs <container> shows the last output before the exit.