Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
A bash script to update a Cloudflare DNS A record with the external IP of the source machine
#!/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
cloudflare_auth_email=me@cloudflare.com
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
@SteveElliott
Copy link

SteveElliott commented Oct 22, 2020

It's better to use use Bear token, just
replace -H "X-Auth-Key: $cloudflare_auth_key" \ to -H "Authorization: Bearer $cloudflare_auth_key" \

this should be
-H "Authorization: Bearer $cloudflare_auth_token"

https://dash.cloudflare.com/profile/api-tokens says:

API Tokens use the standard Authorization: Bearer header for authentication instead of x-auth-email and x-auth-key that API Keys use.

Steve Elliott

@mchepukov
Copy link

mchepukov commented Oct 22, 2020

I agree, but in this script using $cloudflare_auth_key" as variable. As you say, it's better to rename the variable $cloudflare_auth_key" to $cloudflare_auth_token"

@SteveElliott
Copy link

SteveElliott commented Oct 22, 2020

@SteveElliott
Copy link

SteveElliott commented Oct 23, 2020

To clarify:

Your Cloudflare dashboard provides values for:

  • Zone ID
  • Account ID
  • API token
  • Global API key
  • Origin CA KEY

The API key is used for the X-Auth-Key: header
The API token is used for the Authorization: Bearer header

Steve

@IIPoliII
Copy link

IIPoliII commented Nov 11, 2020

How can I say I don't want it to pass through the Cloudflare proxy?

@nhanledev
Copy link

nhanledev commented Feb 28, 2021

Thank you for the script, but it has multiple problems when I use it, so I will recap everything you need to know and the updated script down below for those who interest.

Firstly, you would need to install jq by running sudo apt install -y jq


# 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
cloudflare_auth_email=me@cloudflare.com
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 "Authorization: Bearer $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 "Authorization: Bearer $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 "Authorization: Bearer $cloudflare_auth_key" \
  -H "Content-Type: application/json" \
  --data "{\"type\":\"A\",\"name\":\"$dnsrecord\",\"content\":\"$ip\",\"ttl\":1,\"proxied\":false}" | jq

@NanoG6
Copy link

NanoG6 commented May 24, 2021

@nhanledev and all,
Forgive me for my ignorance, I only have basic knowledge of bash scripting.
But, shouldn't be after "if" there should be "then"?

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

@nhanledev
Copy link

nhanledev commented May 24, 2021

@NanoG6 I don’t understand your statement clearly, but what I see from bash if else the statement has “then” inside the block. The code you quoted above also has “then” at the end of the first line.

@NanoG6
Copy link

NanoG6 commented May 24, 2021

Yes it has “then”, but isn’t it only for “echo”? Or maybe there should be “else” after the “fi” for updating the record?

@nhanledev
Copy link

nhanledev commented May 24, 2021

If I understand it correctly, it is just a check of the current IP address of the input domain. If the if statement is true, the whole script is stopped so there is no need to use else here.

@saminaf
Copy link

saminaf commented Jun 3, 2021

Thanks it Worked for me
used Api key instead of Api token
cloudflare_auth_key=1234567890abcdef1234567890abcdef

Then installed jq
apt-get install jq or yum install jq

and finally it was changing proxied to dns only so i changer the false in the last line to true
--data "{"type":"A","name":"$dnsrecord","content":"$ip","ttl":1,"proxied":false}" | jq

changed --data "{"type":"A","name":"$dnsrecord","content":"$ip","ttl":1,"proxied":true}" | jq

now i will try to make for two domin if worked then i will comment.

@saminaf
Copy link

saminaf commented Jun 3, 2021

yes i done i just added second copy say with different name
cloudflare-ddns-update2.sh

and updated the two lines

zone=seconddomain.com
dnsrecord=seconddomain.com

and at the end write down this line to run second file
sh ./cloudflare-ddns-update2.sh

and its done.......

@wiredyeti
Copy link

wiredyeti commented Aug 21, 2021

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!

@Tras2
Copy link
Author

Tras2 commented Aug 30, 2021

@kenjichanhkg
Copy link

kenjichanhkg commented Sep 15, 2021

Thanks it works on my Mac mini m1

@Megver83
Copy link

Megver83 commented Nov 1, 2021

I've forked this script in my Gitlab to support multiple domains: https://gitlab.com/Megver83/cloudflare-dyndns

@anhdmt-ttm
Copy link

anhdmt-ttm commented Nov 1, 2021

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

@megvadulthangya
Copy link

megvadulthangya commented Nov 27, 2021

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-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:
# ./cloudflare-ddns-update.sh
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

@saminaf
Copy link

saminaf commented Nov 27, 2021

place the .sh file in /var/www
and use the following command as root
cd /var/www && bash dnsnew.sh

@megvadulthangya
Copy link

megvadulthangya commented Nov 27, 2021

{
"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?

@megvadulthangya
Copy link

megvadulthangya commented Nov 27, 2021

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?

@g-vdn
Copy link

g-vdn commented Dec 5, 2021

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

@jooosh
Copy link

jooosh commented Dec 19, 2021

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!

@gadolf66
Copy link

gadolf66 commented Feb 1, 2022

Thank you!
Also thanks @SteveElliot and @jooosh for guiding into the right direction (use of token instead of the API Key)

@namnamir
Copy link

namnamir commented Mar 12, 2022

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

@jstokholm
Copy link

jstokholm commented Mar 19, 2022

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.

@namnamir
Copy link

namnamir commented Mar 20, 2022

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.

@jstokholm
Copy link

jstokholm commented Mar 20, 2022

Thank you very much, I had put something wrong in the text. The script works now even though I don't have an IPv6.

@namnamir
Copy link

namnamir commented Mar 28, 2022

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@gmail.com
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

@matiasba
Copy link

matiasba commented Jun 20, 2022

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment