]>
Commit | Line | Data |
---|---|---|
d6f0c2b5 MZ |
1 | #!/usr/bin/env sh |
2 | ||
506f36b2 | 3 | #PowerDNS Embedded API |
d6f0c2b5 MZ |
4 | #https://doc.powerdns.com/md/httpapi/api_spec/ |
5 | # | |
6 | #PDNS_Url="http://ns.example.com:8081" | |
7 | #PDNS_ServerId="localhost" | |
8 | #PDNS_Token="0123456789ABCDEF" | |
9 | #PDNS_Ttl=60 | |
10 | ||
b9311282 | 11 | DEFAULT_PDNS_TTL=60 |
12 | ||
d6f0c2b5 MZ |
13 | ######## Public functions ##################### |
14 | #Usage: add _acme-challenge.www.domain.com "123456789ABCDEF0000000000000000000000000000000000000" | |
d5c00071 | 15 | #fulldomain |
16 | #txtvalue | |
d6f0c2b5 MZ |
17 | dns_pdns_add() { |
18 | fulldomain=$1 | |
19 | txtvalue=$2 | |
20 | ||
4c2a3841 | 21 | if [ -z "$PDNS_Url" ]; then |
422e5026 | 22 | PDNS_Url="" |
d6f0c2b5 MZ |
23 | _err "You don't specify PowerDNS address." |
24 | _err "Please set PDNS_Url and try again." | |
25 | return 1 | |
26 | fi | |
27 | ||
4c2a3841 | 28 | if [ -z "$PDNS_ServerId" ]; then |
422e5026 | 29 | PDNS_ServerId="" |
d6f0c2b5 MZ |
30 | _err "You don't specify PowerDNS server id." |
31 | _err "Please set you PDNS_ServerId and try again." | |
32 | return 1 | |
33 | fi | |
34 | ||
4c2a3841 | 35 | if [ -z "$PDNS_Token" ]; then |
422e5026 | 36 | PDNS_Token="" |
d6f0c2b5 MZ |
37 | _err "You don't specify PowerDNS token." |
38 | _err "Please create you PDNS_Token and try again." | |
39 | return 1 | |
40 | fi | |
41 | ||
4c2a3841 | 42 | if [ -z "$PDNS_Ttl" ]; then |
c7b16249 | 43 | PDNS_Ttl="$DEFAULT_PDNS_TTL" |
d6f0c2b5 MZ |
44 | fi |
45 | ||
46 | #save the api addr and key to the account conf file. | |
47 | _saveaccountconf PDNS_Url "$PDNS_Url" | |
48 | _saveaccountconf PDNS_ServerId "$PDNS_ServerId" | |
49 | _saveaccountconf PDNS_Token "$PDNS_Token" | |
4c2a3841 | 50 | |
51 | if [ "$PDNS_Ttl" != "$DEFAULT_PDNS_TTL" ]; then | |
b9311282 | 52 | _saveaccountconf PDNS_Ttl "$PDNS_Ttl" |
53 | fi | |
d6f0c2b5 | 54 | |
d5c00071 | 55 | _debug "Detect root zone" |
c7b16249 | 56 | if ! _get_root "$fulldomain"; then |
d6f0c2b5 MZ |
57 | _err "invalid domain" |
58 | return 1 | |
59 | fi | |
60 | _debug _domain "$_domain" | |
61 | ||
4c2a3841 | 62 | if ! set_record "$_domain" "$fulldomain" "$txtvalue"; then |
d6f0c2b5 MZ |
63 | return 1 |
64 | fi | |
65 | ||
66 | return 0 | |
67 | } | |
68 | ||
5d6fd809 | 69 | #fulldomain |
70 | dns_pdns_rm() { | |
71 | fulldomain=$1 | |
a3f7ff90 NS |
72 | txtvalue=$2 |
73 | ||
74 | if [ -z "$PDNS_Ttl" ]; then | |
75 | PDNS_Ttl="$DEFAULT_PDNS_TTL" | |
76 | fi | |
5d6fd809 | 77 | |
d5c00071 | 78 | _debug "Detect root zone" |
79 | if ! _get_root "$fulldomain"; then | |
80 | _err "invalid domain" | |
81 | return 1 | |
82 | fi | |
a3f7ff90 | 83 | |
d5c00071 | 84 | _debug _domain "$_domain" |
85 | ||
a3f7ff90 | 86 | if ! rm_record "$_domain" "$fulldomain" "$txtvalue"; then |
d5c00071 | 87 | return 1 |
88 | fi | |
89 | ||
90 | return 0 | |
5d6fd809 | 91 | } |
92 | ||
d6f0c2b5 MZ |
93 | set_record() { |
94 | _info "Adding record" | |
95 | root=$1 | |
96 | full=$2 | |
af5ff2bb | 97 | new_challenge=$3 |
d6f0c2b5 | 98 | |
af5ff2bb | 99 | _record_string="" |
893917a2 | 100 | _build_record_string "$new_challenge" |
a3f7ff90 | 101 | _list_existingchallenges |
893917a2 | 102 | for oldchallenge in $_existing_challenges; do |
893917a2 | 103 | _build_record_string "$oldchallenge" |
af5ff2bb NS |
104 | done |
105 | ||
eae490b5 | 106 | if ! _pdns_rest "PATCH" "/api/v1/servers/$PDNS_ServerId/zones/$root" "{\"rrsets\": [{\"changetype\": \"REPLACE\", \"name\": \"$full.\", \"type\": \"TXT\", \"ttl\": $PDNS_Ttl, \"records\": [$_record_string]}]}" "application/json"; then |
d6f0c2b5 MZ |
107 | _err "Set txt record error." |
108 | return 1 | |
109 | fi | |
d5c00071 | 110 | |
a3f7ff90 NS |
111 | if ! notify_slaves "$root"; then |
112 | return 1 | |
113 | fi | |
114 | ||
d5c00071 | 115 | return 0 |
116 | } | |
117 | ||
118 | rm_record() { | |
119 | _info "Remove record" | |
120 | root=$1 | |
121 | full=$2 | |
a3f7ff90 | 122 | txtvalue=$3 |
d5c00071 | 123 | |
a3f7ff90 NS |
124 | #Enumerate existing acme challenges |
125 | _list_existingchallenges | |
d5c00071 | 126 | |
a3f7ff90 NS |
127 | if _contains "$_existing_challenges" "$txtvalue"; then |
128 | #Delete all challenges (PowerDNS API does not allow to delete content) | |
eae490b5 | 129 | if ! _pdns_rest "PATCH" "/api/v1/servers/$PDNS_ServerId/zones/$root" "{\"rrsets\": [{\"changetype\": \"DELETE\", \"name\": \"$full.\", \"type\": \"TXT\"}]}" "application/json"; then |
a3f7ff90 NS |
130 | _err "Delete txt record error." |
131 | return 1 | |
132 | fi | |
133 | _record_string="" | |
134 | #If the only existing challenge was the challenge to delete: nothing to do | |
135 | if ! [ "$_existing_challenges" = "$txtvalue" ]; then | |
136 | for oldchallenge in $_existing_challenges; do | |
137 | #Build up the challenges to re-add, ommitting the one what should be deleted | |
138 | if ! [ "$oldchallenge" = "$txtvalue" ]; then | |
139 | _build_record_string "$oldchallenge" | |
140 | fi | |
141 | done | |
142 | #Recreate the existing challenges | |
eae490b5 | 143 | if ! _pdns_rest "PATCH" "/api/v1/servers/$PDNS_ServerId/zones/$root" "{\"rrsets\": [{\"changetype\": \"REPLACE\", \"name\": \"$full.\", \"type\": \"TXT\", \"ttl\": $PDNS_Ttl, \"records\": [$_record_string]}]}" "application/json"; then |
a3f7ff90 NS |
144 | _err "Set txt record error." |
145 | return 1 | |
146 | fi | |
147 | fi | |
148 | if ! notify_slaves "$root"; then | |
149 | return 1 | |
150 | fi | |
151 | else | |
152 | _info "Record not found, nothing to remove" | |
d5c00071 | 153 | fi |
154 | ||
155 | return 0 | |
156 | } | |
157 | ||
158 | notify_slaves() { | |
159 | root=$1 | |
160 | ||
4ae10800 | 161 | if ! _pdns_rest "PUT" "/api/v1/servers/$PDNS_ServerId/zones/$root/notify"; then |
d5c00071 | 162 | _err "Notify slaves error." |
d6f0c2b5 MZ |
163 | return 1 |
164 | fi | |
d5c00071 | 165 | |
d6f0c2b5 MZ |
166 | return 0 |
167 | } | |
168 | ||
329174b6 | 169 | #################### Private functions below ################################## |
d6f0c2b5 MZ |
170 | #_acme-challenge.www.domain.com |
171 | #returns | |
172 | # _domain=domain.com | |
173 | _get_root() { | |
174 | domain=$1 | |
175 | i=1 | |
d6f0c2b5 | 176 | |
4c2a3841 | 177 | if _pdns_rest "GET" "/api/v1/servers/$PDNS_ServerId/zones"; then |
8636d313 | 178 | _zones_response=$(echo "$response" | _normalizeJson) |
d6f0c2b5 MZ |
179 | fi |
180 | ||
c7b16249 | 181 | while true; do |
182 | h=$(printf "%s" "$domain" | cut -d . -f $i-100) | |
d6f0c2b5 | 183 | |
8636d313 | 184 | if _contains "$_zones_response" "\"name\":\"$h.\""; then |
4ae10800 RG |
185 | _domain="$h." |
186 | if [ -z "$h" ]; then | |
187 | _domain="=2E" | |
188 | fi | |
d6f0c2b5 MZ |
189 | return 0 |
190 | fi | |
191 | ||
4ae10800 RG |
192 | if [ -z "$h" ]; then |
193 | return 1 | |
194 | fi | |
c7b16249 | 195 | i=$(_math $i + 1) |
d6f0c2b5 MZ |
196 | done |
197 | _debug "$domain not found" | |
d5c00071 | 198 | |
d6f0c2b5 MZ |
199 | return 1 |
200 | } | |
201 | ||
202 | _pdns_rest() { | |
203 | method=$1 | |
204 | ep=$2 | |
205 | data=$3 | |
eae490b5 | 206 | ct=$4 |
d6f0c2b5 | 207 | |
3ca93f4a | 208 | export _H1="X-API-Key: $PDNS_Token" |
d6f0c2b5 | 209 | |
4c2a3841 | 210 | if [ ! "$method" = "GET" ]; then |
d6f0c2b5 | 211 | _debug data "$data" |
eae490b5 | 212 | response="$(_post "$data" "$PDNS_Url$ep" "" "$method" "$ct")" |
d6f0c2b5 MZ |
213 | else |
214 | response="$(_get "$PDNS_Url$ep")" | |
215 | fi | |
216 | ||
4c2a3841 | 217 | if [ "$?" != "0" ]; then |
d6f0c2b5 MZ |
218 | _err "error $ep" |
219 | return 1 | |
220 | fi | |
221 | _debug2 response "$response" | |
222 | ||
223 | return 0 | |
4c2a3841 | 224 | } |
af5ff2bb NS |
225 | |
226 | _build_record_string() { | |
a3f7ff90 NS |
227 | _record_string="${_record_string:+${_record_string}, }{\"content\": \"\\\"${1}\\\"\", \"disabled\": false}" |
228 | } | |
229 | ||
230 | _list_existingchallenges() { | |
231 | _pdns_rest "GET" "/api/v1/servers/$PDNS_ServerId/zones/$root" | |
232 | _existing_challenges=$(echo "$response" | _normalizeJson | _egrep_o "\"name\":\"${fulldomain}[^]]*}" | _egrep_o 'content\":\"\\"[^\\]*' | sed -n 's/^content":"\\"//p') | |
af5ff2bb | 233 | } |