mirror of
https://github.com/mail-in-a-box/mailinabox.git
synced 2024-11-26 02:57:04 +00:00
merge #476 - multiple secondary NS servers and zone xfr-only servers
closes #476
This commit is contained in:
commit
216acb0eeb
@ -222,14 +222,14 @@ def dns_update():
|
|||||||
@authorized_personnel_only
|
@authorized_personnel_only
|
||||||
def dns_get_secondary_nameserver():
|
def dns_get_secondary_nameserver():
|
||||||
from dns_update import get_custom_dns_config, get_secondary_dns
|
from dns_update import get_custom_dns_config, get_secondary_dns
|
||||||
return json_response({ "hostname": get_secondary_dns(get_custom_dns_config(env)) })
|
return json_response({ "hostnames": get_secondary_dns(get_custom_dns_config(env), mode=None) })
|
||||||
|
|
||||||
@app.route('/dns/secondary-nameserver', methods=['POST'])
|
@app.route('/dns/secondary-nameserver', methods=['POST'])
|
||||||
@authorized_personnel_only
|
@authorized_personnel_only
|
||||||
def dns_set_secondary_nameserver():
|
def dns_set_secondary_nameserver():
|
||||||
from dns_update import set_secondary_dns
|
from dns_update import set_secondary_dns
|
||||||
try:
|
try:
|
||||||
return set_secondary_dns(request.form.get('hostname'), env)
|
return set_secondary_dns([ns.strip() for ns in re.split(r"[, ]+", request.form.get('hostnames') or "") if ns.strip() != ""], env)
|
||||||
except ValueError as e:
|
except ValueError as e:
|
||||||
return (str(e), 400)
|
return (str(e), 400)
|
||||||
|
|
||||||
|
@ -144,7 +144,10 @@ def build_zone(domain, all_domains, additional_records, www_redirect_domains, en
|
|||||||
records.append((None, "NS", "ns1.%s." % env["PRIMARY_HOSTNAME"], False))
|
records.append((None, "NS", "ns1.%s." % env["PRIMARY_HOSTNAME"], False))
|
||||||
|
|
||||||
# Define ns2.PRIMARY_HOSTNAME or whatever the user overrides.
|
# Define ns2.PRIMARY_HOSTNAME or whatever the user overrides.
|
||||||
secondary_ns = get_secondary_dns(additional_records) or ("ns2." + env["PRIMARY_HOSTNAME"])
|
# User may provide one or more additional nameservers
|
||||||
|
secondary_ns_list = get_secondary_dns(additional_records, mode="NS") \
|
||||||
|
or ["ns2." + env["PRIMARY_HOSTNAME"]]
|
||||||
|
for secondary_ns in secondary_ns_list:
|
||||||
records.append((None, "NS", secondary_ns+'.', False))
|
records.append((None, "NS", secondary_ns+'.', False))
|
||||||
|
|
||||||
|
|
||||||
@ -462,17 +465,10 @@ zone:
|
|||||||
zonefile: %s
|
zonefile: %s
|
||||||
""" % (domain, zonefile)
|
""" % (domain, zonefile)
|
||||||
|
|
||||||
# If a custom secondary nameserver has been set, allow zone transfers
|
# If custom secondary nameservers have been set, allow zone transfers
|
||||||
# and notifies to that nameserver.
|
# and notifies to them.
|
||||||
if get_secondary_dns(additional_records):
|
for ipaddr in get_secondary_dns(additional_records, mode="xfr"):
|
||||||
# Get the IP address of the nameserver by resolving it.
|
nsdconf += "\n\tnotify: %s NOKEY\n\tprovide-xfr: %s NOKEY\n" % (ipaddr, ipaddr)
|
||||||
hostname = get_secondary_dns(additional_records)
|
|
||||||
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 file is changing. If it isn't changing,
|
# Check if the file 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.
|
||||||
@ -785,33 +781,63 @@ def set_custom_dns_record(qname, rtype, value, action, env):
|
|||||||
if made_change:
|
if made_change:
|
||||||
# serialize & save
|
# serialize & save
|
||||||
write_custom_dns_config(newconfig, env)
|
write_custom_dns_config(newconfig, env)
|
||||||
|
|
||||||
return made_change
|
return made_change
|
||||||
|
|
||||||
########################################################################
|
########################################################################
|
||||||
|
|
||||||
def get_secondary_dns(custom_dns):
|
def get_secondary_dns(custom_dns, mode=None):
|
||||||
for qname, rtype, value in custom_dns:
|
|
||||||
if qname == "_secondary_nameserver":
|
|
||||||
return value
|
|
||||||
return None
|
|
||||||
|
|
||||||
def set_secondary_dns(hostname, env):
|
|
||||||
|
|
||||||
if hostname in (None, ""):
|
|
||||||
# Clear.
|
|
||||||
set_custom_dns_record("_secondary_nameserver", "A", None, "set", env)
|
|
||||||
else:
|
|
||||||
# Validate.
|
|
||||||
hostname = hostname.strip().lower()
|
|
||||||
resolver = dns.resolver.get_default_resolver()
|
resolver = dns.resolver.get_default_resolver()
|
||||||
|
|
||||||
|
values = []
|
||||||
|
for qname, rtype, value in custom_dns:
|
||||||
|
if qname != '_secondary_nameserver': continue
|
||||||
|
for hostname in value.split(" "):
|
||||||
|
hostname = hostname.strip()
|
||||||
|
if mode == None:
|
||||||
|
# Just return the setting.
|
||||||
|
values.append(hostname)
|
||||||
|
|
||||||
|
# This is a hostname. Before including in zone xfr lines,
|
||||||
|
# resolve to an IP address. Otherwise just return the hostname.
|
||||||
|
if not hostname.startswith("xfr:"):
|
||||||
|
if mode == "xfr":
|
||||||
|
response = dns.resolver.query(hostname+'.', "A")
|
||||||
|
hostname = str(response[0])
|
||||||
|
values.append(hostname)
|
||||||
|
|
||||||
|
# This is a zone-xfer-only IP address. Do not return if
|
||||||
|
# we're querying for NS record hostnames. Only return if
|
||||||
|
# we're querying for zone xfer IP addresses - return the
|
||||||
|
# IP address.
|
||||||
|
elif mode == "xfr":
|
||||||
|
values.append(hostname[4:])
|
||||||
|
|
||||||
|
return values
|
||||||
|
|
||||||
|
def set_secondary_dns(hostnames, env):
|
||||||
|
if len(hostnames) > 0:
|
||||||
|
# Validate that all hostnames are valid and that all zone-xfer IP addresses are valid.
|
||||||
|
resolver = dns.resolver.get_default_resolver()
|
||||||
|
for item in hostnames:
|
||||||
|
if not item.startswith("xfr:"):
|
||||||
|
# Resolve hostname.
|
||||||
try:
|
try:
|
||||||
response = dns.resolver.query(hostname, "A")
|
response = dns.resolver.query(item, "A")
|
||||||
except (dns.resolver.NoNameservers, dns.resolver.NXDOMAIN, dns.resolver.NoAnswer):
|
except (dns.resolver.NoNameservers, dns.resolver.NXDOMAIN, dns.resolver.NoAnswer):
|
||||||
raise ValueError("Could not resolve the IP address of %s." % hostname)
|
raise ValueError("Could not resolve the IP address of %s." % item)
|
||||||
|
else:
|
||||||
|
# Validate IP address.
|
||||||
|
try:
|
||||||
|
v = ipaddress.ip_address(item[4:]) # raises a ValueError if there's a problem
|
||||||
|
if not isinstance(v, ipaddress.IPv4Address): raise ValueError("That's an IPv6 address.")
|
||||||
|
except ValueError:
|
||||||
|
raise ValueError("'%s' is not an IPv4 address." % item[4:])
|
||||||
|
|
||||||
# Set.
|
# Set.
|
||||||
set_custom_dns_record("_secondary_nameserver", "A", hostname, "set", env)
|
set_custom_dns_record("_secondary_nameserver", "A", " ".join(hostnames), "set", env)
|
||||||
|
else:
|
||||||
|
# Clear.
|
||||||
|
set_custom_dns_record("_secondary_nameserver", "A", None, "set", env)
|
||||||
|
|
||||||
# Apply.
|
# Apply.
|
||||||
return do_dns_update(env)
|
return do_dns_update(env)
|
||||||
|
@ -370,12 +370,9 @@ def check_dns_zone(domain, env, output, dns_zonefiles):
|
|||||||
# the TLD, and so we're not actually checking the TLD. For that we'd need
|
# the TLD, and so we're not actually checking the TLD. For that we'd need
|
||||||
# to do a DNS trace.
|
# to do a DNS trace.
|
||||||
ip = query_dns(domain, "A")
|
ip = query_dns(domain, "A")
|
||||||
secondary_ns = get_secondary_dns(get_custom_dns_config(env)) or "ns2." + env['PRIMARY_HOSTNAME']
|
secondary_ns = get_secondary_dns(get_custom_dns_config(env), mode="NS") or ["ns2." + env['PRIMARY_HOSTNAME']]
|
||||||
existing_ns = query_dns(domain, "NS")
|
existing_ns = query_dns(domain, "NS")
|
||||||
correct_ns = "; ".join(sorted([
|
correct_ns = "; ".join(sorted(["ns1." + env['PRIMARY_HOSTNAME']] + secondary_ns))
|
||||||
"ns1." + env['PRIMARY_HOSTNAME'],
|
|
||||||
secondary_ns,
|
|
||||||
]))
|
|
||||||
if existing_ns.lower() == correct_ns.lower():
|
if existing_ns.lower() == correct_ns.lower():
|
||||||
output.print_ok("Nameservers are set correctly at registrar. [%s]" % correct_ns)
|
output.print_ok("Nameservers are set correctly at registrar. [%s]" % correct_ns)
|
||||||
elif ip == env['PUBLIC_IP']:
|
elif ip == env['PUBLIC_IP']:
|
||||||
|
@ -67,7 +67,8 @@
|
|||||||
|
|
||||||
<h3>Using a Secondary Nameserver</h3>
|
<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>
|
<p>If your TLD requires you to have two separate nameservers, you can either set up <a href="#" onclick="return show_panel('external_dns')">external DNS</a> and ignore the DNS server on this box entirely, or use the DNS server on this box but add a secondary (aka “slave”) nameserver.</p>
|
||||||
|
<p>If you choose to use a seconday nameserver, you must find a seconday nameserver service provider. Your domain name registrar or virtual cloud provider may provide this service for you. Once you set up the seconday nameserver service, enter the hostname (not the IP address) of <em>their</em> secondary nameserver in the box below.</p>
|
||||||
|
|
||||||
<form class="form-horizontal" role="form" onsubmit="do_set_secondary_dns(); return false;">
|
<form class="form-horizontal" role="form" onsubmit="do_set_secondary_dns(); return false;">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
@ -83,7 +84,11 @@
|
|||||||
</div>
|
</div>
|
||||||
<div id="secondarydns-clear-instructions" class="form-group" style="display: none">
|
<div id="secondarydns-clear-instructions" class="form-group" style="display: none">
|
||||||
<div class="col-sm-offset-1 col-sm-11">
|
<div class="col-sm-offset-1 col-sm-11">
|
||||||
<p class="small">Clear the input field above and click Update to use this machine itself as secondary DNS, which is the default/normal setup.</p>
|
<p class="small">
|
||||||
|
Multiple secondary servers can be separated with commas or spaces (i.e., <code>ns2.hostingcompany.com ns3.hostingcompany.com</code>).
|
||||||
|
To enable zone transfers to additional servers without listing them as secondary nameservers, add <code>xfr:IPADDRESS</code>.
|
||||||
|
Clear the input field above and click Update to use this machine itself as secondary DNS, which is the default/normal setup.
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
@ -152,8 +157,8 @@ function show_custom_dns() {
|
|||||||
"GET",
|
"GET",
|
||||||
{ },
|
{ },
|
||||||
function(data) {
|
function(data) {
|
||||||
$('#secondarydnsHostname').val(data.hostname ? data.hostname : '');
|
$('#secondarydnsHostname').val(data.hostnames.join(' '));
|
||||||
$('#secondarydns-clear-instructions').toggle(data.hostname != null);
|
$('#secondarydns-clear-instructions').toggle(data.hostnames.length > 0);
|
||||||
});
|
});
|
||||||
|
|
||||||
api(
|
api(
|
||||||
@ -210,7 +215,7 @@ function do_set_secondary_dns() {
|
|||||||
"/dns/secondary-nameserver",
|
"/dns/secondary-nameserver",
|
||||||
"POST",
|
"POST",
|
||||||
{
|
{
|
||||||
hostname: $('#secondarydnsHostname').val()
|
hostnames: $('#secondarydnsHostname').val()
|
||||||
},
|
},
|
||||||
function(data) {
|
function(data) {
|
||||||
if (data == "") return; // nothing updated
|
if (data == "") return; // nothing updated
|
||||||
|
Loading…
Reference in New Issue
Block a user