Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 36 additions & 10 deletions hetzner_ddns.sh
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ conf_ip_check_cooldown=30
conf_request_timeout=10
conf_api_url='https://api.hetzner.cloud/v1'
conf_ip_url='https://ip.hetzner.com/'
conf_auto_create_records=0

log() {
if test -r "$conf_log_file"; then
Expand Down Expand Up @@ -94,6 +95,7 @@ Configuration:
"request_timeout": Maximum duration of HTTP requests
"api_url": URL of the Hetzner Console'\''s API
"ip_url": URL of a service for retrieving external IP addresses
"auto_create_records": Automatically create missing DNS records (default: false)
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While this is arguably nicer user experience-wise, I chose to go against it as adding the record manually is a kind of a double check that user's intention matches what the script's configuration does. It neither adds or deletes records, just updates them. If there is a mistake/typo in JSON config, it will not turn into a garbage record that needs to be removed using Hetzner's website.

I'm not sure which approach is better 🤔

}

"defaults": {
Expand Down Expand Up @@ -177,7 +179,9 @@ load_and_test_api_key() {

# also try to load the api_key from .api_key_file
api_key_file_path=$(jq -r '.api_key_file' "$cfg_file")
api_key_from_file="$(cat "$api_key_file_path")"
if [ -n "$api_key_file_path" ] && [ "$api_key_file_path" != 'null' ]; then
api_key_from_file="$(cat "$api_key_file_path")"
fi

if ([ -z "$api_key" ] || [ "$api_key" = 'null' ]) && [ -z "$api_key_from_file" ]; then
log 'Error: API key neither provided through api_key in config.json nor through api_key_file'
Expand Down Expand Up @@ -339,8 +343,12 @@ EOF
jq '.rrset.records | length'
)"
if [ "$record_entries" -eq 0 ]; then
log "Error: $record_type record '$record_name' for domain '$record_domain' doesn't exist in Hetzner Console"
return 1
if [ "$conf_auto_create_records" = 1 ] || [ "$conf_auto_create_records" = 'true' ]; then
log "Note: $record_type record '$record_name' for domain '$record_domain' does not exist and will be created"
else
log "Error: $record_type record '$record_name' for domain '$record_domain' doesn't exist in Hetzner Console"
return 1
fi
elif [ "$record_entries" -gt 1 ]; then
log "Error: $record_type record '$record_name' for domain '$record_domain' has more than one entry"
return 1
Expand Down Expand Up @@ -499,6 +507,7 @@ update_record() {
ttl=$4
interface=$5
log "Update time reached for $type record '$name' for domain '$domain'"
record_comment="Managed by $program on $(hostname) at $(date -u +"%Y-%m-%dT%H:%M:%SZ")"
current_rrset="$(
curl -s -H "Authorization: Bearer $api_key" \
"$conf_api_url/zones/$domain/rrsets/$name/$type"
Expand All @@ -521,12 +530,29 @@ update_record() {
return 1
fi
if [ -z "$current_value" ] || [ "$current_value" = 'null' ]; then
log "Warning: Unable to fetch value of $type record $name for domain $domain"
return 1
fi
if [ -z "$current_value" ] || [ "$current_value" = 'null' ]; then
log "Warning: Failed reading IPv$version address of interface '$interface'"
return 1
if [ "$conf_auto_create_records" = 1 ] || [ "$conf_auto_create_records" = 'true' ]; then
if curl -s -X POST -H "Authorization: Bearer $api_key" \
-H "Content-Type: application/json" \
-d "{
\"name\": \"$name\",
\"type\": \"$type\",
\"ttl\": $ttl,
\"records\": [
{
\"value\": \"$expected_value\",
\"comment\": \"$record_comment\"
}
]
}" \
"$conf_api_url/zones/$domain/rrsets" >/dev/null; then
log "Created $type record '$name' for domain '$domain': $expected_value"
else
log "Warning: Unable to create $type record '$name' for domain '$domain'"
fi
else
log "Warning: Unable to fetch value of $type record '$name' for domain '$domain'"
fi
return
fi
if [ "$current_value" = "$expected_value" ]; then
log "Keep existing value of $type record '$name' for domain '$domain'"
Expand All @@ -537,7 +563,7 @@ update_record() {
\"records\": [
{
\"value\": \"$expected_value\",
\"comment\": \"Managed by $program on $(hostname)\"
\"comment\": \"$record_comment\"
}
]
}" \
Expand Down