Harden the SSH access security on Debian
The Secure SHell (aka. SSH) access is the main remote administration tool for a Unix server. By default, this access security is provided by the connection encryption and the need of login and password. This how-to propose various methods to harden a SSH server security.
This how-to is tested on:
- Debian 6.0 Squeeze
- Debian 7.0 Wheezy
Prerequisite
Make sure that the Secure SHell server is present:
command apt-get install openssh-server
Parameters
Provide the name of a standard user account to create if not already available on the server. To keep things simple, you can use the same login as the one you use on your personal computer:
STD_USER="myuser"
Prevent DDOS and brute-force attacks
Fail2ban is a tool monitoring SSH connexion failures, and blocking for a short time IP addresses that triggered 5 successive failures. This behaviour allow to prevent most of deny of service attacks and password stealing by brute-force attacks.
Install the software:
command apt-get install fail2ban
Enable fail2ban rules than apply to SSH connexions:
if [ ! -e '/etc/fail2ban/jail.local' ]; then
command touch '/etc/fail2ban/jail.local'
fi
if [ -z "$(command grep "\[ssh-ddos\]" '/etc/fail2ban/jail.local')" ]; then
echo "[ssh-ddos]
enabled = true
[pam-generic]
enabled = true
" >> '/etc/fail2ban/jail.local'
fi
Reload the tool configuration:
/etc/init.d/fail2ban restart
Disable root login
The "root" account is the first target of attacks. A good practice is to disable the possibility to log into the root account from SSH. The account is still available by using "su" and "sudo" tools.
Creation of a unprivileged account
Before disabling SSH root access, make sure a unprivileged account with a complex password is available. If you don't have such a account, you will not be able to log in your server through SSH.
Install a password generator software:
command apt-get install apg
Create a complex password to use with the new account:
command apg -q -m 12 -a 0 -n 1 -M NCLS
Create a unprivileged user account:
command adduser "${STD_USER}"
Create a .ssh folder for this user:
command mkdir --parents "/home/${STD_USER}/.ssh"
command chown "${STD_USER}":"${STD_USER}" "/home/${STD_USER}/.ssh"
Disable root login via SSH
Setup the SSH server to reject connections using root user:
command sed -i -e 's/PermitRootLogin.*/PermitRootLogin no/' '/etc/ssh/sshd_config' /etc/init.d/ssh reload
Filter SSH users
In order to limit the number of user accounts available via SSH, it is possible to filter SSH users by group.
Create the group "ssh-users" containing users allowed to connect by SSH:
if [ -z "$(command grep "^ssh-users" '/etc/group')" ]; then command addgroup --system "ssh-users" fi
Setup the SSH server to reject users that are not members of the group "ssh-users":
command echo '
# Limit access to users of group ssh-users
AllowGroups ssh-users' >> /etc/ssh/sshd_config
Reload the configuration:
/etc/init.d/ssh reload
Add users allowed to connect via SSH to the "ssh-users" group:
command adduser "${STD_USER}" ssh-users
Setting up a public key authentication
On the client computer
Create the unique certificate (private and public keys) identifying your user:
if [ ! -e "${HOME}/.ssh/id_rsa" ]; then
command ssh-keygen -t rsa -f "${HOME}/.ssh/id_rsa"
fi
Important: Save and protect with great care this files:
- ${HOME}/.ssh/id_rsa
- ${HOME}/.ssh/id_rsa.pub
Copy the user public key on the server (replace the bolded value by the server host name or IP address):
command ssh-copy-id -i "${HOME}/.ssh/id_rsa.pub" user@www.ssh-server.com
Note: if ssh-copy-id is not available on your system (Mac OS X), use:
command cat "${HOME}/.ssh/id_rsa.pub" \
| command ssh user@www.ssh-server.com 'umask 077;
test -d ~/.ssh || mkdir ~/.ssh ;
cat >> ~/.ssh/authorized_keys \
&& (test -x /sbin/restorecon \
&& /sbin/restorecon ~/.ssh ~/.ssh/authorized_keys >/dev/null 2>&1 \
|| true)'
On the server
Allow public key authentication:
command sed -i -e 's/^[#\t ]*PubkeyAuthentication[\t ]*.*$/PubkeyAuthentication yes/' \
'/etc/ssh/sshd_config'
Reload the configuration:
/etc/init.d/ssh reload
Note: It is possible to totally disable password authentication, but i don't recommend it. If you loose your private key, you won't be able to gain access to server by SSH.
Change the SSH server listening port
By default, the SSH server listen on port 22. Using another port than the default one limit the number of automated attacks.
On the server
Provide the custom port number (between 2048 and 65535):
SSH_PORT="16022"
Update the SSH server configuration:
command sed -i -e "s/^[#\t ]*Port[\t ]*.*\$/Port ${SSH_PORT}/" \
'/etc/ssh/sshd_config'
Reload the configuration:
/etc/init.d/ssh reload
Update fail2ban configuration:
echo "
# Custom SSH port
[ssh]
port = ${SSH_PORT}" >> '/etc/fail2ban/jail.local'
Restart fail2ban:
/etc/init.d/fail2ban restart
On the client computer
Provide the server host name:
SSH_HOST="www.ssh-server.com"
Provide the customized port number (between 2048 and 65535) used by the SSH server (see before):
SSH_PORT="16022"
Add a configuration section for the target server in SSH client configuration file:
if [ ! -e "${HOME}/.ssh/config" ]; then
touch "${HOME}/.ssh/config"
fi
if [ -z "$(command grep "${SSH_HOST}" "${HOME}/.ssh/config")" ]; then
echo "Host ${SSH_HOST}" >> "${HOME}/.ssh/config"
fi
Add the SSH server port in SSH client configuration file:
command sed -i -e "/${SSH_HOST}/a\\
Port ${SSH_PORT}" "${HOME}/.ssh/config"
Setting up Port Knocking
Le Port Knocking consiste à bloquer les connexions à un port (ici le port SSH) à moins qu'une séquence d'ouverture de connexion sur différents ports ne soit effectuée. Cette séquence correspond à "frapper à la porte" du serveur. C'est une méthode efficace pour masquer le port SSH d'un serveur aux visiteurs malveillants.
Port Knocking goal is to block connections to a port (in this case the SSH port) unless a sequence of connections on different ports is performed. This sequence corresponds to "knock" on the server. This is an effective method to hide the a server SSH port to malicious visitors.
On the server
Detect the SSH server port number:
SSH_PORT=$(command grep '^Port' '/etc/ssh/sshd_config' \
| command sed -e 's/^Port[\t ]*//g')
Provide the list of ports triggering the opening of SSH port:
SSH_KNOCK="9345,2193,37303"
If you want, you can use a list of random ports:
SSH_KNOCK="$((((${RANDOM} + ${RANDOM}) % 63488) + 2048)),$((((${RANDOM} + ${RANDOM}) % 63488) + 2048)),$((((${RANDOM} + ${RANDOM}) % 63488) + 2048))"
echo "SSH_KNOCK='${SSH_KNOCK}'"
Install the knockd daemon:
command apt-get install knockd
Insert the opencloseSSH rule in knockd configuration. This rule open a SSH port for 10 seconds after a "knock":
command echo "
[opencloseSSH]
sequence = ${SSH_KNOCK}
seq_timeout = 5
tcpflags = syn
start_command = /sbin/iptables -I INPUT -s %IP% -p tcp --dport ${SSH_PORT} -j ACCEPT
cmd_timeout = 10
stop_command = /sbin/iptables -D INPUT -s %IP% -p tcp --dport ${SSH_PORT} -j ACCEPT
" >> '/etc/knockd.conf'
Remove the openSSH and closeSSH original rules:
command sed -i \
-e '/\[openSSH\]/,/^[\t ]*$/{d}' \
-e '/\[closeSSH\]/,/^[\t ]*$/{d}' \
'/etc/knockd.conf'
Enable the daemon:
command sed -i \
-e 's/^START_KNOCKD=.*/START_KNOCKD=1/' \
'/etc/default/knockd'
Start the daemon:
/etc/init.d/knockd start
Setup a iptables rule rejecting by default new connections to the SSH port. The knockd rules selectively allow SSH access:
command echo "#"\!"/bin/bash
# Allow SSH access from domU
command iptables -C INPUT -p tcp -m state --state NEW --dport ${SSH_PORT} -j DROP > '/dev/null' \\
|| command iptables -A INPUT -p tcp -m state --state NEW --dport ${SSH_PORT} -j DROP" \
> '/etc/network/if-up.d/iptables-ssh-reject'
command chmod +x '/etc/network/if-up.d/iptables-ssh-reject'
Load the rule:
/etc/network/if-up.d/iptables-ssh-reject
Important: To prevent problems, stay connected to your SSH session until you make sure to be able to open a new connection from the client computer.
On the client computer
Provide the server host name:
SSH_HOST="www.ssh-server.com"
Provide the knocking ports sequence needed to open the SSH port on the server:
SSH_KNOCK="9345,2193,37303"
Install knockd package (for the knock tool) and socket tool:
command apt-get install knockd socket
Add a configuration section for the target server in SSH client configuration file:
if [ ! -e "${HOME}/.ssh/config" ]; then
touch "${HOME}/.ssh/config"
fi
if [ -z "$(command grep "${SSH_HOST}" "${HOME}/.ssh/config")" ]; then
echo "Host ${SSH_HOST}" >> "${HOME}/.ssh/config"
fi
Setup the ProxyCommand option to use for knocking on the SSH server ports:
command sed -i -e "/${SSH_HOST}/a\\
TcpKeepAlive yes\\
ServerAliveInterval 150\\
ProxyCommand /bin/bash -c 'command knock %h $(echo ${SSH_KNOCK} | command tr ',' ' '); sleep 1; exec socket %h %p'" "${HOME}/.ssh/config"
Note: For Microsoft Windows clients, use KnockKnock.
References
These books can help you:
Thanks
- Thanks to ID's blog for Knock Knock.