Skip to content

Commit

Permalink
triton-dehydrated#7 Support overriding hook functions
Browse files Browse the repository at this point in the history
  • Loading branch information
bahamat authored and arekinath committed Jul 12, 2018
1 parent 45cf6e7 commit 93e06cf
Show file tree
Hide file tree
Showing 3 changed files with 306 additions and 297 deletions.
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,5 +107,10 @@ Now you will find your certificate files in `./certs/blog.example.com/`. You
should configure your webserver to get the private key and certificate file
(with chain) directly from this folder.
You can also create override hooks in a file named `override-hook`. The format
for this file is the same as for `dehydrated`'s hook file but should only have
the `deploy_cert` and/or `unchanged_cert` functions. Use override hooks in a
zone to do things like restart local services.
Finally, you can set up a cron job to re-run `./dehydrated -c` at least once a
week (and then do a graceful reload of your web server configuration).
301 changes: 4 additions & 297 deletions cns-hook
Original file line number Diff line number Diff line change
@@ -1,302 +1,9 @@
#!/usr/bin/env bash

function getservice {
local vmuuid="$(dig +short txt "${domain}" @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)
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}" @8.8.8.8 | sed 's/"//g' | grep -- "^${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}" @8.8.8.8)"
if [ "a$cname" != "a" ]; then
local acmecname="$(dig +short cname "_acme-challenge.${domain}" @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
while [ $count -lt 10 ]; do
if dig +short txt "_acme-challenge.${domain}" @8.8.8.8 | grep -- "${txtval}" >/dev/null; then
count=$(($count + 1))
else
count=0
sleep 1
fi
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 mdata_put {
local path=/usr/sbin/mdata-put
if [ ! -e "$path" ]; then
path=/native/usr/sbin/mdata-put
if [ ! -e "$path" ]; then
echo "ERROR: failed to find mdata-put 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
$cmd "$@"
}

function mdata_delete {
local path=/usr/sbin/mdata-delete
if [ ! -e "$path" ]; then
path=/native/usr/sbin/mdata-delete
if [ ! -e "$path" ]; then
echo "ERROR: failed to find mdata-delete 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
$cmd "$@"
}

function deploy_challenge {
local domain="${1}" tokenfn="${2}" tokenval="${3}"

local zname=$(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)
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" | \
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=$(zonename)
if [ "$zname" != "global" ]; then
verifyvm "$domain" "$zname"
mdata_delete "triton.cns.acme-challenge"
else
local vmuuid
case "$(getservice "${domain}")" in
cloudapi|adminui|docker)
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=$(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
;;
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=$(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
;;
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
}
source "${BASEDIR}/cns-hook-util"
if [[ -f "${BASEDIR}/override-hook" ]]; then
source "${BASEDIR}/override-hook"
fi

handler=$1
shift
Expand Down
Loading

0 comments on commit 93e06cf

Please sign in to comment.