forked from TritonDataCenter/triton-dehydrated
-
Notifications
You must be signed in to change notification settings - Fork 0
/
cns-hook-util
executable file
·352 lines (318 loc) · 10.9 KB
/
cns-hook-util
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
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
function getservice {
local vmuuid="$(dig +short txt "${domain}" @${NAMESERVER:-8.8.8.8} | tail -1 | sed 's/"//g')"
if [ "a$vmuuid" == "a" ]; then
echo "ERROR: ${domain} does not appear to be a CNS name or CNAME to a CNS name" >&2
exit 1
fi
local sdcrole="$(sdc-vmadm get ${vmuuid} | json tags.smartdc_role)"
local mantarole="$(sdc-vmadm get ${vmuuid} | json tags.manta_role)"
case "$sdcrole" in
cloudapi|adminui|docker|cmon)
echo "$sdcrole"
;;
*)
case "$mantarole" in
loadbalancer)
echo "manta"
;;
*)
echo "ERROR: ${domain} points at VM ${vmuuid} with role ${role}, which is not supported" >&2
exit 1
;;
esac
;;
esac
}
function verifyvm {
local domain="${1}" vmuuid="${2}"
if ! dig +short txt "${domain}" @${NAMESERVER:-8.8.8.8} | sed 's/"//g' | grep -w -- "${vmuuid}" >/dev/null; then
echo "ERROR: ${domain} does not appear to be a CNS name or CNAME to a CNS name for VM ${vmuuid}" >&2
exit 1
fi
local cname="$(dig +short cname "${domain}" @${NAMESERVER:-8.8.8.8})"
if [ "a$cname" != "a" ]; then
local acmecname="$(dig +short cname "_acme-challenge.${domain}" @${NAMESERVER:-8.8.8.8})"
if [ "a$acmecname" == "a" ]; then
echo "ERROR: ${domain} is a CNAME to ${cname}, but _acme-challenge.${domain} is not a CNAME" >&2
exit 1
fi
fi
}
function waitfortxt {
local domain="$1" txtval="$2"
local count=0
read -r -a values <<< "$txtval"
for val in "${values[@]}"; do
while [ $count -lt 3 ]; do
dig +short txt "_acme-challenge.${domain}" @${NAMESERVER:-8.8.8.8}
if dig +short txt "_acme-challenge.${domain}" @${NAMESERVER:-8.8.8.8} | grep -w "$val" >/dev/null; then
count=$(($count + 1))
else
count=0
sleep 1
fi
done
count=0
done
}
function update_localvm {
local vmuuid="$1" tokenval="$2"
echo "{\"set_customer_metadata\":{\"triton.cns.acme-challenge\":\"${tokenval}\"}}" | \
vmadm update "$vmuuid"
}
function update_remotevm {
local vmuuid="$1" tokenval="$2"
sdc-vmadm get $vmuuid | \
json -j customer_metadata not_a_key | \
json -j -e "this.customer_metadata['triton.cns.acme-challenge'] = '${tokenval}'" | \
json -j -e "this.payload = { customer_metadata: this.customer_metadata };" payload not_a_key | \
sdc-vmadm update $vmuuid
}
function find_path {
local path="$1"
if [ ! -e "$path" ]; then
path=/native/$1
if [ ! -e "$path" ]; then
echo "ERROR: failed to find $(basename $path) command" >&2
echo " is this a Triton zone?" >&2
exit 1
fi
fi
cmd="$path"
if [ -e "/usr/bin/pfexec" ]; then
cmd="/usr/bin/pfexec ${cmd}"
fi
echo "$cmd"
}
function mdata_get {
local cmd=$(find_path /usr/sbin/mdata-get)
$cmd "$@"
}
function mdata_put {
local cmd=$(find_path /usr/sbin/mdata-put)
$cmd "$@"
}
function mdata_delete {
local cmd=$(find_path /usr/sbin/mdata-delete)
$cmd "$@"
}
function get_zonename {
local cmd=$(find_path /usr/bin/zonename)
$cmd
}
function merge_token {
local domain="${1}" tokenval="${2}"
local existing_token
local zname=$(get_zonename)
if [ "$zname" != "global" ]; then
existing_token=$(mdata_get triton.cns.acme-challenge)
else
local vmservice vmuuid
vmservice=$(getservice "${domain}")
case "$vmservice" in
cloudapi|adminui|docker|cmon)
vmuuid="$(vmadm lookup alias=~${vmservice})"
existing_token=$(vmadm get "$vmuuid" | json 'customer_metadata.["triton.cns.acme-challenge"]')
;;
manta)
local poseidon=$(sdc-useradm get poseidon | json uuid)
vmuuid=$(sdc-vmadm list -H -o uuid \
owner_uuid="$poseidon" alias="loadbalancer" | \
sort | tail -1)
existing_token=$(sdc-vmapi "/vms/$vmuuid" | json -H 'customer_metadata.["triton.cns.acme-challenge"]')
;;
esac
fi
if [ -n "$existing_token" ]; then
new_token=$(echo "$existing_token $tokenval" | tr ' ' '\n' | sort -u | xargs)
else
new_token="$tokenval"
fi
echo "$new_token"
}
function deploy_challenge {
local domain="${1}" tokenfn="${2}" tokenval="${3}"
tokenval=$(merge_token "$domain" "$tokenval")
local zname=$(get_zonename)
if [ "$zname" != "global" ]; then
verifyvm "$domain" "$zname"
mdata_put "triton.cns.acme-challenge" "$tokenval"
else
local vmuuid
case "$(getservice "${domain}")" in
cloudapi|adminui|docker|cmon)
local alias="$(getservice "${domain}")0"
vmuuid="$(vmadm lookup alias=$alias)"
verifyvm "$domain" "$vmuuid"
update_localvm "$vmuuid" "$tokenval"
;;
manta)
local poseidon=$(sdc-useradm get poseidon | json uuid)
vmuuid=$(sdc-vmadm list -H -o uuid \
owner_uuid="$poseidon" alias="loadbalancer" | \
sort | tail -1)
verifyvm "$domain" "$vmuuid"
update_remotevm "$vmuuid" "$tokenval"
;;
*)
echo "ERROR: Unknown domain: ${domain}" >&2
exit 1
;;
esac
fi
waitfortxt "$domain" "$tokenval"
echo "OK: deployed dns token for ${domain} successfully" >&2
exit 0
}
function clean_challenge {
local domain="${1}" tokenfn="${2}" tokenval="${3}"
local zname=$(get_zonename)
if [ "$zname" != "global" ]; then
verifyvm "$domain" "$zname"
mdata_delete "triton.cns.acme-challenge"
else
local vmuuid
case "$(getservice "${domain}")" in
cloudapi|adminui|docker|cmon)
local alias="$(getservice "${domain}")0"
vmuuid="$(vmadm lookup alias=$alias)"
verifyvm "$domain" "$vmuuid"
echo "{\"remove_customer_metadata\":[\"triton.cns.acme-challenge\"]}" | \
vmadm update "$vmuuid"
;;
esac
fi
exit 0
}
function deploy_cert {
local domain="${1}" keyfile="${2}" certfile="${3}" fullchainfile="${4}" chainfile="${5}"
local zname=$(get_zonename)
if [ "$zname" != "global" ]; then
exit 0
fi
local vmuuid
local certdir="$(dirname "$certfile")"
case "$(getservice "${domain}")" in
cloudapi)
vmuuid="$(vmadm lookup alias=cloudapi0)"
if [ ! -f "${certdir}/dhparams.pem" ]; then
openssl dhparam 2048 > "${certdir}/dhparams.pem"
fi
cat "${keyfile}" "${fullchainfile}" "${certdir}/dhparams.pem" > "${certdir}/stud.pem"
local subd="$(cat "${certdir}/stud.pem" | tr '\n' '\\' | sed 's/\\/\\n/g')"
local muuid
read muuid < <(sdc-sapi /manifests -XPOST -d '{"name": "cert", "version":"1.0.0", "path": "/opt/smartdc/cloudapi/ssl/stud.pem","post_cmd":"/usr/sbin/svcadm restart stud","template":"'"${subd}"'"}' | json -H uuid)
local svcuuid
read svcuuid < <(sdc-sapi /services?name=cloudapi | json -Ha uuid)
sdc-sapi /services/${svcuuid} -XPUT -d '{"manifests":{"cert":"'"${muuid}"'"}}' >/dev/null
echo "OK: cloudapi certificate deployed (sapi manifest updated)" >&2
;;
adminui)
vmuuid="$(vmadm lookup alias=adminui0)"
cat "${keyfile}" "${fullchainfile}" > "${certdir}/combined.pem"
cp "${certdir}/combined.pem" "/zones/${vmuuid}/root/opt/smartdc/adminui/etc/ssl/default.pem"
rm /zones/${vmuuid}/root/opt/smartdc/adminui/etc/ssl/ADMINUI.*
zlogin "${vmuuid}" svcadm restart adminui
echo "OK: adminui certificate deployed, and adminui restarted" >&2
;;
docker)
sdcadm experimental install-docker-cert \
-k "${keyfile}" -c "${fullchainfile}"
local ep="tcp://${domain}:2376"
local cloudapi_svc=$(sdc-sapi /services?name=cloudapi | json -H 0.uuid)
sapiadm get $cloudapi_svc | \
json -e "
svcs = JSON.parse(this.metadata.CLOUDAPI_SERVICES || '{}');
svcs.docker = '${ep}';
this.update = {metadata: {CLOUDAPI_SERVICES: JSON.stringify(svcs)}};
" update | \
sapiadm update $cloudapi_svc
echo "OK: docker certificate deployed" >&2
;;
cmon)
cmon_servers=($(sdcadm insts cmon -Ho server))
cmon_servers=$(tr ' ' , <<< "${cmon_servers[@]}")
sdc-oneachnode -n "$cmon_servers" -X -g "${fullchainfile}" -d /tmp
sdc-oneachnode -n "$cmon_servers" -X -g "${keyfile}" -d /tmp
sdc-oneachnode -n "$cmon_servers" '
mv /tmp/fullchain.pem /zones/$(vmadm lookup alias=~cmon)/root/data/tls/cert.pem
mv /tmp/privkey.pem /zones/$(vmadm lookup alias=~cmon)/root/data/tls/key.pem
svcadm -z $(vmadm lookup alias=~cmon) restart cmon
'
;;
manta)
if [ ! -f "${certdir}/dhparams.pem" ]; then
openssl dhparam 2048 > "${certdir}/dhparams.pem"
fi
cat "${keyfile}" "${fullchainfile}" "${certdir}/dhparams.pem" > "${certdir}/stud.pem"
local mantazone=$(vmadm lookup alias=manta0)
cp "${certdir}/stud.pem" /zones/$mantazone/root/var/tmp/stud.pem
zlogin $mantazone bash --login -c '/opt/smartdc/manta-deployment/cmd/manta-replace-cert.js /var/tmp/stud.pem'
rm /zones/$mantazone/root/var/tmp/stud.pem
echo "OK: New Manta certificate deployed, but loadbalancers have not been restarted." >&2
echo " You will need to visit each loadbalancer instance with manta-login and " >&2
echo " restart the 'stud' service." >&2
;;
*)
echo "ERROR: Unknown domain: ${domain}" >&2
exit 1
;;
esac
}
function unchanged_cert {
local domain="${1}" keyfile="${2}" certfile="${3}" fullchainfile="${4}" chainfile="${5}"
local zname=$(get_zonename)
if [ "$zname" != "global" ]; then
exit 0
fi
local vmuuid
local certdir="$(dirname "$certfile")"
case "$(getservice "${domain}")" in
cloudapi)
vmuuid="$(vmadm lookup alias=cloudapi0)"
if [ ! -f "${certdir}/dhparams.pem" ]; then
openssl dhparam 2048 > "${certdir}/dhparams.pem"
fi
cat "${keyfile}" "${fullchainfile}" "${certdir}/dhparams.pem" > "${certdir}/stud.pem"
local target="/zones/${vmuuid}/root/opt/smartdc/cloudapi/ssl/stud.pem"
if ! diff "${certdir}/stud.pem" "${target}" >/dev/null; then
local subd="$(cat "${certdir}/stud.pem" | tr '\n' '\\' | sed 's/\\/\\n/g')"
local muuid
read muuid < <(sdc-sapi /manifests -XPOST -d '{"name": "cert", "version":"1.0.0", "path": "/opt/smartdc/cloudapi/ssl/stud.pem","post_cmd":"/usr/sbin/svcadm restart stud","template":"'"${subd}"'"}' | json -H uuid)
local svcuuid
read svcuuid < <(sdc-sapi /services?name=cloudapi | json -Ha uuid)
sdc-sapi /services/${svcuuid} -XPUT -d '{"manifests":{"cert":"'"${muuid}"'"}}' >/dev/null
echo "OK: cloudapi certificate deployed (sapi manifest updated)" >&2
fi
echo "OK: cloudapi certificate up to date" >&2
;;
adminui)
vmuuid="$(vmadm lookup alias=adminui0)"
cat "${keyfile}" "${fullchainfile}" > "${certdir}/combined.pem"
local target="/zones/${vmuuid}/root/opt/smartdc/adminui/etc/ssl/default.pem"
if ! diff "${certdir}/combined.pem" "${target}" >/dev/null; then
cp "${certdir}/combined.pem" "/zones/${vmuuid}/root/opt/smartdc/adminui/etc/ssl/default.pem"
rm /zones/${vmuuid}/root/opt/smartdc/adminui/etc/ssl/ADMINUI.*
zlogin "${vmuuid}" svcadm restart adminui
echo "OK: adminui certificate deployed, and adminui restarted" >&2
fi
echo "OK: adminui certificate up to date" >&2
;;
cmon|docker|manta)
echo "OK: certificate up to date" >&2
;;
*)
echo "ERROR: Unknown domain: ${domain}" >&2
exit 1
;;
esac
}
function exit_hook {
exit 0
}
function invalid_challenge {
exit 0
}
function startup_hook {
exit 0
}