About me: My name is Solène Rapenne, pronouns she/her. I like learning and sharing knowledge. Hobbies: '(BSD OpenBSD Qubes OS Lisp cmdline gaming security QubesOS internet-stuff). I love percent and lambda characters. OpenBSD developer solene@. No AI is involved in this blog.

Contact me: solene at dataswamp dot org or @solene@bsd.network (mastodon).

You can sponsor my work financially if you want to help me writing this blog and contributing to Free Software as my daily job.

Safely restrict commands through SSH

Written by Solène, on 08 November 2018.
Tags: #ssh #security #openbsd #highlight

Comments on Fediverse/Mastodon

sshd(8) has a very nice feature that is often overlooked. That feature is the ability to allow a ssh user to run a specified command and nothing else, not even a login shell.

This is really easy to use and the magic happens in the file authorized_keys which can be used to restrict commands per public key.

For example, if you want to allow someone to run the “uptime” command on your server, you can create a user account for that person, with no password so the password login will be disabled, and add his/her ssh public key in ~/.ssh/authorized_keys of that new user, with the following content.

restrict,command="/usr/bin/uptime" ssh-rsa the_key_content_here

The user will not be able to log-in, and doing the command ssh remoteserver will return the output of uptime. There is no way to escape this.

While running uptime is not really helpful, this can be used for a much more interesting use case, like allowing remote users to use vmctl without giving a shell account. The vmctl command requires parameters, the configuration will be slightly different.

restrict,pty,command="/usr/sbin/vmctl $SSH_ORIGINAL_COMMAND" ssh-rsa the_key_content_here"

The variable SSH_ORIGINAL_COMMAND contains the value of what is passed as parameter to ssh. The pty keyword also make an appearance, that will be explained later.

If the user connects to ssh, vmctl with no parameter will be output.

$ ssh remotehost
usage:  vmctl [-v] command [arg ...]
    vmctl console id
    vmctl create "path" [-b base] [-i disk] [-s size]
    vmctl load "path"
    vmctl log [verbose|brief]
    vmctl reload
    vmctl reset [all|vms|switches]
    vmctl show [id]
    vmctl start "name" [-Lc] [-b image] [-r image] [-m size]
            [-n switch] [-i count] [-d disk]* [-t name]
    vmctl status [id]
    vmctl stop [id|-a] [-fw]
    vmctl pause id
    vmctl unpause id
    vmctl send id
    vmctl receive id

If you pass parameters to ssh, it will be passed to vmctl.

$ ssh remotehost show
   ID   PID VCPUS  MAXMEM  CURMEM     TTY        OWNER NAME
1     -     1    1.0G       -       -       solene test
$ ssh remotehost start test
vmctl: started vm 1 successfully, tty /dev/ttyp9
$ ssh -t remotehost console test
(I)nstall, (U)pgrade, (A)utoinstall or (S)hell?

The ssh connections become a call to vmctl and ssh parameters become vmctl parameters.

Note that in the last example, I use “ssh -t”, this is so to force allocation of a pseudo tty device. This is required for vmctl console to get a fully working console. The keyword restrict does not allow pty allocation, that is why we have to add pty after restrict, to allow it.