content/blog/2024-03-15-self-hosting-ddns-updater.org
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=!