Trust your SSH server
This article you're reading is hosted on my own server. This last one runs a lot of services : web, mail, database, XMPP... and to manage it I need an SSH connection which is the more secure way to connect to a remote server. But, how I can trust this connection in an hostile environment ?
Connection protocols and key exchange has greatly evolved the last 20 years, but there are still based on a root asymmetric key pair (RSA, DSA, ECDSA...). When you connect to a server for the first time, you get a message like this :
The authenticity of host 'mint.phcomp.co.uk (78.32.209.33)' can't be established.
RSA key fingerprint is 6a:de:e0:af:56:f8:0c:04:11:5b:ef:4d:49:ad:09:23.
Are you sure you want to continue connecting (yes/no)? no
This is a human readable fingerprint of the root key used to establish a connection. Personally, I don't know my server fingerprint by heart. There is some solutions to check it :
- Manually by printing it on a paper/on your phone/on a USB key
- Register it with a DNS record, but DNS server/response can be easily spoofed
- Using a public key based connection (you need to keep it on a USB key)
The better remains having the secret (key or fingerprint) somewhere you could access it. I propose in this article an other solution you can always run in an hostile environment without any previous setup.
The idea is to create a restricted user that can only run a verification script that will check fingerprint once connection is established which avoid Man In The Middle attacks !
Setup
First, we'll have to create this user named check-user :
su
useradd --create-home --no-user-group --shell /bin/rbash check-user
cd /home/check-user
You can set a password or not. I don't do it, so I cannot open a connection from external nor internal as my server always checks for password (we can still use su/sudo command). I also set a restricted shell (rbash).
Then, we have to create a key pair
su check-user
ssh-keygen
cp .ssh/id_rsa.pub .ssh/authorized_keys
You can set or not a password for this key. Then, edit .ssh/authorized_keys and add :
command="rbash check_ssh_server.sh" ssh-rsa AAAA...
Now, downloads check_ssh_server.sh in /home/check-user and set execution permissions.
Then, go to your webserver directory were you can put some downloadable files (something like /var/www) and copy SSH the private key.
cd /var/www
cp /home/check-user/.ssh/id_rsa ssh_check
chmod a+r ssh_check
Now, you can edit and run check_ssh_client.sh from any network !
How does it works ?
The client starts by establishing an SSH connection and close it immediately in order to retrieve remote fingerprint. Then, it downloads check-user SSH private key and use it to connect to the server and send the fingerprint. The only command that can be run with this key is rbash check_ssh_server.sh which get the fingerprint and compare with the ones installed on the server side. A message is then displayed which indicates if the connection is secure or not.
Scripts
check_ssh_server.sh
#!/bin/bash
target_key=`echo $SSH_ORIGINAL_COMMAND| tr -d "\r\n"`
if [ -z "${target_key}" ] ; then
echo "Empty key provided, abort"
exit 0
fi
for keyfile in /etc/ssh/ssh_host_*_key.pub ; do
a=`ssh-keygen -l -f ${keyfile}|grep "${target_key}"` # To avoid print
if [ $? -eq 0 ] ; then
echo "Target key found, your connection is secure !"
exit 0
fi
done
echo "!!! WARNING !!! Key not found, the connection may not be secure"
exit 1
check_ssh_client.sh
#!/bin/bash
KEY_TRACE="Server host key:"
SSH_CHECK_KEY="https://soutade.fr/files/ssh_check/ssh_check"
REMOTE_USER="check-user"
if [ -z "$1" ] ; then
echo "usage : $@ <ssh server>"
exit 0
fi
echo "Retrieve remote key for $1"
tmp_file=`mktemp`
ssh -v -o "NumberOfPasswordPrompts=0" $@ >${tmp_file} 2>&1
key=`cat ${tmp_file}|grep "${KEY_TRACE}"`
key=`echo ${key}|cut -d" " -f6`
rm -f ${tmp_file}
echo "Retrieve SSH private key from ${SSH_CHECK_KEY}"
wget -O ssh_check ${SSH_CHECK_KEY}
chmod 0400 ssh_check
echo "Check for key ${key}"
ssh -l ${REMOTE_USER} -i ssh_check $@ "${key}"
echo "Cleaning"
rm -f ssh_check