From 4b0198379cd516325b11d7893f873eb5544f4d63 Mon Sep 17 00:00:00 2001 From: Francois Deppierraz <francois@ctrlaltdel.ch> Date: Fri, 19 May 2017 13:07:39 +0200 Subject: [PATCH] Add support for NS records in custom domains --- management/dns_update.py | 18 ++++++++++++++++++ management/templates/custom-dns.html | 3 ++- 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/management/dns_update.py b/management/dns_update.py index 7299ce21..76daa40b 100755 --- a/management/dns_update.py +++ b/management/dns_update.py @@ -12,6 +12,11 @@ import dns.resolver from mailconfig import get_mail_domains from utils import shell, load_env_vars_from_file, safe_domain_name, sort_domains +# From https://stackoverflow.com/questions/3026957/how-to-validate-a-domain-name-using-regex-php/16491074#16491074 +# Thanks to Onur Yıldırım +# This regular expression matches domain names according to RFCs, it also accepts fqdn with an leading dot +DOMAIN_RE = "^(?!\-)(?:[a-zA-Z\d\-]{0,62}[a-zA-Z\d]\.){1,126}(?!\d+)[a-zA-Z\d]{1,63}(\.?)$" + def get_dns_domains(env): # Add all domain names in use by email users and mail aliases and ensure # PRIMARY_HOSTNAME is in the list. @@ -759,6 +764,9 @@ def set_custom_dns_record(qname, rtype, value, action, env): if qname != "_secondary_nameserver": raise ValueError("%s is not a domain name or a subdomain of a domain name managed by this box." % qname) + if not re.search(DOMAIN_RE, qname): + raise ValueError("Invalid name.") + # validate rtype rtype = rtype.upper() if value is not None and qname != "_secondary_nameserver": @@ -767,6 +775,16 @@ def set_custom_dns_record(qname, rtype, value, action, env): v = ipaddress.ip_address(value) # raises a ValueError if there's a problem if rtype == "A" and not isinstance(v, ipaddress.IPv4Address): raise ValueError("That's an IPv6 address.") if rtype == "AAAA" and not isinstance(v, ipaddress.IPv6Address): raise ValueError("That's an IPv4 address.") + elif rtype in ("CNAME", "NS"): + if rtype == "NS" and qname == zone: + raise ValueError("NS records can only be set for subdomains.") + + # ensure value has a trailing dot + if not value.endswith("."): + value = value + "." + + if not re.search(DOMAIN_RE, value): + raise ValueError("Invalid value.") elif rtype in ("CNAME", "TXT", "SRV", "MX", "SSHFP", "CAA"): # anything goes pass diff --git a/management/templates/custom-dns.html b/management/templates/custom-dns.html index ac530cd2..bd51d151 100644 --- a/management/templates/custom-dns.html +++ b/management/templates/custom-dns.html @@ -39,6 +39,7 @@ <option value="MX" data-hint="Enter record in the form of PRIORITY DOMAIN., including trailing period (e.g. 20 mx.example.com.).">MX (mail exchanger)</option> <option value="SRV" data-hint="Enter record in the form of PRIORITY WEIGHT PORT TARGET., including trailing period (e.g. 10 10 5060 sip.example.com.).">SRV (service record)</option> <option value="SSHFP" data-hint="Enter record in the form of ALGORITHM TYPE FINGERPRINT.">SSHFP (SSH fingerprint record)</option> + <option value="NS" data-hint="Enter a hostname to which this subdomain should be delegated to">NS (DNS subdomain delegation)</option> </select> </div> </div> @@ -126,7 +127,7 @@ <tr><td>email</td> <td>The email address of any administrative user here.</td></tr> <tr><td>password</td> <td>That user’s password.</td></tr> <tr><td>qname</td> <td>The fully qualified domain name for the record you are trying to set. It must be one of the domain names or a subdomain of one of the domain names hosted on this box. (Add mail users or aliases to add new domains.)</td></tr> -<tr><td>rtype</td> <td>The resource type. Defaults to <code>A</code> if omitted. Possible values: <code>A</code> (an IPv4 address), <code>AAAA</code> (an IPv6 address), <code>TXT</code> (a text string), <code>CNAME</code> (an alias, which is a fully qualified domain name — don’t forget the final period), <code>MX</code>, <code>SRV</code>, <code>SSHFP</code> or <code>CAA</code>.</td></tr> +<tr><td>rtype</td> <td>The resource type. Defaults to <code>A</code> if omitted. Possible values: <code>A</code> (an IPv4 address), <code>AAAA</code> (an IPv6 address), <code>TXT</code> (a text string), <code>CNAME</code> (an alias, which is a fully qualified domain name — don’t forget the final period), <code>MX</code>, <code>SRV</code>, <code>SSHFP</code>, <code>CAA</code> or <code>NS</code>.</td></tr> <tr><td>value</td> <td>For PUT, POST, and DELETE, the record’s value. If the <code>rtype</code> is <code>A</code> or <code>AAAA</code> and <code>value</code> is empty or omitted, the IPv4 or IPv6 address of the remote host is used (be sure to use the <code>-4</code> or <code>-6</code> options to curl). This is handy for dynamic DNS!</td></tr> </table>