zivildienst/infrastructure/modules/compute/certbot.sh

122 lines
4.6 KiB
Bash

#!/bin/sh
args="$@"
set -o nounset
set -e errexit
certbot=/run/current-system/sw/bin/certbot
jq=/run/current-system/sw/bin/jq
curl=/run/current-system/sw/bin/curl
cert_name="$SERVICE-$ENVIRONMENT"
domains="-d $DOMAIN_NAME"
for san in $ALTERNATIVE_NAMES; do
domains="$domains -d $san"
done
case "$args" in
"--renew")
printf "Running Certbot (for renewal) before deploying certificate..\n\n"
$certbot certonly --non-interactive --agree-tos -m webmaster@"$DOMAIN_NAME" --work-dir "$LETSENCRYPT_DIR"/lib --logs-dir "$LETSENCRYPT_DIR"/log --config-dir "$LETSENCRYPT_DIR"/etc --dns-route53 --preferred-challenges dns $domains
;;
*)
printf "Deploying existing certificate without running Certbot..\n\n"
;;
esac
key="$(sed 's/$/\\n/' $LETSENCRYPT_DIR/etc/live/$DOMAIN_NAME/privkey.pem | tr -d '\n')"
fullchain="$(sed 's/$/\\n/' $LETSENCRYPT_DIR/etc/live/$DOMAIN_NAME/fullchain.pem | tr -d '\n')"
service_ports="$(printf "%s" "$SERVICE_PORTS" | tr ',' ' ' | xargs)"
lb_id=$($curl -s -H "Authorization: Bearer $API_TOKEN" "https://api.hetzner.cloud/v1/load_balancers?label_selector=service==$SERVICE%2Cenvironment==$ENVIRONMENT" | $jq '.load_balancers[0].id')
case "$lb_id" in
""|*[!0-9]*)
printf "Load balancer for %s in environment \"%s\" could not be found\n" "$SERVICE" "$ENVIRONMENT"
exit 1
;;
esac
update_load_balancer() {
case "$1" in
*[0-9]*)
_proto=https
_certs="[$1]"
;;
"")
printf "Temporarily disabling https for renewal\n"
_proto=http
_redirect_http="false"
_certs="[]"
;;
*)
printf "Something went wrong, tried to process certificate with ID \"%s\"\n" "$cert_id"
exit 1
;;
esac
for sp in $service_ports; do
case "$sp" in
"443")
_redirect_http="true"
;;
*)
_redirect_http="false"
;;
esac
error=$($curl -s -XPOST -H "Authorization: Bearer $API_TOKEN" -H "Content-Type: application/json" -d "{\"listen_port\": $sp, \"protocol\": \"$_proto\", \"http\":{\"redirect_http\": $_redirect_http, \"certificates\": $_certs}}" "https://api.hetzner.cloud/v1/load_balancers/$lb_id/actions/update_service" | $jq -r '.error')
case "$(printf "%s" "$error" | $jq -r '.code')" in
"null")
printf "Certificate with ID \"%s\" has been successfully assigned to service port %s on the load balancer with ID \"%s\"\n" "$1" "$sp" "$lb_id"
;;
*)
printf "There has been an unexpected error: %s\n" "$error"
exit 1
;;
esac
done
}
cert_id=$($curl -s -H "Authorization: Bearer $API_TOKEN" "https://api.hetzner.cloud/v1/certificates?label_selector=service=$SERVICE%2Cenvironment=$ENVIRONMENT" | $jq '.certificates[0].id')
#
# Determine whether a certificate for the same service and environment already exists
#
case "$cert_id" in
""|*[0-9]*)
update_load_balancer ""
printf "Deleting existing certificate with id %s so that it can be replaced by a new one\n" "$cert_id"
$curl -s -X DELETE -H "Authorization: Bearer $API_TOKEN" "https://api.hetzner.cloud/v1/certificates/$cert_id"
;;
"null")
printf "Currently there is no certificate deployed for %s in environment \"%s\", setting up a new one\n" "$SERVICE" "$ENVIRONMENT"
;;
*)
printf "Something went wrong: %s\n" "$cert_id"
exit 1
;;
esac
#
# Deploy key and certificate to Hetzner security service
#
error=$($curl -s -XPOST -H "Authorization: Bearer $API_TOKEN" -H "Content-Type: application/json" -d "{\"name\":\"$cert_name\",\"labels\":{\"service\": \"$SERVICE\", \"environment\": \"$ENVIRONMENT\"},\"certificate\":\"$fullchain\",\"private_key\":\"$key\"}" 'https://api.hetzner.cloud/v1/certificates' | $jq '.error')
case "$(printf "%s" "$error" | $jq -r '.code')" in
"null")
cert_id=$($curl -s -H "Authorization: Bearer $API_TOKEN" "https://api.hetzner.cloud/v1/certificates?label_selector=service=$SERVICE%2Cenvironment=$ENVIRONMENT" | $jq '.certificates[0].id')
printf "The certificate %s has been successfully added to the Hetzner security service with ID \"%s\"\n" "$cert_name" "$cert_id"
update_load_balancer "$cert_id"
;;
"uniqueness_error")
printf "A certificate with the same fingerprint already exists! Exiting\n"
exit 1
;;
*)
printf "There has been an unexpected error: %s\n" "$error"
exit 1
;;
esac