content/blog/2024-03-15-self-hosting-ddns-updater.org

e8577d98faff1bed72ba6f2d204b8b44c6827c23
cleberg.net/content/blog/2024-03-15-self-hosting-ddns-updater.org rendered · source · history · blame · raw

305 lines · 10271 bytes

  1#+date:        <2024-03-15 Fri 00:00:00>
  2#+title:       Setup Instructions for Self-Hosted Dynamic DNS Updater with Docker and Cloudflare
  3#+description: Step-by-step configuration guide for deploying a Dynamic DNS updater using Docker Compose, integrating with Cloudflare API, and securing with Nginx reverse proxy.
  4#+slug:        self-hosting-ddns-updater
  5#+filetags:    :ddns:docker:nginx:
  6
  7#+caption: DDNS Updater Web View
  8[[https://img.cleberg.net/blog/20240315-ddns-updater/ddns.png]]
  9
 10[[https://github.com/qdm12/ddns-updater][DDNS Updater]] is a program to keep DNS A and/or AAAA records updated for multiple
 11DNS providers.
 12
 13If you've read any of my other posts, you'll notice that I have been searching
 14for and using a few different DDNS updating solutions for years. You'll also
 15notice that I love any projects that offer a Docker Compose solution.
 16
 17Luckily, DDNS Upater fits both of these preferences.
 18
 19** Installation
 20
 21To get started, always make sure to review the project's [[https://github.com/qdm12/ddns-updater/blob/master/README.md][README]]. I'll be
 22documenting my steps below, but they may have changed by the time you read this.
 23
 24The first step is to set up the directories and files required for the project.
 25
 26#+begin_src sh
 27mkdir ~/ddns-updater
 28mkdir ~/ddns-updater/data
 29touch ~/ddns-updater/data/config.json
 30#+end_src
 31
 32*** Configuration
 33
 34The main configuration you need to update is the =data/config.json= file. There
 35is a large list of supported providers in the README, but I'm going to use
 36Cloudflare in this example.
 37
 38#+begin_src sh
 39nano ~/ddns-updater/data/config.json
 40#+end_src
 41
 42When setting up the configuration for Cloudflare, you'll need the following:
 43
 44- Required Parameters
 45  - ="zone_identifier"= is the Zone ID of your site from the domain overview
 46    page
 47  - ="host"= is your host and can be ="@"=, a subdomain or the wildcard ="*"=.
 48    See [[https://github.com/qdm12/ddns-updater/issues/243#issuecomment-928313949][this issue comment for context]].
 49  - ="ttl"= integer value for record TTL in seconds (specify 1 for automatic)
 50  - One of the following ([[https://developers.cloudflare.com/fundamentals/api/get-started/][how to find API keys]]):
 51    - Email ="email"= and Global API Key ="key"=
 52    - User service key ="user_service_key"=
 53    - API Token ="token"=, configured with DNS edit permissions for your DNS
 54      name's zone
 55- Optional Parameters
 56  - ="proxied"= can be set to =true= to use the proxy services of Cloudflare
 57  - ="ip_version"= can be =ipv4= (A records), or =ipv6= (AAAA records) or =ipv4
 58    or ipv6= (update one of the two, depending on the public ip found). It
 59    defaults to =ipv4 or ipv6=.
 60  - ="ipv6_suffix"= is the IPv6 interface identifier suffix to use. It can be
 61    for example =0:0:0:0:72ad:8fbb:a54e:bedd/64=. If left empty, it defaults to
 62    no suffix and the raw public IPv6 address obtained is used in the record
 63    updating.
 64
 65#+begin_src conf
 66{
 67  "settings": [
 68    {
 69      "provider": "cloudflare",
 70      "zone_identifier": "some id",
 71      "domain": "domain.com",
 72      "host": "@",
 73      "ttl": 1,
 74      "proxied": true,
 75      "token": "yourtoken",
 76      "ip_version": "ipv4",
 77      "ipv6_suffix": ""
 78    }
 79  ]
 80}
 81#+end_src
 82
 83Once you have configured the provider of your choice, correct the file and
 84directory permissions and ownership.
 85
 86#+begin_src sh
 87cd ~/ddns_updater
 88# Owned by user ID of Docker container (1000)
 89chown -R 1000 data
 90# all access (for creating json database file data/updates.json)
 91chmod 700 data
 92# read access only
 93chmod 400 data/config.json
 94#+end_src
 95
 96*** Docker Compose
 97
 98After creating the project structure, let's create the =docker-compose.yml=
 99file.
100
101#+begin_src sh
102nano ~/ddns_-pdater/docker-compose.yml
103#+end_src
104
105#+begin_src config
106version: "3.7"
107services:
108  ddns-updater:
109    image: qmcgaw/ddns-updater
110    container_name: ddns-updater
111    network_mode: bridge
112    ports:
113      - 8097:8000/tcp # Change the 8097 value to whichever port you want to use
114    volumes:
115      - ./data:/updater/data
116    environment:
117      - CONFIG=
118      - PERIOD=5m
119      - UPDATE_COOLDOWN_PERIOD=5m
120      - PUBLICIP_FETCHERS=all
121      - PUBLICIP_HTTP_PROVIDERS=all
122      - PUBLICIPV4_HTTP_PROVIDERS=all
123      - PUBLICIPV6_HTTP_PROVIDERS=all
124      - PUBLICIP_DNS_PROVIDERS=all
125      - PUBLICIP_DNS_TIMEOUT=3s
126      - HTTP_TIMEOUT=10s
127
128      # Web UI
129      - LISTENING_ADDRESS=:8000
130      - ROOT_URL=/
131
132      # Backup
133      - BACKUP_PERIOD=0 # 0 to disable
134      - BACKUP_DIRECTORY=/updater/data
135
136      # Other
137      - LOG_LEVEL=info
138      - LOG_CALLER=hidden
139      - SHOUTRRR_ADDRESSES=
140    restart: always
141#+end_src
142
143After configuring your preferences in the =docker-compose.yml=, launch the
144container.
145
146#+begin_src sh
147cd ~/ddns-updater
148sudo docker-compose up -d
149#+end_src
150
151If you've launched this on your local machine, you can launch =localhost:8097=
152in your browser to see the results.
153
154*** Nginx Reverse Proxy
155
156If you launched this service on a server, other machine, or just want to access
157it remotely via a domain name, you can use Nginx as a reverse proxy to expose
158the service publicly.
159
160Start by creating the Nginx configuration file.
161
162#+begin_src sh
163sudo nano /etc/nginx/sites-available/ddns
164#+end_src
165
166Here's a basic example that should work properly.
167
168#+begin_src conf
169server {
170    # If using 443, remember to include your ssl_certificate
171    # and ssl_certificate_key
172    listen [::]:80;
173    listen 80;
174    server_name ddns.example.com;
175
176    location / {
177        set $upstream_ao http://127.0.0.1:9380;
178        proxy_pass $upstream_ao;
179
180        # May need some additional proxy_* parameters,
181        # see the full example below if necessary
182    }
183}
184#+end_src
185
186Here's a full example that uses my Authelia authentication service to require
187authentication before someone can access the web page.
188
189#+begin_src conf
190server {
191    if ($host ~ ^[^.]+\.example\.com$) {
192        return 301 https://$host$request_uri;
193    }
194
195    listen [::]:80;
196    listen 80;
197    server_name ddns.example.com;
198    return 404;
199}
200
201server {
202    listen [::]:443 ssl http2;
203    listen 443 ssl http2;
204    server_name ddns.example.com;
205    access_log  /var/log/nginx/ddns.access.log;
206    error_log   /var/log/nginx/ddns.error.log;
207
208    add_header X-Content-Type-Options "nosniff";
209    add_header X-XSS-Protection "1; mode=block";
210    add_header X-Frame-Options "DENY";
211    add_header Strict-Transport-Security "max-age=63072000; includeSubDomains";
212    add_header Referrer-Policy "no-referrer";
213
214    ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
215    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
216    include /etc/letsencrypt/options-ssl-nginx.conf;
217    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
218
219    location /authelia {
220        internal;
221        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
222        proxy_pass_request_body off;
223        proxy_pass $upstream_authelia;
224        proxy_set_header Content-Length "";
225
226        # Timeout if the real server is dead
227        proxy_next_upstream error timeout invalid_header http_500 http_502 http_503;
228        client_body_buffer_size 128k;
229        proxy_set_header Host $host;
230        proxy_set_header X-Original-URL $scheme://$http_host$request_uri;
231        proxy_set_header X-Real-IP $remote_addr;
232        proxy_set_header X-Forwarded-For $remote_addr;
233        proxy_set_header X-Forwarded-Proto $scheme;
234        proxy_set_header X-Forwarded-Host $http_host;
235        proxy_set_header X-Forwarded-Uri $request_uri;
236        proxy_set_header X-Forwarded-Ssl on;
237        proxy_redirect  http://  $scheme://;
238        proxy_http_version 1.1;
239        proxy_set_header Connection "";
240        proxy_cache_bypass $cookie_session;
241        proxy_no_cache $cookie_session;
242        proxy_buffers 4 32k;
243
244        send_timeout 5m;
245        proxy_read_timeout 240;
246        proxy_send_timeout 240;
247        proxy_connect_timeout 240;
248    }
249
250    location / {
251        set $upstream_ddns http://127.0.0.1:8097; #change ddns to match your container name: $upstream_some-container-name or $upstream_somecontainername
252        proxy_pass $upstream_ddns; #change ddns to match your container name: $upstream_some-container-name or $upstream_somecontainername
253
254        auth_request /authelia;
255        auth_request_set $target_url https://$http_host$request_uri;
256        auth_request_set $user $upstream_http_remote_user;
257        auth_request_set $email $upstream_http_remote_email;
258        auth_request_set $groups $upstream_http_remote_groups;
259        proxy_set_header Remote-User $user;
260        proxy_set_header Remote-Email $email;
261        proxy_set_header Remote-Groups $groups;
262
263        error_page 401 =302 https://auth.example.com/?rd=$target_url; #change this to match your authentication domain/subdomain
264
265        client_body_buffer_size 128k;
266
267        proxy_next_upstream error timeout invalid_header http_500 http_502 http_503;
268
269        send_timeout 5m;
270        proxy_read_timeout 360;
271        proxy_send_timeout 360;
272        proxy_connect_timeout 360;
273
274        proxy_set_header Host $host;
275        proxy_set_header Upgrade $http_upgrade;
276        proxy_set_header Connection upgrade;
277        proxy_set_header Accept-Encoding gzip;
278        proxy_set_header X-Real-IP $remote_addr;
279        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
280        proxy_set_header X-Forwarded-Proto $scheme;
281        proxy_set_header X-Forwarded-Host $http_host;
282        proxy_set_header X-Forwarded-Uri $request_uri;
283        proxy_set_header X-Forwarded-Ssl on;
284        proxy_redirect  http://  $scheme://;
285        proxy_http_version 1.1;
286        proxy_set_header Connection "";
287        proxy_cache_bypass $cookie_session;
288        proxy_no_cache $cookie_session;
289        proxy_buffers 64 256k;
290
291        # set_real_ip_from 192.168.1.0/16; #make sure this matches your network setup
292        # real_ip_header CF-Connecting-IP;
293        # real_ip_recursive on;
294    }
295}
296#+end_src
297
298When complete, simply link the file and restart the web server.
299
300#+begin_src sh
301sudo ln -s /etc/nginx/sites-available/ddns /etc/nginx/sites-enabled/ddns
302sudo systemctl restart nginx.service
303#+end_src
304
305Your ddns-updater service will now be available via =ddns.example.com=!