blob: 2bbfd0fcfdfc0bde1b20a97702a9ee9b5d27787b (
plain) (
blame)
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
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
|
#! /bin/sh
# This source code is released into the public domain.
. /usr/local/share/lfacme/init.sh
# begin, done or failed
ACTION=$1
# ACME method, must be dns-01.
METHOD=$2
# This is the full domain name we're authorising.
DOMAIN=$3
# Token name, not used for dns-01.
TOKEN=$4
# The token value we need to create.
AUTH=$5
if [ "$#" -ne 5 ]; then
_fatal "missing arguments"
fi
if [ "$METHOD" != "dns-01" ]; then
exit 1
fi
if [ -z "$ACME_KERBEROS_KEYTAB" ]; then
ACME_KERBEROS_KEYTAB="/etc/krb5.keytab"
fi
if ! kinit -k -t "$ACME_KERBEROS_KEYTAB" "$ACME_KERBEROS_PRINCIPAL"; then
_fatal "failed to obtain a Kerberos ticket"
fi
# Keep removing labels from the name until we find one with nameservers.
_getnameservers() {
local domain="$1"
local _trydomain="$domain"
while ! [ -z "$_trydomain" ]; do
if [ "$_trydomain" = "${_trydomain#*.}" ]; then
# If there are no dots in the domain, we couldn't
# find the nameservers.
break
fi
# For CNAME records, a query for NS will return the CNAME.
# Therefore we have to check we actually got NS records.
local nameservers="$(
dig "$_trydomain" ns +noall +answer | \
awk '$4 == "NS" { print $5 }'
)"
if ! [ -z "$nameservers" ]; then
echo "$nameservers"
return
fi
_trydomain="${_trydomain#*.}"
done
_fatal "unable to find nameservers for %s" "$_trydomain"
}
# Add a new record using nsupdate.
_add_record() {
local domain="$1"
local auth="$2"
nsupdate -g <<EOF
update add _acme-challenge.${DOMAIN}. 300 IN TXT "${AUTH}"
send
EOF
return $?
}
# Remove an existing record using nsupdate.
_remove_record() {
local domain="$1"
local auth="$2"
nsupdate -g <<EOF
update delete _acme-challenge.${DOMAIN}. 300 IN TXT "${AUTH}"
send
EOF
return $?
}
# Wait for the DNS record to appear on a specific nameserver.
_wait_for_nameserver() {
local domain="$1"
local auth="$2"
local nameserver="$3"
_verbose "waiting for nameserver %s" "$nameserver"
local waited=0
local waitlimit=60
while sleep 1; do
waited=$((waited + 1))
if [ "$waited" -ge "$waitlimit" ]; then
_error "timed out waiting for '%s' on '%s'" \
"$domain" "$nameserver"
return 1
fi
local _rdatas="$(
dig "_acme-challenge.$domain" txt @$nameserver \
+noall +answer \
| awk '$4 == "TXT" { print $5 }'
)"
for rdata in $_rdatas; do
if [ "$rdata" = "\"$auth\"" ]; then
return 0
fi
done
done
}
# Wait for DNS servers to have the given record.
_wait_for_record() {
local domain="$1"
local auth="$2"
local nameservers="$(_getnameservers "$domain")"
_verbose "waiting for the DNS record '%s' to be published" "$domain"
for ns in $nameservers; do
_wait_for_nameserver "$domain" "$auth" "$ns" || return 1
done
return 0
}
case "$ACTION" in
begin)
_add_record "$DOMAIN" "$AUTH" \
&& _wait_for_record "$DOMAIN" "$AUTH"
exit $?
;;
done|failed)
_remove_record "$DOMAIN" "$AUTH"
exit $?
;;
*)
_fatal "unknown action: %s" "$ACTION"
;;
esac
|