aboutsummaryrefslogtreecommitdiffstats
path: root/kerberos.sh
blob: 9b5d3aec4e5b454ac245698eac2ee860d69eb46c (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
#! /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 ! kinit -k -t /etc/krb5.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"

	echo "waiting for $domain on nameserver $ns..."

	local waited=0
	local waitlimit=60
	while sleep 1; do
		waited=$((waited + 1))
		if [ "$waited" -ge "$waitlimit" ]; then
			_error "timed out waiting for nameserver update for %s" \
				"$domain"
			return 1
		fi

		data="$(dig "_acme-challenge.$domain" txt @$nameserver +short)"
		if [ -z "$data" ]; then
			continue
		fi

		if [ "$data" = "\"$auth\"" ]; then
			return 0
		fi
	done
}

# Wait for DNS servers to have the given record.
_wait_for_record() {
	local domain="$1"
	local auth="$2"
	local nameservers="$(_getnameservers "$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