In een wereld vol DHCP-adressen is externe toegang tot je pc of server thuis een gedoe. Heb je een provider die je een dynamisch publiek IP-adres toewijst, dan heb je een probleem: je IP verandert regelmatig, en daarmee breekt de verbinding met je apparaat thuis via een statisch domein.
Dit gratis Dynamic DNS (DDNS) updater script voor TransIP lost dat op. Het actualiseert je DNS-records zodra je externe IP verandert, zodat je domein (bijv. vpn.example.com) altijd naar het juiste IP wijst. Dit script is een must voor iedereen die TransIP’s DNS-diensten gebruikt.
Belangrijkste features van het script
- Geautomatiseerde DNS-updates:
- Controleert automatisch je huidige externe IP en vergelijkt dat met het DNS A-record van je domein.
- Maakt het A-record aan of werkt het bij zodat het overeenkomt met je nieuwste externe IP.
- Dynamic DNS voor dynamische IP’s:
- Ideaal voor situaties waarin je publieke IP regelmatig verandert, zoals een internetverbinding thuis met dynamische DHCP-toewijzing.
- Veilige authenticatie:
- Gebruikt TransIP’s API met een private key voor veilige communicatie.
- Genereert kort geldige tokens voor extra veiligheid (maximaal 10 minuten per sessie).
- Aanpasbare instellingen:
- Configureer eenvoudig domein, subdomein, TTL (time-to-live) en logging.
- Pas het script aan naar je eigen wensen.
- Logging en foutafhandeling:
- Uitgebreide logging houdt elke stap van het updateproces bij.
- Ingebouwde foutafhandeling voor een soepelere uitvoering en makkelijker debuggen.
Hoe het werkt
- Initiële setup:
- Maak een TransIP-account aan en genereer een private key voor API-toegang op https://www.transip.nl/cp/account/api/
- Configureer het script met je domeinnaam, subdomein en het pad naar je private key.
- Extern IP ophalen:
- Het script haalt je huidige externe IP op via een publieke API zoals
ipinfo.io.
- Het script haalt je huidige externe IP op via een publieke API zoals
- Token genereren:
- Genereert een kort geldig access token via TransIP’s authenticatie-endpoint.
- DNS-record controleren:
- Controleert of het huidige DNS A-record overeenkomt met je externe IP.
- Verschilt het, dan werkt het script het record bij; ontbreekt het, dan maakt het een nieuw aan.
- DNS updaten:
- Gebruikt TransIP’s beveiligde API om het DNS A-record bij te werken of aan te maken met je huidige externe IP.
- Logging en meldingen:
- Logt elke stap en geeft duidelijke meldingen als er iets misgaat.
Vereisten
Voordat je het script gebruikt, zorg dat je het volgende hebt:
- Een TransIP-account met API-toegang ingeschakeld.
- Een geregistreerd domein in je TransIP-account.
- Een statische private key voor veilige API-authenticatie.
- De benodigde dependencies op je systeem:
curl,opensslenjq.
sudo apt update
sudo apt install -y curl openssl jqWaarom deze oplossing?
In tegenstelling tot betaalde externe DDNS-providers geeft dit script je gratis controle over je eigen DNS-beheer, met privacy en zonder vendor lock-in. Het is op maat gemaakt.
#!/bin/bash
# Configuration settings
LOGIN_NAME="your-login-name" # Fill in your TransIP login name
PRIVATE_KEY_PATH="/path/transip_private_key.pem" # Path to your private key file
AUTH_URL="https://api.transip.nl/v6/auth"
TOKEN_FILE="/var/tmp/transip_token.json" # File to store token and expiration date
DOMAIN="example.com" # Your main or test domain
SUBDOMAIN="vpn" # The subdomain you want to update (e.g., test.example.com)
RECORD_TYPE="A"
TTL=60 # Time-to-live for DNS records
LOG_FILE="/var/log/transip_dyndns.log"
# Function to update a log file
log_message() {
echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" > $LOG_FILE
}
# Function to log an error and stop the script
handle_error() {
log_message "ERROR: $1"
echo "ERROR: $1" >&2
exit 1
}
# Function to obtain a new access token with a short validity period (10 minutes)
generate_new_token() {
log_message "Generating a new token with a validity period of 10 minutes."
NONCE=$(head /dev/urandom | tr -dc A-Za-z0-9 | head -c 16)
TIMESTAMP=$(date +%s)
LABEL="Set DNS to external DHCP $TIMESTAMP"
REQUEST_BODY=$(printf '{"login":"%s","nonce":"%s","read_only":false,"expiration_time":"10 minutes","label":"%s","global_key":true}' "$LOGIN_NAME" "$NONCE" "$LABEL")
log_message "Request Body: $REQUEST_BODY"
SIGNATURE=$(echo -n "$REQUEST_BODY" | openssl dgst -sha512 -sign "$PRIVATE_KEY_PATH" | base64 | tr -d '\n')
log_message "Generated signature: $SIGNATURE"
RESPONSE=$(curl -s -X POST "$AUTH_URL" \
-H "Signature: $SIGNATURE" \
-H "Content-Type: application/json" \
-d "$REQUEST_BODY") || handle_error "Failed to connect to TransIP API"
log_message "API-response received: $RESPONSE"
ACCESS_TOKEN=$(echo "$RESPONSE" | jq -r '.token // empty')
if [ -z "$ACCESS_TOKEN" ]; then
handle_error "The received access token is empty or invalid. Check the API-response: $RESPONSE"
fi
log_message "Received token: $ACCESS_TOKEN"
}
# Function to get the current DNS A-record from TransIP
get_dns_record() {
# Get API response and store status code
RESPONSE=$(curl -s -X GET \
-H "Authorization: Bearer $ACCESS_TOKEN" \
"https://api.transip.nl/v6/domains/$DOMAIN/dns")
# Check if response contains JSON and no errors
if echo "$RESPONSE" | jq empty 2>/dev/null; then
RECORD_CONTENT=$(echo $RESPONSE | jq -r '.dnsEntries[] | select(.name=="'$SUBDOMAIN'" and .type=="'$RECORD_TYPE'") | .content')
else
handle_error "API response does not contain valid JSON or contains an error: $RESPONSE"
fi
# Check if we found a valid record
if [ -z "$RECORD_CONTENT" ]; then
log_message "No valid DNS record found for $SUBDOMAIN.$DOMAIN. Record may need to be created."
return 1
fi
echo "$RECORD_CONTENT"
return 0
}
# Function to update or create DNS record
update_or_create_dns_record() {
# Check if record exists
CURRENT_DNS_IP=$(get_dns_record)
if [ -z "$CURRENT_DNS_IP" ]; then
log_message "No DNS A-record found for $SUBDOMAIN.$DOMAIN. Creating record."
# Create request body for new record (TTL set to 60 seconds)
DNS_ENTRY=$(jq -n \
--arg name "$SUBDOMAIN" \
--arg expire "60" \
--arg type "$RECORD_TYPE" \
--arg content "$CURRENT_IP" \
'{"dnsEntry": {"name": $name, "expire": ($expire | tonumber), "type": $type, "content": $content}}')
log_message "DNS entry (create): $DNS_ENTRY"
# Execute POST request to create new DNS entry with better status code handling
RESPONSE=$(curl -s -w "%{http_code}" -X POST \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d "$DNS_ENTRY" \
"https://api.transip.nl/v6/domains/$DOMAIN/dns")
HTTP_STATUS_UPDATE=$(echo "$RESPONSE" | tail -n1)
RESPONSE_BODY=$(echo "$RESPONSE" | head -n -1)
log_message "API response for creating DNS entry: $RESPONSE_BODY"
log_message "HTTP status code received for creation: $HTTP_STATUS_UPDATE"
if [ "$HTTP_STATUS_UPDATE" -ne 201 ]; then
handle_error "API call for creating DNS entry failed with status code $HTTP_STATUS_UPDATE."
fi
log_message "New DNS A-record successfully created."
else
log_message "Updating DNS A-record for $SUBDOMAIN.$DOMAIN to IP $CURRENT_IP"
# Create request body for updating the record
DNS_ENTRY=$(jq -n \
--arg name "$SUBDOMAIN" \
--arg expire "$TTL" \
--arg type "$RECORD_TYPE" \
--arg content "$CURRENT_IP" \
'{"dnsEntry": {"name": $name, "expire": ($expire | tonumber), "type": $type, "content": $content}}')
log_message "DNS entry (update): $DNS_ENTRY"
# Execute PATCH request to update the existing DNS record
RESPONSE=$(curl -s -w "%{http_code}" -X PATCH \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d "$DNS_ENTRY" \
"https://api.transip.nl/v6/domains/$DOMAIN/dns")
HTTP_STATUS_UPDATE=$(echo "$RESPONSE" | tail -n1)
RESPONSE_UPDATE_BODY=$(echo "$RESPONSE" | head -n -1)
log_message "API update response received: $RESPONSE_UPDATE_BODY"
log_message "HTTP status code received for update: $HTTP_STATUS_UPDATE"
# Accept both 200 and 204 as successful updates
if [ "$HTTP_STATUS_UPDATE" -ne 200 ] && [ "$HTTP_STATUS_UPDATE" -ne 204 ]; then
handle_error "API call for DNS update failed with status code $HTTP_STATUS_UPDATE."
else
log_message "DNS A-record successfully updated to $CURRENT_IP"
fi
fi
}
# Get the current external IP
CURRENT_IP=$(curl -s http://ipinfo.io/ip) || handle_error "Failed to get external IP"
# Log the start
log_message "---- Start new run ----"
log_message "Current external IP: $CURRENT_IP"
# Always generate a short lived new token.
generate_new_token
# Try to update or create the DNS record
update_or_create_dns_record
log_message "---- End of run ----"


