-
-
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 |
Thanks it works on my Mac mini m1
I've forked this script in my Gitlab to support multiple domains: https://gitlab.com/Megver83/cloudflare-dyndns
Get the current external IP address
ip=$(curl -s -X GET https://checkip.amazonaws.com)
host is slower than nslookup, so i change from host to nslookup
cfip=$(nslookup $dnsrecord | awk -F': ' 'NR==6 { print $2 } ')
echo "Local IP is $ip"
echo "Cloudflare IP is $cfip"
if [ "$cfip" == "$ip" ]; then
echo "$dnsrecord is currently set to $ip; no changes needed"
exit
fi
Please someone tell me how to use this? I apt-get install -y jq but still have this(when i use nhanledev Version):
root@debtest:# ./cloudflare-ddns-update2.sh# ./cloudflare-ddns-update.sh
./cloudflare-ddns-update2.sh: line 4: $'\r': command not found
./cloudflare-ddns-update2.sh: line 7: $'\r': command not found
./cloudflare-ddns-update2.sh: line 12: $'\r': command not found
./cloudflare-ddns-update2.sh: line 17: $'\r': command not found
./cloudflare-ddns-update2.sh: line 18: $'\r': command not found
./cloudflare-ddns-update2.sh: line 21: $'\r': command not found
Current IP is 85.66.227.135
./cloudflare-ddns-update2.sh: line 23: $'\r': command not found
./cloudflare-ddns-update2.sh: line 56: syntax error: unexpected end of file
root@debtest:
bash: ./cloudflare-ddns-update.sh: /bin/bash^M: bad interpreter: No such file or directory
Then this when i use the original version :
bash: ./cloudflare-ddns-update.sh: /bin/bash^M: bad interpreter: No such file or directory
root@debtest:~#
Please help
place the .sh file in /var/www
and use the following command as root
cd /var/www && bash dnsnew.sh
{
"success": false,
"errors": [
{
"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"
}
],
"messages": [],
"result": null
}
What is zone exactly?
That was an user error. Its working, but changing to my proxied settings to dns only, if i change the end of the file the proxied false to proxied true i guess its stays on proxied...
IPV6? Thats not changed.
But, how can i setup to work with pppoe... Run when reconnects and gets new ip? Can someone help to make that script? Crontab maybe?
or not...
/etc/ppp/ip-up.d/0clampmss ?
Put that this row:
bash /etc/ppp/ip-up.d/cloudflare-ddns-update.sh
And put there that script?
FIX: Removing the "| jq" at the end fixed the issue, script running fine and updating the IP on cloudflare. Not sure on what the jq does, except for formatting the output into a more readable one for the user, but it seems that it is crashing the script when run in crontab.
Having troubles to run this script in crontab. Any ideas on how to do this? Running manually works great (with the modifications presented in this thread, changing proxied to true and changing to bearer auth). Fails at updating the record:
Current IP is xxx.xxx.xxx.xxx
Zone id for mydomain.com is xxxxxxxxxxxxxx
DNS record id for mydomain.com is xxxxxxxxxxxx
jq - commandline JSON processor [version 1.5-1-a5b5cbe]
Usage: jq [options] [file...]
jq is a tool for processing JSON inputs, applying the
given filter to its JSON text inputs and producing the
filter's results as JSON on standard output.
The simplest filter is ., which is the identity filter,
copying jq's input to its output unmodified (except for
formatting).
For more advanced filters see the jq(1) manpage ("man jq")
and/or https://stedolan.github.io/jq
Some of the options include:
-c compact instead of pretty-printed output;
-n use `null` as the single input value;
-e set the exit status code based on the output;
-s read (slurp) all inputs into an array; apply filter to it;
-r output raw strings, not JSON texts;
-R read raw strings, not JSON texts;
-C colorize JSON;
-M monochrome (don't colorize JSON);
-S sort keys of objects on output;
--tab use tabs for indentation;
--arg a v set variable $a to value <v>;
--argjson a v set variable $a to JSON value <v>;
--slurpfile a f set variable $a to an array of JSON texts read from <f>;
See the manpage for more options.
(23) Failed writing body
Thanks @Tras2 for the simple script here and for everyone's comments. I had previously tried getting DDNS updates on Cloudflare with ddclient but kept banging my head against the wall with no success. As @SteveElliott called out, you should use a Cloudflare token. Using this method, an account email address is not required. With that in mind I created a fork that worked for me, and yes, you may get it to work without installing jq
but I prefer the JSON output. Hopefully this helps someone else out who stumbles along here. Cheers!
Thank you!
Also thanks @SteveElliot and @jooosh for guiding into the right direction (use of token instead of the API Key)
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
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.
Could something like this be installed on a Ubiquity UDM-Pro if you can SSH into the console??
This is becoming a nightmare just trying to get a DDNS option setup and since my domain is already using cloudfare nameservers something like this seems like the perfect solution!