Blame view

README.md 29 KB
Frederik Lindenaar authored
1
2
3
4
dyndns.pl
=========

Perl CGI-BIN script to handle Dynamic DNS updates through HTTP (e.g. from a
Frederik Lindenaar authored
5
6
router), updating DNS records through secure DNS update statements to run your
own Dynamic DNS Service.
Frederik Lindenaar authored
7
Frederik Lindenaar authored
8
**Version 1.1**, latest version, documentation and bugtracker available on my
Frederik Lindenaar authored
9
10
[GitLab instance](https://gitlab.lindenaar.net/scripts/dyndns)
Frederik Lindenaar authored
11
Copyright (c) 2013 - 2019 Frederik Lindenaar. free for distribution under the
Frederik Lindenaar authored
12
13
14
15
16
GNU License, see [below](#license)


Introduction
------------
Frederik Lindenaar authored
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
`dyndns.pl` provides a simple interface to allow Dynamic DNS updates for DNS
zones through HTTP requests. It is intended  for routers and (aDSL) modems to
register their IP address by simply opening a URL (this is supported by most
modern devices) but can also be used by end-users (either directly by using a
client). The script itself uses DNS' `nsupdate` calls to perform the update.
With this script you can integrate devices not supporting `nsupdate` and
environments where the master DNS server is not publicly available. The script
suits my setup/and needs and still might have glitches, but turned out to be a
very stable solution the last 6 years on both Linux as well as MacOS.

Please [see below](#integration) on how to setup the client side including:
  * [Cisco Routers](#cisco_integration)
  * [AVM Fritz!Box routers](#fritzbox_integration)
  * [Synology DSM](#synology_integration) (NAS)

In case you have any comments / questions or issues, please raise them through
my [GitLab instance](https://gitlab.lindenaar.net/scripts/dyndns) so that other
users can benefit and respond. Please also use this to submit setup instructions
for other devices you have set up for inclusion in this document.


Setup of the server side
========================
Frederik Lindenaar authored
40
This script is to be executed as CGI-BIN script by a web server. As it is
Frederik Lindenaar authored
41
42
43
44
written in Perl, it requires that installed (which is pretty standard on \*nix
platforms). This description covers the installation on Apache 2.4 and should be
similar for other web servers, with ISC Bind v9. For performance reasons
consider using the Apache mod_perl module for highly a volatile domain.
Frederik Lindenaar authored
45
Frederik Lindenaar authored
46
47
48

<a name=installation>Installation</a>
-------------------------------------
Frederik Lindenaar authored
49
50
51
The setup of this solution consists of the following steps:

  1. Ensure that the Perl modules CGI and Net::DNS are installed.
Frederik Lindenaar authored
52
     * on Debian/Ubuntu linux this can be done by:
Frederik Lindenaar authored
53
       ~~~
Frederik Lindenaar authored
54
       sudo apt-get install libcgi-pm-perl libnet-dns-perl
Frederik Lindenaar authored
55
       ~~~
Frederik Lindenaar authored
56
     * or directly from CPAN (assuming that is installed):
Frederik Lindenaar authored
57
       ~~~
Frederik Lindenaar authored
58
       cpan CGI Net::DNS
Frederik Lindenaar authored
59
       ~~~
Frederik Lindenaar authored
60
61
62
63
64
65
66
67
68
69
70
71
72
73

  2. Install the file `dyndns.pl` either in your cgi-bin directory or in a
     separate folder

  3. Update the configuration section at the top of the script to match your
     environment (see the section on [configuration](#configuration) below).
     The least you need to change `$DNSServer` to point to your DNS server and
     you probably want to have a look at the `$AllowDebugKey` (useful for
     getting things started but you want to set this to 'off' in production.

  4. To have a nicer URL (or in case the script is not installed in the web
     server's cgi-bin directory) add the following line to your Apache virtual
     host configuration (replacing `[INSTALL_DIR]` with the install directory):
Frederik Lindenaar authored
74
     ~~~
Frederik Lindenaar authored
75
     ScriptAlias /dyndns   [INSTALL_DIR]/dyndns.pl
Frederik Lindenaar authored
76
     ~~~
Frederik Lindenaar authored
77
78
79
80
81

     in case you have installed the script in a non-standard folder, you will
     also need the following to make this work on Apache 2.4 (again replacing
     `[INSTALL_DIR]` with the install directory):
Frederik Lindenaar authored
82
     ~~~
Frederik Lindenaar authored
83
84
85
86
87
     <Directory [INSTALL_DIR]/>
           AllowOverride None
           Options +ExecCGI -MultiViews -Indexes
           Require all granted
     </Directory>
Frederik Lindenaar authored
88
     ~~~
Frederik Lindenaar authored
89
90

     reload apache with `/etc/init.d/apache reload` to make the script
Frederik Lindenaar authored
91
92
93
94
     available at <http://myserver.mydomain.tld/dyndns>.

     It is also possible to run as a virtual host, [see below](#VirtualHost) for
     an example of that.
Frederik Lindenaar authored
95
96
97
98
99
100
101

  5. To setup your Bind nameserver, either update `named.conf` direcly or create
     a separate file (e.g. `named.dyndns.conf` in the Bind configuration
     directory and include that in your setup with the `include` directive
     (e.g. `include "named.dyndns.conf";`). For a basic dynamic DNS setup a
     configuration like below is required:
Frederik Lindenaar authored
102
     ~~~
Frederik Lindenaar authored
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
     // Define the keys for DynDNS
     key "dyndns.mydomain.tld" {
         algorithm hmac-md5; secret "QdDJC7QVYmsCxgWoSAUmBg==";
     };

     key "siteuser" {
         algorithm hmac-md5; secret "R6Xkbn+FP85Hq3EDNmv+GQ==";
     };

     // Define the DDNS zone
     zone "dyndns.mydomain.tld" IN {
          type master;
          file "dyndns/db.dyndns.mydomain.tld";

          // enable this for list and expire support
          // allow-transfer { 192.168.0.2; };

          update-policy {
                 grant dyndns.mydomain.tld zonesub ANY;
                 grant siteuser name site.dyndns.mydomain.tld ANY;
          };
     };
Frederik Lindenaar authored
125
     ~~~
Frederik Lindenaar authored
126
127
128
129
130

     The above defines a domain zone file `dyndns/db.dyndns.mydomain.tld` with
     two signer/keys. *siteuser* only can update `site.dyndns.mydomain.tld`
     while *dyndns.mydomain.tld* can update all entries in the domain (intended
     for expiry). If you intend to use expiry or want to be able to retrieve a
Frederik Lindenaar authored
131
     list of all entries, uncomment the `allow-transfer` statement and update
Frederik Lindenaar authored
132
133
134
135
136
137
138
     the IP adres to that of your web server.

     To seed these entries with fresh keys), use the following
     commands and copy the generated keys into the config file.

       * to generate a new key *dyndns.mydomain.tld*:
Frederik Lindenaar authored
139
         ~~~
Frederik Lindenaar authored
140
         ddns-confgen -a hmac-md5 -k dyndns.mydomain.tld -z dyndns.mydomain.tld
Frederik Lindenaar authored
141
         ~~~
Frederik Lindenaar authored
142
143
144

       * generate the required configuration for *siteuser* (or any new user):
Frederik Lindenaar authored
145
         ~~~
Frederik Lindenaar authored
146
         ddns-confgen -a hmac-md5 -k siteuser -s site.dyndns.mydomain.tld
Frederik Lindenaar authored
147
         ~~~
Frederik Lindenaar authored
148
149
150
151

  6. Generate an initial zone file like the one below for the dyndns domain in
     the location specified in the config file above.
Frederik Lindenaar authored
152
     ~~~
Frederik Lindenaar authored
153
154
     $TTL 3600       ; 1 hour
     @               IN  SOA auth.dns.mydomain.tld. hostmaster.mydomain.tld. (
Frederik Lindenaar authored
155
                         2019000001 ; serial
Frederik Lindenaar authored
156
157
158
159
160
161
162
163
                         43200      ; refresh (12 hours)
                          3600      ; retry (1 hour)
                         86400      ; expire (24 hours)
                           900      ; minimum (15 minutes)
                     )
                     TXT   "Dynamic DNS zone for mydomain.tld"

     site            A       1.2.3.4
Frederik Lindenaar authored
164
     ~~~
Frederik Lindenaar authored
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184

     Please note that Bind will rewrite this file and you need to be careful
     with it. Entries do not need to exist initially, as long as the signer/key
     has access to a hostname, the entry can be created (so the only thing
     required to setup a new host is to register a signer/key).

     If you do need to update the zone file to change entries, consider using
     the bind `nsupdate` command instead. If that is inconvenient, the following
     steps must be followed not to get our of sync with Bind's zone database
     (please note that when you have views this works slightly differently):
       * execute the command `rndc freeze [zone]`
       * edit the zone file for [zone]
       * execute the command `rndc unfreeze [zone]`

  7. Last step is to instruct bind to reload it's configuration (`rndc reload`)
     and test the setup. please see [below how to invoke the script](#invoking).

     URLs / checks to perform are:
     * <http://myserver.mydomain.tld/dyndns/list?domain=dyndns.mydomain.tld>
       to list the entries in the domain (requires zone transfer rights!)
Frederik Lindenaar authored
185
     * <http://myserver.mydomain.tld/dyndns/update?host=site.dyndns.mydomain.tld&user=siteuser&secret=......>
Frederik Lindenaar authored
186
       to add/update a site and
Frederik Lindenaar authored
187
     * <http://myserver.mydomain.tld/dyndns/delete?host=site.dyndns.mydomain.tld&user=siteuser&secret=......>
Frederik Lindenaar authored
188
189
190
191
192
       to delete (clear) it.

Please read the section below as well on the configuration and different modes
(operations) available.
Frederik Lindenaar authored
193
Frederik Lindenaar authored
194
195
196
<a name=configuration>Configuration</a>
---------------------------------------
At the top of the script is a "Configuration" section, which contains the
Frederik Lindenaar authored
197
198
199
200
configurable options of the scripts. As of version 1.1 the script also supports
a [configuration file](#config_file) so that modifying the script is no longer
required.
Frederik Lindenaar authored
201
202

Parameter        | Description
Frederik Lindenaar authored
203
204
:----------------|:-------------------------------------------------------------
`$ConfigFile`    | Enable/disable config file support, [see below](config_file)
Frederik Lindenaar authored
205
`$DNSServer`     | IP address of the DNS Server to send DNS update requests to
Frederik Lindenaar authored
206
207
208
`@DNSDomain`     | How to determine the host's domain name, [see below](#conf_dnszone)
`$DomainListKey` | Secret required to use the list mode, set to '' to always enable and to 'off' to disable this mode
`$ExpandCNAMEs`  | Max. CNAME lookups for `$host` (0 to disable), [see below](#conf_cname)
Frederik Lindenaar authored
209
`$AllowDebugKey` | Output debug log after result when `debug` parameter equals this value. Set to '' to always enable and to 'off' to disable debugging
Frederik Lindenaar authored
210
`$AuthMode`      | Defines how to authenticate DNS update requests, [see below](#conf_auth)
Frederik Lindenaar authored
211
212
`$StaticSigner`  | Static signer ID to be used for AuthMode `static` or `both`
`$StaticKey`     | Static signing key to be used for AuthMode `static` or `both`
Frederik Lindenaar authored
213
214
`$RequireRR`     | Require an existing DNS record of this type to allow updates
`$ExpireAfter`   | Expire time for registrations in minutes, hours, weeks or seconds. Format is number optionally followed by m, h, w, s (seconds is default)
Frederik Lindenaar authored
215
216
`@ReplaceRR`     | List of DNS Record types to remove (clear) as part of update.
`$UpdateTXT`     | Add host TXT record during update with this text followed by a timestamp. Used for expiry (so don't change!), leave empty to not add this
Frederik Lindenaar authored
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
`$DeleteTXT`     | Set TXT record upon deletion with this text and a timestamp.
`$RecordTTL`     | TTL for created records in minutes, hours, weeks or seconds. Format is number optionally followed by m, h, w, s (seconds is default)

Please note: when changing the script all values must be correctly quoted, etc.
not to break the script. Therefore as of version 1.1 a config file is supported
(preferred), [see below](config_file).


#### <a name=config_file>Configuration File</a>
The script can read its settings from a config located in the same directory as
the script and with the extension `.cfg` (ignoring a `.pl` extension) so the
default config file would be `dyndns.cfg`. The behavior of how to support the
config file is configured through the variable `$ConfigFile` and can be one of:
  * `optional` - config file is read if it exists, this is the default
  * `required` - config file is read and must exist (or the script will fail)
  * `ignore` - config file is ignored and not read, configuration in the script

The general format of the config file is `keyword = value`, see the table below
for a mapping of the parameters to keywords. For lists (variables starting with
a `@`) the value is comma-separated. The config file supports comments, ignores
empty lines, starting/trailing spaces and everything following a `#`. Refer to
`dyndns.cfg.dist` for an example config file. Please note that the script will
fail if it encounters an error or unknown keyword in the config file.

Parameter         | Config Setting     | Default value
:-----------------|:-------------------|:------------------------------
`$AllowDebugKey`  | `allow_debug_key`  | `off` (debugging disabled)
`$AuthMode`       | `auth_mode`        | `remote` ([see below](#conf_auth))
`$DeleteTXT`      | `delete_txt`       | `DynDNS cleared on`
`$DNSServer`      | `dns_server`       | `192.168.1.1`
`@DNSDomain`      | `dns_domain`       | `?, !, 0` ([see below](#conf_dnszone))
`$DomainListKey`  | `domain_list_key`  | `off` (domain list disabled)
`$ExpandCNAMEs`   | `expand_cnames`    | `1` (1 level, [see below](#conf_cname))
`$ExpireAfter`    | `expire_after`     | `1w` (1 week, [see below](#expire))
`$RecordTTL`      | `record_ttl`       | `1h` (1 hour)
`$RequireRR`      | `require_rr`       |
`@ReplaceRR`      | `replace_rr`       | `A, AAAA, TXT`
`$StaticKey`      | `static_key`       |
`$StaticSigner`   | `static_signer`    |
`$UpdateTXT`      | `update_txt`       | `Last DynDNS update on`

Please note that since `$ConfigFile` determines config file support, it cannot
be configured in the file. By default the config file is optional not to break
existing configurations.


#### <a name=conf_dnszone>DNS Zone (Domain Name) Selection</a>
In order to send the right update request to the DNS server, the correct DNS
zone to update must be determined based on the request's hostname. Most of the
time an update for `hostname.subdomain.mydomain.tld` is an update of `hostname`
the DNS zone `subdomain.mydomain.tld` and then the defaults are sufficient.
However, in some scenarios (e.g. one of my use cases) an update should be sent
for hostname `hostname.subdomain` in the zone `mydomain.tld` instead. The DNS
server cannot figure this out itself (at least ISC's Bind9 can not) so it is
implemented here.

The array `@DNSDomain` contains a list of values matched against the hostname
to determine the DNS zone to update and can contain:

Value            | match hostname ending with
:----------------|:-------------------------------------------------------
`"?"`            | the domain name from parameter `domain`
`"!"`            | server name the HTTP(S) request was sent to
`0`              | domain from hostname (strip of everythin till first `.`)
positive number  | last # parts from hostname
negative number  | last # parts of server name the HTTP(S) request was sent to
any other string | use value specified

The first parameter matching the hostname's end will be used. The default is
`( '?', '!', 0 )`, which should be OK in most cases.


#### <a name=conf_cname>CNAME Support</a>
Frederik Lindenaar authored
290
291
292
293
294
295
296
297
298
299
300
301
The script supports using separate subdomain (e.g. dyndns.mydomain.tld) for
dynamic DNS and CNAMEs to entries in that subdomain from another zone (e.g.
mydomain.tld). The advantage of such a setup is that only one zone (SOA file)
within the domain will have frequent updates (and hence requires a short TTL
so prevent it from being cached) while the rest of the domain's zones can be
cached.

The user does not have to notice this at all as script supports check whether
the host provided is a CNAME and if so, performs the request for the actual
hostname instead of the provided one. The value of `$ExpandCNAMEs` determines
the maximum number of CNAME lookups supported (so nesting is allowed and this
limits the level of nesting to prevent loops).
Frederik Lindenaar authored
302
Frederik Lindenaar authored
303
304
305
To disable lookups for CNAME expansion, set `$ExpandCNAMEs` to 0.
Frederik Lindenaar authored
306
#### <a name=conf_auth>Authentication Modes</a>
Frederik Lindenaar authored
307
308
309
310
For signing DNS update requests sent to the DNS server the script supports 3
ways to obtain the signer and key:

AuthMode | Description
Frederik Lindenaar authored
311
:--------|:---------------------------------------------------------------------
Frederik Lindenaar authored
312
*static* | use only static authentication information from `$StaticSigner` and`$StaticKey` (and ignore authentication information provided in the request)
Frederik Lindenaar authored
313
*remote* | use only authentication information provided in the request
Frederik Lindenaar authored
314
315
316
317
318
319
320
*both*   | use authentication information provided in the request (fields `user` and `secret`) when provided, otherwise use static values from `$StaticSigner` and `$StaticKey`. Please note that this is checked per parameter


Supported Operations
--------------------
The script can perform the following operations (modes):
Frederik Lindenaar authored
321
322
323
324
325
326
327
Mode   | Description            | Required Parameters | Optional Parameters
:------|:-----------------------|:--------------------|:----------------------
list   | List zone __***__      | `secret` __***__    | `domain`__**__
view   | Show host's DNS entry  | `host`              |
update | Update/add a DDNS host | `host` + auth.__*__ | `ipv4addr`, `ipv6addr`
delete | Remove registration    | `host` + auth.__*__ |
expire | Expire registrations   | `domain`__**__ + auth.__*__
Frederik Lindenaar authored
328
Frederik Lindenaar authored
329
330
__*__   modes that change DNS require authentication, depending on the value of
        `$AuthMode` the parameters `user` and `secret` may be required
Frederik Lindenaar authored
331
332
333
        (`$AuthMode` *remote*) required or optional (`$AuthMode` *both*)

__**__  in case `domain` is omitted, it will be determined using the `host`
Frederik Lindenaar authored
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
        parameter, if provided, or by using the virtualhost the script runs on
        based on the `@DNSDomain` setting

__***__ list mode is only available when `$DomainListKey` is not set to `off`,
        in case `$DomainListKey` is not empty, `secret` is required and must
        equal the key in `$DomainListKey`


#### <a name=req_params>Request Parameters</a>
The script supports (requires) the following parameters (please see the table
above for which is needed for what mode):

Parameter  | Description
:----------|:-------------------------------------------------------------------
`mode`     | the action to perform (if not provided as part of the path name)
`domain`   | domain for list/expire request, determined from `host` if ommitted
`host`     | hostname to act on, expand CNAMEs max. `$ExpandCNAMEs` levels deep
`ip`       | alias / shortcut for `ipv4addr`
`ipv4addr` | The IPv4 address to register for the host (update mode only) __*__
`ipv6`     | alias / shortcut for `ipv6addr`
`ipv6addr` | The IPv6 address to register for the host (update mode only) __*__
`user`     | signer of the DNS Update, used for `AuthMode` *remote* and *both*
`secret`   | key to sign the DNS Update, used for `AuthMode` *remote* and *both*, also used as `$DomainListKey` for list mode.
`debug`    | debug key, show debug information if this equals `$AllowDebugKey`

__*__   in update mode, if `ipv4addr` or `ipv6addr` is set to `auto` in the
Frederik Lindenaar authored
360
        request, the CGI variable `$REMOTE_ADDR` (the client address), its value
Frederik Lindenaar authored
361
362
        will be used instead as IPv4/IPv6 address. __Please Note__ that if both
        are omitted existing addresses will be removed!
Frederik Lindenaar authored
363
364
Frederik Lindenaar authored
365
#### <a name=invoking>Invoking the script</a>
Frederik Lindenaar authored
366
367
368
369
370
The script is implemented using the perl CGI module so for testing purposes it
can be called from the command line with parameters as arguments, i.e.

    ./dyndns.pl mode=expire domain=mydomain.tld debug=....
Frederik Lindenaar authored
371
372
373
Which is quite handy for debugging. Please note that the Perl CGI library sets
`$REMOTE_ADDR` to 127.0.0.1, the server name in this case will be `localhost`
and that the output is the HTML result.
Frederik Lindenaar authored
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393

The standard way to use the script is to place it in the cgi-bin folder your
server, which allows it to be called as:

    http://myserver.mydomain.tld/cgi-bin/dyndns.pl?mode=list&domain=mydomain.tld&debug=...

As per the setup instruction above, there are various ways to make the URL
cleaner, i.e.

    http://myserver.mydomain.tld/dyndns?mode=list&domain=mydomain.tld&debug=...

The script also supports include the mode variable as part of the location
(using and the CGI variable `$PATH_INFO` to set the mode), i.e.

    http://myserver.mydomain.tld/cgi-bin/dyndns.pl/list?domain=mydomain.tld&debug=...

When combining the setup would become:

    http://myserver.mydomain.tld/dyndns/list?domain=mydomain.tld&debug=...
Frederik Lindenaar authored
394
395
396
397
If using a dedicated virtual host [see below](#VirtualHost) it becomes:

    http://myserver.mydomain.tld/list?domain=mydomain.tld&debug=...
Frederik Lindenaar authored
398
399
400
Which is how I use it.
Frederik Lindenaar authored
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
### <a name=expire>Expiring Records</a>
The script can expire registrations after a while. For this, it must add a TXT
record containing the date of the last change (on by default) and when requested
it will remove any entry older than the value configured in `$ExpireAfter`.

Please note that:
  * as this is dependent on the value of a TXT record, it may fail if these
    records are updated through another method.
  * there is no security implemented (other than the value of `$ExpireAfter`)

To initiate the expiry, the script must be called with two parameters:
  1. `mode` should be set to `expire`
  2. `domain` must be set to the DNS Zone (domain) to run against.

Both can be setup easily in cron with entries like:

~~~
# Samples to run the expiry every hour

# Cron fields definition:
#.---------------- minute (0 - 59)
#| .------------- hour (0 - 23)
#| |  .---------- day of month (1 - 31)
#| |  |  .------- month (1 - 12) OR jan,feb,mar,apr ...
#| |  |  |  .---- day of week (0 - 6) (Sunday=0 or 7) OR sun,mon,tue,wed,thu,fri,sat
#| |  |  |  |
#* *  *  *  * user-name command to be executed

# Directly run the script, does not require specific permissions
15 *  *  *  * www-data  [INSTALL_DIR]/dyndns.pl mode=expire domain=mydomain.tld

# example using curl
15 *  *  *  * www-data  curl https://myserver.mydomain.tld/expire?domain=mydomain.tld > /dev/null
~~~
Frederik Lindenaar authored
437
438
439
440
Name Server Setup Requirements
------------------------------
As the script is only translating requests, depends heavily on the setup of the
nameserver. The DNS server (obviously) needs to allow DNS updates. In addition
Frederik Lindenaar authored
441
to the setup described above, please note that:
Frederik Lindenaar authored
442
443
444
445
446
447
448
449
450
451
452

  * For the modes list and expire to work, the script needs to perform a DNS
    zone transfer (AXFR). This must be allowed for the host running the script.
  * for each DDNS host, a signer and key must have the rights to change the
    entry (one signer/key can be setup to change multiple hosts).
  * The expire mode requires a signer and key that can change all DDNS hosts
    within the domain.
  * The script currently only supports HMAC-MD5 type keys (limitation of the
    used Perl Net::DNS library). The keys setup in the nameservers must
    therefore be of the same time or authentication won't work.
Frederik Lindenaar authored
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
This setup has been tested against ISC Bind version 9 and scales pretty well.
Adding the keys to the nameserver configuration is still manual in my setup but
bit difficult script, if needed.


<a name=VirtualHost>Configure as Virtual Host</a>
-------------------------------------------------
Running dyndns.pl on a Virtual Host is possible using `mod_rewrite`. This is how
I use it as it allows the URLs to become even more simple, e.g.:

  * to update: `https://dyndns.mydomain.tld/update/hostname.mydomain.tld?secret=...`
  * to delete: `https://dyndns.mydomain.tld/delete/hostname.mydomain.tld?secret=...`
  * to view:   `https://dyndns.mydomain.tld/view/hostname.mydomain.tld`
  * to list:   `https://dyndns.mydomain.tld/list/mydomain.tld?secret=...`
  * to expire: `https://dyndns.mydomain.tld/expire/mydomain.tld`

An example Apache 2.4 config is shown below (please replace `[INSTALL_DIR]` with
the install directory and obviously replace the server name as well):

~~~
<VirtualHost *:80 *:443>
  ServerName dyndns.mydomain.tld

  # Enable URL Rewriting
  RewriteEngine On
Frederik Lindenaar authored
478
Frederik Lindenaar authored
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
  # Enforce HTTPS access
  RewriteCond %{HTTPS} off
  RewriteRule /         https://%{HTTP_HOST}%{REQUEST_URI}      [R]

  # re-route everything to the dyndns script
  RewriteRule (.*)      /dyndns/$1                              [PT]

  ScriptAlias /dyndns           [INSTALL_DIR]/dyndns.pl

  <Directory [INSTALL_DIR]>
        AllowOverride None
        Options +ExecCGI -MultiViews
        Require all granted
  </Directory>

</VirtualHost>
~~~


<a name=integration>Integration with devices</a>
================================================
Integration on routers and other devices is straigtforward, provided do support
DDNS registrations using a custom URL. The Basic format for the registration
URL to register is:

~~~
Frederik Lindenaar authored
505
https://SERVER/cgi-bin/dyndns/update?host=HOSTNAME&ip=IPADDRESS&secret=KEY
Frederik Lindenaar authored
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
~~~

Check the [list of parameters supported](req_params) for all available options,
the above URL contains the absolute minimum where:

Parameter   | Value
:-----------|:---------------------------------------------------------------
`SERVER`    | is the host the script is installed on
`HOSTNAME`  | is the client's hostname as configured in the DNS server
`SECRET`    | is the secret key as configured in the DNS server
`IPADDRESS` | is the ipv4 address (often dynamic, can also be set to `auto`)

Depending on how you have configured the URL of the script to be, the path
(`/cgi-bin/dyndns/` may need to be altered as per your setup).

Please note that:
  * The generated secret may contain a `+`, which must be encoded correctly in
    the request or it will fail. I found that not all clients (e.g. a Fritz!Box)
    do this correctly, make sure that your secrets either don't contain a `+`
    or encode it manually (replace any `+` with `%2B` in that case).
  * In case the IP address of the device is behind NAT and you want to have the
    public address register, use the `auto` value for parameters `ip`/`ipv4addr`
    and `ipv6`/`ipv6addr` to have the script auto-detect it (though that this
    can only be used for either an IPv4 or an IPv6 address and will only work
    for devices registering using that protocol!)
  * Some devices have a preference to connect over IPv6 (e.g. Cisco routers).
    This can be used to register the IPv4 and IPv6 addresses together by passing
    the IPv4 address as parameter en setting the IPv6 parameter to `auto`.
  * Some devices (e.g. a Fritz!Box) support a separate URL for IPv4 and IPv6
    registrations. Unfortunately this script cannot handle this yet and will
    unregister a previous registration when the second request comes in. Please
    raise a ticket if you have such a situation to work on a solution together.

To check whether the client's registration was successful (and correct) visit:

~~~
https://SERVER/cgi-bin/dyndns/view?host
~~~


<a name=cisco_integration>Cisco Routers</a>
-------------------------------------------
For Cisco routers add the following config:
Frederik Lindenaar authored
550
~~~
Frederik Lindenaar authored
551
552
553
554
555
ip ddns update method DYNDNS
 HTTP
  add https://SERVER/cgi-bin/dyndns/update?host=<h>&ip=<a>&secret=SECRET
  remove https://SERVER/cgi-bin/dyndns/delete?host=<h>&secret=SECRET
 interval maximum 0 1 0 0
Frederik Lindenaar authored
556
~~~
Frederik Lindenaar authored
557
558
559
560
561
562
563

replacing `SERVER` for the host the script is installed on and `SECRET`
for a DNS key authorized to update the record. The cisco router will replace <a>
and <h> with the IPv4 address and hostname.

To setup interface `Dialer0` to register as `hostname.dyndns.mydomain.tld` add:
Frederik Lindenaar authored
564
~~~
Frederik Lindenaar authored
565
566
567
interface Dialer0
 ip ddns update hostname hostname.dyndns.mydomain.tld
 ip ddns update DYNDNS
Frederik Lindenaar authored
568
~~~
Frederik Lindenaar authored
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628

Which instructs to register using the address of Dialer0 as soon as that is up
or changes (this also works for non-dialer devices).

Please note that before entering the `?` as part of the URL, a `CTRL`-`V` is
required to prevent the Cisco CLI to list the available command parameters.


<a name=fritzbox_integration>AVM Fritz!Box routers</a>
------------------------------------------------------
To setup DynDNS on a Fritz!Box perform the following steps:
  * Login to your Fritz!Box as an admin user
  * Open the 'Internet' menu an go through the 'External Access' page
  * Open the 'DynDNS' tab
  * Enable the 'Use DynDNS' checkbox
  * Select DynDNS Provider: 'User-defined'
  * Enter the following data (replacing `YOURDOMAIN` with your DynDNS domain
    and `SERVER` with your server name - check the rest of the URL as well!)
    - Update URL: `https://SERVER/cgi-bin/dyndns/update?host=<domain>&ip=<ipaddr>&secret=<pass>`
    - Domain name: hostname setup in DNS
    - Username/Email: put here something, not used unless you add it to the URL
    - Password: secret key setup in DNS
  * Click 'Apply' to store and activate the DDNS registrations

Check [this page](https://service.avm.de/help/en/FRITZ-Box-7581/017p2/hilfe_dyndns)
for the available parameters that can be substituted in the URL.

The status of the DynDNS registrations can be seen in the 'Internet' menu on the
'Online Monitor' page.

To stop DynDNS registrations, uncheck 'Use DynDNS' from the same screen.


<a name=synology_integration>Synology DSM (NAS)</a>
---------------------------------------------------
To setup DynDNS on a Synology NAS (DSM 6 or later) perform the following steps:
  * Login to your Synology NAS DSM as an admin user
  * Open the Control Panel and go to 'External Access'
  * Click 'Customize' to add a new DDNS provider
  * Enter the following data (replacing `YOURDOMAIN` with your DynDNS domain
    and `SERVER` with your server name - check the rest of the URL as well!)
    - Service Provider: `YOURDOMAIN`
    - Query URL `https://SERVER/cgi-bin/dyndns/update?host=__HOSTNAME__&ip=__MYIP__&secret=__PASSWORD__`
  * Click Save to store the custom DDNS provider
  * Click Add to register the DDNS registration and enter:
    - Service Provider: select the name you have just added (`*YOURDOMAIN`)
    - Hostname: hostname setup in DNS
    - Username/Email: put here something, not used unless you add it to the URL
    - Password/Key: secret key setup in DNS
  * Click 'OK' to store and activate the DDNS registrations

After a while the screen should display that the status is Normal and when the
last update occurred.

To stop DDNS registrations, 'Delete' the registration from the same screen.


<a name=license>License</a>
=============================
This script, documentation and configuration examples are free software: you can
Frederik Lindenaar authored
629
630
631
632
redistribute and/or modify it under the terms of the GNU General Public License
as published by the Free Software Foundation, either version 3 of the License,
or (at your option) any later version.
Frederik Lindenaar authored
633
This script, documentation and configuration examples are distributed in the
Frederik Lindenaar authored
634
635
636
637
638
639
hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
General Public License for more details.

You should have received a copy of the GNU General Public License along with
this program.  If not, download it from <http://www.gnu.org/licenses/>.
Frederik Lindenaar authored
640