3 min read

Vultr firewall: automatically update a rule with dynamic IP

Vultr firewall: automatically update a rule with dynamic IP
Photo by Sigmund / Unsplash

I wanted to filter the SSH access to an instance, but I couldn't do it with my home IP because it's a dynamic IP.

This blog post is only centered around Vultr API. However, you can apply the same idea with any provider that support firewall as a service in front of your instance. You just need an API to play with the firewall rules.

Here's how to do it in a few steps.

Dynamic DNS service

First thing, you will need to link your dynamic IP to a DDNS (Dynamic Domain Name System) service. Some are free, personnally I'm using the dynamic DNS service embeded in my router, provided by the manufacturer .

Enable Vultr API

Just go there: https://my.vultr.com/settings/#settingsapi and enable the API. Add the IP from where you gonna run the script and copy your API key.

Install Vultr CLI

Now you need to install vultr-cli. It's the official command line and the easiest way to interact with the API from a shell.

Download the release for your server (amd and arm are available, for Mac, Linux and Windows): https://github.com/vultr/vultr-cli/releases and extract it.

Configure the CLI

Create a file ~/.vultr-cli.yamland add your API key in it:

api-key: $YOUR_API_KEY

Test

To check if it's working properly try to list the existing instances:

./vultr-cli instance list

You should see the list of all the instances associated with the account.

Create the appropriate rules

Now we need to create a rule with your current dynamic IP. This rule will be checked next time and removed if the IP change.

The script will recreate a new rule with the new IP. The note part is important, as we gonna filter on this value the next time we want to check if the IP is still correct.

I decided to put home_ip as note but you can put whatever you want, as long as you update the script accordingly.

Step 1

Get the firewall group ID:

./vultr-cli firewall group list

Step 2

Add a comment so we can filter it after easily. On my side I just have 1 firewall group so I added firewall as comment, but feel free to replace with what make sense for you.

Step 3

Create the rule, replacing FW_GR_IP by the ID retrieved above, IP with your current IP adress, and PORT with the port you want to allow. You can also replace tcp but the protocol you want, and size if you want to allow a subnet and not a single IP. If it's a single ipv6 don't forget to put 64 as size.

./vultr-cli firewall rule create --id FW_GR_ID --ip-type v4 --protocol tcp --size 32 --subnet IP --port PORT --notes home_ip

Once it's done, you're all set to use the script!

How the script works

I will explain each part then put the complete code below.

Here's some variables that we will use later:

DYNDNS=yourdyndnshost
IP_TYPE=v4
SIZE=32 
PROTOCOL=tcp
PORT=22
MATCH_STRING=home_ip

We need to grab the group ID. For that we'll filter with the note we added earlier:

GROUP=$(./vultr-cli firewall group list | grep firewall | awk '{print $1}')

Using the firewall group ID, we can now grab the current rule number and the IP that we'll store in 2 variables named RULE_NUMBER and CURRENT_IP . We'll filter which rule searching for the comment home_ip :

set -- $(/root/vultr-cli firewall rule list $FW_GROUP | grep home_ip | awk '{print $1, $6}')
RULE_NUMBER=$1
CURRENT_IP=$2

Next, let's retrieve the IP associated with your dynamic DNS hostname:

IP=`/usr/bin/dig +short $DYNDNS | /usr/bin/tail -n 1`

And to finish, a simple if to exit if the IP didn't change, and else to apply our modification if the IP changed:

if [ "$IP/32" == "$CURRENT_IP" ]; then
    exit
else
    /root/vultr-cli firewall rule delete $GROUP $RULE_NUMBER
    /root/vultr-cli firewall rule create --id $GROUP --ip-type $IP_TYPE --protocol $PROTOCOL --size $SIZE --subnet $IP --port $PORT --notes $MATCH_STRING
    exit
fi

Let's create the script:

vi ~/vultr-dyndns.sh
#!/bin/bash

DYNDNS=yourdyndnshostname
IP_TYPE=v4
SIZE=32
PROTOCOL=tcp
PORT=22
MATCH_STRING=home_ip
GROUP=$(./vultr-cli firewall group list | grep firewall | awk '{print $1}')
set -- $(/root/vultr-cli firewall rule list $GROUP | grep home_ip | awk '{print $1, $6}')
RULE_NUMBER=$1
CURRENT_IP=$2
IP=`/usr/bin/dig +short $DYNDNS | /usr/bin/tail -n 1`

if [ "$IP/32" == "$CURRENT_IP" ]; then
    echo "MATCH"
    exit
else
    /root/vultr-cli firewall rule delete $GROUP $RULE_NUMBER
    /root/vultr-cli firewall rule create --id $GROUP --ip-type v4 --protocol $PROTOCOL --size $SIZE --subnet $IP --port $PORT --notes $MATCH_STRING
    exit
fi

You can automate the task by adding the script to cron. Here's the cron to execute the script every 12 hours:

* */12 * * * ~/vultr-dyndns.sh >/dev/null 2>&1

Final thoughts

We could use direct API call with curl but I decided not to for various reason, mostly by laziness 💀.