How to Secure External facing SSH Servers from attacks

Write tutorials for Linux Mint here
More tutorials on https://github.com/orgs/linuxmint/discu ... /tutorials and (archive) on https://community.linuxmint.com/tutorial
Forum rules
Don't add support questions to tutorials; start your own topic in the appropriate sub-forum instead. Before you post read forum rules
Post Reply
miket

How to Secure External facing SSH Servers from attacks

Post by miket »

This article is the result of questions I have been asked on the LinuxMint IRC Channel, questions
I've received via email and questions via this forum.

It is by no means a complete guide to security for a Linux system, however it addresses one particular
problem, that being the ever increasing number of brute force ssh attacks now taking place on the internet today.

It would appear that the old ssh Trojan is back in circulation and that more and more Linux machines are
being infected due to users setting weak passwords for their user accounts and then having these machines externally
facing on the internet which leads to them being hacked and the Trojan being installed without them realising.

This article is by no means a criticism of Linux users in general, more an aid to help anyone that has an externally
facing Linux system protect it against such ssh attacks and to help reduce the numbers of new systems being infected.

So, how does this Trojan work ?
Well basically it has two lists, a list of usernames and a list of passwords, it then tries combinations of the two
against unsuspecting ssh serving Linux systems until it gets a match and can login, once logged in
it installs another copy of the Trojan and then the process starts again. It uses a very simple process,
one which is very easy to block with a little careful configuration.

So on to the interesting part, how do we stop these attacks from causing us problems ?

One of the simplest ways to stop these attacks is to use two widely available applications and a simple shell script to
pass data from one to the other.

The first of these applications is a program called "denyhosts". "denyhosts" monitors the /var/log/auth.log file for sequential
failed ssh login attempts and then (depending how it's config file is setup) denies the attacker access to the sshd port. This is great
as it puts an immediate stop to the attack, normally within a few seconds of the attack starting.

"denyhosts" stops the attacker accessing the ssh port (port 22 normally) by adding their IP Address into the /etc/hosts.deny file.

This is very handy as a simple shell script can read this information and then pass it to a firewall for it to be automatically added
to the ruleset to deny the attacker access to any port on the system thus increasing security even further.

I've decided to use ufw for the firewall interface as it is easy for the newbie to get to grips with.

So, lets get on with the installation and configuration!


First we need to install denyhosts, the easiest way is to do it from the command line, so open a Terminal and enter the following command:

Code: Select all

sudo apt-get install denyhosts
So that's denyhosts installed, but we now need to configure it, so lets stop it for the moment to make configuration easier,
so in the same terminal type:

Code: Select all

sudo /etc/init.d/denyhosts stop
Great, now we can edit the config file!
In the terminal enter the following command to open the editor with the denyhosts.conf file in it:

Code: Select all

sudo gedit /etc/denyhosts.conf
Edit the file to make the settings the same as below :

Code: Select all

     
# Debian:
SECURE_LOG = /var/log/auth.log

# Most operating systems:
HOSTS_DENY = /etc/hosts.deny

# never purge:
PURGE_DENY = 

# To block only sshd:
BLOCK_SERVICE  = sshd

DENY_THRESHOLD_INVALID = 1
DENY_THRESHOLD_VALID = 3
DENY_THRESHOLD_ROOT = 1
DENY_THRESHOLD_RESTRICTED = 1
WORK_DIR = /var/lib/denyhosts
SUSPICIOUS_LOGIN_REPORT_ALLOWED_HOSTS=YES
HOSTNAME_LOOKUP=YES

# Debian
LOCK_FILE = /var/run/denyhosts.pid

# Disable Email notification.
ADMIN_EMAIL = 


SMTP_HOST = localhost
SMTP_PORT = 25
SMTP_FROM = DenyHosts <nobody@localhost>
SMTP_SUBJECT = DenyHosts Report

SYSLOG_REPORT=YES
AGE_RESET_VALID=
AGE_RESET_ROOT=
AGE_RESET_RESTRICTED=
AGE_RESET_INVALID=

DAEMON_LOG = /var/log/denyhosts
DAEMON_SLEEP = 5s
DAEMON_PURGE =

Any setting not listed above should be left as default in the denyhosts.conf file.
Once you made the necessary changes, save the file and exit the editor.

Now we need to restart the denyhosts daemon, so now enter the following command:

Code: Select all

sudo /etc/init.d/denyhosts start
I'm assuming you have already installed the ssh server, however if you haven't you'll need to enter the following command to install it:

Code: Select all

sudo apt-get install openssh-server
Now we need to stop the sshd daemon so that we can edit the config file, enter the following command in the terminal:

Code: Select all

sudo /etc/init.d/ssh stop
Now we can edit the sshd_config file.
Note: I have decided not to cover key authentication in this article, as I'm keeping to the KISS principle, but I will cover it in a later article,
so this example allows login via ssh using username and pasword, so make sure you use a mix of UPPER and lower case characters with a splattering of
numbers in your password, don't use easy to guess words and names!!

So to edit the sshd_config file, enter the following command in the terminal:

Code: Select all

sudo gedit /etc/ssh/sshd_config
Change the entries in the sshd_config file to match the following, make sure you change the network addresses to match your network
and the username to match your Linux Login name.

Code: Select all

# Only allow IPV4 connections
AddressFamily inet

# What ports, IPs and protocols we listen for
Port 22

# Use these options to restrict which interfaces/protocols sshd will bind to
# Change this to match your network subnet!!

#ListenAddress ::
#ListenAddress 0.0.0.0
ListenAddress 192.168.100.125
ListenAddress 127.0.0.1

Protocol 2
# HostKeys for protocol version 2
HostKey /etc/ssh/ssh_host_rsa_key
HostKey /etc/ssh/ssh_host_dsa_key
#Privilege Separation is turned on for security
UsePrivilegeSeparation yes

# Lifetime and size of ephemeral version 1 server key
KeyRegenerationInterval 3600
ServerKeyBits 768

# Logging
SyslogFacility AUTH
LogLevel INFO

# Authentication:
LoginGraceTime 30
PermitRootLogin no
StrictModes yes
MaxAuthTries 2

RSAAuthentication yes
PubkeyAuthentication yes
#AuthorizedKeysFile	%h/.ssh/authorized_keys

# Don't read the user's ~/.rhosts and ~/.shosts files
IgnoreRhosts yes
# For this to work you will also need host keys in /etc/ssh_known_hosts
RhostsRSAAuthentication no
# similar for protocol version 2
HostbasedAuthentication no
# Uncomment if you don't trust ~/.ssh/known_hosts for RhostsRSAAuthentication
#IgnoreUserKnownHosts yes

# To enable empty passwords, change to yes (NOT RECOMMENDED)
PermitEmptyPasswords no

# Change to yes to enable challenge-response passwords (beware issues with
# some PAM modules and threads)
ChallengeResponseAuthentication no

# Change to no to disable tunnelled clear text passwords
#PasswordAuthentication yes

# Kerberos options
#KerberosAuthentication no
#KerberosGetAFSToken no
#KerberosOrLocalPasswd yes
#KerberosTicketCleanup yes

# GSSAPI options
#GSSAPIAuthentication no
#GSSAPICleanupCredentials yes

X11Forwarding yes
X11DisplayOffset 10
PrintMotd no
PrintLastLog yes
TCPKeepAlive yes
#UseLogin no

#MaxStartups 10:30:60
#Banner /etc/issue.net

# Allow client to pass locale environment variables
AcceptEnv LANG LC_*

Subsystem sftp /usr/lib/openssh/sftp-server

UsePAM yes

# Change YOURUSERNAME to your Linux Login name!
AllowUsers YOURUSERNAME

# NEVER allow root to login via SSH !!
DenyUsers root

So once the settings are changed accordingly, save the file and then exit the editor.

Now restart the sshd daemon using the following command in the terminal:

Code: Select all

sudo /etc/init.d/ssh start
The next step in the process is the installation of the firewall management program, as I said earlier in this article,
I've decided to use ufw as it is very easy for the Linux newbie to understand.

First we need to install it!
The terminal command for this is:

Code: Select all

sudo apt-get install ufw
Next we need to enable the firewall with the following command:

Code: Select all

sudo ufw enable
and now we need to ensure that the default rule for all incoming traffic that hasn't originated from this host is DENY:

Code: Select all

sudo ufw default deny
We are now ready to put the last piece of the jigsaw in place!
This piece consists of a simple shell script that will form the link between denyhosts and the ufw firewall control program.
For those of you who are experienced shell scriptors this is a very simple script, it's simple deliberately and yes there is
scope for improvement, however I've kept it simple for a reason, it's easy to understand and reliable!

Since the script is going to be run by the root user via cron I will suggest you install it in /root ... however if you prefer
you may install it else where, but make sure you make the necessary changes to make it work!

so, lets create the script, in the terminal type:

Code: Select all

sudo gedit /root/stopfailedssh.sh
In the blank editor enter the following code in it's entirety, make sure you change the IP Address to match your network
and add/delete the services as required (ufw commands), Note These rules DO NOT affect OUTGOING network traffic, only INCOMING :

Code: Select all

#!/bin/bash

######################################################################################
#
# This script is released into the public domain under the Open Source GPL Licence.
# Originally written by MikeT, http://www.avanceit.co.uk
#
# This script comes with no guarantees or warranties, you run it at your own risk!!
#
######################################################################################

######################################################################################
# stopfailedssh.sh
#
# Simple script to collect IP Addresses of hackers trying to get in via ssh
# and add them to the firewall with DENIED access ruleset
# IP Addresses are supplied by denyhosts and are listed in the /etc/hosts.deny file
# marked by sshd in the first field.
######################################################################################

######################################################################################
# MikeT - v1.0 15/11/08
# Update - v1.5 21/11/08
# Update - v2.0 24/11/08
######################################################################################

######################################################################################
# This script is called by adding the following into the /etc/crontab file
#
#      * * * * *	root	cd /root && ./stopfailedssh.sh
#
# This will run the script every minute, which is what we want.
# Remember to remove the "#" at the beginning of the line when adding to the crontab file!!
#
# Save this script into /root/stopfailedssh.sh
# chown root:root & chmod 700
######################################################################################

######################################################################################
# Packages required by this script:
#
# sudo apt-get install ufw
# sudo apt-get install denyhosts
# sudo apt-get install openssh-server
#
######################################################################################

######################################################################################
# Declare Global Variables

INPUTFILE="/etc/hosts.deny"
LISTOFIPS=`cat $INPUTFILE | grep sshd | awk '{print $2}'`
UFWCOMMAND="/usr/sbin/ufw"
TOUCHFILE="/root/.stopfailedssh"
LASTUPDATE=`cat /root/.stopfailedssh.UPDATE`
COUNTFILE="/root/.stopfailedssh.UPDATE"

# Change the netork address belwo to match your network IP range!!
MYNETWORK="192.168.100.0/24"

######################################################################################
# Make a note of the date/time we ran
touch $TOUCHFILE

######################################################################################
# First of all lets see if there are any changes we need to incorporate into the firewall ruleset

CURRENTCOUNT=`grep sshd $INPUTFILE | wc -l `

case $CURRENTCOUNT in

	$LASTUPDATE )  # logger -p user.notice "stopfailedssh.sh:  LastCount=$LASTUPDATE : Current Count=$CURRENTCOUNT - Nothing to do, exiting ...."
			exit 
			;;
esac

######################################################################################
# If we get to here then there are new entries in the /etc/hosts.deny file that need processing!

######################################################################################
# Remove the Allow Entries in the firewall rule set as they must stay
# at the bottom as the rules are applied from the top down.
# So all DENY rules MUST be before any ALLOW rules.

# Uncommment for Jabber server
#$UFWCOMMAND delete allow 5222/tcp
#$UFWCOMMAND delete allow 5269/tcp

# Uncomment for IRC server
#$UFWCOMMAND delete allow 6667/tcp

# Allow connections from our own network
$UFWCOMMAND delete allow from $MYNETWORK

# Allow ssh connections from anywhere
$UFWCOMMAND delete allow 22/tcp

# Uncomment for WebServer
#$UFWCOMMAND delete allow 80/tcp

logger -p user.notice "stopfailedssh.sh:  Updating Firewall Rule Set now ...."

for ENTRY in $LISTOFIPS
	do
		$UFWCOMMAND deny proto any from $ENTRY to any
		logger -p user.notice "$UFWCOMMAND deny proto any from $ENTRY to any"
	done

######################################################################################
# Now put all the ALLOW rules back into the firewall
# so that they are after all the DENY rules.

# Uncomment for Jabber Server
#$UFWCOMMAND allow 5222/tcp
#$UFWCOMMAND allow 5269/tcp

# Uncomment for IRC Server
#$UFWCOMMAND allow 6667/tcp

# Allow connections from our own network
$UFWCOMMAND allow from $MYNETWORK

# Allow ssh from anywhere
$UFWCOMMAND allow 22/tcp

# Uncommeent for WebServer
#$UFWCOMMAND allow 80/tcp

######################################################################################
# Now the catch all, if none of the above rules apply then there is something
# fishy going on, so DENY them any access at all!

$UFWCOMMAND default deny

######################################################################################
# Tidy up and lets get out of here !

echo $CURRENTCOUNT > $COUNTFILE
logger -p user.notice "stopfailedssh.sh:  LastCount=$LASTUPDATE : Current Count=$CURRENTCOUNT - Updates Complete!"

exit
Once you copied the code and made the necessary changes save the file and exit the editor.

Now we need to make the executable by root only:

Code: Select all

sudo chmod 700 /root/stopfailedssh.sh
chown root:root /root/stopfailedssh.sh
Next we need to put an entry into the system crontab file for this script to be called every minute, so once again
here is the terminal command to do it:

Code: Select all

sudo gedit /etc/crontab
Go to the bottom of the file and add the following:

Code: Select all

* * * * *	root	cd /root && ./stopfailedssh.sh
Save and exit the editor.
Just bounce the cron daemon to make sure it picks up the change:

Code: Select all

sudo /etc/init.d/cron restart
So the system is now in place, if anyone makes brute force attacks on your external facing ssh service then this
neat little system will detect it, block it and then deny the attacker access to any port on your server!

I hope this all makes sense, please feel free to post any questions !

Mike.
Fred

Re: How to Secure External facing SSH Servers from attacks

Post by Fred »

AvanceIT,

Nicely done. Thank you. :-)

Fred
N1ckR

Re: How to Secure External facing SSH Servers from attacks

Post by N1ckR »

Those are good settings.

Additionally, which is not much effort is change the standard port to one above 1024, adds a little obscurity, as there are quite a lot of people only check for port 22 open.

Advanced stuff:

Though I do not personally run them, you could run http://www.snort.org/ to protect from port scans (in the case of some scanning for open ssh port) or running something like knockd (useful ssh instructions: http://www.ducea.com/2006/07/05/how-to- ... -firewall/) which monitors for port sequences so you can only allow an SSH connection if 3 specific ports in specific order are hit.

Cheers, Nick
EllisD

Re: How to Secure External facing SSH Servers from attacks

Post by EllisD »

Thanks for all the hardwork creating this document.

I also found a very useful app called "Fail2ban". Very easy to install, works for SSH straight out of the box.

E
trapperjohn

Re: How to Secure External facing SSH Servers from attacks

Post by trapperjohn »

Good article and well designed for a new comer.

I usually use ssh with key ecryption. It can be setup and done easily with a gui... Putty and puttygen. Here's an article I wrote on how to make this simple. http://www.mark-bishop.net/ssh.php
num7

Re: How to Secure External facing SSH Servers from attacks

Post by num7 »

Hi, thanks miket for writing this nice howto!!

I have some questions:
question to: /etc/ssh/sshd_config
#1#
Do i have to set:

Code: Select all

# Only allow IPV4 connections
AddressFamily inet
? and why?
#2#
What is the ListenAddress 192.168.100.125 ? Can i find it out with ifconfig. Is it the locale IP of my own machine? ifconfig says my local IP is: inet addr:192.168.178.26

question to: /root/stopfailedssh.sh

Code: Select all

MYNETWORK="192.168.100.0/24"
Is the the same as the local IP of my machine in the LAN? and what does "/24" do at the end of the address?

Most of this i don't understand anyway, now. :cry:
#3#

Code: Select all

case $CURRENTCOUNT in

   $LASTUPDATE )  # logger -p user.notice "stopfailedssh.sh:  LastCount=$LASTUPDATE : Current Count=$CURRENTCOUNT - Nothing to do, exiting ...."
         exit 
Isn't the an " <- missing? (Count=$CURRENTCOUNT - Nothing to do, exiting ....")

How can i test if the script is running fine and did no tipping errors?
Can i test if Brute-Force Attacks are denied now?

Thanks for taking into account my posts!
best regards! :D
Post Reply

Return to “Tutorials”