Bug 13071 - Could we add Cloudflare dynamic DNS
Summary: Could we add Cloudflare dynamic DNS
Status: NEW
Alias: None
Product: DDNS Updater
Classification: Unclassified
Component: Providers (show other bugs)
Version: unspecified
Hardware: all All
: - Unknown - Minor Usability
Assignee: Stefan Schantl
QA Contact: Michael Tremer
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2023-03-24 13:00 UTC by R R
Modified: 2024-04-04 07:17 UTC (History)
1 user (show)

See Also:


Attachments
updated providers.py for cloudflare (54.93 KB, text/x-python)
2024-04-04 07:10 UTC, Desmond Wong
Details

Note You need to log in before you can comment on or make changes to this bug.
Comment 1 Michael Tremer 2023-03-24 13:11:18 UTC
Are you the person I chatted to on the IRC channel about this this week?
Comment 2 R R 2023-03-24 14:43:41 UTC
No, I am another dude :-)
Comment 3 Michael Tremer 2023-04-11 14:01:14 UTC
(In reply to R R from comment #2)
> No, I am another dude :-)

Ah okay. So I just had a conversation with someone who was working on this. I have no idea what his progress is - or even his name.
Comment 4 R R 2023-10-24 13:47:31 UTC
Hi! I do not really know how to add stuff to the Ipfire web pages, but I did this script that can update the IP in Cloudflare. Maybe it can help a developer to add Cloudflare to the DDNS flavors.

cat ddns-cloudflare.sh

#!/bin/bash

# This script is originaly from:
# https://dev.to/ordigital/cloudflare-ddns-on-linux-4p0d

# Check for current external IP
IP=`dig +short txt ch whoami.cloudflare @1.0.0.1| tr -d '"'`

# === TOKEN ===
# Get an API Token with permissions to modify DNS records:
# Go to https://dash.cloudflare.com/profile/api-tokens and Create
# a new Token. Click on Edit zone DNS template and then add your
# preferred zone

# === DNS_ZONE_ID ===
# The "DNS_ZONE_ID" can be found in Cloudflare Dashboard, logged in
# the Cloudflare Dashboard, check the Domain page, "Zone ID"
# information will appearance at API block, right hand side.
# For more info, see:
# https://developers.cloudflare.com/fundamentals/setup/find-account-and-zone-ids/

# === DNS_ENTRY_ID ===
# Run this command to find the "DNS_ENTRY_ID":
# curl https://api.cloudflare.com/client/v4/zones/DNS_ZONE_ID/dns_records -H "Authorization: Bearer TOKEN"
#
# It should show something like this:
# "result":[{"id":"here_is_the_DNS_ENTRY_ID","zone_id":"here_is_the_DNS_ZONE_ID",
# "zone_name":"some.domain.org","name":"www.some.domain.org","type":"A",
# "content":"1.1.1.1", etc, etc

# Set Cloudflare API
DNS_ZONE_ID="qwertyqwertyqwertyqwerty"
DNS_ENTRY_ID="wertyqwertyqwertyqwerty"
TOKEN="ertyqwertyqwertyqwertyqwerty"
NAME="www.some.domain.org"

URL="https://api.cloudflare.com/client/v4/zones/$DNS_ZONE_ID/dns_records/$DNS_ENTRY_ID"


# Connect to Cloudflare
cf() {
curl -X ${1} "${URL}" \
     -H "Content-Type: application/json" \
     -H "Authorization: Bearer ${TOKEN}" \
      ${2} ${3}
}

# Get current DNS data
RESULT=$(cf GET)
IP_CF=$(jq -r '.result.content' <<< ${RESULT})

DATE=`date +"%b %d %T"`

# Compare IPs
if [ "$IP" = "$IP_CF" ]; then
    echo "$DATE ipfire ddns[0]: No change. Checked IP $IP = IP in DNS $IP_CF. $RESULT" | tee -a /var/log/messages
else
    RESULT=$(cf PUT --data "{\"type\":\"A\",\"name\":\"${NAME}\",\"content\":\"${IP}\"}")
    echo "$DATE ipfire ddns[0]: DNS updated: $RESULT" | tee -a /var/log/messages
fi
Comment 5 R R 2023-10-25 08:00:21 UTC
If i try to follow the code at https://community.ipfire.org/t/cloudflare-ddns/893 it seems like in that code it uses another approach:

It uses only these three infos to do everything
* **self.password** = X-Auth-Email = myemail@somewhere.org
* **self.username** = X-Auth-Key = Global API Key = Go to Your profile -> Overview -> Get your API token -> Global API Key = qwertyqwertyqwertyqwerty
* **self.hostname** = www.mydomain.org

First with "**self.password** = X-Auth-Email" and "**self.username** = X-Auth-Key" it does something like this:
`curl https://api.cloudflare.com/client/v4/zones -H "X-Auth-Email: myemail@somewhere.org" -H "X-Auth-Key: qwertyqwertyqwertyqwerty" -H "Content-Type: application/json"`

And it gets:
* zone_id = zdzdzdzdzdzdzdzdzdzd

With this new info it does something like this:
`curl https://api.cloudflare.com/client/v4/zones/zdzdzdzdzdzdzdzdzdzd/dns_records -H "X-Auth-Email: myemail@somewhere.org" -H "X-Auth-Key: qwertyqwertyqwertyqwerty" -H "Content-Type: application/json"`

And with the info from above command and the help of **self.hostname** it gets
* host_id = hdhdhdhhdhdhdhdhdhd

And now it has everything it needs to check IP and change if needed
Comment 6 R R 2023-10-25 10:10:59 UTC
I also just realized that the code in https://community.ipfire.org/t/cloudflare-ddns/893 is very similar to the code in https://cgit.ipfire.org/oddments/ddns.git/tree/src/ddns/providers.py (if this is the right place to look).

In my naive eyes it looks like copying all code to **providers.py** and changing stuff like:
```
requests.put(self.url bla bla ...
```
To
```
self.send_request(self.url bla bla ...
```
And maybe it would work with a few changes in providers.py?
Comment 7 R R 2023-10-25 10:24:01 UTC
(In reply to R R from comment #5)
> If i try to follow the code at
> https://community.ipfire.org/t/cloudflare-ddns/893 it seems like in that
> code it uses another approach:
> 
> It uses only these three infos to do everything

I wrote wrong, switched username & password, I meant:

It uses only these three infos to do everything
* self.username = X-Auth-Email = myemail@somewhere.org
* self.password = X-Auth-Key = Global API Key = Go to Your profile → Overview → Get your API token → Global API Key = qwertyqwertyqwertyqwerty
* self.hostname = www.mydomain.org

First with “self.username = X-Auth-Email” and “self.password = X-Auth-Key” it does something like this:
curl https://api.cloudflare.com/client/v4/zones -H "X-Auth-Email: myemail@somewhere.org" -H "X-Auth-Key: qwertyqwertyqwertyqwerty" -H "Content-Type: application/json"

And it gets:
* zone_id = zdzdzdzdzdzdzdzdzdzd

With this new info it does something like this:
curl https://api.cloudflare.com/client/v4/zones/zdzdzdzdzdzdzdzdzdzd/dns_records -H "X-Auth-Email: myemail@somewhere.org" -H "X-Auth-Key: qwertyqwertyqwertyqwerty" -H "Content-Type: application/json"

And with the info from above command and the help of self.hostname it gets
* host_id = hdhdhdhhdhdhdhdhdhd

And now it has everything it needs to check IP and change if needed
Comment 8 Desmond Wong 2024-01-27 09:30:48 UTC
I'm not really a developer and not sure how to write a test case etc for this. Wondering if this is still being worked on, or if anyone minds if I have a crack at this in my spare time to add this to the list of ddns providers? 

I am thinking I have enough ability to add it to the providers.py and test it locally for myself, would this be enough of a starting point to help contribute?
Comment 9 Michael Tremer 2024-01-27 11:32:28 UTC
Hello Desmond,

this would absolutely be a starting point. As far as I know there is nobody who is working on this right now and I cannot test this as I don't have a Cloudflare account.

But we are of course here to review and help.
Comment 10 Desmond Wong 2024-04-04 07:10:28 UTC
Created attachment 1512 [details]
updated providers.py for cloudflare
Comment 11 Desmond Wong 2024-04-04 07:17:11 UTC
I've spent a bit of time doing a crash course in python to see if I can get this working. 

So far I have implemented a working updater for CloudFlare in the providers.py file.

I'm not sure how to handle the web requests via the current methods the other providers are using the self.send_request(url, data=data) and would like to redo the code I have added from lines 1960 onwards to use those instead of the requests library.

I've noticed that this code doesn't seem to make the current hosts name green in the dynamic dns admin page, but it does update after some time. This might be just how this operates so happy to be corrected if I have missed something that triggers this to go green and confirm the state of the DDNS update. 

I am hoping this is a good starting point - there are improvements that can be made to capture the different failures and return that info, I haven't understood the code well enough yet to be able to work out where best to set those and return them (and when returned where they go).