# Blijf bereikbaar met een dynamic DNS updater voor TransIP

> Makkelijk toegang tot je pc thuis, ook met een dynamisch IP! Dit script werkt je TransIP DNS-records automatisch bij met je huidige IP, zodat je altijd verbinding hebt. Veilig, aanpasbaar en perfect voor thuiswerken of het hosten van diensten. Blijf moeiteloos bereikbaar!

- URL: https://www.allsubjectsmatter.nl/nl/stay-connected-with-a-dynamic-dns-updater-for-transip/
- Language: nl
- Published: 2024-11-29
- Updated: 2024-11-29
- Author: Remco Loup (https://www.allsubjectsmatter.nl/nl/author/remco/)
- Tags: Tech
- Original (en, written by the author): https://www.allsubjectsmatter.nl/stay-connected-with-a-dynamic-dns-updater-for-transip/
- Note: this text was translated with AI from the original.

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

1. **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.
2. **Dynamic DNS voor dynamische IP's:**
    - Ideaal voor situaties waarin je publieke IP regelmatig verandert, zoals een internetverbinding thuis met dynamische DHCP-toewijzing.
3. **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).
4. **Aanpasbare instellingen:**
    - Configureer eenvoudig domein, subdomein, TTL (time-to-live) en logging.
    - Pas het script aan naar je eigen wensen.
5. **Logging en foutafhandeling:**
    - Uitgebreide logging houdt elke stap van het updateproces bij.
    - Ingebouwde foutafhandeling voor een soepelere uitvoering en makkelijker debuggen.

## Hoe het werkt

1. **Initiële setup:**
    - Maak een TransIP-account aan en genereer een private key voor API-toegang op [https://www.transip.nl/cp/account/api/](https://www.transip.nl/cp/account/api/)
    - Configureer het script met je domeinnaam, subdomein en het pad naar je private key.
2. **Extern IP ophalen:**
    - Het script haalt je huidige externe IP op via een publieke API zoals `ipinfo.io`.
3. **Token genereren:**
    - Genereert een kort geldig access token via TransIP's authenticatie-endpoint.
4. **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.
5. **DNS updaten:**
    - Gebruikt TransIP's beveiligde API om het DNS A-record bij te werken of aan te maken met je huidige externe IP.
6. **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:

1. Een TransIP-account met API-toegang ingeschakeld.
2. Een geregistreerd domein in je TransIP-account.
3. Een statische private key voor veilige API-authenticatie.
4. De benodigde dependencies op je systeem:
    - `curl`, `openssl` en `jq`.

```bash
sudo apt update
sudo apt install -y curl openssl jq
```

## Waarom 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.

```bash
#!/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 ----"
```
