OpenSSH provides a way to limit what a user is able to execute on a remote host. This feature can be used to create users with limited rights for demos, monitoring tools, system administration tasks…
This post shows how to limit a hypothetical user account called foo
on a host named bar
, so that it can only execute the command nuke
.
1 User account configuration
The command restriction functionality is part of the public key authentication method of OpenSSH. To enable it, a /home/foo/.ssh/authorized_keys
file must be created on bar
. It contains a list of public keys allowed to login into that user account:
OPTIONS KEY_TYPE KEY COMMENT ... OPTIONS KEY_TYPE KEY COMMENT
We first need to create a key pair that will be used to login into the limited account:
$ ssh-keygen
This will ask for a filename and a passphrase, and will create two files, FILENAME
and FILENAME.pub
. You need to copy those to every machine you want to login from.
Then, we have to configure the authorized_keys
file on the remote host. KEY_TYPE
, KEY
and COMMENT
are the contents of FILENAME.pub
, verbatim. For the options field, you probably want:
no-port-forwarding,no-x11-forwarding,no-agent-forwarding
If the command you want to allow does not require a terminal, you may also add no-pty
to the OPTIONS
string.
From now on, you can login into [email protected]
with this command, using the passphrase you provided and irrespective of foo
‘s password on bar
:
$ ssh -i FILENAME [email protected]
2 Command without arguments
In the simplest case, the user is only allowed to execute a single command, without any arguments. This can be achieved with a command
option that points to the executable.
In our example, the contents of the /home/foo/.ssh/authorized_keys
file in bar
should be:
command="./nuke",no-port-forwarding,no-x11-forwarding,no-agent-forwarding KEY_TYPE KEY COMMENT
If a relative path is used (like in the example), it refers to the (remote) home directory of the user.
3 Command with fixed arguments
The argument to the command
option is interpreted by whatever shell is configured for the user. It can thus contain arguments to the executable, shell variables, use PATH
expansion, etc. However, it may be tricky to get quoting properly with arguments containing spaces or other special characters.
In this example, the nuke
command gets the shell configured for the remote user as argument:
command="./nuke $SHELL",no-port-forwarding,no-x11-forwarding,no-agent-forwarding KEY_TYPE KEY COMMENT
4 Command with arbitrary arguments
If the user should be able to specify arbitrary arguments for the allowed command, one way to do it is using the $SSH_ORIGINAL_COMMAND
trick.
The following authorized_keys
file passes the final arguments of the ssh
call to nuke
:
command="./nuke $SSH_ORIGINAL_COMMAND",no-port-forwarding,no-x11-forwarding,no-agent-forwarding KEY_TYPE KEY COMMENT
To use this functionality, the user invokes ssh
with the desired arguments for the restricted command at the end:
$ ssh [email protected] arg1 arg2 ...
However, the way the restricted command uses its arguments should be audited, since this technique could pose security risks.
Excelent, the SSH_ORIGINAL_COMMAND trick is what I need.
Thank you very much.
Nice blog post! Just a question on the security risks of $SSH_ORIGINAL_COMMAND:
Do you know of any way to restrict $SSH_ORIGINAL_COMMAND to a certain set of characters, or even sandbox the execution? For example, I would just to a “cd ~/$SSH_ORIGINAL_COMMAND && git pull”, but this construct is vulnerable to injection, whether you quote it or not. :-(
Peter, those values are environment variables and are not interpolated by the SSH daemon before running the command, so you _can_ safely use them simply by quoting them e.g. you might make it so that the command looked something like this:
[[ “$SSH_ORIGINAL_COMMAND” =~ ^(a_safe_folder|another_safe_directory)$ ]] && cd “$SSH_ORIGINAL_COMMAND” && git pull
However, since those are just environment variables, my personal preference is to not try and shove a long script into the command parameter and instead do something simpler like command=”/path/to/script” then put all of the logic into the language of your choice be it Bash, Python, Perl, etc. into that one file. This also enables you to lock down the account that’s being SSH’d into by changing that user’s shell to something like /bin/sh to reduce the likelihood of being impacted by something like the “shellshock” bug. There are also various restricted shells out although some are not without their own vulnerabilities and issues.
Thanks Eric!
I’ve found another interesting article about using the SSH authorized_keys file’s command feature. What’s particularly interesting, it shows how to experiment with the command using e.g. the `env` command to understand the feature better.
– https://sixohthree.com/1458/locking-down-rsync-using-ssh