allow overriding the second nameserver with a secondary/slave server
fixes #151 fixes #223
This commit is contained in:
parent
092c842a87
commit
f42a1c5a74
|
@ -180,6 +180,22 @@ def dns_update():
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
return (str(e), 500)
|
return (str(e), 500)
|
||||||
|
|
||||||
|
@app.route('/dns/secondary-nameserver')
|
||||||
|
@authorized_personnel_only
|
||||||
|
def dns_get_secondary_nameserver():
|
||||||
|
from dns_update import get_custom_dns_config
|
||||||
|
return json_response({ "hostname": get_custom_dns_config(env).get("_secondary_nameserver") })
|
||||||
|
|
||||||
|
@app.route('/dns/secondary-nameserver', methods=['POST'])
|
||||||
|
@authorized_personnel_only
|
||||||
|
def dns_set_secondary_nameserver():
|
||||||
|
from dns_update import set_secondary_dns
|
||||||
|
try:
|
||||||
|
return set_secondary_dns(request.form.get('hostname'), env)
|
||||||
|
except ValueError as e:
|
||||||
|
return (str(e), 400)
|
||||||
|
|
||||||
|
|
||||||
@app.route('/dns/set/<qname>', methods=['POST'])
|
@app.route('/dns/set/<qname>', methods=['POST'])
|
||||||
@app.route('/dns/set/<qname>/<rtype>', methods=['POST'])
|
@app.route('/dns/set/<qname>/<rtype>', methods=['POST'])
|
||||||
@app.route('/dns/set/<qname>/<rtype>/<value>', methods=['POST'])
|
@app.route('/dns/set/<qname>/<rtype>/<value>', methods=['POST'])
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
import os, os.path, urllib.parse, datetime, re, hashlib, base64
|
import os, os.path, urllib.parse, datetime, re, hashlib, base64
|
||||||
import ipaddress
|
import ipaddress
|
||||||
import rtyaml
|
import rtyaml
|
||||||
|
import dns.resolver
|
||||||
|
|
||||||
from mailconfig import get_mail_domains
|
from mailconfig import get_mail_domains
|
||||||
from utils import shell, load_env_vars_from_file, safe_domain_name, sort_domains
|
from utils import shell, load_env_vars_from_file, safe_domain_name, sort_domains
|
||||||
|
@ -55,6 +56,11 @@ def get_custom_dns_config(env):
|
||||||
except:
|
except:
|
||||||
return { }
|
return { }
|
||||||
|
|
||||||
|
def write_custom_dns_config(config, env):
|
||||||
|
config_yaml = rtyaml.dump(config)
|
||||||
|
with open(os.path.join(env['STORAGE_ROOT'], 'dns/custom.yaml'), "w") as f:
|
||||||
|
f.write(config_yaml)
|
||||||
|
|
||||||
def do_dns_update(env, force=False):
|
def do_dns_update(env, force=False):
|
||||||
# What domains (and their zone filenames) should we build?
|
# What domains (and their zone filenames) should we build?
|
||||||
domains = get_dns_domains(env)
|
domains = get_dns_domains(env)
|
||||||
|
@ -105,7 +111,7 @@ def do_dns_update(env, force=False):
|
||||||
zonefiles[i][1] += ".signed"
|
zonefiles[i][1] += ".signed"
|
||||||
|
|
||||||
# Write the main nsd.conf file.
|
# Write the main nsd.conf file.
|
||||||
if write_nsd_conf(zonefiles, env):
|
if write_nsd_conf(zonefiles, additional_records, env):
|
||||||
# Make sure updated_domains contains *something* if we wrote an updated
|
# Make sure updated_domains contains *something* if we wrote an updated
|
||||||
# nsd.conf so that we know to restart nsd.
|
# nsd.conf so that we know to restart nsd.
|
||||||
if len(updated_domains) == 0:
|
if len(updated_domains) == 0:
|
||||||
|
@ -134,12 +140,22 @@ def do_dns_update(env, force=False):
|
||||||
def build_zone(domain, all_domains, additional_records, env, is_zone=True):
|
def build_zone(domain, all_domains, additional_records, env, is_zone=True):
|
||||||
records = []
|
records = []
|
||||||
|
|
||||||
# For top-level zones, define ourselves as the authoritative name server.
|
# For top-level zones, define the authoritative name servers.
|
||||||
|
#
|
||||||
|
# Normally we are our own nameservers. Some TLDs require two distinct IP addresses,
|
||||||
|
# so we allow the user to override the second nameserver definition so that
|
||||||
|
# secondary DNS can be set up elsewhere.
|
||||||
|
#
|
||||||
# 'False' in the tuple indicates these records would not be used if the zone
|
# 'False' in the tuple indicates these records would not be used if the zone
|
||||||
# is managed outside of the box.
|
# is managed outside of the box.
|
||||||
if is_zone:
|
if is_zone:
|
||||||
|
# Obligatory definition of ns1.PRIMARY_HOSTNAME.
|
||||||
records.append((None, "NS", "ns1.%s." % env["PRIMARY_HOSTNAME"], False))
|
records.append((None, "NS", "ns1.%s." % env["PRIMARY_HOSTNAME"], False))
|
||||||
records.append((None, "NS", "ns2.%s." % env["PRIMARY_HOSTNAME"], False))
|
|
||||||
|
# Define ns2.PRIMARY_HOSTNAME or whatever the user overrides.
|
||||||
|
secondary_ns = additional_records.get("_secondary_nameserver", "ns2." + env["PRIMARY_HOSTNAME"])
|
||||||
|
records.append((None, "NS", secondary_ns+'.', False))
|
||||||
|
|
||||||
|
|
||||||
# In PRIMARY_HOSTNAME...
|
# In PRIMARY_HOSTNAME...
|
||||||
if domain == env["PRIMARY_HOSTNAME"]:
|
if domain == env["PRIMARY_HOSTNAME"]:
|
||||||
|
@ -437,7 +453,7 @@ $TTL 1800 ; default time to live
|
||||||
|
|
||||||
########################################################################
|
########################################################################
|
||||||
|
|
||||||
def write_nsd_conf(zonefiles, env):
|
def write_nsd_conf(zonefiles, additional_records, env):
|
||||||
# Basic header.
|
# Basic header.
|
||||||
nsdconf = """
|
nsdconf = """
|
||||||
server:
|
server:
|
||||||
|
@ -465,6 +481,19 @@ zone:
|
||||||
name: %s
|
name: %s
|
||||||
zonefile: %s
|
zonefile: %s
|
||||||
""" % (domain, zonefile)
|
""" % (domain, zonefile)
|
||||||
|
|
||||||
|
# If a custom secondary nameserver has been set, allow zone transfers
|
||||||
|
# and notifies to that nameserver.
|
||||||
|
if additional_records.get("_secondary_nameserver"):
|
||||||
|
# Get the IP address of the nameserver by resolving it.
|
||||||
|
hostname = additional_records.get("_secondary_nameserver")
|
||||||
|
resolver = dns.resolver.get_default_resolver()
|
||||||
|
response = dns.resolver.query(hostname, "A")
|
||||||
|
ipaddr = str(response[0])
|
||||||
|
nsdconf += """\tnotify: %s NOKEY
|
||||||
|
provide-xfr: %s NOKEY
|
||||||
|
""" % (ipaddr, ipaddr)
|
||||||
|
|
||||||
|
|
||||||
# Check if the nsd.conf is changing. If it isn't changing,
|
# Check if the nsd.conf is changing. If it isn't changing,
|
||||||
# return False to flag that no change was made.
|
# return False to flag that no change was made.
|
||||||
|
@ -688,14 +717,38 @@ def set_custom_dns_record(qname, rtype, value, env):
|
||||||
config[qname][rtype] = value
|
config[qname][rtype] = value
|
||||||
|
|
||||||
# serialize & save
|
# serialize & save
|
||||||
config_yaml = rtyaml.dump(config)
|
write_custom_dns_config(config, env)
|
||||||
with open(os.path.join(env['STORAGE_ROOT'], 'dns/custom.yaml'), "w") as f:
|
|
||||||
f.write(config_yaml)
|
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
########################################################################
|
########################################################################
|
||||||
|
|
||||||
|
def set_secondary_dns(hostname, env):
|
||||||
|
config = get_custom_dns_config(env)
|
||||||
|
|
||||||
|
if hostname in (None, ""):
|
||||||
|
# Clear.
|
||||||
|
if "_secondary_nameserver" in config:
|
||||||
|
del config["_secondary_nameserver"]
|
||||||
|
else:
|
||||||
|
# Validate.
|
||||||
|
hostname = hostname.strip().lower()
|
||||||
|
resolver = dns.resolver.get_default_resolver()
|
||||||
|
try:
|
||||||
|
response = dns.resolver.query(hostname, "A")
|
||||||
|
except (dns.resolver.NoNameservers, dns.resolver.NXDOMAIN, dns.resolver.NoAnswer):
|
||||||
|
raise ValueError("Could not resolve the IP address of %s." % hostname)
|
||||||
|
|
||||||
|
# Set.
|
||||||
|
config["_secondary_nameserver"] = hostname
|
||||||
|
|
||||||
|
# Save and apply.
|
||||||
|
write_custom_dns_config(config, env)
|
||||||
|
return do_dns_update(env)
|
||||||
|
|
||||||
|
|
||||||
|
########################################################################
|
||||||
|
|
||||||
def justtestingdotemail(domain, records):
|
def justtestingdotemail(domain, records):
|
||||||
# If the domain is a subdomain of justtesting.email, which we own,
|
# If the domain is a subdomain of justtesting.email, which we own,
|
||||||
# automatically populate the zone where it is set up on dns4e.com.
|
# automatically populate the zone where it is set up on dns4e.com.
|
||||||
|
|
|
@ -1,16 +1,45 @@
|
||||||
<style>
|
<style>
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<h2>Custom DNS (Advanced)</h2>
|
<h2>Custom DNS</h2>
|
||||||
|
|
||||||
<p>It is possible to set custom DNS records on domains hosted here. For instance, you can create your own dynamic DNS service. To do so, you will need to call your box’s DNS API.</p>
|
<p class="text-warning">This is an advanced configuration page.</p>
|
||||||
|
|
||||||
<h4>The HTTP POST request</h4>
|
<p>It is possible to set custom DNS records on domains hosted here.</p>
|
||||||
|
|
||||||
|
<h3>Using a Secondary Nameserver</h3>
|
||||||
|
|
||||||
|
<p>If your TLD requires you to have two separate nameservers, you can either set up a secondary (aka “slave”) nameserver or, alternatively, set up <a href="#" onclick="return show_panel('external_dns')">external DNS</a> and ignore the DNS server on this box. If you choose to use a seconday/slave nameserver, you must find a seconday/slave nameserver service provider. Your domain name registrar or virtual cloud provider may provide this service for you. Once you set up the seconday/slave nameserver service, enter the hostname of <em>their</em> secondary nameserver:</p>
|
||||||
|
|
||||||
|
<form class="form-horizontal" role="form" onsubmit="do_set_secondary_dns(); return false;">
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="secondarydnsHostname" class="col-sm-1 control-label">Hostname</label>
|
||||||
|
<div class="col-sm-10">
|
||||||
|
<input type="text" class="form-control" id="secondarydnsHostname" placeholder="ns1.cloudprovider.com">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="col-sm-offset-1 col-sm-11">
|
||||||
|
<button type="submit" class="btn btn-primary">Update</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="col-sm-offset-1 col-sm-11">
|
||||||
|
<p class="small">Clear the box to use the box itself as secondary DNS, which is the default/normal setup.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<h3>Custom DNS API</h3>
|
||||||
|
|
||||||
|
<p>Use your box’s DNS API to set custom DNS records on domains hosted here. For instance, you can create your own dynamic DNS service.</p>
|
||||||
|
|
||||||
<p>Send a POST request like this:</p>
|
<p>Send a POST request like this:</p>
|
||||||
|
|
||||||
<pre>curl -d "" --user {email}:{password} https://{{hostname}}/admin/dns/set/<b>qname</b>[/<b>rtype</b>[/<b>value</b>]]</pre>
|
<pre>curl -d "" --user {email}:{password} https://{{hostname}}/admin/dns/set/<b>qname</b>[/<b>rtype</b>[/<b>value</b>]]</pre>
|
||||||
|
|
||||||
|
<h4>HTTP POST parameters</h4>
|
||||||
|
|
||||||
<table class="table">
|
<table class="table">
|
||||||
<thead><th>Parameter</th> <th>Value</th></thead>
|
<thead><th>Parameter</th> <th>Value</th></thead>
|
||||||
<tr><td>email</td> <td>The email address of any administrative user here.</td></tr>
|
<tr><td>email</td> <td>The email address of any administrative user here.</td></tr>
|
||||||
|
@ -41,5 +70,28 @@ curl -d "value=something%20here" --user me@mydomain.com:###### https://{{hostnam
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
function show_custom_dns() {
|
function show_custom_dns() {
|
||||||
|
api(
|
||||||
|
"/dns/secondary-nameserver",
|
||||||
|
"GET",
|
||||||
|
{ },
|
||||||
|
function(data) {
|
||||||
|
$('#secondarydnsHostname').val(data.hostname ? data.hostname : '');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function do_set_secondary_dns() {
|
||||||
|
api(
|
||||||
|
"/dns/secondary-nameserver",
|
||||||
|
"POST",
|
||||||
|
{
|
||||||
|
hostname: $('#secondarydnsHostname').val()
|
||||||
|
},
|
||||||
|
function(data) {
|
||||||
|
if (data == "") return; // nothing updated
|
||||||
|
show_modal_error("Secondary DNS", $("<pre/>").text(data));
|
||||||
|
},
|
||||||
|
function(err) {
|
||||||
|
show_modal_error("Secondary DNS", $("<pre/>").text(err));
|
||||||
|
});
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -25,9 +25,11 @@
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<h2>External DNS (Advanced)</h2>
|
<h2>External DNS</h2>
|
||||||
|
|
||||||
<p>Although your box is configured to serve its own DNS, it is possible to host your DNS elsewhere by copying the DNS zone information shown in the table below.</p>
|
<p class="text-warning">This is an advanced configuration page.</p>
|
||||||
|
|
||||||
|
<p>Although your box is configured to serve its own DNS, it is possible to host your DNS elsewhere — such as in the DNS control panel provided by your domain name registrar or virtual cloud provider — by copying the DNS zone information shown in the table below into your external DNS server’s control panel.</p>
|
||||||
|
|
||||||
<p>If you do so, you are responsible for keeping your DNS entries up to date! If you previously enabled DNSSEC on your domain name by setting a DS record at your registrar, you will likely have to turn it off before changing nameservers.</p>
|
<p>If you do so, you are responsible for keeping your DNS entries up to date! If you previously enabled DNSSEC on your domain name by setting a DS record at your registrar, you will likely have to turn it off before changing nameservers.</p>
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue