Public IP Changing? Python DDNS for Self-hosting

Siraj Sabihuddin

A scientist and engineer implementing a simple approach to pointing your domain to your self-hosted website. This article took considerable time to write. If you found it useful, consider buying me a coffee by clicking the donate button.

In an earlier article I made the case that it doesn’t really make financial sense to both register your domain and host your website with a provider such as GoDaddy. In my case, the costs of this economy, high latency hosting hit the price of $1018 USD over 2 years for a set of minimal services that are comparable to self-hosting. By comparison setting up a NAS is costing me $937 USD over 2 years, with no other recurring costs other than the $40 to $100 USD per additional year for a relatively low demand domain and potentially some other services such as a Virtual Private Server (VPS) – note that this number excludes the payment for my internet connection via my Internet Service Provider (ISP) connection. So just by eliminating the hosting side of things, its possible to save considerable amounts after the first two years. My approach, therefore, is to try and keep my domain registration with GoDaddy and move hosting and associated features to my own Network Access Storage (NAS). This allows me to setup SSL, email, git (for coding) and other useful services that I might need as an engineer without the associated costs.

The problem lies in having the knowledge to setup all the various bits of open source software to get your self-hosted website and other features setup. In this article, I’ll explore one step in this process. Namely that of setting up the routing of your Registered Domain to your NAS home gateway IP – this is the IP address that your ISP gives you when you pay for your service. This Gateway IP is typically the IP address of the ISP provided modem.

The method for this automatic routing is called Dynamic Domain Name System (DDNS) service. It relies on having access to an API and API key for GoDaddy or whatever other domain provider you are using. While this is possible with other domain providers, for this article, I’m using GoDaddy as an example. Hopefully a similar approach can be used for other domain providers as long as they offer API access. If your domain provider doesn’t provide this, there is a way forward still by using a headless browser and a bot script that pretends to be a real user to login to your domain provider account and do the same thing. This is a little harder and I’ll leave this approach for another day.

1. Solution Applicability

Note that the method I describe in this article assumes that your ISP doesn’t provide a static IP address. As IPv4 addresses have been mostly exhausted, there is good chance that your provider doesn’t provide you with a static IP and getting one will likely cost a large sum of money. If you have static IP, the process of pointing your domain to your NAS or computer is relatively easy and involves simply changing the A record in your Domain Provider’s DNS settings to point to your home’s static public IP address. As such I won’t address this case in this article.

The method presented in this article assumes that your home modem’s IP address is a dynamically assigned public IP address. This kind of dynamic IP address has a habit of changing on a regular basis – on a daily to monthly basis – based on some set of rules implemented by your ISP. This is the case for which this article presents a solution.

Dynamic Public IP assignment by ISP

Unfortunately, with the limited number of IPv4 addresses, some ISPs, particularly Mobile Providers have an additional layer of sharing such that they don’t provide you with a public IP address at all, but create a private IP address to you instead. This approach is called Carrier Grade NAT (CGNAT). I won’t deal with CGNAT in this article but rather only with cases where the ISP provides you with a dynamic public IP address.

CGNAT based Private IP

There is a last case that I don’t address. As IPv4 addresses have run out, the internet has been in transition to IPv6 addressing. There are plenty of IPv6 addresses that you can have a dedicated IPv6 address. This address typically looks like: : 2125:0322:2CAF:0F0F:1F00:0517:1633:23B1. If you see that your modem has an IPv6 address when exploring the web interface, then you can actually directly use this as your public IP. Unfortunately, not all network equipment among all ISPs has IPv6 implemented – especially true for smaller ISPs. This means that for those people who are behind such an ISP, they will not be able to route to your self-hosted IPv6 website. So your website will have limited reach. Once again, this article only looks at dealing dynamic IPv4 public IP addresses and pointing your domain dynamically to your home IP. I’ll address the issue of IPv6 in a future article.

How to figure out which case you are?

Typically you can figure out what kind of IP address your ISP has provided you by monitoring your gateway address periodically by going to a public website such as https://whatismyipaddress.com/. Doing this periodically over a period of about 2 weeks to a month and recording the IP address can give you an idea whether your public IP address is changing or not.

However, an added layer of checking is needed to see if you are behind a CGNAT or not. To do this you should look at the modem that your ISP has provided, dig out the default IP address of this modem (e.g. 192.168.xx.xx) and navigate to it on your browser while connected to the WIFI/ethernet of that modem. Once on the modems login page, find the default username and password of this modem model and login. Typically you can find at least the default IP and default password if you crack open the modem’s cover. Default values should be printed inside it.

In the modem web interface, you should be able to locate the Wide Area Network (WAN) IP address – this address is the address through which the modem accesses the ISP network. If this WAN IP address does not match the public IP address from https://whatismyipaddress.com/ then you are likely behind a CGNAT. WAN IPs that begin with 100.64.xxx.xxx are reserved for CGNAT applications by the Internet Corporation for Assigned Names and Numbers (ICANN) – a U.S. government administered organization.

2. The Overall Approach

Let me start by building you a picture (see below). When you purchase a domain and host together with a provider like GoDaddy, they will point the domain to the IP address of the hosting they are providing. Typically, in economy hosting the hosting provider will point your domain to a shared IP address (i.e. dynamic IP). This saves them from having to provide you with a unique IP that belongs only to you. What we need to do is change the IP address to which your domain is pointing to. This is typically configured via a DNS A record as setup and configured by the domain provider.

Unfortunately, your typical home ISP doesn’t provide you with a static public IP address. Recently, we’ve exhausted all IPv4 addresses available. So when you connect your modem to the ISP line, the ISP will continually update your IP address at some frequency. Making some observations, what I’ve noted is that its somewhere around every two weeks to a month or so. In my case, I use 4G internet for my internet access at home, so I don’t even have a traditional ISP. In the case of a 4G hotspot, the public IP address is separated by an ISP network in between. So you may have a public IP address that is changing and have a different private IP address for your modem (CGNAT). This solution can’t be used in this last case. It is really only intended for a dynamically assigned public IP.

Given the situation of a dynamically assigned public IP, what we need to do is develop a script that runs on our NAS via a Task Scheduler. This script might be executed once everyday. Its purpose is to figure out what the current IP address your ISP has provided is – this is your home gateway IP address. Once discovered, the script needs to access your domain provider’s A record and update it with the IP address of your home gateway. In this way you implement something called a Dynamic Domain Name System (DDNS). Some people go to third party paid DDNS services to provide this service, but this is not actually necessary when working with dynamic Public IP addresses.

2. Accessing the Go Daddy API

To create my DDNS script, I will use python. I need to note a few things here. The GoDaddy API responds to requests in the form of a JSON reply. We need to send an HTTP request with an API access key to a specific API URL as defined by the GoDaddy API located here.

You must have an API key from GoDaddy. This can be obtained by logging into your GoDaddy account as shown below using the developers link.

GoDaddy Domain Provider’s Developer Web Interface

Copy the API key consisting of an Access Key and a Secret Key generated from GoDaddy when you first create the API key. I will use this in our Python script. Its important to keep this key completely secret. Failing to do so will allow someone to basically change your domain details and potentially take control of your GoDaddy account.

3. Writing a Python DDNS Script

I will store my Python script directly on the NAS. But to test, its ok to run on a local computer. Python conveniently has two variations of libraries available for making HTTP requests: PYCURL and REQUESTS. There are some challenges setting up PYCURL on the Synology NAS – no version is available – so I use REQUESTS instead. In addition I’d like to be able to pass in some command line parameters and parse the JSON response from the GoDaddy API so I can import JSON and SYS libraries as well. See below.

Python

Once imported, its time to create a main function. I set this up to take a set of arguments from the command line. Here I’ve set up the script to be executed with the following command line: python ddns.py True False. The script is saved as ddns.py. This becomes the first command line argument. The second command line argument is whether to write or not. If write=True then the script reads then writes the A record. If write=False, the script reads the A record but doesn’t write. The third command line argument determines if I want to reset the A record to the original A record created by your domain provider or not (i.e. reset=True or reset=False). You need to copy this directly from Go Daddy and paste it for your python script.

Python

The function above is called directly as below. Here the command line arguments are directly passed to the function via the SYS library.

Python

Now inside the main function I will use the api.ipfiy.org to extract the gateway address for my NAS since this script is running on the NAS itself. The code for this is shown below.

Python

When making HTTP requests to the Go Daddy API, the access key and secret key need to be passed via an HTTP header a shown below.

Python

Accessing the API requires construction of a custom URL. A variety of domain features are available via the API as shown in the screenshot below:

Building this in python can be done as shown below by constructing a string representing the URL of the specific API command. This URL contains the type and name variables: ‘A’ and ‘@’ respectively. The base URL is the API URL: https://api.godaddy.com/v1/.

Python

At this stage the results of the request can be printed out. If the request was successful it returns a JSON reply containing the DNS record from Go Daddy. The code below demonstrates. Notice, just in-case something goes wrong, I’ve also saved the original IP address to which my domain was pointing. This is so that, if I overwrite the address incorrectly, I can go back and make sure my website on GoDaddy’s hosting platform can be brought online again.

Python

At this stage the remote DNS A record can be replaced with the external IP address of the Synology NAS. Note that port forwarding from the gateway router to the Synology NAS router is necessary. So my first step here is to enable the gateway (modem or) router’s port forwarding of port 80 (HTTP) and port 443 (HTTPS) to the Synology router. This Synology router must then also port forward to the Synology NAS IP address. Some of this effort can be saved by configuring the Synology router (if one exists) to bridge mode. This way only the modem/router at the gateway needs to be setup for port forwarding.

Earlier, I had passed in the write and reset command line arguments. If reset is enabled and write enabled, the script will reset the A record to point to the original GoDaddy hosting. Otherwise if reset is False the script will write the gateway address of the home NAS into the A record. If write is disabled then the script will simple do no write and just read the record and gateway IP as shown above and exit. This code is implemented below.

Python

Just to make sure that the write has been successful, I can now print the updated A record again and see if there has been a change. The code for this is shown below.

Python

The complete output for this code should look something like below. This is if you are reading your home NAS gateway IP and the A record using the command with writing: python ddns.py False False. Note that I’ve redacted IP address displays so that hackers have less information for initiating attacks after reading this blog.

I’ve included the complete code for this ddns.py script here. So feel free to try it out yourself. Note though that you will have to add your own API key and website domain as well as your own hosting IP to get the code working. And of-course, you’ll need to get Python installed as well on your NAS. You can run this code on your local computer as well as on a NAS – one of the beauties of using generic code and python.

Reference Code Showing a PYCURL Approach

I had mentioned that an alternative to using the REQUESTS library was to make use of the PYCURL library. Just for your reference, I’ve included an example showing how you can make a request to the Go Daddy API with this PYCURL library. This code only shows an example of reading the A record. Nothing more. I leave it up to you, dear reader, to finish the code.

Python

4. Synology NAS to Run Script

The next steps of setting up the DDNS script is to configure the NAS. This involves the following steps:

  • Installing Python & setting up the SSH
  • Installing PIP & setting up a virtual environment
  • Running Python script & setting up a task
Installing Python & Setting up the SSH

Installing Python is very simple through the Synology NAS package manager. See the screen shots below:

The Desktop of the Synology NAS as accessed through a browser
Installing Python via the Package Manager

I use Python 3.9 for the remaining steps, not Python 2. However, the python in the package manager doesn’t include PIP or CONDA. We can however install PIP through python.

To use Python, you need access to the terminal. I use SSH to get terminal access to my NAS. This requires setting up the NAS for SSH. This can be setup by enabling SSH port 22 via the control panel as shown below:

Setting up SSH Terminal in Synology NAS
Installing PIP & Setting up a virtual environment

Now that SSH is enabled. You can easily SSH into your NAS with an admin account. Your computer must be connected to the same home network as the NAS. The following command is used:

ssh <YourUsername>@<YourNASIPaddress> -p 22
Logging into Synology NAS via SSH

An example of the computer terminal screenshot shows this – I’ve redacted the red areas just to make life a little more difficult for hackers who wish to do a brute force attack. Ok. Now that you are logged in its possible to setup and install PIP using the following command:

sudo python -m ensurepip --upgrade
sudo python -m pip install --upgrade pip

You can check the version using the command below. If all is well you should see the following displayed. The output resulting from this command is shown as well:

python -m pip -V
OUTPUT:
pip 22.1.1 from /var/services/homes/yourhomedir/.local/lib/python3.8/site-packages/pip (python 3.8)

From here we are ready to setup a new PIP virtual environment and activate it. The virtual environment allows us to setup packages that are isolated from the root python installation. So if for some reason we break the system with conflicting packages and the like, we can easily delete the environment and start fresh again without impacting the root python system.

python -m pip install virtualenv

Once completed we create a virtual environment for our website related services using the following command:

python -m virtualenv your_environment

The data related to the virtual environment will be stored in your home directory’s your_environment/bin folder. Before installing any PIP libraries, you need to make sure to activate this environment using the command:

source ~/your_environment/bin/activate

You can now use pip install to install the requests library from the activated virtual environment. I just needed the REQUESTS library for the code I introduced earlier. This can be installed using PIP via:

pip install requests
Running Python script & setting up a task

To do a test run of the Python script, I simply used the python command as shown earlier when I was introducing the code. But, what I really wanted to do was not run through ssh but to run the python script automatically via Synology’s Task Scheduler. To do this I created a simple shell script called ddns.sh as shown below:

!/bin/bash
echo 'Running ddns update script'
source ~/your_environment/bin/activate || echo 'Pip environment activation failed.'
~/your_environment/bin/python ~/yourcodedir/ddns.py $1 $2 || echo 'Failed to execute python script'

Once this is setup, in the Synology control panel there is a Task Scheduler program as shown in the screen shot below:

The Synology NAS task scheduler
Calling the shell script. Note that this script is currently only
reading. You will want to change this to write your A record. And you should change
the directory to match yourcodedir

Well. That’s it. You are done. Now to check that the code is working I can run the task anytime and see the standard output right in the Task scheduler. Use the RUN command after selecting the task then selecting the task and clicking on the Action -> View results menu. the following screenshot should present itself.

Checking the results of the run task using the Action -> View Results menu.

Well. That’s it everyone. Enjoy. And feel free to buy me a coffee by clicking on the the button below. At some point you should be able to shop as well. So you can visit my shop too.

Leave a Reply

Your email address will not be published. Required fields are marked *