add an api for setting custom DNS records
Works like this: ```curl -d "" --user email:password https://.../admin/dns/set/qname/rtype/value``` where the rtype and value default to "A" and the remote IP address of the request, so that a simple, empty POST to ```https://.../admin/dns/set/desktop.mydomain.com``` will point desktop.mydomain.com to the caller's IPv4 address. closes #140
This commit is contained in:
parent
5d42c125eb
commit
df20d447a9
|
@ -28,6 +28,7 @@ server {
|
|||
rewrite ^/admin$ /admin/;
|
||||
location /admin/ {
|
||||
proxy_pass http://localhost:10222/;
|
||||
proxy_set_header X-Forwarded-For $remote_addr;
|
||||
}
|
||||
|
||||
# Roundcube Webmail configuration.
|
||||
|
|
|
@ -172,6 +172,30 @@ def dns_update():
|
|||
except Exception as e:
|
||||
return (str(e), 500)
|
||||
|
||||
@app.route('/dns/set/<qname>', methods=['POST'])
|
||||
@app.route('/dns/set/<qname>/<rtype>', methods=['POST'])
|
||||
@app.route('/dns/set/<qname>/<rtype>/<value>', methods=['POST'])
|
||||
@authorized_personnel_only
|
||||
def dns_set_record(qname, rtype="A", value=None):
|
||||
from dns_update import do_dns_update, set_custom_dns_record
|
||||
try:
|
||||
# Get the value from the URL, then the POST parameters, or if it is not set then
|
||||
# use the remote IP address of the request --- makes dynamic DNS easy. To clear a
|
||||
# value, '' must be explicitly passed.
|
||||
print(request.environ)
|
||||
if value is None:
|
||||
value = request.form.get("value")
|
||||
if value is None:
|
||||
value = request.environ.get("HTTP_X_FORWARDED_FOR") # normally REMOTE_ADDR but we're behind nginx as a reverse proxy
|
||||
if value == '':
|
||||
# request deletion
|
||||
value = None
|
||||
if set_custom_dns_record(qname, rtype, value, env):
|
||||
return do_dns_update(env)
|
||||
return "OK"
|
||||
except ValueError as e:
|
||||
return (str(e), 400)
|
||||
|
||||
@app.route('/dns/dump')
|
||||
@authorized_personnel_only
|
||||
def dns_get_dump():
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
########################################################################
|
||||
|
||||
import os, os.path, urllib.parse, datetime, re, hashlib
|
||||
import ipaddress
|
||||
import rtyaml
|
||||
|
||||
from mailconfig import get_mail_domains
|
||||
|
@ -551,6 +552,79 @@ def write_opendkim_tables(zonefiles, env):
|
|||
|
||||
########################################################################
|
||||
|
||||
def set_custom_dns_record(qname, rtype, value, env):
|
||||
# validate
|
||||
rtype = rtype.upper()
|
||||
if value is not None:
|
||||
if rtype in ("A", "AAAA"):
|
||||
v = ipaddress.ip_address(value)
|
||||
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", "TXT"):
|
||||
# anything goes
|
||||
pass
|
||||
else:
|
||||
raise ValueError("Unknown record type '%s'." % rtype)
|
||||
|
||||
# load existing config
|
||||
config = get_custom_dns_config(env)
|
||||
|
||||
# update
|
||||
if qname not in config:
|
||||
if value is None:
|
||||
# Is asking to delete a record that does not exist.
|
||||
return False
|
||||
elif rtype == "A":
|
||||
# Add this record using the short form 'qname: value'.
|
||||
config[qname] = value
|
||||
else:
|
||||
# Add this record. This is the qname's first record.
|
||||
config[qname] = { rtype: value }
|
||||
else:
|
||||
if isinstance(config[qname], str):
|
||||
# This is a short-form 'qname: value' implicit-A record.
|
||||
if value is None and rtype != "A":
|
||||
# Is asking to delete a record that doesn't exist.
|
||||
return False
|
||||
elif value is None and rtype == "A":
|
||||
# Delete record.
|
||||
del config[qname]
|
||||
elif rtype == "A":
|
||||
# Update, keeping short form.
|
||||
if config[qname] == "value":
|
||||
# No change.
|
||||
return False
|
||||
config[qname] = value
|
||||
else:
|
||||
# Expand short form so we can add a new record type.
|
||||
config[qname] = { "A": config[qname], rtype: value }
|
||||
else:
|
||||
# This is the qname: { ... } (dict) format.
|
||||
if value is None:
|
||||
if rtype not in config[qname]:
|
||||
# Is asking to delete a record that doesn't exist.
|
||||
return False
|
||||
else:
|
||||
# Delete the record. If it's the last record, delete the domain.
|
||||
del config[qname][rtype]
|
||||
if len(config[qname]) == 0:
|
||||
del config[qname]
|
||||
else:
|
||||
# Update the record.
|
||||
if config[qname].get(rtype) == "value":
|
||||
# No change.
|
||||
return False
|
||||
config[qname][rtype] = value
|
||||
|
||||
# serialize & save
|
||||
config_yaml = rtyaml.dump(config)
|
||||
with open(os.path.join(env['STORAGE_ROOT'], 'dns/custom.yaml'), "w") as f:
|
||||
f.write(config_yaml)
|
||||
|
||||
return True
|
||||
|
||||
########################################################################
|
||||
|
||||
def justtestingdotemail(domain, records):
|
||||
# If the domain is a subdomain of justtesting.email, which we own,
|
||||
# automatically populate the zone where it is set up on dns4e.com.
|
||||
|
|
Loading…
Reference in New Issue