Vultr firewall: automatically update a rule with dynamic IP
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.yaml
and 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 💀.