How to restrict SSH commands per key in authorized_keys

A stolen automation key is less damaging when the server accepts it only for the job it was meant to run. The authorized_keys command= option lets OpenSSH bind one public key to one forced command, so the same Unix account can keep an unrestricted administrator key and a restricted backup or deployment key on separate lines.

Each key line can start with comma-separated options before the key type. The restrict option disables PTY allocation, agent forwarding, X11 forwarding, TCP forwarding, and user rc processing for that key; pairing it with command=“…” makes sshd ignore any shell or remote command requested by the client and run the server-side command instead.

The command path must already exist and be executable by the target account when the key authenticates. Test with a separate administrator session or console path available, because a bad key line or missing script can break the automation login until authorized_keys is repaired.

Steps to restrict SSH commands per authorized_keys key:

  1. Open a terminal on the SSH server as the account whose key will be restricted.
  2. Keep a separate administrator session open.
  3. Back up the account's authorized_keys file.
    $ cp ~/.ssh/authorized_keys ~/.ssh/authorized_keys.pre-restrict

    A bad edit can remove key-based access for this account. Keep the backup until the forced-command login test succeeds.

  4. Create the forced command script.
    $ sudoedit /usr/local/bin/backup-report.sh
  5. Save the script body.
    #!/usr/bin/env bash
    printf 'Backup command allowed\n'
    printf 'job=nightly-backup\n'
    if [ -n "${SSH_ORIGINAL_COMMAND:-}" ]; then
      printf 'requested=%s\n' "${SSH_ORIGINAL_COMMAND}"
    fi

    The SSH_ORIGINAL_COMMAND variable contains the command requested by the client. The script can log it or reject it, but the client request does not run directly.

  6. Set root ownership on the forced command script.
    $ sudo chown root:root /usr/local/bin/backup-report.sh
  7. Set executable permissions on the forced command script.
    $ sudo chmod 0755 /usr/local/bin/backup-report.sh

    Do not leave a forced-command script writable by the restricted account. A writable script lets that account change what the restricted key can execute.

  8. Open the account's authorized_keys file.
    $ nano ~/.ssh/authorized_keys
  9. Prefix the target public key line with restrict and command= options.
    # Restricted key for the backup job
    restrict,command="/usr/local/bin/backup-report.sh" ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIExampleRestrictedKey backup@client1
     
    # Unrestricted administrator key
    ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIExampleAdminKey admin@workstation

    All authorized_keys options must appear before the key type and stay on the same physical line as the public key. Use commas between options and avoid spaces except inside quoted option values.

    Use the public-key extractor when a missing .pub file or copied key handoff needs a reviewed authorized_keys line.
    Tool: SSH Public Key Extractor

  10. Restrict the .ssh directory permissions.
    $ chmod 700 ~/.ssh
  11. Restrict the authorized_keys file permissions.
    $ chmod 600 ~/.ssh/authorized_keys
  12. Check the account key-file permissions.
    $ stat -c "%a %U %n" ~/.ssh ~/.ssh/authorized_keys
    700 backupcmd ~/.ssh
    600 backupcmd ~/.ssh/authorized_keys
  13. Test the restricted key from the client that holds the matching private key.
    $ ssh -T -i ~/.ssh/backupcmd-key backupcmd@host.example.net
    Backup command allowed
    job=nightly-backup

    -T avoids a PTY request, which is blocked by the restrict key option.

  14. Request a different command with the same key.
    $ ssh -T -i ~/.ssh/backupcmd-key backupcmd@host.example.net 'uname -a'
    Backup command allowed
    job=nightly-backup
    requested=uname -a

    The absence of uname output confirms that sshd ignored the client-requested command and ran the per-key forced command instead.