Created
August 7, 2021 22:14
-
-
Save cendyne/83b8dd6f050b20cfee430e3b4f94577e to your computer and use it in GitHub Desktop.
tls-rotate-cloudflare.sh
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/bash | |
HOST="mail.cendyne.dev" | |
ZONE_ID=REDACTED | |
TOKEN=REDACTED | |
PEM="/etc/letsencrypt/live/$HOST/cert.pem" | |
PORTS="25 443" | |
PROTOCOL="tcp" | |
NAMES="" | |
for port in $PORTS; do | |
NAMES="$NAMES _$port._$PROTOCOL.$HOST" | |
done | |
function hex() { | |
echo "$1" | xxd -p -c 1000000 | |
} | |
function gettlsa() { | |
curl -X GET "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/dns_records?type=TLSA&match=all" \ | |
-H "Authorization: Bearer $TOKEN" \ | |
-H "Content-Type: application/json" | |
} | |
function digestpem() { | |
openssl x509 -in "$1" -noout -pubkey | openssl pkey -pubin -outform DER | openssl dgst -sha256 -binary | xxd -p -c 100000 | |
} | |
DIGEST="$(digestpem "$PEM")" | |
EXPECTED="3 1 1 $DIGEST" | |
ZONES="$(gettlsa)" | |
echo $EXPECTED | |
function extractentries() { | |
echo "$1" | jq -r ".result[] | {content: .content, id: .id, name: .name} | @base64" | |
} | |
TODELETE="" | |
for row in $(extractentries "$ZONES"); do | |
ROW="$(echo "$row" | base64 --decode)" | |
CONTENT="$(echo "$ROW" | jq -r ".content")" | |
ID="$(echo "$ROW" | jq -r ".id")" | |
NAME="$(echo "$ROW" | jq -r ".name")" | |
if echo "$NAMES" | grep -w "$NAME" > /dev/null; then | |
echo "id: $ID name: $NAME content: $CONTENT" | |
if [[ "$CONTENT" != "$EXPECTED" ]]; then | |
TODELETE="$TODELETE $ID" | |
fi | |
fi | |
done | |
echo "EXPECTED: $EXPECTED" | |
echo "TO Delete $TODELETE" | |
certbot renew --quiet | |
NEWDIGEST="$(digestpem "$PEM")" | |
NEWCONTENT="3 1 1 $NEWDIGEST" | |
if [[ "$NEWCONTENT" == "$CONTENT" ]]; then | |
echo "Renewall is a no-op, no changes to be done" | |
exit | |
fi | |
function puttlsa() { | |
curl -X POST "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/dns_records" \ | |
-H "Authorization: Bearer $TOKEN" \ | |
-H "Content-Type: application/json" \ | |
--data "{\"type\":\"TLSA\",\"name\":\"$1\",\"content\":\"$2\",\"ttl\":1,\"data\":{\"usage\":3,\"selector\":1,\"matching_type\":1,\"certificate\":\"$3\"}}" | |
} | |
for name in $NAMES; do | |
echo "create dns record for $name with $NEWCONTENT" | |
RESULT="$(puttlsa "$name" "$NEWCONTENT" "$NEWDIGEST")" | |
echo $RESULT | |
SUCCESS="$(echo "$RESULT" | jq -r '.success')" | |
if [[ "$SUCCESS" != "true" ]]; then | |
ERROR="$(echo "$RESULT" | jq -r '.errors[0].message')" | |
if [[ "$ERROR" != "Record already exists." ]]; then | |
echo "Failure in applying '$name' with '$NEWCONTENT' due to $ERROR" | |
exit | |
fi #end error != ... | |
fi #end success != true | |
done | |
echo "Sleeping for 5 minutes" | |
sleep 300 | |
# Reload applications that rely on certificate | |
echo "Reloading" | |
systemctl reload postfix dovecot nginx | |
echo "Sleeping for 5 minutes" | |
sleep 300 | |
function deletedns() { | |
curl -X DELETE "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/dns_records/$1" \ | |
-H "Authorization: Bearer $TOKEN" \ | |
-H "Content-Type: application/json" | |
echo "" | |
} | |
echo "Deleting old records" | |
for id in $TODELETE; do | |
echo "Deleting dns record $id with old content $CONTENT" | |
deletedns $id | |
done | |
echo "Done!" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment