Skip to content

Latest commit

 

History

History
168 lines (154 loc) · 8.48 KB

File metadata and controls

168 lines (154 loc) · 8.48 KB

Best Dynamic DNS (DDNS) Services

I have looked up mublitple DDNS services. Including the following ones:

All will provide you with a subdomain and let you set your IP on it dynamically. The problem is with the security of the IP. All might reveal the IP to the public. Therefore, finding a solution helping with the security is crucial.

I realized using Cloudflare Edit Zone API is a solution. Fortunately, there are scripts like Cloudflare DDNS (Python 3) that makes it easy; a PowerShell solution is provided here as well.

Cloudflare

There is a possibility to frequently update DNS records (A and/or AAAA) of a domain on Clouldflare via API. To do so, there is a need to get the an API token:

  1. Go to the My Account -> API Tokens.
  2. Create an API with the template "Edit Zone DNS".
  3. Select the zone (domain name).

Use the API on Linux

  1. Save the following script in a file, e.g. cloudflare.sh. For the single run use bash cloudflare.sh rather than sh as sh doesn't support [[]] in the IF statement.
  2. Make it executable by chmod +x /path/to/cloudflare.sh.
  3. Run crontab -e and add a line like @weekly /path/to/cloudflare.sh to the end of the file to run the script periodically. Use tools like Crontab Guru to define the frequency properly.
#!/bin/bash
# based on https://gist.github.com/Tras2/cba88201b17d765ec065ccbedfb16d9a
# initial data; they need to be filled by the user
## API token; e.g. FErsdfklw3er59dUlDce44-3D43dsfs3sddsFoD3
api_token=<YOUR_API_TOKEN>
## the email address associated with the Cloudflare account; e.g. [email protected]
email=<YOUR_EMAIL>
## the zone (domain) should be modified; e.g. example.com
zone_name=<YOUR_DOMAIN>
## the dns record (sub-domain) that needs to be modified; e.g. sub.example.com
dns_record=<YOUR_SUB_DOMAIN>

# Check if the script is already running
if [ ps ax | grep $0 | grep -v $$ | grep bash | grep -v grep ]; then
    echo -e "\033[0;31m [-] The script is already running."
    exit 1
fi

# Check if jq is installed
check_jq=$(which jq)
if [ -z "${check_jq}" ]; then
    echo -e "\033[0;31m [-] jq is not installed. Install it by 'sudo apt install jq'."
    exit 1
fi

# check the subdomain
# check if the dns_record field (subdomain) contains dot
if [[ $dns_record == *.* ]]; then
    # if the zone_name field (domain) is not in the dns_record
    if [[ $dns_record != *.$zone_name ]]; then
        echo -e "\033[0;31m [-] The Zone in DNS Record does not match the defined Zone; check it and try again."
        exit 1
    fi
# if the dns_record (subdomain) is not complete and contains invalid characters
elif ! [[ $dns_record =~ [\w\d\-]+ ]]; then
    echo -e "\033[0;31m [-] The DNS Record contains illegal charecters, i.e., @, %, *, _, etc.; fix it and run the script again."
    exit 1
# if the dns_record (subdomain) is not complete, complete it
else
    dns_record="$dns_record.$zone_name"
fi

# Check if DNS Records Exists
check_record_ipv4=$(dig -t a +short ${dns_record} | tail -n1)
check_record_ipv6=$(dig -t aaaa +short ${dns_record} | tail -n1)

# get the basic data
ipv4=$(curl -s -X GET -4 https://ifconfig.co)
ipv6=$(curl -s -X GET -6 https://ifconfig.co)
user_id=$(curl -s -X GET "https://api.cloudflare.com/client/v4/user/tokens/verify" \
               -H "Authorization: Bearer $api_token" \
               -H "Content-Type:application/json" \
          | jq -r '{"result"}[] | .id'
         )

# write down IPv4 and/or IPv6
if [ $ipv4 ]; then echo -e "\033[0;32m [+] Your public IPv4 address: $ipv4"; else echo -e "\033[0;33m [!] Unable to get any public IPv4 address."; fi
if [ $ipv6 ]; then echo -e "\033[0;32m [+] Your public IPv6 address: $ipv6"; else echo -e "\033[0;33m [!] Unable to get any public IPv6 address."; fi

# check if the user API is valid and the email is correct
if [ $user_id ]; then
    zone_id=$(curl -s -X GET "https://api.cloudflare.com/client/v4/zones?name=$zone_name&status=active" \
                   -H "Content-Type: application/json" \
                   -H "X-Auth-Email: $email" \
                   -H "Authorization: Bearer $api_token" \
              | jq -r '{"result"}[] | .[0] | .id'
             )
    # check if the zone ID is avilable
    if [ $zone_id ]; then
        # check if there is any IP version 4
        if [ $ipv4 ]; then
            # Check if A Record exists
            if [ -z "${check_record_ipv4}" ]; then
                echo -e "\033[0;31m [-] No A Record is set for ${dns_record}. This should be created first!"
                exit 1
            fi
            dns_record_a_id=$(curl -s -X GET "https://api.cloudflare.com/client/v4/zones/$zone_id/dns_records?type=A&name=$dns_record"  \
                                   -H "Content-Type: application/json" \
                                   -H "X-Auth-Email: $email" \
                                   -H "Authorization: Bearer $api_token"
                             )
            dns_record_a_ip=$(echo $dns_record_a_id |  jq -r '{"result"}[] | .[0] | .content')
            # if a new IPv4 exist; current IPv4 is different with the actual IPv4
            if [ $dns_record_a_ip != $ipv4 ]; then
                # change the A record
                curl -s -X PUT "https://api.cloudflare.com/client/v4/zones/$zone_id/dns_records/$(echo $dns_record_a_id | jq -r '{"result"}[] | .[0] | .id')" \
                     -H "Content-Type: application/json" \
                     -H "X-Auth-Email: $email" \
                     -H "Authorization: Bearer $api_token" \
                     --data "{\"type\":\"A\",\"name\":\"$dns_record\",\"content\":\"$ipv4\",\"ttl\":1,\"proxied\":false}" \
                | jq -r '.errors'
                # write the result
                echo -e "\033[0;32m [+] Updated: The IPv4 is successfully set on Cloudflare as the A Record with the value of: $ipv4."
                exit 0
            else
                echo -e "\033[0;37m [~] No change: The current IPv4 address matches Cloudflare."
                exit 0
            fi
        fi

        # check if there is any IP version 6
        if [ $ipv6 ]
        then
            # Check A Record exists
            if [ -z "${check_record_ipv6}" ]; then
                echo -e "\033[0;31m [-] No AAAA Record called ${dns_record}. This must be created first!"
                exit 1
            fi
            dns_record_aaaa_id=$(curl -s -X GET "https://api.cloudflare.com/client/v4/zones/$zone_id/dns_records?type=AAAA&name=$dns_record"  \
                                      -H "Content-Type: application/json" \
                                      -H "X-Auth-Email: $email" \
                                      -H "Authorization: Bearer $api_token"
                                )
            dns_record_aaaa_ip=$(echo $dns_record_aaaa_id | jq -r '{"result"}[] | .[0] | .content')
            # if a new IPv6 exist; current IPv6 is different with the actual IPv6
            if [ $dns_record_aaaa_ip != $ipv6 ]; then
                # change the AAAA record
                curl -s -X PUT "https://api.cloudflare.com/client/v4/zones/$zone_id/dns_records/$(echo $dns_record_aaaa_id | jq -r '{"result"}[] | .[0] | .id')" \
                     -H "Content-Type: application/json" \
                     -H "X-Auth-Email: $email" \
                     -H "Authorization: Bearer $api_token" \
                     --data "{\"type\":\"AAAA\",\"name\":\"$dns_record\",\"content\":\"$ipv6\",\"ttl\":1,\"proxied\":false}" \
                | jq -r '.errors'
                # write the result
                echo -e "\033[0;32m [+] Updated: The IPv6 is successfully set on Cloudflare as the AAAA Record with the value of: $ipv6."
                exit 0
            else
                echo -e "\033[0;37m [~] No change: The current IPv6 address matches the existing records on Cloudflare."
                exit 0
            fi
        fi
    else
        echo -e "\033[0;31m [-] There is a problem with getting the Zone ID (sub-domain) or the email address (username). Check them and try again."
        exit 1
    fi
else
    echo -e "\033[0;31m [-] There is a problem with either the API token. Check it and try again."
    exit 1
fi

Use the API on Windows

Use this article to set it up on Windows.