Skip to content

Instantly share code, notes, and snippets.

@wardpieters
Last active May 28, 2025 12:56
Show Gist options
  • Save wardpieters/63b879365230331da6eb17d241c5809d to your computer and use it in GitHub Desktop.
Save wardpieters/63b879365230331da6eb17d241c5809d to your computer and use it in GitHub Desktop.
Cloudflare TLSA update script for mailcow
#!/bin/bash
zone=domain.nl
dnsrecord=some-server.domain.nl
## Cloudflare authentication details
## keep these private
cloudflare_token="XXXXXX"
# get certificate hash
chain_hash=$(openssl x509 -in /opt/mailcow-dockerized/data/assets/ssl/cert.pem -noout -pubkey | openssl pkey -pubin -outform DER | openssl dgst -sha256 -binary | hexdump -ve '/1 "%02x"')
# get the zone id for the requested zone
zone_id=$(curl -s -X GET "https://api.cloudflare.com/client/v4/zones?name=$zone&status=active" \
-H "Authorization: Bearer $cloudflare_token" \
-H "Content-Type: application/json" | jq -r '{"result"}[] | .[0] | .id')
echo "ID for $zone is $zone_id"
ports=("_25._tcp")
for i in "${ports[@]}"
do
# get the dns record id
dnsrecord_req=$(curl -s -X GET "https://api.cloudflare.com/client/v4/zones/$zone_id/dns_records?type=TLSA&name=$i.$dnsrecord" \
-H "Authorization: Bearer $cloudflare_token" \
-H "Content-Type: application/json")
dnsrecord_id=$(echo "$dnsrecord_req" | jq -r '{"result"}[] | .[0] | .id')
dnsrecord_hash=$(echo "$dnsrecord_req" | jq -r '{"result"}[] | .[0] | .data.certificate')
echo "Processing record $i.$dnsrecord ..."
if [ -z "$dnsrecord_id" ] || [ $dnsrecord_id == "null" ]
then
# Add the record
curl -s -X POST "https://api.cloudflare.com/client/v4/zones/$zone_id/dns_records" \
-H "Authorization: Bearer $cloudflare_token" \
-H "Content-Type: application/json" \
--data "{\"type\":\"TLSA\",\"name\":\"$i.$dnsrecord\", \"data\": {\"usage\": \"3\", \"selector\": \"1\", \"matching_type\": \"1\", \"certificate\":\"$chain_hash\"},\"ttl\":1,\"proxied\":false}" | jq
echo "Record $i.$dnsrecord added!"
else
if [[ "$dnsrecord_hash" != "$chain_hash" ]]
then
# Update the record
curl -s -X PUT "https://api.cloudflare.com/client/v4/zones/$zone_id/dns_records/$dnsrecord_id" \
-H "Authorization: Bearer $cloudflare_token" \
-H "Content-Type: application/json" \
--data "{\"type\":\"TLSA\",\"name\":\"$i.$dnsrecord\", \"data\": {\"usage\": \"3\", \"selector\": \"1\", \"matching_type\": \"1\", \"certificate\":\"$chain_hash\"},\"ttl\":1,\"proxied\":false}" | jq
echo "Record $i.$dnsrecord updated!"
else
echo "Record $i.$dnsrecord does not need to be updated!"
fi
fi
done
@tomlawesome
Copy link

tomlawesome commented Sep 12, 2023

Hi!

I just wanted to comment and say thanks so much for this gist. It's saved me a lot of effort and hassle. After switching to using LetsEncrypt I was having to renew TLSA manually on a regular basis. This has totally fixed the issue, so it's all automated again.

Many thanks.

@thefeli73
Copy link

Wonderful script, thank you very much! Exactly what i needed for my mailcow setup behind Nginx Proxy Manager.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment