Unprivileged SSH Account
Overview
I've had a need quite a few times to create an "unprivileged" account for use with SSH,
allowing only a command or subset of commands. After researching it, I've mixed and
matched a few methods to come up with one that I like.
A few examples/use cases are given below, and its entirely notable that you're responsible
for sanity checking them and making sure they're secure in your environment. Allowing ANY
access to your servers for ANY purpose carries risks.
"Client" Machine Setup
General Info
IP: 10.0.0.2
Hostname: client.example.com
Account username: username
Generate SSH key (passphrase-less)
$ ssh-keygen -t rsa -b 2048 -N -f $HOME/.ssh/id_rsa_nopassphrase Generating public/private rsa key pair. Your identification has been saved in /home/username/.ssh/id_rsa_nopassphrase. Your public key has been saved in /home/username/.ssh/id_rsa_nopassphrase.pub. The key fingerprint is: 1b:8d:5a:4c:14:b6:c9:20:ec:ad:c5:1b:9c:a9:34:7d username@client.example.com
"Server" Machine Setup
General Info
IP: 10.0.0.1
Hostname: server.example.com
Account username: unpriv
Create Account
Let's assume this is a Linux machine, yours may be different.
$ sudo adduser -c "Unprivileged User" -d /home/unpriv -m -s /bin/bash unpriv
Setup SSH authorized_keys
It will probably be easiest to copy/paste the public key generated on the client, because
instead of the standard line in authorized_keys, we're going to do something a little
strange. The line you insert into $HOME/.ssh/authorized_keys on the server should look
like this:
from="10.0.0.2",command="/home/unpriv/bin/limited.sh" ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAtE/.....CulL6pnubGIwR6n5QfGc3Q== username@client.example.com
Make sure permissions on this file (/home/unpriv/.ssh/authorized_keys) are a+r,o+w (644),
but owned by root. This is possibly unnecessary, but a safe step: it will allow you to
safeguard against someone modifying your command= to allow whatever they want, should both
your key on the client get compromised, and you screw up the limited.sh script.
"limited.sh" Script
Now create a directory:
# mkdir /home/unpriv/bin # chown root /home/unpriv/bin # chmod 755 /home/unpriv/bin
Create another directory, for use later:
# mkdir /home/unpriv/dropfile-tmp # chown unpriv /home/unpriv/dropfile-tmp
In this directory, create a file called "limited.sh", with the following contents.
This file should also be owned by root, and unwritable (but readable/executable) by
'unpriv' user.
#!/bin/sh case "$SSH_ORIGINAL_COMMAND" in *\&*) echo "Rejected" ;; *\(*) echo "Rejected" ;; *\{*) echo "Rejected" ;; *\;*) echo "Rejected" ;; *\<*) echo "Rejected" ;; *\`*) echo "Rejected" ;; *\|*) echo "Rejected" ;; dropfile-list1) cat >$HOME/dropfile-tmp/list1.txt exit 0 ;; dropfile-list2) cat >$HOME/dropfile-tmp/list2.txt exit 0 ;; uptime) uptime exit 0 ;; *) echo "Rejected" ;; esac exit -1
Example Results
"dropfile"
The above 'limited.sh' script allows two "dropfile" commands, sort of like standard scp,
but less useful and more restricted. This means no weird dependency on scponly, and no
hassles of setting it up. You get a different set of hassles!
From the client, you can use this as so:
$ cat /path/to/list1.txt | ssh -i $HOME/.ssh/id_rsa_nopassphrase unpriv@server.example.com dropfile-list1
This will create a file (remember in limited.sh, ">list1.txt"), overwriting its contents
with whatever is in list1.txt on the client. Ideally, you can't use this dropfile command
to create any other file on the system. You also shouldn't be able to read any files.
"uptime"
The above 'limited.sh' script allows the client to run "uptime" as well. Like so:
$ ssh -i $HOME/.ssh/id_rsa_nopassphrase unpriv@server.example.com uptime 16:05:14 up 17 days, 6:52, 1 user, load average: 0.00, 0.00, 0.00
Note that the "command" specified in limited.sh is an arbitrary string, doesn't like to have
spaces or other similar characters in it, and simple executes a block of shell commands when
called.
Failure results
If you call a command that doesn't exist in limited.sh, here's what it looks like:
$ ssh -i $HOME/.ssh/id_rsa_nopassphrase unpriv@server.example.com dropfile-etc-passwd Rejected $ ssh -i $HOME/.ssh/id_rsa_nopassphrase unpriv@server.example.com id Rejected
Other Ideas
I use this mechanism as a simplified way to call custom nagios plugins on remote hosts, without
something like NRPE, etc. It's a bit more manual in the way it is setup, but it is also more
explicit, which I like.