IPTables is a very powerful tool for creating a firewall on your Linux system. However, all the rules are based on IP addresses. For example, you can open a port to a specific source IP address.

What if you have a client that needs to connect to a service that does not have a static IP address? The client would need to monitor changes to their IP address, send you the new IP, and you would then have to manually update your iptables rules to allow them access. There has to be a better way.

This was my thought when I had a customer that needed SSH access to his server from his home. He has a cable internet connection that changes his pubic IP address at least once a month (sometimes once a week). I could just open SSH to the world and allow him to connect, but that is not very secure. The solution we worked out is to use Dynamic DNS to tie his IP address to a hostname, then use IP Sets and a simple Bash script to automatically update iptables.

NOTE: Configuring the client of Dyamic DNS is outside the scope of this tutorial. There are several methods to accomplish this. Let us know in the comments if you need help.

Configuring IP Sets with iptables

IP Sets is a framework that allows you to create "sets" of IP addresses, MAC address, networks, port numbers and more. These sets can then be used inside of iptables rules. This may seem complicated, however, it is very simple. With just a few commands you can configure an IP Set. Then we will create a simple bash script to update the set.

For this example, let's assume the client has the following dynamic DNS name setup:
client1.example.com

Creating Your IP Set

The first step is to create your IP Set to hold the IP address of your client.

[mcherisi@putor ~]$ sudo ipset create ssh-allowed hash:ip

Here we are telling the ipset utility to create a set called "ssh-allowed" with the type of "hash:ip". This will be used to store the IP addresses of systems allowed to SSH into our server.

We can now view this IP Set and see that is has no members. This is expected since we have not yet added any IP addresses. In the next steps we will configure iptables to use this IP Set. Then create a Bash script to add our clients IP address based of their Dynamic DNS name.

[mcherisi@putor ~]$ sudo ipset list ssh-allowed
Name: ssh-allowed
Type: hash:ip
Revision: 4
Header: family inet hashsize 1024 maxelem 65536
Size in memory: 200
References: 0
Number of entries: 0
Members:

You can manually add IP addresses to the set for testing.

[mcherisi@putor ~]$ sudo ipset add ssh-allowed 192.168.100.197

Now if we list the "ssh-allowed" IP Set, we will see we have one member.

[mcherisi@putor ~]$ sudo ipset list ssh-allowed
Name: ssh-allowed
Type: hash:ip
Revision: 4
Header: family inet hashsize 1024 maxelem 65536
Size in memory: 248
References: 1
Number of entries: 1
Members:
192.168.100.197

Using the IP Set to Create an iptables Rule

Now that we have our IP Set created, let's create a rule in iptables that tells it to allow SSH traffic from addresses inside this IP Set.

sudo iptables -I INPUT -p tcp --dport 22 -m set --match-set ssh-allowed src -j ACCEPT

Now iptables is configured to check the "ssh-allowed" IP set for source IP addresses for incoming SSH connections.

[mcherisi@putor ~]$ sudo iptables -L -vn
Chain INPUT (policy ACCEPT 0 packets, 0 bytes)
 pkts bytes target     prot opt in     out     source               destination         
    0     0 ACCEPT     tcp  --  *      *       0.0.0.0/0            0.0.0.0/0            tcp dpt:22 match-set ssh-allowed src
...OUTPUT TRUNCATED...

Any IP address that is a member of the "ssh-allowed" IP Set is now permitted to connect to SSH on destination port 22.

Creating a Bash Script to Automatically Update IP Tables by Hostname

Our client has Dynamic DNS configured to update the hostname client1.example.com with their IP address automatically. All that remains is to set up a script to update our "ssh-allow" IP set with the IP address of that hostname.

NOTE: You must have the dig utility installed.

The script below will use the dig utility to find the IP address of client1.example.com and set the ip variable with it. It will then flush the IP Set to remove any old IP addresses, then insert the new IP address allowing it access to the server via SSH.

#!/bin/bash
# Find IP address and store it in $ip
ip=`dig +short client1.example.com`

# Flush old IP addresses from ssh-allowed IP Set
ipset flush ssh-allowed

# Add new IP address to ssh-allowed IP Set
ipset add ssh-allowed $ip

We can place this script in /etc/cron.daily to run every day, or create a basic cron job to run it as often as we like. As the client automatically updates Dynamic DNS, this script will ensure they still have access to the server.

Conclusion

Using IP sets allows you to create an easy to manage list of IP addresses that iptables can use in it's rules. As we have shown when paired with a simple Bash script it can also help to create rules from a hostname or domain name.

There is a full tutorial in the works for ipset. Sign up for our newsletter to keep up with all our new releases.

Resources and Links