Forced Command in SSH: Running Only One Command Without Shell Access
Greetings!

In this note, we will look at the “Forced command” mechanism in SSH, which allows SSH keys to be limited to executing a single command.

Forced command is a mechanism that allows an SSH key to be bound to executing strictly one command specified in authorized_keys. The user does not receive an interactive shell, cannot execute other commands, and works only within the specified scenario.

This mechanism is used for:

In a recent article about how to set up your own static website with Hugo, I used this SSH technique to initiate running a deploy script in Bash.

Since the topic is interesting, I decided to make a separate note with examples👨‍💻.

Preparation on the Client - desktop.lan

First, let’s define the input data:

KeyValue
Servertest.r4ven.me
Clientdesktop.lan
Userivan

Generate a new SSH key on the client machine:

BASH
ssh-keygen -q -N "" -t ed25519 -f ~/.ssh/id_ed25519_test
Click to expand and view more

In this case, the keys will be saved in the ~/.ssh directory under the names id_ed25519_test (private) and id_ed25519_test.pub (public).

Print the public part of our key, the one with the .pub suffix, to the terminal:

BASH
cat ~/.ssh/id_ed25519_test.pub
Click to expand and view more

You will get a line like:

id_ed25519_test.pub
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAXsCuHrzzbYmJXJqeui8j78pkYf+VbYuGeX8iK/W0EW ivan@r4ven-me
Click to expand and view more

This completes the client setup. Let’s move on to the server side.

Preparation on the Server - test.r4ven.me

Now create a specially configured authorized_keys for SSH on the server:

BASH
mkdir -p ~/.ssh/

chmod 700 ~/.ssh/

echo 'command="echo Pong",no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAXsCuHrzzbYmJXJqeui8j78pkYf+VbYuGeX8iK/W0EW ivan@r4ven-me' >> ~/.ssh/authorized_keys
Click to expand and view more

Where instead of ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAXsCuHrzzbYmJXJqeui8j78pkYf+VbYuGeX8iK/W0EW ivan@r4ven-me specify your public key, which you printed to the terminal on the client machine in the previous step.

The line in the authorized_keys file contains the public SSH key and a specific command. The point is that when connecting via SSH using the corresponding private key, instead of opening an interactive shell, only the specified command will be executed, in this example - echo Pong🏓.

Additional options are also specified:

The server side is configured too. Let’s move on to tests.

Testing Force Command Operation

Try connecting to the server using our new key:

BASH
ssh -i ~/.ssh/id_ed25519_test ivan@test.r4ven.me
Click to expand and view more

If you try to execute an arbitrary command, for example ls -l :

The output will still show Pong🏓.

Or forward an SSH port:

BASH
ssh -i ~/.ssh/id_ed25519_test -L 8888:localhost:9999 ivan@test.r4ven.me
Click to expand and view more

Still Pong🏓.

Practical Example: Running a Command with sudo

Now let’s configure, for example, running the /opt/script.sh script through the sudo utility:

Create a sudoers file:

BASH
sudo visudo -f /etc/sudoers.d/90_script
Click to expand and view more

Fill it with:

PLAINTEXT
ivan ALL=(root) NOPASSWD: /opt/script.sh
Click to expand and view more

Now create a test script.sh on the server:

BASH
echo -e '#!/usr/bin/bash\necho Sudo-Script-Pong' | sudo tee /opt/script.sh

sudo chmod 700 /opt/script.sh
Click to expand and view more

Edit the ~/.ssh/authorized_keys file and change the command to sudo /opt/script.sh:

BASH
from="10.10.10.5",command="sudo /opt/script.sh",no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAXsCuHrzzbYmJXJqeui8j78pkYf+VbYuGeX8iK/W0EW ivan@r4ven-me
Click to expand and view more

Let’s test it from the client where we generated the key: id_ed25519_test.

Try connecting to the server:

BASH
ssh -i ~/.ssh/id_ed25519_test ivan@test.r4ven.me
Click to expand and view more

In response, you should get Sudo-Script-Pong from our script.sh:

If so, we configured everything correctly. Fill script.sh with the logic you need and run it safely.

Allow Only File Transfer via sftp

In a similar way, you can allow access only to the sftp file transfer subsystem. In this case, the line in authorized_keys will look like this:

BASH
command="internal-sftp",no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAXsCuHrzzbYmJXJqeui8j78pkYf+VbYuGeX8iK/W0EW ivan@r4ven-me
Click to expand and view more

And connect like this:

BASH
sftp -i ~/.ssh/id_ed25519_test ivan@test.r4ven.me
Click to expand and view more

Instead of a Conclusion

It is important to understand that the forced command mechanism in SSH significantly reduces the risk of unauthorized key use, but it is not absolute protection: if the restricted command or script contains vulnerabilities (for example, allows arbitrary arguments, file substitution, or privilege escalation), an attacker can still bypass the restrictions.

Therefore, approach the security configuration of your systems wisely. Good luck!

Copyright Notice

Author: Иван Чёрный

Link: https://r4ven.me/en/networking/forced-command-v-ssh-zapusk-tolko-odnoy-komandy-bez-dostupa-k-obolochke/

License: CC BY-NC-SA 4.0

Использование материалов блога разрешается при условии: указания авторства/источника, некоммерческого использования и сохранения лицензии.

Start searching

Enter keywords to search articles

↑↓
ESC
⌘K Shortcut