Setting up your first firewall via SSH

This is a wiki page. Be bold and improve it!

If you have any questions about the content on this page, don't hesitate to open a new ticket and we'll do our best to assist you.

Ok, now you have just acquired your first remote server, and you are about to configure its firewall via SSH. This is an important step that you must get right if you don't want to find yourself locked out of the server with no possibility to restore your remote access.

Introduction

There are many firewall front ends which are supposed to make administering firewalls a breeze, but most if not all utilize iptables as their back end. Iptables is itself a Linux user-space front end for Netfilter, the kernel-space back end. Therefore, the examples here will directly use iptables. You are free to transpose the rules for your chosen firewall front end.

Rules are executed in order. Only the first rule that matches the current request applies, so it's important to get the ordering right.

Hidden dangers of front ends

The first rule, then, should be to allow SSH connections, allowing you to peacefully continue remotely administer the server.

Here is good example why using some front ends can lead to disastrous outcome. Front ends are supposed to make life easier by having a user-friendly syntax. For example, with ufw (Uncomplicated Firewall), allowing SSH connections over TCP is supposed to be as simple as typing the following command:

sudo ufw allow ssh/tcp

The command is quite explicit and clear. However ufw assumes that SSH is always on the standard port 22. What if for security reasons you are using a different port for SSH? If after adding the above command, you add another command to drop all connections, you will find yourself locked out of your server as soon as you enable the firewall.

That's why it's important to understand what you are actually doing, and how the commands entered with your front end are translated in terms of actual iptables rules.

Safely testing a remote firewall

Starting and stopping the firewall

In order to safely create our first firewall over a remote SSH connection, we are first going to learn how to stop and start the firewall. By default, iptables is enabled but comes with an empty rule set which lets everything in and out:

# iptables -L -v -n # check the iptables manpage for the description of the commands.
Chain INPUT (policy ACCEPT 3 packets, 252 bytes)
pkts bytes target     prot opt in     out     source               destination        

Chain FORWARD (policy ACCEPT 0 packets, 0 bytes)
pkts bytes target     prot opt in     out     source               destination        

Chain OUTPUT (policy ACCEPT 6 packets, 1164 bytes)
pkts bytes target     prot opt in     out     source               destination  

For Red Had and derivative distributions (CentOS/Fedora Core), iptables is set up as a service. Thus saving/stopping/restarting the firewall is pretty straightforward:

# /etc/init.d/iptables save
# /etc/init.d/iptables stop
# /etc/init.d/iptables start
# chkconfig iptables off # Turn off at boot time.

With other distributions (Debian, Ubuntu, etc.) we are going to use a simple script to help us.
We are going to:

  1. Create a script to reset the firewall (disable it).
  2. Add a cron job to periodically rest the firewall (to avoid being locked out in case we make a mistake).
  3. Add a simple rule and save it.
  4. Check that the cron job does its job.
  5. Restore our previously saved rules.
  6. Add more elaborate rules (those that have the potential of locking us out if things go wrong).

Reset the firewall

We are going to use the script below:

#!/bin/bash
# reset.fw - Reset firewall
# set x to 0 - No reset
# set x to 1 - Reset firewall
#
# Added support for IPV6 Firewall
#
# Written by Vivek Gite <vivek .at. nixcraft.com>
# Source: http:// www .cyberciti.biz/faq/turn-on-turn-off-firewall-in-linux/
#
# You can copy / paste / redistribute this script under GPL version 2.0 or above
# =============================================================
x=1

# set to true if it is CentOS / RHEL / Fedora box
RHEL=false

### no need to edit below  ###
IPT=/sbin/iptables
IPT6=/sbin/ip6tables


if [ "$x" == "1" ];
then
  if [ "$RHEL" == "true" ];
  then
    # reset firewall using redhat script
    /etc/init.d/iptables stop
    /etc/init.d/ip6tables stop
  else
    # for all other Linux distro use following rules to reset firewall

    ### reset ipv4 iptales ###
    $IPT -P INPUT ACCEPT
    $IPT -P OUTPUT ACCEPT
    $IPT -P FORWARD ACCEPT
    $IPT -F
    $IPT -X
    $IPT -Z
    for table in $(</proc/net/ip_tables_names)
    do
      $IPT -t $table -F
      $IPT -t $table -X
      $IPT -t $table -Z
    done

    ### reset ipv6 iptales ###
    $IPT6 -P INPUT ACCEPT
    $IPT6 -P OUTPUT ACCEPT
    $IPT6 -P FORWARD ACCEPT
    $IPT6 -F
    $IPT6 -X
    $IPT6 -Z
    for table in $(</proc/net/ip6_tables_names)
    do
      $IPT6 -t $table -F
      $IPT6 -t $table -X
      $IPT6 -t $table -Z
    done
  fi
else
  :
fi

Save this script, make it executable.

Add a cron job

Edit /etc/crontab and add the following line:

*/5 *  * * * root   /root/reset_fw.sh

Further down, we are going to make sure that this cron job effectively disables the firewall every five minutes.

Create a first rule

We are now going to create our very first rule in the chain INPUT of the table 'filter' (the default table).
For the command below, check the significance of each parameter in the iptables manpage:

iptables -A INPUT -i eth0 -p tcp --dport 22 -j ACCEPT

This rule is to accept tcp connection on the port 22 (the default SSH port). Since the default for the chain INPUT is ACCEPT, and since there are no other rules, this rule on its own does nothing more: everything is still accepted.

But now, we can check that the rule has been added to our iptables rule set:

# iptables -L
Chain INPUT (policy ACCEPT)
target     prot opt source               destination        
ACCEPT     tcp  --  anywhere             anywhere             tcp dpt:ssh

Chain FORWARD (policy ACCEPT)
target     prot opt source               destination        

Chain OUTPUT (policy ACCEPT)
target     prot opt source               destination 

We are now going to save our rule set to a file before the cron job flushes everything:

/root # iptables-save > firewall.rules

Then, after 5 minutes, check again with iptables -L to see whether the added rule has been flushed.

Restore our iptables rule set

Now that we are certain that the firewall is completely reset with the cron job, we can restore our previously saved rule set:

/root # iptables-restore < firewall.rules

Check again that everything is as it should be at this stage.

Adding common rules

Now that we are certain that we are not going to be locked out of our remote system, we can start adding more rules, periodically saving them.

Our last rule will be to drop everything, but first, we must explicitly allow the incoming traffic that we actually want.

For all the examples below, check the iptables manpage for an explanation of each parameter. This way, you will learn much more than if we were to explain ourselves the significance of each.

Accepting traffic

- Accept establish connections:

iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT

- Accept SSH traffic:

iptables -A INPUT -p tcp --dport ssh -j ACCEPT

Note that the above will allow traffic on the standard port for SSH, i.e. port 22. If for security reasons you have configured SSH to use another port, you will have to specify it explicitly:

iptables -A INPUT -p tcp --dport 2223 -j ACCEPT

See /etc/ssh/sshd_config for the exact port used.

It is important to get this rule right, because it is the one that will allow you to remote administer your server.

- Accept any traffic on the loopback interface. We insert this rule at the 1st position.

iptables -I INPUT 1 -i lo -j ACCEPT

To see the list of interfaces on your server, do:
cat /etc/network/interfaces

- accept HTTP traffic:

iptables -A INPUT -p tcp --dport 80 -j ACCEPT

- accept DNS traffic, if you operate a name server:

iptables -A INPUT -p tcp --dport 53 -j ACCEPT
iptables -A INPUT -p udp --dport 53 -j ACCEPT

At this stage, you may want to check the list of TCP and UDP port numbers, checking for any service you might be running and accept the related traffic.

Blocking traffic

We are now ready to actually block some traffic. We are going to log all dropped traffic. For this, we are going to create a custom chain that we shall name 'logdrop':

iptables -N logdrop

We are going to add the following two rules to this chain:

iptables -A logdrop -j LOG
iptables -A logdrop -j DROP

Thus, any traffic that is handled by this chain will be logged and dropped.

Having previously accepted all traffic on the loopback interface, we can now drop all other traffic (i.e. all other traffic on the eth0 or other interfaces).

iptables -A INPUT -j logdrop

Testing your firewall

Now that the firewall is all set up, it is time to test it.

Use ping to test the ICMP.
Use nmap to test specific ports via UDP/TCP, e.g.:

nmap -p 80 example.com

You can also use telnet

Other tutorials

IptablesHowTo at the Ubuntu wiki.

Issues related to this page:

ProjectSummaryStatusPriorityCategoryLast updatedAssigned to
Linux serverHow to start/stop firewall / set up firewall vi…activenormalsupport request5 years 3 days
Linux serverset up iptables as a serviceactiveminorfeature request5 years 3 days