SSH restrictions using public keys, commands and allowed hosts

Description

You want to manage an ssh server that can be reached by specific IPs and only specified commands will be accessible to the ssh user.

Explanation

In our example we have 2 hosts located in the same network (to prove the concept) with followings IP's: 192.168.122.1 (backend) and 192.168.122.157 (frontend) and we'll export squid logs from the backend to the frontend using rsync.  Squid is running as proxy.proxy user.

In our case the only allowed command is rsync, all the others are rejected. According with your needs you can modify the script and allow your own specific command/commands.

Steps to achieve our goal include: creating of the user on the host,  creating specific ssh keys (public/private key pair), defining specific commands that can be run.

On frontend
A1) Create restricted user

useradd -d /home/restricted_ssh_user -m -s /bin/bash restricted_ssh_user

A2) Create a strong password for the user - usually I'm using a (pseudo) random one generated with OpenSSL

#openssl rand -base64 9

A3) Change the password for the user (use the generated one)

#passwd restricted_ssh_user

A4) Create ssh keys for the restricted_ssh_user user

#su - restricted_ssh_user
$ssh-keygen -t rsa -b 2048 (just press enter for default)

The content of the /home/restricted_ssh_user/.ssh/id_rsa.pub file  (which is the public key of the user) will be placed on each sever in /home/restricted_ssh_user/.ssh/authorized_keys for which we want to add ssh authentication using public keys.
In our case we placed it only on backend server (192.168.122.1).

On /home/restricted_ssh_user/.ssh create rsync script (called Rsync_SSH.sh) (The presence of the script can be enforced by any automation system like puppet, chef, or ansible).

#!/bin/bash
HOSTS=()
#HOSTS+=('backend')
HOSTS+=('192.168.122.1')
#HOSTS+=('backend-01')
#HOSTS+=('backend-02')
EXIT=0
RSYNCDIR=/home/restricted_ssh_user/LOGS

if [ ! -d ${RSYNCDIR} ]; then
            mkdir ${RSYNCDIR}
fi
for i in `seq 0 $(( ${#HOSTS[@]}-1 ))` ; do
     h=${HOSTS[$i]}
     echo rsync up $h
     mkdir -p ${RSYNCDIR}/${h}
     rsync -avz -e ssh restricted_ssh_user@${h}:}:/var/log/squid/*.log ${RSYNCDIR}/${h}
     if [ $? != 0 ]; then
         echo "RSYNC of $h failed."
         EXIT=1
     fi
 done

Make the script executable

 $chmod +x /home/restricted_ssh_user/.ssh/Rsync_SSH.sh

A5) Run the script as a cron job every hour

#touch /etc/cron.d/rsync_squid_logs
1 * * * * restricted_ssh_user /home/restricted_ssh_user/.ssh/Rsync_SSH.sh

Restart cron

# /etc/init.d/cron restart

 

On backend server (192.168.122.1)(If you have more than one, repeat the procedure for all of them.)

B1) Create restricted user user without password

 #useradd -d /home/restricted_ssh_user -m -s /bin/bash restricted_ssh_user

B2) Add restricted_ssh_user to proxy group (You need read access to the squid logs. Squid runs as proxy.proxy in our scenario).

 #usermod -a -G proxy restricted_ssh_user

B3)  Create .ssh structure for the user

#su - restricted_ssh_user
$ssh-keygen -t rsa -b 2048 (hit enter for default)
$cd .ssh
$ touch authorized_keys
$ chmod 600 authorized_keys

B4) Copy the content of the /home/restricted_ssh_user/.ssh/id_rsa.pub from the
frontend server (which was generated at point A4) to authorized_keys file.

B5)  Improve security by adding commands restriction and IP restrictions

In /home/restricted_ssh_user/.ssh directory

 $ touch Verify_Rsync.sh
 $ chmod +x Verify_Rsync.sh

$ cat Verify_Rsync.sh

#!/bin/sh
 case "$SSH_ORIGINAL_COMMAND" in
 *\&*)
      echo "Rejected"
 ;;
 *\(*)
      echo "Rejected"
 ;;
 *\{*)
      echo "Rejected"
 ;;
 *\;*)
      echo "Rejected"
 ;;
 *\<*)
      echo "Rejected"
 ;;
 *\>*)
      echo "Rejected"
 ;;
 *\`*)
      echo "Rejected"
 ;;
 *\|*)
      echo "Rejected"
 ;;
 rsync\ --server*)
      $SSH_ORIGINAL_COMMAND
 ;;
 *)
      echo "Rejected"
 ;;
 esac

The presence of the script can be enforced by any automation system like puppet, chef, or ansible.

B6) Now the content of the /home/restricted_ssh_user/.ssh/authorized_keys content should look similar:

 from="192.168.122.157",command="/home/restricted_ssh_user/.ssh/Verify_Rsync.sh" ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEArVSkVFLsW2secu14TX8tOEnC2B0Y6/q0Ws18r2xPPnw79QKjYCBPZ+RnW4tcHY0kMEahn4X1AcYgbTrQaO0UqkPZkFy5mKRS9R+OWJDUxYGQnGHegWqrM7oWYBR3Bvj39z1U4sUvxrBxluYjUM6FnkYL1wsdsd9998K3axfAxg0mSTq2u0lGCd71MTyvlf677777cfvebtthrhrr+3ApDDDDFvR+pN7MJFzcbrRds5l8PuSDDFUYLAEqqlDDDDvPPPPPPfbwcsassasBjYTmasasaUOt4FW3aPmiWA2WCasasmIn8rMdQ2UdQpivJ+ymAd7NVTtwx/ewSzLRScHxX9WQKIw== restricted_ssh_user@frontend

Note: The key is not real. Use your own.

The authorized keys file now says that restricted_ssh_user is allowed to ssh only from 192.168.122.157 IP address and allowed command is specified by the script Verify_Rsync.sh (which checks for rsync).

Testing - being on frontend
From frontend try to ssh or scp to backend using restricted user

 ssh [email protected]
 Rejected
 Connection to 192.168.122.1 closed.
 scp 192.168.122.1:/var/log/squid/*.log .
 Rejected

Now try to test our implementation

whoami
restricted_ssh_user

restricted_ssh_user@U_NEW_16:~/.ssh$ /home/restricted_ssh_user/.ssh/Rsync_SSH.sh
rsync up 192.168.122.1
receiving incremental file list

sent 20 bytes received 85 bytes 210.00 bytes/sec
 total size is 8,246 speedup is 78.53
 restricted_ssh_user@U_NEW_16:~/.ssh$

As we can see it's a success. If you are here it a REAL SUCCESS.

Author: techwritter