-
-
Save Tras2/cba88201b17d765ec065ccbedfb16d9a to your computer and use it in GitHub Desktop.
#!/bin/bash | |
# A bash script to update a Cloudflare DNS A record with the external IP of the source machine | |
# Used to provide DDNS service for my home | |
# Needs the DNS record pre-creating on Cloudflare | |
# Proxy - uncomment and provide details if using a proxy | |
#export https_proxy=http://<proxyuser>:<proxypassword>@<proxyip>:<proxyport> | |
# Cloudflare zone is the zone which holds the record | |
zone=example.com | |
# dnsrecord is the A record which will be updated | |
dnsrecord=www.example.com | |
## Cloudflare authentication details | |
## keep these private | |
[email protected] | |
cloudflare_auth_key=1234567890abcdef1234567890abcdef | |
# Get the current external IP address | |
ip=$(curl -s -X GET https://checkip.amazonaws.com) | |
echo "Current IP is $ip" | |
if host $dnsrecord 1.1.1.1 | grep "has address" | grep "$ip"; then | |
echo "$dnsrecord is currently set to $ip; no changes needed" | |
exit | |
fi | |
# if here, the dns record needs updating | |
# get the zone id for the requested zone | |
zoneid=$(curl -s -X GET "https://api.cloudflare.com/client/v4/zones?name=$zone&status=active" \ | |
-H "X-Auth-Email: $cloudflare_auth_email" \ | |
-H "X-Auth-Key: $cloudflare_auth_key" \ | |
-H "Content-Type: application/json" | jq -r '{"result"}[] | .[0] | .id') | |
echo "Zoneid for $zone is $zoneid" | |
# get the dns record id | |
dnsrecordid=$(curl -s -X GET "https://api.cloudflare.com/client/v4/zones/$zoneid/dns_records?type=A&name=$dnsrecord" \ | |
-H "X-Auth-Email: $cloudflare_auth_email" \ | |
-H "X-Auth-Key: $cloudflare_auth_key" \ | |
-H "Content-Type: application/json" | jq -r '{"result"}[] | .[0] | .id') | |
echo "DNSrecordid for $dnsrecord is $dnsrecordid" | |
# update the record | |
curl -s -X PUT "https://api.cloudflare.com/client/v4/zones/$zoneid/dns_records/$dnsrecordid" \ | |
-H "X-Auth-Email: $cloudflare_auth_email" \ | |
-H "X-Auth-Key: $cloudflare_auth_key" \ | |
-H "Content-Type: application/json" \ | |
--data "{\"type\":\"A\",\"name\":\"$dnsrecord\",\"content\":\"$ip\",\"ttl\":1,\"proxied\":false}" | jq |
It is a great work, however the authentication of Cloudflare is altered. If someone looks for a working version of this script, I have modified it that could be found here.
#!/bin/bash # based on https://gist.github.com/Tras2/cba88201b17d765ec065ccbedfb16d9a # initial data; they need to be filled by the user ## API token api_token=<YOUR_API_TOKEN> ## email address associated with the Cloudflare account email=<YOUR_EMAIL> ## the zone (domain) should be modified zone_name=<YOUR_DOMAIN> ## the dns record (sub-domain) should be modified dns_record=<YOUR_SUB_DOMAIN> # 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' ) echo "Your IPv4 is: $ipv4" echo "Your IPv6 is: $ipv6" # 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 if [ $zone_id ] then # check if there is any IP version 4 if [ $ipv4 ] then 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" ) # if the IPv6 exist dns_record_a_ip=$(echo $dns_record_a_id | jq -r '{"result"}[] | .[0] | .content') echo "The set IPv4 on Cloudflare (A Record) is: $dns_record_a_ip" 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' else echo "The current IPv4 and DNS record IPv4 are the same." fi else echo "Could not get your IPv4. Check if you have it; e.g. on https://ifconfig.co" fi # check if there is any IP version 6 if [ $ipv6 ] then 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" ) # if the IPv6 exist dns_record_aaaa_ip=$(echo $dns_record_aaaa_id | jq -r '{"result"}[] | .[0] | .content') echo "The set IPv6 on Cloudflare (AAAA Record) is: $dns_record_aaaa_ip" 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' else echo "The current IPv6 and DNS record IPv6 are the same." fi else echo "Could not get your IPv6. Check if you have it; e.g. on https://ifconfig.co" fi else echo "There is a problem with getting the Zone ID. Check if the Zone Name is correct." fi else echo "There is a problem with either the email or the password" fi
I have tried this updated script after making a api_token from the template.
getting this error and the record is not updated:
./dyndns.sh: line 5: XXX: not found
./dyndns.sh: line 11: home: not found
Your IPv4 is: x.x.x.x
Your IPv6 is:
The set IPv4 on Cloudflare (A Record) is: null
[
{
"code": 7003,
"message": "Could not route to /zones/null/dns_records/null, perhaps your object identifier is invalid?"
},
{
"code": 7000,
"message": "No route for that URI"
}
]
Could not get your IPv6. Check if you have it; e.g. on https://ifconfig.co
The token works well with Cloudflares test command.
The warning Could not get your IPv6. Check if you have it; e.g. on https://ifconfig.co
says that you do not have any IPv6. It should work :) check your Cloudflare.
But I will add some lines to give messages that the settings is done.
Thank you very much, I had put something wrong in the text. The script works now even though I don't have an IPv6.
I have just updated the code; thanks to @jstokholm that could be found here.
#!/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) should be modified; e.g. sub.example.com
dns_record=<YOUR_SUB_DOMAIN>
# 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
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"
)
# if the IPv4 exist
dns_record_a_ip=$(echo $dns_record_a_id | jq -r '{"result"}[] | .[0] | .content')
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 [+] The IPv4 is successfully set on Cloudflare as the A Record with the value of: $dns_record_a_ip"
else
echo -e "\033[0;37m [~] The current IPv4 and the existing on on Cloudflare are the same; there is no need to apply it."
fi
fi
# check if there is any IP version 6
if [ $ipv6 ]
then
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"
)
# if the IPv6 exist
dns_record_aaaa_ip=$(echo $dns_record_aaaa_id | jq -r '{"result"}[] | .[0] | .content')
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 [+] The IPv6 is successfully set on Cloudflare as the AAAA Record with the value of: $dns_record_aaaa_ip"
else
echo -e "\033[0;37m [~] The current IPv6 address and the existing on on Cloudflare are the same; there is no need to apply it."
fi
fi
else
echo -e "\033[0;31m [-] There is a problem with getting the Zone ID (subdomain) or the email address (username). Check them and try again."
fi
else
echo -e "\033[0;31m [-] There is a problem with either the API token. Check it and try again."
fi
Im just going to leave my take on this script here. It only supports ipv4 but allows to use the https_proxy funcionality of cloudflare.
Hello guys, can someone tell me how to i find the zone?
I would basically your domain(s) that is defined for the API key.
@namnamir Thank you for the updated script, it works like a charm. I needed to update multiple hostnames and some of those had proxy set to true. I made changes to your script to accommodate. Also, for some reason, the script was timing out on the first section where it grabs the ip from ifconfig so I increased the wait time. It is available here.
Thanks, this was all I needed! ddclient was just overly complicated!
Picking up on the above, have made a few changes to fit my requirements.
- Checks for changes
- Logs each check
- Verifies if new "IP" is of correct format (to eliminate errors)
- Updates multiple entries (proxied and not)
- Logs each change
Now running a cronjob to check and update my DNS records (proxied and not) when there is an IP change, see this repo if looking for similar.
Full article can be found here.
Stripped down IPv4 version based on previous scripts. You'll need jq and curl.
#!/usr/bin/env bash
api_token=<CF token>
email=<CF account email>
zone_name=<DNS zone>
dns_record=<A record FQDN>
set -e
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')
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')
record_data=$(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")
record_id=$(jq -r '{"result"}[] | .[0] | .id' <<< $record_data)
cf_ip=$(jq -r '{"result"}[] | .[0] | .content' <<< $record_data)
ext_ip=$(curl -s -X GET -4 https://ifconfig.co)
if [[ $cf_ip != $ext_ip ]]; then
result=$(curl -s \
-X PUT "https://api.cloudflare.com/client/v4/zones/$zone_id/dns_records/$record_id" \
-H "Content-Type: application/json" \
-H "X-Auth-Email: $email" \
-H "Authorization: Bearer $api_token" \
--data "{\"type\":\"A\",\"name\":\"$dns_record\",\"content\":\"$ext_ip\",\"ttl\":1,\"proxied\":false}" \
| jq .success)
if [[ $result == "true" ]]; then
echo "$dns_record updated to: $ext_ip"
exit 0
else
echo "$dns_record update failed"
exit 1
fi
else
echo "$dns_record already up do date"
exit 0
fi
Liked your stripped down version. Added IPV6, config in separate file, logging and many subdomains.
It is a great work, however the authentication of Cloudflare is altered. If someone looks for a working version of this script, I have modified it that could be found here.