Commit 348b8d9a292ae45029e2a5b59038dfa7d8a759c4

Authored by Frederik Lindenaar
1 parent e22945cc

First description on how to install and available parameters, etc.

Showing 1 changed file with 335 additions and 0 deletions
README.md 0 → 100644
  1 +dyndns.pl
  2 +=========
  3 +
  4 +Perl CGI-BIN script to handle Dynamic DNS updates through HTTP (e.g. from a
  5 +router), updating DNS records through secure DNS update statements.
  6 +
  7 +**Version 1.0**, latest version, documentation and bugtracker available on my
  8 +[GitLab instance](https://gitlab.lindenaar.net/scripts/dyndns)
  9 +
  10 +Copyright (c) 2013 - 2015 Frederik Lindenaar. free for distribution under the
  11 +GNU License, see [below](#license)
  12 +
  13 +
  14 +Introduction
  15 +------------
  16 +This script provides a simple interface to allow Dynamic DNS updates for DNS
  17 +zones. It is intended to be used for routers and (aDSL) modems to register their
  18 +IP address by simply opening a URL (this is supported by many modern devices)
  19 +but can also be used by end-users (either directly by using a client). Please
  20 +bear in mind that this script suits my setup and still might have glitches, but
  21 +so far turned out to be a quite stable solution for my needs and I use it in a
  22 +production setup. In case you have any comments / questions or issues, please
  23 +raise them through my
  24 +[GitLab instance](https://gitlab.lindenaar.net/scripts/dyndns) so that all
  25 +users benefit.
  26 +
  27 +Setup
  28 +-----
  29 +This script is to be executed as CGI-BIN script by a web server. As it is
  30 +written in Perl, it requires that installed (which is pretty standard nowadays
  31 +on all *nix platforms). This description covers the installation on Apache 2.4,
  32 +which should be similar for other web servers, with ISC Bind v9. For performance
  33 +reasons consider using the Apache mod_perl module for highly a volatile domain.
  34 +
  35 +The setup of this solution consists of the following steps:
  36 +
  37 + 1. Ensure that the Perl modules CGI and Net::DNS are installed.
  38 + * on Debian/Ubunto linux this can be done by:
  39 +
  40 + ~~~~
  41 + sudo apt-get install libcgi-pm-perl libnet-dns-perl
  42 + ~~~~
  43 +
  44 + * or if you have cpan installed:
  45 +
  46 + ~~~
  47 + cpan CGI Net::DNS
  48 + ~~~
  49 +
  50 + 2. Install the file `dyndns.pl` either in your cgi-bin directory or in a
  51 + separate folder
  52 +
  53 + 3. Update the configuration section at the top of the script to match your
  54 + environment (see the section on [configuration](#configuration) below).
  55 + The least you need to change `$DNSServer` to point to your DNS server and
  56 + you probably want to have a look at the `$AllowDebugKey` (useful for
  57 + getting things started but you want to set this to 'off' in production.
  58 +
  59 + 4. To have a nicer URL (or in case the script is not installed in the web
  60 + server's cgi-bin directory) add the following line to your Apache virtual
  61 + host configuration (replacing `[INSTALL_DIR]` with the install directory):
  62 +
  63 + ScriptAlias /dyndns [INSTALL_DIR]/dyndns.pl
  64 +
  65 + in case you have installed the script in a non-standard folder, you will
  66 + also need the following to make this work on Apache 2.4 (again replacing
  67 + `[INSTALL_DIR]` with the install directory):
  68 +
  69 + ~~~
  70 + <Directory [INSTALL_DIR]/>
  71 + AllowOverride None
  72 + Options +ExecCGI -MultiViews -Indexes
  73 + Require all granted
  74 + </Directory>
  75 + ~~~
  76 +
  77 + reload apache with `/etc/init.d/apache reload` to make the script
  78 + available at <http://myserver.mydomain.tld/dyndns>
  79 +
  80 + 5. To setup your Bind nameserver, either update `named.conf` direcly or create
  81 + a separate file (e.g. `named.dyndns.conf` in the Bind configuration
  82 + directory and include that in your setup with the `include` directive
  83 + (e.g. `include "named.dyndns.conf";`). For a basic dynamic DNS setup a
  84 + configuration like below is required:
  85 +
  86 + ~~~
  87 + // Define the keys for DynDNS
  88 + key "dyndns.mydomain.tld" {
  89 + algorithm hmac-md5; secret "QdDJC7QVYmsCxgWoSAUmBg==";
  90 + };
  91 +
  92 + key "siteuser" {
  93 + algorithm hmac-md5; secret "R6Xkbn+FP85Hq3EDNmv+GQ==";
  94 + };
  95 +
  96 + // Define the DDNS zone
  97 + zone "dyndns.mydomain.tld" IN {
  98 + type master;
  99 + file "dyndns/db.dyndns.mydomain.tld";
  100 +
  101 + // enable this for list and expire support
  102 + // allow-transfer { 192.168.0.2; };
  103 +
  104 + update-policy {
  105 + grant dyndns.mydomain.tld zonesub ANY;
  106 + grant siteuser name site.dyndns.mydomain.tld ANY;
  107 + };
  108 + };
  109 + ~~~
  110 +
  111 + The above defines a domain zone file `dyndns/db.dyndns.mydomain.tld` with
  112 + two signer/keys. *siteuser* only can update `site.dyndns.mydomain.tld`
  113 + while *dyndns.mydomain.tld* can update all entries in the domain (intended
  114 + for expiry). If you intend to use expiry or want to be able to retrieve a
  115 + list of all entries, comment out the `allow-transfer` statement and update
  116 + the IP adres to that of your web server.
  117 +
  118 + To seed these entries with fresh keys), use the following
  119 + commands and copy the generated keys into the config file.
  120 +
  121 + * to generate a new key *dyndns.mydomain.tld*:
  122 +
  123 + ~~~
  124 + ddns-confgen -a hmac-md5 -k dyndns.mydomain.tld -z dyndns.mydomain.tld
  125 + ~~~
  126 +
  127 + * generate the required configuration for *siteuser* (or any new user):
  128 +
  129 + ~~~
  130 + ddns-confgen -a hmac-md5 -k siteuser -s site.dyndns.mydomain.tld
  131 + ~~~
  132 +
  133 + 6. Generate an initial zone file like the one below for the dyndns domain in
  134 + the location specified in the config file above.
  135 +
  136 + ~~~
  137 + $TTL 3600 ; 1 hour
  138 + @ IN SOA auth.dns.mydomain.tld. hostmaster.mydomain.tld. (
  139 + 2015051401 ; serial
  140 + 43200 ; refresh (12 hours)
  141 + 3600 ; retry (1 hour)
  142 + 86400 ; expire (24 hours)
  143 + 900 ; minimum (15 minutes)
  144 + )
  145 + TXT "Dynamic DNS zone for mydomain.tld"
  146 +
  147 + site A 1.2.3.4
  148 + ~~~
  149 +
  150 + Please note that Bind will rewrite this file and you need to be careful
  151 + with it. Entries do not need to exist initially, as long as the signer/key
  152 + has access to a hostname, the entry can be created (so the only thing
  153 + required to setup a new host is to register a signer/key).
  154 +
  155 + If you do need to update the zone file to change entries, consider using
  156 + the bind `nsupdate` command instead. If that is inconvenient, the following
  157 + steps must be followed not to get our of sync with Bind's zone database
  158 + (please note that when you have views this works slightly differently):
  159 +
  160 + * execute the command `rndc freeze [zone]`
  161 + * edit the zone file for [zone]
  162 + * execute the command `rndc unfreeze [zone]`
  163 +
  164 + 7. Last step is to instruct bind to reload it's configuration (`rndc reload`)
  165 + and test the setup. please see [below how to invoke the script](#invoking).
  166 +
  167 + URLs / checks to perform are:
  168 +
  169 + * <http://myserver.mydomain.tld/dyndns/list?domain=dyndns.mydomain.tld>
  170 + to list the entries in the domain (requires zone transfer rights!)
  171 + * <http://myserver.mydomain.tld/dyndns/update?host=site.dyndns.mydomain.tld&user=siteuser&key=......>
  172 + to add/update a site and
  173 + * <http://myserver.mydomain.tld/dyndns/delete?host=site.dyndns.mydomain.tld&user=siteuser&key=......>
  174 + to delete (clear) it.
  175 +
  176 +Please read the section below as well on the configuration and different modes
  177 +(operations) available.
  178 +
  179 +<a name=configuration>Configuration</a>
  180 +---------------------------------------
  181 +
  182 +At the top of the script is a "Configuration" section, which contains the
  183 +configurable options of the scripts.
  184 +
  185 +Parameter | Description
  186 +:----------------+:-------------------------------------------------------------
  187 +`$DNSServer` | IP address of the DNS Server to send DNS update requests to
  188 +`$ExpandCNAMEs` | Max. CNAME lookups for `$host` (0 to disable), see below
  189 +`$AllowDebugKey` | Output debug log after result when `debug` parameter equals this value. Set to '' to always enable and to 'off' to disable debugging
  190 +`$AuthMode` | Defines how to authenticate DNS update requests, see below
  191 +`$StaticSigner` | Static signer ID to be used for AuthMode `static` or `both`
  192 +`$StaticKey` | Static signing key to be used for AuthMode `static` or `both`
  193 +`$RequireRR` | Require an existing DNS record of this type to allow updates.
  194 +`$ExpireAfter` | Expire time for registrations in minutes, hours, weeks or seconds. Format is number optionally followed by m, h, w, s (seconds is default).
  195 +`@ReplaceRR` | List of DNS Record types to remove (clear) as part of update.
  196 +`$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
  197 +`$DeleteTXT` | Set TXT record upon deletion with this text and a timestamp.
  198 +
  199 +Please note that the values must be correctly quoted, etc. not to break the script.
  200 +
  201 +
  202 +#### CNAME Support
  203 +The script supports using separate subdomain (e.g. dyndns.mydomain.tld) for
  204 +dynamic DNS and CNAMEs to entries in that subdomain from another zone (e.g.
  205 +mydomain.tld). The advantage of such a setup is that only one zone (SOA file)
  206 +within the domain will have frequent updates (and hence requires a short TTL
  207 +so prevent it from being cached) while the rest of the domain's zones can be
  208 +cached.
  209 +
  210 +The user does not have to notice this at all as script supports check whether
  211 +the host provided is a CNAME and if so, performs the request for the actual
  212 +hostname instead of the provided one. The value of `$ExpandCNAMEs` determines
  213 +the maximum number of CNAME lookups supported (so nesting is allowed and this
  214 +limits the level of nesting to prevent loops).
  215 +To disable lookups for CNAME expansion, set `$ExpandCNAMEs` to 0.
  216 +
  217 +
  218 +#### Authentication Modes
  219 +For signing DNS update requests sent to the DNS server the script supports 3
  220 +ways to obtain the signer and key:
  221 +
  222 +AuthMode | Description
  223 +:--------+:---------------------------------------------------------------------
  224 +*static* | use only static authentication information from `$StaticSigner` and`$StaticKey` (and ignore authentication information provided in the request)
  225 +*remote* | use only authentication information provided in the request
  226 +*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
  227 +
  228 +
  229 +Supported Operations
  230 +--------------------
  231 +The script can perform the following operations (modes):
  232 +
  233 +Mode | Description | Required Parameters | Optional Parameters
  234 +:------+:-------------------------+:--------------------+:----------------------
  235 +list | Show DDNS domain entries | `domain`__**__ |
  236 +view | Show DDNS hostname entry | `host` |
  237 +update | Update/add a DDNS host | `host` + auth.__*__ | `ipv4addr`, `ipv6addr`
  238 +delete | Remove DDNS registration | `host` + auth.__*__ |
  239 +expire | Expire registrations | `domain`__**__ + auth.__*__
  240 +
  241 +__*__ Modes that change registrations require authentication, depending on the
  242 + value of `$AuthMode` the parameters `user` and `secret` may be required
  243 + (`$AuthMode` *remote*) required or optional (`$AuthMode` *both*)
  244 +
  245 +__**__ in case `domain` is omitted, it will be determined using the `host`
  246 + parameter, if provided
  247 +
  248 +
  249 +#### Parameters
  250 +The script supports the following parameters (please see the table above for which is needed for what mode):
  251 +
  252 +Parameter | Description
  253 +:---------+:--------------------------------------------------------------------
  254 +`mode` | the action to perform (if not provided as part of the path name)
  255 +`domain` | domain for list/expire request, determined from `host` if ommitted
  256 +`host` | hostname to act on, expand CNAMEs max. `$ExpandCNAMEs` levels deep
  257 +`ip` | alias / shortcut for `ipv4addr`
  258 +`ipv4addr`| The IPv4 address to register for the host (update mode only) __*__
  259 +`ipv6addr`| The IPv6 address to register for the host (update mode only) __*__
  260 +`user` | signer of the DNS Update, used for `AuthMode` *remote* and *both*
  261 +`key` | key to sign the DNS Update, used for `AuthMode` *remote* and *both*
  262 +`debug` | debug key, show debug information if this equals `$AllowDebugKey`
  263 +
  264 +__*__ in update mode, if `ipv4addr` or `ipv6addr` is not provided with the
  265 + request, the CGI variable `$REMOTE_ADDR` (the client address), its value
  266 + will be used instead as IPv4/IPv6 address.
  267 +
  268 +
  269 +#### <a name="invoking">Invoking the script</a>
  270 +The script is implemented using the perl CGI module so for testing purposes it
  271 +can be called from the command line with parameters as arguments, i.e.
  272 +
  273 + ./dyndns.pl mode=expire domain=mydomain.tld debug=....
  274 +
  275 +Which is quite handy for debugging purposes. Please note that the Perl CGI
  276 +library sets `$REMOTE_ADDR` to 127.0.0.1 and that the output will always be
  277 +the HTML-based result.
  278 +
  279 +The standard way to use the script is to place it in the cgi-bin folder your
  280 +server, which allows it to be called as:
  281 +
  282 + http://myserver.mydomain.tld/cgi-bin/dyndns.pl?mode=list&domain=mydomain.tld&debug=...
  283 +
  284 +As per the setup instruction above, there are various ways to make the URL
  285 +cleaner, i.e.
  286 +
  287 + http://myserver.mydomain.tld/dyndns?mode=list&domain=mydomain.tld&debug=...
  288 +
  289 +The script also supports include the mode variable as part of the location
  290 +(using and the CGI variable `$PATH_INFO` to set the mode), i.e.
  291 +
  292 + http://myserver.mydomain.tld/cgi-bin/dyndns.pl/list?domain=mydomain.tld&debug=...
  293 +
  294 +When combining the setup would become:
  295 +
  296 + http://myserver.mydomain.tld/dyndns/list?domain=mydomain.tld&debug=...
  297 +
  298 +Which is how I use it.
  299 +
  300 +
  301 +Name Server Setup Requirements
  302 +------------------------------
  303 +As the script is only translating requests, depends heavily on the setup of the
  304 +nameserver. The DNS server (obviously) needs to allow DNS updates. In addition
  305 +to the setup described above, please note that:
  306 +
  307 + * For the modes list and expire to work, the script needs to perform a DNS
  308 + zone transfer (AXFR). This must be allowed for the host running the script.
  309 + * for each DDNS host, a signer and key must have the rights to change the
  310 + entry (one signer/key can be setup to change multiple hosts).
  311 + * The expire mode requires a signer and key that can change all DDNS hosts
  312 + within the domain.
  313 + * The script currently only supports HMAC-MD5 type keys (limitation of the
  314 + used Perl Net::DNS library). The keys setup in the nameservers must
  315 + therefore be of the same time or authentication won't work.
  316 +
  317 +The solution scales reasonable well, although adding the keys to the nameserver
  318 +configuration is still manual in my setup (but since it does not happen that
  319 +often, it's no hassle). This setup has been tested against ISC Bind version 9.
  320 +
  321 +
  322 +<a name="license">License</a>
  323 +-----------------------------
  324 +This script, documentation and configration examples are free software: you can
  325 +redistribute and/or modify it under the terms of the GNU General Public License
  326 +as published by the Free Software Foundation, either version 3 of the License,
  327 +or (at your option) any later version.
  328 +
  329 +This script, documenatation and configuration examples are distributed in the
  330 +hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
  331 +warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  332 +General Public License for more details.
  333 +
  334 +You should have received a copy of the GNU General Public License along with
  335 +this program. If not, download it from <http://www.gnu.org/licenses/>.
... ...