Hacking Linux Exposed





previous article
next article
The Authprogs SSH Command Authenticator (Passwordless SSH part 4)
By Bri Hatch.

Summary: Introducing Authprogs, software which lets you control which machines can run authorized commands via SSH using SSH Identities.

In the previous three[1] articles, I've shown you how to manually set up identity-based authentication with SSH, and how to use it to force a specific command, regardless the actual command that the client attempts to run. Unfortunately, this procedure requires that you have one identity for each program you want to allow, which can be a very big hassle.

In this article, I introduce you to authprogs, which can be used to control what commands can be run on a host-by-host basis. I've been using this program for ages now, and finally took the time to put in comments and make it readable. I'll be maintaining the code at http://www.hackinglinuxexposed.com/tools/authprogs. There are several things left to be done[2] and I encourage folks to develop it further. Right now I'd call it version 0.5.

Authprogs is a very simple perl script.[3] It looks for a file named ~/.ssh/authprogs.conf to get its configuration. This file simply lists the programs that are allowed to be run from specified hosts. You begin with an IP address or list of IP addresses in brackets. On the following lines you put the commands that are allowed from this host or hosts. Here's an example configuration:

  # The uptime command is allowed from any host
  [ ALL ]
  # Localhost can list /tmp (now that's useless)
  [ ]
          /bin/ls /tmp/
  # allow multiple machines by listing them together
  [ ]
          rsync --server --sender -logtpr . /var/www/

  [ ]
          # Need to imbed spaces?  Use quotes.
          ls -l "/path/to/some graphic.png"

  [ ]
	  cd /webroot; rm *.html; wmk

In this case, any machine (ALL is a wildcard for any machine) can run uptime. The machine (localhost) can run /bin/ls /tmp[4]

In the third section, we list an rsync command that can be run by two machines. You could have created a separate section for with this rsync command, and copied the command in the previous entry for, but this is a bit cleaner, depending on your needs. The entire file is read until a match is found, so you can have multiple sections for a given host.

As you see in the fourth section, if you need to allow spaces in your command arguments, just use double or single quotes normally. You can also put comments on their own line anywhere in this file, as long as you have them on their own line.

Lastly, it's completely fine to have multiple commands on one line, or even shell meta-characters. For example if the user from wanted to run the authorized command above, they'd run the following:$ ssh user@server -i /path/to/identity 'cd /webroot; rm *.html; wmk'

The quotes here are needed to make sure that the local shell doesn't expand *.html or execute the rm and wmk locally. All the commands to be run on the server must be passed through unaltered.

So, how do you install the authprogs program? You can put it in your ~/.ssh/ directory, or install it system wide in /usr/local/bin, or anywhere else you want. Just make sure to chmod a+rx it when you're done. Then set up your authorized_keys to run authprogs using the command= option in your authorized_keys file, create a ~/.ssh/authprogs.conf file, and you're all set.

Ok, so that's a long list. We'll walk through it.

Let's craft an example similar to the one from last week. The host beepbeep.example.net is our backup server. It wants to run the following program to back up the /etc/ directory on host futzy.example.net:

  backups@beepbeep$ cat backup_futzy

    # Go into our backups directory for futzy
    cd /backups/futzy/etc
    # Tell rsync to use ssh with a specific key.  The key we
    # created for this purpose is stored in /home/backups/keys/futzy
    RSYNC_RSH="ssh -i /home/backups/keys/futzy"
    export RSYNC_RSH
    # Sync the etc directory to a local backup.
    rsync -logptr root@futzy.example.net:/etc/ .

Let's set up futzy to allow the rsync command to succeed. First, install authprogs:

  root@futzy# cp authprogs /usr/local/bin/authprogs
  root@futzy# chmod a+rx /usr/local/bin/authprogs

Now install the public key part of /home/backups/keys/futzy on futzy.

  backups@beepbeep$ scp /home/backups/keys/futzy.pub root@futzy:/root
  root's password: <enter password>

  root@futzy# cat /root/futzy.pub
  ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAIEA3nCnBRQR2x4Ak7I3gS62ASXGiC+5o
  kc5iAyVnHAf08Lqk= backups@beepbeep.example.net

  root@futzy# vi /root/.ssh/authorized_keys
  # Now read in futzy.pub and make changes until it looks like
  # the listing below.

  root@futzy# cat /root/.ssh/authorized_keys
  ommand="/usr/local/bin/authprogs" ssh-rsa AAAAB3NzaC1yc2EAAAABIwA
  lXFYh49a18i++SScHnycmiL8AmEb06Obrhkc5iAyVnHAf08Lqk= backups@beepb

We added several "no-" arguments to turn off SSH features this key doesn't need access to, and then the crucial "command=" argument which will force our authprogs command to run. You may notice that I left off the "from=" option which could have been used to limit this key to work only from specified hosts. You can either use it or not as you choose. The authprogs program will drop unauthorized commands or unauthorized hosts, so it's not strictly needed.

Now we haven't set up our authprogs.conf file yet, so the rsync command should fail. Let's run it for grins:

  # Sync the etc directory to a local backup.
  backups@beepbeep$ rsync -logptr root@futzy.example.net:/etc/ .
  You're not allowed to run 'rsync --server --sender -logtpr . /etc/'
      from 53251 22
  protocol version mismatch - is your shell clean?
  (see the rsync man page for an explanation)
  rsync error: protocol incompatibility (code 2) at compat.c(58)

The authprogs program actually shoots an error to STDERR (the "You're not allowed..." line above) to let the client know the command is rejected. You can use this error to determine what command was sent, and add this command to the authprogs.conf file.

However, the authprogs program also keeps a log of the commands that are attempted. Looking at the log, we can see which commands were run, and which were denied:

  root@futzy# tail -1 /root/.ssh/authprogs.log
  2003/01/14 17:02:43 authprogs[25958]: Denying request
   'rsync --server --sender -logtpr . /etc/' from 53251 22

This log entry indicates that the machine (beepbeep's IP address) tried to run the command "rsync --server --sender -logtpr . /etc/". The two numbers 53251 and 22 at the end of the line indicate the source and destination port of this SSH connection, and aren't terribly interesting.

So, armed with the knowledge of the command rsync is attempting to run on the server, let's set up our authprogs.conf file:

  root@futzy# vi /root/.ssh/authprogs.conf
  # make changes

  root@futzy# cat /root/.ssh/authprogs.conf

      # Allow beepbeep to rsync the /etc/ directory:
      [ ]
	    rsync --server --sender -logtpr . /etc/

Excellent! Let's run our command from beepbeep and test it out:

  backups@beepbeep$ du -s .
  1      .

  # Sync the etc directory to a local backup.
  backups@beepbeep$ rsync -logptr root@futzy.example.net:/etc/ .
  (rsync runs...)

  backups@beepbeep$ du -s .
  9928   .

Voila! It works!

Now the beauty of authprogs is that you can use this key for many different commands, without ever allowing it to have unrestricted access to the system.[5] It also means you have one file to maintain which lists the hosts that can run specific commands without a password.

You can use the same private key on multiple machines, because the authprogs.conf file still dictates which commands a machine can run. However I still prefer creating unique keys on each client that needs passwordless access, just to be safe. Reusing keys doesn't buy you much.

In summary, the authprogs program is a very simply way to create passwordless access to accounts with fine granularity. Unlike custom shells, it is usable on any account. (You wouldn't want to change root's shell to something that only allowed you to run "hostname" for example.) There are some obvious areas it could be improved, but it works as advertized.


[1] Starting with this article

[2] The TODO is available here

[3] I apologize to the purists who think everything should be written in C. Call me a Perl lover, a lazy hack, or whatever you like.

[4] An example of a completely useless example if I ever wrote one.

[5] Of course you should really avoid having any shell meta-characters or backticks available in your allowed commands, lest an attacker manage to trick them into running commands you didn't think of. Explicit commands are not prone to this sort of trickery, and are your best bet to keeping things secure.

Bri Hatch is Chief Hacker at Onsight, Inc and author of Hacking Linux Exposed and Building Linux VPNs. He's been using SSH to secure his remote logins since Tatu posted the first version of the code - even if the administrators of those machines refused to install it for him. Bri can be reached at bri@hackinglinuxexposed.com.

Copyright Bri Hatch, 2003

This is the January 15, 2003 issue of the Linux Security: Tips, Tricks, and Hackery newsletter. If you wish to subscribe, visit http://lists.onsight.com/ or send email to Linux_Security-request@lists.onsight.com.