cmc/cleberg.net

My personal web garden & blog.

clone: git clone https://gitbay.org/cmc/cleberg.net.git

ef6048e9e5520b926193b54d191b22ee32e7ebad

unsigned

author: Christian Cleberg <hello@cleberg.net> · 2024-03-15T14:59:57Z

add ddns-updater post
 .../blog/2024-03-15-self-hosting-ddns-updater.md   | 284 +++++++++++++++++++++
 1 file changed, 284 insertions(+)

diff --git a/content/blog/2024-03-15-self-hosting-ddns-updater.md b/content/blog/2024-03-15-self-hosting-ddns-updater.md
new file mode 100644
index 0000000..baec495
--- /dev/null
+++ b/content/blog/2024-03-15-self-hosting-ddns-updater.md
@@ -0,0 +1,284 @@
++++
+date = 2024-03-15T14:49:59+00:00
+title = "Self-Hosting DDNS Updater"
+description = "A guide to self-hosting the DDNS Updater container."
++++
+
+![DDNS Updater Web View](https://img.cleberg.net/blog/20240315-ddns-updater/ddns.png)
+
+[DDNS Updater](https://github.com/qdm12/ddns-updater) is a program to keep DNS A and/or AAAA records updated for multiple DNS providers.
+
+If you've read any of my other posts, you'll notice that I have been searching for and using a few different DDNS updating solutions for years. You'll also notice that I love any projects that offer a Docker Compose solution.
+
+Luckily, DDNS Upater fits both of these preferences.
+
+## Installation
+
+To get started, always make sure to review the project's [README](https://github.com/qdm12/ddns-updater/blob/master/README.md). I'll be documenting my steps below, but they may have changed by the time you read this.
+
+The first step is to set up the directories and files required for the project. 
+
+```sh
+mkdir ~/ddns-updater
+mkdir ~/ddns-updater/data
+touch ~/ddns-updater/data/config.json
+```
+
+### Configuration
+
+The main configuration you need to update is the `data/config.json` file. There is a large list of supported providers in the README, but I'm going to use Cloudflare in this example.
+
+```sh
+nano ~/ddns-updater/data/config.json
+```
+
+When setting up the configuration for Cloudflare, you'll need the following:
+
+- Required Parameters
+  - `"zone_identifier"` is the Zone ID of your site from the domain overview page
+  - `"host"` is your host and can be `"@"`, a subdomain or the wildcard `"*"`. See [this issue comment for context](https://github.com/qdm12/ddns-updater/issues/243#issuecomment-928313949).
+  - `"ttl"` integer value for record TTL in seconds (specify 1 for automatic)
+  - One of the following ([how to find API keys](https://developers.cloudflare.com/fundamentals/api/get-started/)):
+    - Email `"email"` and Global API Key `"key"`
+    - User service key `"user_service_key"`
+    - API Token `"token"`, configured with DNS edit permissions for your DNS name's zone
+- Optional Parameters
+  - `"proxied"` can be set to `true` to use the proxy services of Cloudflare
+  - `"ip_version"` can be `ipv4` (A records), or `ipv6` (AAAA records) or `ipv4 or ipv6` (update one of the two, depending on the public ip found). It defaults to `ipv4 or ipv6`.
+  - `"ipv6_suffix"` is the IPv6 interface identifier suffix to use. It can be for example `0:0:0:0:72ad:8fbb:a54e:bedd/64`. If left empty, it defaults to no suffix and the raw public IPv6 address obtained is used in the record updating.
+
+```conf
+{
+  "settings": [
+    {
+      "provider": "cloudflare",
+      "zone_identifier": "some id",
+      "domain": "domain.com",
+      "host": "@",
+      "ttl": 1,
+      "proxied": true,
+      "token": "yourtoken",
+      "ip_version": "ipv4",
+      "ipv6_suffix": ""
+    }
+  ]
+}
+```
+
+Once you have configured the provider of your choice, correct the file and directory permissions and ownership.
+
+```sh
+cd ~/ddns_updater
+# Owned by user ID of Docker container (1000)
+chown -R 1000 data
+# all access (for creating json database file data/updates.json)
+chmod 700 data
+# read access only
+chmod 400 data/config.json
+```
+
+### Docker Compose
+
+After creating the project structure, let's create the `docker-compose.yml` file.
+
+```sh
+nano ~/ddns_-pdater/docker-compose.yml
+```
+
+```config
+version: "3.7"
+services:
+  ddns-updater:
+    image: qmcgaw/ddns-updater
+    container_name: ddns-updater
+    network_mode: bridge
+    ports:
+      - 8097:8000/tcp # Change the 8097 value to whichever port you want to use
+    volumes:
+      - ./data:/updater/data
+    environment:
+      - CONFIG=
+      - PERIOD=5m
+      - UPDATE_COOLDOWN_PERIOD=5m
+      - PUBLICIP_FETCHERS=all
+      - PUBLICIP_HTTP_PROVIDERS=all
+      - PUBLICIPV4_HTTP_PROVIDERS=all
+      - PUBLICIPV6_HTTP_PROVIDERS=all
+      - PUBLICIP_DNS_PROVIDERS=all
+      - PUBLICIP_DNS_TIMEOUT=3s
+      - HTTP_TIMEOUT=10s
+
+      # Web UI
+      - LISTENING_ADDRESS=:8000
+      - ROOT_URL=/
+
+      # Backup
+      - BACKUP_PERIOD=0 # 0 to disable
+      - BACKUP_DIRECTORY=/updater/data
+
+      # Other
+      - LOG_LEVEL=info
+      - LOG_CALLER=hidden
+      - SHOUTRRR_ADDRESSES=
+    restart: always
+```
+
+After configuring your preferences in the `docker-compose.yml`, launch the container.
+
+```sh
+cd ~/ddns-updater
+sudo docker-compose up -d
+```
+
+If you've launched this on your local machine, you can launch `localhost:8097` in your browser to see the results.
+
+### Nginx Reverse Proxy
+
+If you launched this service on a server, other machine, or just want to access it remotely via a domain name, you can use Nginx as a reverse proxy to expose the service publicly.
+
+Start by creating the Nginx configuration file.
+
+```sh
+sudo nano /etc/nginx/sites-available/ddns
+```
+
+Here's a basic example that should work properly.
+
+```conf
+server {
+    # If using 443, remember to include your ssl_certificate
+    # and ssl_certificate_key
+    listen [::]:80;
+    listen 80;
+    server_name ddns.example.com;
+
+    location / {
+        set $upstream_ao http://127.0.0.1:9380;
+        proxy_pass $upstream_ao;
+
+        # May need some additional proxy_* parameters,
+        # see the full example below if necessary
+    }
+}
+```
+
+Here's a full example that uses my Authelia authentication service to require authentication before someone can access the web page.
+
+```conf
+server {
+	if ($host ~ ^[^.]+\.example\.com$) {
+		return 301 https://$host$request_uri;
+	}
+
+	listen [::]:80;
+	listen 80;
+	server_name ddns.example.com;
+	return 404;
+}
+
+server {
+	listen [::]:443 ssl http2;
+	listen 443 ssl http2;
+	server_name ddns.example.com;
+	access_log  /var/log/nginx/ddns.access.log;
+	error_log   /var/log/nginx/ddns.error.log;
+
+	add_header X-Content-Type-Options "nosniff";
+	add_header X-XSS-Protection "1; mode=block";
+	add_header X-Frame-Options "DENY";
+	add_header Strict-Transport-Security "max-age=63072000; includeSubDomains";
+	add_header Referrer-Policy "no-referrer";
+
+	ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
+	ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
+	include /etc/letsencrypt/options-ssl-nginx.conf;
+	ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
+
+	location /authelia {
+		internal;
+		set $upstream_authelia http://127.0.0.1:9091/api/verify; #change the IP and Port to match the IP and Port of your Authelia container
+		proxy_pass_request_body off;
+		proxy_pass $upstream_authelia;
+		proxy_set_header Content-Length "";
+
+		# Timeout if the real server is dead
+		proxy_next_upstream error timeout invalid_header http_500 http_502 http_503;
+		client_body_buffer_size 128k;
+		proxy_set_header Host $host;
+		proxy_set_header X-Original-URL $scheme://$http_host$request_uri;
+		proxy_set_header X-Real-IP $remote_addr;
+		proxy_set_header X-Forwarded-For $remote_addr;
+		proxy_set_header X-Forwarded-Proto $scheme;
+		proxy_set_header X-Forwarded-Host $http_host;
+		proxy_set_header X-Forwarded-Uri $request_uri;
+		proxy_set_header X-Forwarded-Ssl on;
+		proxy_redirect  http://  $scheme://;
+		proxy_http_version 1.1;
+		proxy_set_header Connection "";
+		proxy_cache_bypass $cookie_session;
+		proxy_no_cache $cookie_session;
+		proxy_buffers 4 32k;
+
+		send_timeout 5m;
+		proxy_read_timeout 240;
+		proxy_send_timeout 240;
+		proxy_connect_timeout 240;
+	}
+
+	location / {
+		set $upstream_ddns http://127.0.0.1:8097; #change ddns to match your container name: $upstream_some-container-name or $upstream_somecontainername
+		proxy_pass $upstream_ddns; #change ddns to match your container name: $upstream_some-container-name or $upstream_somecontainername
+
+		auth_request /authelia;
+		auth_request_set $target_url https://$http_host$request_uri;
+		auth_request_set $user $upstream_http_remote_user;
+		auth_request_set $email $upstream_http_remote_email;
+		auth_request_set $groups $upstream_http_remote_groups;
+		proxy_set_header Remote-User $user;
+		proxy_set_header Remote-Email $email;
+		proxy_set_header Remote-Groups $groups;
+
+		error_page 401 =302 https://auth.example.com/?rd=$target_url; #change this to match your authentication domain/subdomain
+
+		client_body_buffer_size 128k;
+
+		proxy_next_upstream error timeout invalid_header http_500 http_502 http_503;
+
+		send_timeout 5m;
+		proxy_read_timeout 360;
+		proxy_send_timeout 360;
+		proxy_connect_timeout 360;
+
+		proxy_set_header Host $host;
+		proxy_set_header Upgrade $http_upgrade;
+		proxy_set_header Connection upgrade;
+		proxy_set_header Accept-Encoding gzip;
+		proxy_set_header X-Real-IP $remote_addr;
+		proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+		proxy_set_header X-Forwarded-Proto $scheme;
+		proxy_set_header X-Forwarded-Host $http_host;
+		proxy_set_header X-Forwarded-Uri $request_uri;
+		proxy_set_header X-Forwarded-Ssl on;
+		proxy_redirect  http://  $scheme://;
+		proxy_http_version 1.1;
+		proxy_set_header Connection "";
+		proxy_cache_bypass $cookie_session;
+		proxy_no_cache $cookie_session;
+		proxy_buffers 64 256k;
+
+		# set_real_ip_from 192.168.1.0/16; #make sure this matches your network setup
+		# real_ip_header CF-Connecting-IP;
+		# real_ip_recursive on;
+	}
+}
+```
+
+When complete, simply link the file and restart the web server.
+
+```sh
+sudo ln -s /etc/nginx/sites-available/ddns /etc/nginx/sites-enabled/ddns
+sudo systemctl restart nginx.service
+```
+
+Your ddns-updater service will now be available via `ddns.example.com`!
+