Restricting SSH keys to specific commands limits what automated jobs and external systems can do on a server, even if a key is stolen or misused. Per-key command restrictions in the authorized_keys file provide fine-grained control without needing separate Unix accounts for each integration.
The OpenSSH server reads /home//user/.ssh/authorized_keys (or the system-wide equivalent) and interprets each line as a public key plus optional restrictions. Options such as command=, no-port-forwarding, no-agent-forwarding, no-X11-forwarding, and no-pty are evaluated before the key is accepted and can enforce a single forced command whenever that key authenticates.
Per-key restrictions apply only to new SSH sessions and do not affect existing connections. Forced commands override whatever the SSH client requests, so a restricted key always runs the configured command and never receives an interactive shell. Careful sequencing of options and correct file permissions on /.ssh/authorized_keys and any called scripts is essential to avoid lockouts and unintended command execution.
$ whoami backupcmd
$ cp ~/.ssh/authorized_keys ~/.ssh/authorized_keys.bak-$(date +%F)
Accidentally overwriting /.ssh/authorized_keys without a backup can remove all keys for the account and block key-based login until restored.
$ nl -ba ~/.ssh/authorized_keys
1 command="/usr/local/bin/backup-run.sh",no-port-forwarding,no-agent-forwarding,no-X11-forwarding,no-pty ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPEPnTarmhV1ZKnuPQ3Bi9/k7+Fs0aJaGZFsAbmZ/P3B user@host
2 ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMB5gIrE+VlSXny5KlaOW4NT8KbII3Kpc5uIheOiLU/t user@host
nl with -ba prints line numbers even for blank lines, which helps when editing specific entries.
$ nano ~/.ssh/authorized_keys
# Key restricted to a backup script and no interactive use command="/usr/local/bin/backup-run.sh",no-port-forwarding,no-agent-forwarding,no-X11-forwarding,no-pty ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGenericKeyMaterialOne backup@client1 # Unrestricted key for interactive admin (unchanged) ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGenericKeyMaterialTwo deploy@ci
All authorized_keys options, including command= and no-port-forwarding, must appear before the key type (for example, ssh-ed25519) and be separated by commas with no spaces.
$ ls -l /usr/local/bin/backup-run.sh -rwxr-xr-x 1 root root 125 Jan 11 06:53 /usr/local/bin/backup-run.sh
If the forced command binary or script does not exist or is not executable, logins using the restricted key will fail with a generic error and the backup or automation task will not run.
$ chmod 700 ~/.ssh $ chmod 600 ~/.ssh/authorized_keys $ ls -ld ~/.ssh ~/.ssh/authorized_keys drwx------ 2 backupcmd backupcmd 4096 Jan 11 06:53 /home/backupcmd/.ssh -rw------- 1 backupcmd backupcmd 285 Jan 11 06:53 /home/backupcmd/.ssh/authorized_keys
OpenSSH refuses to use /.ssh/authorized_keys if it is writable by group or others, which can silently disable key-based access.
$ ssh -i ~/.ssh/backupcmd-key.ed25519 backupcmd@host.example.net Running scheduled backup via backup-run.sh... Backup completed successfully.
$ ssh -i ~/.ssh/backupcmd-key.ed25519 backupcmd@host.example.net "uname -a" Running scheduled backup via backup-run.sh... Backup completed successfully.
A key with command= set cannot execute arbitrary client-supplied commands; the SSH server always invokes the forced command, which ignores the remote command string.
#!/bin/bash
set -euo pipefail
LOG_FILE="/var/log/ssh-allowed-commands.log"
case "${SSH_ORIGINAL_COMMAND:-}" in
"uptime")
exec /usr/bin/uptime
;;
"whoami")
exec /usr/bin/whoami
;;
"")
echo "Interactive shells are not permitted for this key." >&2
exit 1
;;
*)
echo "$(date -Is) denied: ${SSH_ORIGINAL_COMMAND} from ${SSH_CLIENT:-unknown}" >> "${LOG_FILE}"
echo "Requested command is not permitted for this key." >&2
exit 1
;;
esac
To use a wrapper, set the per-key option to command=“/usr/local/bin/ssh-allowed-commands.sh” so the script receives any requested command via SSH_ORIGINAL_COMMAND and can enforce a whitelist.
$ chown root:root /usr/local/bin/ssh-allowed-commands.sh $ chmod 700 /usr/local/bin/ssh-allowed-commands.sh $ ls -l /usr/local/bin/ssh-allowed-commands.sh -rwx------ 1 root root 420 Jan 11 06:53 /usr/local/bin/ssh-allowed-commands.sh
World-writable forced-command scripts allow any local user to change the behavior of restricted keys, defeating the purpose of per-key command locking.
$ ssh -i ~/.ssh/backupcmd-key.ed25519 -tt backupcmd@host.example.net