Bash Script Update UFW Rule for Dynamic Host
If you are a road warrior and need access to your servers how do you update your firewall rules? A simple bash script like this one will update your UFW firewall rules so you can access your server wherever you are.
Let me start by saying we didn’t write this, but one of our techs found it from This Tech Blog and we use it so frequently, we want to document it for others in case it disappears. We did add a few other minor things from the original script (putting date time stamp in log, adding comments), but the idea belongs to the creator linked above.
Option 1: Update UFW by Hand
sudo ufw allow from 1.2.3.4 to any port 22 comment 'some comment here'
We are going to assume you have UFW running and enabled for the rest of this article!
Option 2: Automate UFW Updates With a Script!!
-
Register a Dynamic Hostname
-
Write the Bash Script
-
Create Dynamic Host Object for UFW
-
Add Cron Job
-
Verify Owner/Permissions
-
Install Dynamic Host Updater on Client
-
Verify Your Script is Working on Server
The rest of this article will detail the steps above to automate your UFW bash script to update firewall rules based on your dynamic host.
Register a Dynamic Hostname
There are several free services for dynamic DNS. The way we do it is to register at least two dynamic hosts. One dynamic host is tied to the router, so when we are in the office, that IP is registered. If that IP happens to change, it will be updated for all of the computers at that location. The second dynamic host is used by installing the client on the laptop. Now when that laptop uses any hotspot, it is also allowed into the network. We are using Dyn DNS but you can use any service so long as it’s regularly updated and easy to use.
We use Dyn DNS because the routers are typically setup to use it as a default Dynamic DNS.
You’ll need to install the client on your travel machine so it can register whenever the laptop travels to a new network.
Your server will also need “dig” installed. If it’s not installed “which dig”, then you can install it easily:
sudo apt-get install dnsutils
Write the Bash Shell Script
$ cat /usr/local/sbin/ufw-dynhostupdate.sh
#!/bin/bash
start=$(date +"%Y-%m-%d %T")
HOSTS_ALLOW=/etc/ufw-dynamic-hosts.allow
IPS_ALLOW=/var/tmp/ufw-dynamic-ips.allow
add_rule() {
local proto=$1
local port=$2
local ip=$3
local comment=$4
local regex="${port}\/${proto}.*ALLOW.*IN.*${ip}"
local rule=$(/usr/sbin/ufw status numbered | grep $regex)
if [ -z "$rule" ]; then
/usr/sbin/ufw allow proto ${proto} from ${ip} to any port ${port} comment "${comment}"
echo "${start} ADDED: ${proto} from ${ip} to any ${port} comment '${comment}'"
else
echo "${start} EXISTS: ${proto} ${ip} to ${port} (${comment})"
fi
}
delete_rule() {
local proto=$1
local port=$2
local ip=$3
local comment=$4
local regex="${port}\/${proto}.*ALLOW.*IN.*${ip}"
local rule=$(/usr/sbin/ufw status numbered | grep $regex)
if [ -n "$rule" ]; then
/usr/sbin/ufw delete allow proto ${proto} from ${ip} to any port ${port}
echo "${start} DELETED: ${proto} ${ip} to ${port} (${comment})"
else
echo "${start} NO DELETE: rule does not exist"
fi
}
sed '/^[[:space:]]*$/d' ${HOSTS_ALLOW} | sed '/^[[:space:]]*#/d' | while read line
do
proto=$(echo ${line} | cut -d: -f1)
port=$(echo ${line} | cut -d: -f2)
host=$(echo ${line} | cut -d: -f3)
comment=$(echo ${line} | cut -d: -f4)
if [ -f ${IPS_ALLOW} ]; then
old_ip=$(cat ${IPS_ALLOW} | grep ${host} | cut -d: -f2)
fi
ip=$(dig +short $host | tail -n 1)
if [ -z ${ip} ]; then
if [ -n "${old_ip}" ]; then
delete_rule $proto $port $old_ip
# echo "${start} ${proto} ${port} ${old_ip} removed"
fi
echo "${start} FAIL: Failed to resolve the ip address of ${host}." 1>&2
exit 1
fi
if [ -n "${old_ip}" ]; then
if [ ${ip} != ${old_ip} ]; then
delete_rule $proto $port $old_ip $comment
fi
fi
add_rule $proto $port $ip $comment
if [ -f ${IPS_ALLOW} ]; then
sed -i.bak /^${host}*/d ${IPS_ALLOW}
fi
echo "${host}:${ip}" >> ${IPS_ALLOW}
done
Set Execute Permissions On The Script
We need this Bash script to execute at regular intervals. In order for to execute, we need our script to have execute permissions (and later we’ll add a cron job too).
$ chmod +x /usr/local/sbin/ufw-dyhostupdate.sh
As for the group and owner, only root can normally update UFW. So, unless you modify your wheel group or otherwise give your user permissions to specifically edit UFW, you’ll need to use root as the owner of the script and all files. For security practices, this isn’t the greatest idea and will fail some audits. More importantly, it would be better to update your user to have UFW permissions and then run all of the job, file permissions etc as that owner.
An even better practice is to give a special user priveledges to ONLY touch UFW or automate certain tasks and then run said tasks as that owner. To keep it simple for documentation purposes, we’ll pretend that root is fine for our audits.
Create Dynamic Host Object for UFW
This object is simply a file storing the most recently found IP of your dynamic host. We’ll create and preload it with the data it needs, but this isn’t necessary:
$ cat /var/tmp/ufw-dynamic-ips.allow
yourdynamichost.selfip.com:1.2.3.4
In addition, make sure the object is readable and writable so our UFW script can make changes to it:
$ ls /var/tmp/ufw-dynamic-ips.allow
-rw-r--r-- 1 root root 42 Oct 27 12:35 /var/tmp/ufw-dynamic-ips.allow
Create the Dynamic Host Database
This is a simple text file stating what ports to open for each dynamic host. You can have multiple lines in it, 1 line per host. In the following example we are saying “TCP 22 should be open from yourdymanichost.selfip.com”:
$ cat /etc/ufw-dynamic-hosts.allow
tcp:22:yourdynamichost.selfip.com:your_comment_here
Test the Script Manually
You should be able to run the script manually at this point and then verify your UFW logs and rules to see them update.
You can view your UFW rules with the following command before and after testing your script:
$ ufw status numbered
Before: After:
Add Cron Job
Now that you have a scripts, temporary objects, and a dynamic host database all working… you need to setup the script to run on autopilot. Use the crontab -e command to edit the cronjob. Remember it must as a job on the user that owns the script and that user must have permission to edit UFW rules!
You can verify the job is setup in cron by looking at the cron jobs. We are hiding the messages and keeping the errors to syslog using the reroute to dev null:
$ crontab -l
*/5 * * * * /usr/local/sbin/ufw-dynhostupdate.sh >/dev/null 2>&1
# or if you want to log the changes that happen/don't happen:
*/5 * * * * /usr/local/sbin/ufw-dynhostupdate.sh >>/var/log/ufwdynhost.log 2>&1
Verify Your Script is Working on Server
If it’s all working you should get an updated UFW status, and see an entry in syslog about the UFW changes:
$ ufw status numbered
To verify recent CRON jobs, view your syslog:
$ cat /var/log/syslog | grep CRON
Oct 28 09:10:01 rubysash CRON[11113]: (root) CMD (/usr/local/sbin/ufw-dynhostupdate.sh >/dev/null 2>&1)