Created
January 16, 2025 18:23
-
-
Save tkn777/f148a8df45abc98ab6463eadb2c72f4d to your computer and use it in GitHub Desktop.
Create / removes acme dns records by modifying bind zone files (without using RFC2136)
This file contains hidden or 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 | |
# | |
# Create / Removes acme dns records by modifying bind zone files. | |
# It does not use RFC2136, so zone files can still be edited manually. :) | |
# | |
# Notes: | |
# - This script is non-concurrent! | |
# | |
# Assumptions: | |
# - There is one zone file 'db.<domain>'. And there are no zone files for sub domains. | |
# - All zone files are under /etc/bind | |
# - The zone files are writeable for user 'bind'. | |
# - Every zone file contains exactly one SOA record (with the serial). | |
# - The serial is an incrementing one (and e.g. no date) | |
# | |
# Author: Thomas Kuhlmann ( [email protected] ) | |
# License: LGPLv3 | |
# | |
# Get command line arguments | |
MODE=$1 | |
HOST_NAME=$2 | |
TOKEN=$3 | |
# Check command line arguments | |
if [ $# -lt 2 ] || ! [[ "$MODE" =~ ^[01]$ ]] || { [ "$MODE" -eq 1 ] && [ -z "$TOKEN" ]; }; then | |
echo "Usage: $0 MODE HOST [TOKEN]" >&2 | |
echo "- MODE must be 0 (remove acme-entry) or 1 (add acme-entry)" >&2 | |
echo "- TOKEN is required for MODE 1" >&2 | |
exit 1 | |
fi | |
# Get domain name by host name | |
DOMAIN_NAME=$(echo ${HOST_NAME} | rev | cut -d '.' -f 1-2 | rev) | |
if [ -z $DOMAIN_NAME ]; then | |
echo "Error: Could not retrieve domain name from host name ${HOST_NAME}" >&2 | |
exit 1 | |
fi | |
# Search zone file by domain name | |
ZONE_FILE_NAME="db.${DOMAIN_NAME}" | |
ZONE_FILE=$(find /etc/bind/ -type f -name "${ZONE_FILE_NAME}") | |
if [ ! -w "$ZONE_FILE" ]; then | |
echo "Error: Zone file (name: ${ZONE_FILE_NAME}) for host ${HOST_NAME} (domain: ${DOMAIN_NAME}) not found or not writable for user '$USER'." >&2 | |
exit 1 | |
fi | |
# Increment serial in zone file (inline) | |
ZONE_CONTENT=$(<${ZONE_FILE}) | |
OLD_SERIAL=$(echo ${ZONE_CONTENT} | grep -oP 'IN\s+SOA.*\(\s*\d+' | tr "(" " " | rev | cut -d ' ' -f 1 | rev) | |
if [ -z $OLD_SERIAL ]; then | |
echo "Error: Found no serial in zone file ${ZONE_FILE}" >&2 | |
exit 1 | |
fi | |
NEW_SERIAL=$(printf "%06d" $((10#${OLD_SERIAL} + 1))) | |
sed -i "s/${OLD_SERIAL}/${NEW_SERIAL}/" ${ZONE_FILE} | |
# Add / remove acme-challenge entry in zone file (inline) | |
ZONE_RECORD_NAME="_acme-challenge.${HOST_NAME}." | |
sed -i "/${ZONE_RECORD_NAME}/d" ${ZONE_FILE} # always remove existin one(s), if any | |
if [ "${MODE}" -eq 1 ]; then | |
echo "${ZONE_RECORD_NAME} 60 IN TXT \"${TOKEN}\"" >> ${ZONE_FILE} | |
fi | |
# Reload bind9 zones | |
/usr/sbin/rndc reload > /dev/null | |
if [ $? -ne 0 ]; then | |
echo "Error: While reloading zones." >&2 | |
exit 1 | |
fi | |
# In case of an add, wait for publish of dns record | |
if [ "${MODE}" -eq 1 ]; then | |
MAX_WAIT=60; ELAPSED=0 | |
while [ ${ELAPSED} -lt ${MAX_WAIT} ]; do | |
sleep 1; ELAPSED=$((ELAPSED + 1)) | |
RESULT=$(dig @9.9.9.9 _acme-challenge.${HOST_NAME} TXT +short) | |
if [ -n "${RESULT}" ]; then break; fi | |
done | |
if [ $ELAPSED -ge $MAX_WAIT ]; then | |
echo "Error: DNS record for _acme-challenge.${HOST_NAME} not found after 60 seconds." >&2 | |
exit 1 | |
fi | |
fi | |
# Regular exit | |
exit 0 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment