summaryrefslogtreecommitdiff
path: root/dns_acme_update.py
blob: e1b5fe46e3e11aef0145489583edbd0eb4175cdb (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
#!/usr/bin/env python3
import argparse
import dns.name
import n.gssapi
import n.kerberos
import n.log
import os
import subprocess
import sys
import time

'''
certbot certonly \
	--manual \
	--manual-auth-hook /usr/local/nullroute/certbot_manual_hook.py \
	--manual-cleanup-hook /usr/local/nullroute/certbot_manual_hook.py \
	--preferred-challenges dns \
	--debug-challenges \
	-d ldap.nullroute.lt
'''

def replace_domain_suffix(name, old_suffix, new_suffix):
    name = ".%s" % name.strip(".")
    old_suffix = ".%s" % old_suffix.strip(".")
    new_suffix = ".%s" % new_suffix.strip(".")
    if name == old_suffix:
        name = new_suffix
    elif name.endswith(old_suffix):
        name = name[:-len(old_suffix)] + new_suffix
    else:
        raise ValueError("name %r not underneath %r" % (name.lstrip("."), old_suffix))
    return name.lstrip(".")

parser = argparse.ArgumentParser()
parser.add_argument("-d", "--domain",
                        required=True,
                        help="domain or subdomain being validated")
parser.add_argument("-c", "--challenge",
                        help="ACME challenge to insert")
parser.add_argument("-N", "--use-nsupdate",
                        action="store_true",
                        help="use BIND 'nsupdate' instead of dnspython")
parser.add_argument("-v", "--verbose",
                        action="store_true",
                        help="show operation details")
args = parser.parse_args()

logger = n.log.init("dns_acme_update", args.verbose)

domain = args.domain
challenge = args.challenge
use_nsupdate = args.use_nsupdate

if os.getuid() == 0:
    # Ensure we always identify as host@ and not just whatever happens to be
    # first in the keytab.
    n.kerberos.use_system_keytab()
    n.gssapi.host_credentials()

# Currently we only support this single domain.
raw_zone = "nullroute.lt"
dyn_zone = "dyn.nullroute.lt"
fqdn = "_acme-challenge.%s" % replace_domain_suffix(domain, raw_zone, dyn_zone)
fqdn = dns.name.from_text(fqdn)

if challenge:
    print(f"Inserting challenge {challenge!r} at [{fqdn}]", flush=True)
else:
    print(f"Removing all challenges at [{fqdn}]", flush=True)

if use_nsupdate:
    if challenge:
        cmds = [f"zone {dyn_zone}",
                f"del {fqdn}",
                f"add {fqdn} 60 TXT \"{challenge}\"",
                f"send"]
    else:
        cmds = [f"zone {dyn_zone}",
                f"del {fqdn}",
                f"send"]
    cmds = "".join(f"{c}\n" for c in cmds)
    subprocess.run(["nsupdate", "-g"],
                   input=cmds.encode(),
                   check=True)
else:
    import n.dns.gssapi
    import dns.update

    msg = dns.update.UpdateMessage(zone=dyn_zone)
    msg.delete(fqdn)
    if challenge:
        msg.add(fqdn, 60, "TXT", challenge)
    if args.verbose:
        print(msg, file=sys.stderr)
    n.dns.gssapi.gss_tsig_update(dyn_zone, msg)