linkiwi / cloudflare-ddns-client Goto Github PK
View Code? Open in Web Editor NEWLinux client for automatic dynamic DNS updates on CloudFlare-managed domains
License: MIT License
Linux client for automatic dynamic DNS updates on CloudFlare-managed domains
License: MIT License
I have 2 domains under my Cloudflare account
and I followed the instruction to set it up
a.com,b.net
but when I do
cloudflare-ddns --update-now
it only updates a.com and ignores b.net
Output:
Updating the A record (ID .....) of (sub)domain a.com (ID ...) to 123.456.789.0.
DNS record is already up-to-date; taking no action
Hi,
there's a typo (I think) in your script. Instead of get_tld, you use get_fld. This does not work here (fails to import error). When I change get_fld to get_tld in your script, the script works
I have a problem with the token authentification.
Everytime I run the script I got the message that I should check my login data. But they are correct. So I started to search where the authentification fails.
I found that
zone_resp = requests.get(CLOUDFLARE_ZONE_QUERY_API, headers=auth, timeout=6, params={'per_page': 50, 'page': cur_page})
print(zone_resp)
is not working properly.
zone_resp: <Response [403]>
I have no clue why this is happening. If I find a fix to this problem I'll make a pull request.
Hey,
I have recently started to use your tool.
However, there were a few things that bothered me.
If you do not mind, I would like to contribute and improve feed back messages in the CLI and various other stuff. Feel free to assign me to this issue.
Just posting this issue so noone else works on the same stuff at the same time ๐.
Unable to update the IP, following error happens:
root@master:~# cloudflare-ddns --update-now
From cffi callback <function _verify_callback at 0x7600d430>:
Traceback (most recent call last):
File "/usr/local/lib/python2.7/dist-packages/OpenSSL/SSL.py", line 309, in wrapper
_lib.X509_up_ref(x509)
AttributeError: 'module' object has no attribute 'X509_up_ref'
Traceback (most recent call last):
File "/usr/local/bin/cloudflare-ddns", line 193, in <module>
main()
File "/usr/local/bin/cloudflare-ddns", line 183, in main
external_ip = get_external_ip()
File "/usr/local/bin/cloudflare-ddns", line 91, in get_external_ip
return requests.get(EXTERNAL_IP_QUERY_API, timeout=6).json()['ip']
File "/usr/local/lib/python2.7/dist-packages/requests/api.py", line 75, in get
return request('get', url, params=params, **kwargs)
File "/usr/local/lib/python2.7/dist-packages/requests/api.py", line 60, in request
return session.request(method=method, url=url, **kwargs)
File "/usr/local/lib/python2.7/dist-packages/requests/sessions.py", line 533, in request
resp = self.send(prep, **send_kwargs)
File "/usr/local/lib/python2.7/dist-packages/requests/sessions.py", line 646, in send
r = adapter.send(request, **kwargs)
File "/usr/local/lib/python2.7/dist-packages/requests/adapters.py", line 514, in send
raise SSLError(e, request=request)
requests.exceptions.SSLError: HTTPSConnectionPool(host='api.ipify.org', port=443): Max retries exceeded with url: /?format=json (Caused by SSLError(SSLError("bad handshake: Error([('SSL routines', 'tls_process_server_certificate', 'certificate verify failed')],)",),))
I'm able to curl https://api.ipify.org
with no errors, also the API SSL is fine
I'm able to fix this bt running sudo python -m easy_install --upgrade pyOpenSSL
so it might be using incorrect versions by default
Hello,
Fresh install (ubuntu 18), when I launched => cloudflare-ddns --configure
it asked me for my api token and sub domain (nothing else).
When I tried to launch => cloudflare-ddns --update-now
=> Configuration file /root/.cloudflare-ddns not found or invalid! Did you run cloudflare-ddns --configure?
I had to manually check your code and edit the configure file to manually add :
auth_type
email
api_key
because I only had "domains" in my configure file for some reason.
Also, this => https://api.ipify.org/?format=json
timed-out, had to use the http link.
Now it's working, I'm just letting you know about the issue I had.
Thanks for the script !
After hours of trying to master the Cloudflare API (and failing!), I've installed your script which works great.
There's obviously a lot of work gone into writing this, and wanted to thank you for sharing.
Paul
I'm experiencing an weird error
As you can see above, tld
module is installed alright, and I can import the module manually. But when I run cloudflare-ddns
command it tells me no module named tld is there.
I reinstalled the package with umask
set to 022
(as suggested here) and added /usr/local/lib
to $PATH where python module packages reside just in case, none of which worked.
Maybe little bit of help would be really appreciated. Thanks.
I see the latest commit does address the issue of local addresses being assigned as permanent addresses on the interface, but I wouldn't consider all other addresses to be "permanent" or even suitable for inclusion in a DNS AAAA record.
Here's an example:
$ ip -6 addr show scope global
2: eno1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 state UP qlen 1000
inet6 2601:844:4000:750:XXXX:XXXX:XXXX:9431/64 scope global dynamic mngtmpaddr noprefixroute
valid_lft 86383sec preferred_lft 14383sec
3: eno2: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 state UP qlen 1000
inet6 2601:844:4000:750:ffff:74:8:10/128 scope global dynamic noprefixroute
valid_lft 4470sec preferred_lft 1770sec
inet6 2601:844:4000:750:XXXX:XXXX:XXXX:9432/64 scope global dynamic mngtmpaddr noprefixroute
valid_lft 86383sec preferred_lft 14383sec
Each interface has a private, temporary address: marked by mngtmpaddr
which means:
(IPv6 only) make the kernel manage temporary addresses created from this one as
template on behalf of Privacy Extensions (RFC3041). For this to become active, the
use_tempaddr sysctl setting has to be set to a value greater than zero. The given
address needs to have a prefix length of 64. This flag allows to use privacy
extensions in a manually configured network, just like if stateless auto-
configuration was active.
And I think that if an address is intended to be 'private' and 'temporary' we should instead prefer a non-private address, even if it might still be labeled as 'temporary' by the lifetime valid/preferred timers or 'dynamic'. ('dynamic' addresses can still be assigned to something unchanging by the router, like it is in my case, which also has SLAAC)
Hi there,
every time the --update-now command is ran manually or via crontab, the cloudflare protection is being turned off.
I mean the cloud icon in the DNS section of cloudflare.com.
This is a very useful feature i want to always be enabled. It protects/hides my IP.
Is this behaivor intentional?
Best regards
cryllical
Hey! This doesn't seem to be the most major issue ever but occasionally I seem to get timeout errors from https://api.ipify.org. Perhaps it might be a good idea to use multiple redundant sources to get the machine's public IP address since a single service might one day go down. Some other external IP services include https://ifconfig.io/ or https://ident.me/ or https://ifconfig.me/ or https://icanhazip.com
Ideally if one service fails, the script would handle it and try another provider.
Mar 27 11:33:45 hug systemd[1]: Starting Automatically updates the CloudFlare DNS record upon IP address change....
Mar 27 11:33:52 hug cloudflare-ddns[9827]: Traceback (most recent call last):
Mar 27 11:33:52 hug cloudflare-ddns[9827]: File "/usr/lib/python3.6/site-packages/urllib3/connectionpool.py", line 384, in _make_request
Mar 27 11:33:52 hug cloudflare-ddns[9827]: six.raise_from(e, None)
Mar 27 11:33:52 hug cloudflare-ddns[9827]: File "<string>", line 3, in raise_from
Mar 27 11:33:52 hug cloudflare-ddns[9827]: File "/usr/lib/python3.6/site-packages/urllib3/connectionpool.py", line 380, in _make_request
Mar 27 11:33:52 hug cloudflare-ddns[9827]: httplib_response = conn.getresponse()
Mar 27 11:33:52 hug cloudflare-ddns[9827]: File "/usr/lib64/python3.6/http/client.py", line 1346, in getresponse
Mar 27 11:33:52 hug cloudflare-ddns[9827]: response.begin()
Mar 27 11:33:52 hug cloudflare-ddns[9827]: File "/usr/lib64/python3.6/http/client.py", line 307, in begin
Mar 27 11:33:52 hug cloudflare-ddns[9827]: version, status, reason = self._read_status()
Mar 27 11:33:52 hug cloudflare-ddns[9827]: File "/usr/lib64/python3.6/http/client.py", line 268, in _read_status
Mar 27 11:33:52 hug cloudflare-ddns[9827]: line = str(self.fp.readline(_MAXLINE + 1), "iso-8859-1")
Mar 27 11:33:52 hug cloudflare-ddns[9827]: File "/usr/lib64/python3.6/socket.py", line 586, in readinto
Mar 27 11:33:52 hug cloudflare-ddns[9827]: return self._sock.recv_into(b)
Mar 27 11:33:52 hug cloudflare-ddns[9827]: File "/usr/lib64/python3.6/ssl.py", line 968, in recv_into
Mar 27 11:33:52 hug cloudflare-ddns[9827]: return self.read(nbytes, buffer)
Mar 27 11:33:52 hug cloudflare-ddns[9827]: File "/usr/lib64/python3.6/ssl.py", line 830, in read
Mar 27 11:33:52 hug cloudflare-ddns[9827]: return self._sslobj.read(len, buffer)
Mar 27 11:33:52 hug cloudflare-ddns[9827]: File "/usr/lib64/python3.6/ssl.py", line 587, in read
Mar 27 11:33:52 hug cloudflare-ddns[9827]: v = self._sslobj.read(len, buffer)
Mar 27 11:33:52 hug cloudflare-ddns[9827]: socket.timeout: The read operation timed out
Mar 27 11:33:52 hug cloudflare-ddns[9827]: During handling of the above exception, another exception occurred:
Mar 27 11:33:52 hug cloudflare-ddns[9827]: Traceback (most recent call last):
Mar 27 11:33:52 hug cloudflare-ddns[9827]: File "/usr/lib/python3.6/site-packages/requests/adapters.py", line 449, in send
Mar 27 11:33:52 hug cloudflare-ddns[9827]: timeout=timeout
Mar 27 11:33:52 hug cloudflare-ddns[9827]: File "/usr/lib/python3.6/site-packages/urllib3/connectionpool.py", line 638, in urlopen
Mar 27 11:33:52 hug cloudflare-ddns[9827]: _stacktrace=sys.exc_info()[2])
Mar 27 11:33:52 hug cloudflare-ddns[9827]: File "/usr/lib/python3.6/site-packages/urllib3/util/retry.py", line 368, in increment
Mar 27 11:33:52 hug cloudflare-ddns[9827]: raise six.reraise(type(error), error, _stacktrace)
Mar 27 11:33:52 hug cloudflare-ddns[9827]: File "/usr/lib/python3.6/site-packages/urllib3/packages/six.py", line 693, in reraise
Mar 27 11:33:52 hug cloudflare-ddns[9827]: raise value
Mar 27 11:33:52 hug cloudflare-ddns[9827]: File "/usr/lib/python3.6/site-packages/urllib3/connectionpool.py", line 600, in urlopen
Mar 27 11:33:52 hug cloudflare-ddns[9827]: chunked=chunked)
Mar 27 11:33:52 hug cloudflare-ddns[9827]: File "/usr/lib/python3.6/site-packages/urllib3/connectionpool.py", line 386, in _make_request
Mar 27 11:33:52 hug cloudflare-ddns[9827]: self._raise_timeout(err=e, url=url, timeout_value=read_timeout)
Mar 27 11:33:52 hug cloudflare-ddns[9827]: File "/usr/lib/python3.6/site-packages/urllib3/connectionpool.py", line 306, in _raise_timeout
Mar 27 11:33:52 hug cloudflare-ddns[9827]: raise ReadTimeoutError(self, url, "Read timed out. (read timeout=%s)" % timeout_value)
Mar 27 11:33:52 hug cloudflare-ddns[9827]: urllib3.exceptions.ReadTimeoutError: HTTPSConnectionPool(host='api.ipify.org', port=443): Read timed out. (read timeout=6)
Mar 27 11:33:52 hug cloudflare-ddns[9827]: During handling of the above exception, another exception occurred:
Mar 27 11:33:52 hug cloudflare-ddns[9827]: Traceback (most recent call last):
Mar 27 11:33:52 hug cloudflare-ddns[9827]: File "/usr/local/bin/cloudflare-ddns", line 298, in <module>
Mar 27 11:33:52 hug cloudflare-ddns[9827]: main()
Mar 27 11:33:52 hug cloudflare-ddns[9827]: File "/usr/local/bin/cloudflare-ddns", line 279, in main
Mar 27 11:33:52 hug cloudflare-ddns[9827]: external_ip = get_external_ip()
Mar 27 11:33:52 hug cloudflare-ddns[9827]: File "/usr/local/bin/cloudflare-ddns", line 115, in get_external_ip
Mar 27 11:33:52 hug cloudflare-ddns[9827]: r = requests.get(EXTERNAL_IP_QUERY_API, timeout=6)
Mar 27 11:33:52 hug cloudflare-ddns[9827]: File "/usr/lib/python3.6/site-packages/requests/api.py", line 75, in get
Mar 27 11:33:52 hug cloudflare-ddns[9827]: return request('get', url, params=params, **kwargs)
Mar 27 11:33:52 hug cloudflare-ddns[9827]: File "/usr/lib/python3.6/site-packages/requests/api.py", line 60, in request
Mar 27 11:33:52 hug cloudflare-ddns[9827]: return session.request(method=method, url=url, **kwargs)
Mar 27 11:33:52 hug cloudflare-ddns[9827]: File "/usr/lib/python3.6/site-packages/requests/sessions.py", line 533, in request
Mar 27 11:33:52 hug cloudflare-ddns[9827]: resp = self.send(prep, **send_kwargs)
Mar 27 11:33:52 hug cloudflare-ddns[9827]: File "/usr/lib/python3.6/site-packages/requests/sessions.py", line 646, in send
Mar 27 11:33:52 hug cloudflare-ddns[9827]: r = adapter.send(request, **kwargs)
Mar 27 11:33:52 hug cloudflare-ddns[9827]: File "/usr/lib/python3.6/site-packages/requests/adapters.py", line 529, in send
Mar 27 11:33:52 hug cloudflare-ddns[9827]: raise ReadTimeout(e, request=request)
Mar 27 11:33:52 hug cloudflare-ddns[9827]: requests.exceptions.ReadTimeout: HTTPSConnectionPool(host='api.ipify.org', port=443): Read timed out. (read timeout=6)
Mar 27 11:33:52 hug systemd[1]: ddns.service: Main process exited, code=exited, status=1/FAILURE
Mar 27 11:33:52 hug systemd[1]: ddns.service: Failed with result 'exit-code'.
Mar 27 11:33:52 hug systemd[1]: Failed to start Automatically updates the CloudFlare DNS record upon IP address change..
Mar 27 11:35:19 hug systemd[1]: Starting Automatically updates the CloudFlare DNS record upon IP address change....
Mar 27 11:35:21 hug cloudflare-ddns[9853]: Found external IPv4: "123.123.123.123"
When using in a cron job I think I'd be nice to only receive output emails when an action was taken.
Running once a day at midnight with local mail results in the same repeated message of "no changes", but with a quiet option the command would only produce output (and hence an email) if a change was made.
Perhaps a --quiet
or --silent-unchanged
flag could be added?
It would be aweseom if --configure would ask the user wether he wants to change his autherization method or not.
Since I am playing with my (sub)domains I often search for my old api token or just generate a new one. This
really annoying as the only thing I am changing are the domains.
That would be really helpful feature.
We should probably chmod 600 ~/.cloudflare-ddns
.
I have 3 servers, one local and 2 cloud servers.
The local uses the domain myserver.com
and is running ddns-client.
The cloud servers use sub-domains of myserver.com
;
cloudone.myserver.com
& cloudtwo.myserver.com
.
My .cloudflare-ddns
config file is;
[email protected]
api_key=a5daf3b6c0if7f7dfsd6fsd9e56d7c2fb7c
domains=myserver.com
As both cloud servers using the sub-domain names have fixed IP addresses, I don't need ddns-client to update their IP's, but everytime ddns-client is run, it also updates one of the sub-domain IP addresses in Cloudflare to the IP address of my local server. The result is that one of my cloud servers cannot then be accessed, and I have to login to Cloudflare and manually change the IP back again to it's fixed IP address.
There seems to be an issue with alternate IP address services added in #39 on IPv6 capable networks where the IPv6 address is returned and this script tries to update an A record with it.
Thankfully, the AAAA record gets updated correctly! We should not be trying to update an A record with an IPv6 address though.
โ ddns.service - Automatically updates the CloudFlare DNS record upon IP address change.
Loaded: loaded (/etc/systemd/system/ddns.service; static; vendor preset: disabled)
Active: inactive (dead) since Wed 2020-06-17 12:31:06 BST; 8s ago
Process: 307083 ExecStart=/usr/local/bin/cloudflare-ddns --update-now (code=exited, status=0/SUCCESS)
Main PID: 307083 (code=exited, status=0/SUCCESS)
Jun 17 12:33:21 hug systemd[1]: Starting Automatically updates the CloudFlare DNS record upon IP address change....
Jun 17 12:33:24 hug cloudflare-ddns[307665]: Cannot fetch your external ip. https://api.ipify.org not reachable.
Jun 17 12:33:24 hug cloudflare-ddns[307665]: Found external IPv4: "33a4:a2c9:9db2:4690:18d2:faed:64fe:087c"
Jun 17 12:33:24 hug cloudflare-ddns[307665]: Found external IPv6: "33a4:a2c9:9db2:4690:18d2:faed:64fe:087c"
Jun 17 12:33:24 hug cloudflare-ddns[307665]: Listing all zones.
Jun 17 12:33:24 hug cloudflare-ddns[307665]: Finding all DNS records.
Jun 17 12:33:24 hug cloudflare-ddns[307665]: Updating the A record (ID 123) of (sub)domain domain.com (ID 123) to 33a4:a2c9:9db2:4690:18d2:faed:64fe:087c.
โ ๏ธ Jun 17 12:33:24 hug cloudflare-ddns[307665]: DNS record failed to update.
โ ๏ธ Jun 17 12:33:24 hug cloudflare-ddns[307665]: CloudFlare returned the following errors: [{'code': 9004, 'message': 'This kind of record cannot be proxied'}, {'code': 9005, 'message': 'Content for A record is invalid. Must be a valid IPv4 address'}].
Jun 17 12:33:24 hug cloudflare-ddns[307665]: CloudFlare returned the following messages: []
Jun 17 12:33:24 hug cloudflare-ddns[307665]: Updating the AAAA record (ID 123) of (sub)domain domain.com (ID 123) to 33a4:a2c9:9db2:4690:18d2:faed:64fe:087c.
Jun 17 12:33:24 hug cloudflare-ddns[307665]: DNS record is already up-to-date; taking no action
Jun 17 12:33:24 hug systemd[1]: Started Automatically updates the CloudFlare DNS record upon IP address change..
[root@localhost ~]# /usr/local/bin/cloudflare-ddns --update-now
This is the output
Found external IPv4: "1.164.249.xxx"
Listing all zones.
Finding all DNS records.
Actually still not updated
I have records which, when fetched by the api, report like so:
type: A
name: *.domain.comtype: CNAME
name: www.domain.comtype: MX
name: domain.comtype: TXT
name: domain.com
The docs for this tool suggest that when I provide my domain name, I provide it in the format domain.com
. However, the code uses a filter parameter with the api which filters against the "name" attribute of each record. This means, in my example, I only get the MX and TXT records returned so the A and CNAME records do not get returned to the program and therefore not updated.
There are multiple workarounds for this including modifying my config to be domains=domain.com,*.domain.com,www.domain.com
but this feels somewhat unintuitive unless mentioned specifically in the docs. Alternatively, I can modify the script to search for those alternative subdomains automatically or I could remove the name param entirely but this could possibly have adverse affects for some users whereby records are unintentionally updated. (not sure)
I am starting this issue to discuss the most viable course of action to fix this. Happy to do a PR if you like. Thanks!
When I try to configure the software it is only asking for the API key and the domain(s) but it's not asking for the email address. Also, I do not see the API key in the config file, only the domains. Where is it stored?
Can you give me a sample for the config file so I can configure it manually and make it work?
Thanks
As there has been some mistakes on the master branch, I would suggest to add something like
git fetch
git pull origin master
To the execution when running cloudflare-ddns --update-now
.
This update process can be made optional by asking the user during cloudflare-ddns --configure
if he would like to receive updates.
What are your thoughts on that @LINKIWI ?
Will this update the subdomains as well?
I have add this entry to my crontab: 0 0 * * * /usr/local/bin/cloudflare-ddns --update-now
and it doesn't update cloudflare
When I configure to update sub.domain.com it comes back with:
The domain sub.domain.com doesn't appear to be one of your CloudFlare domains. We only found [...]
and the list of TLDs it finds (.com, .co.uk, .uk) is a fraction of what I have in my CF account. Is there some restriction on TLDs that are pulled back?
Every time DNS is updated all the hostnames are put back to 'DNS Only' and not 'Proxied'
After doing a git clone and make install as root ran the command: cloudflare-ddns --configure
Traceback (most recent call last): File "/usr/local/bin/cloudflare-ddns", line 10, in <module> from tld import get_fld File "/usr/local/lib/python2.7/dist-packages/tld/__init__.py", line 7, in <module> from .utils import get_fld, get_tld, get_tld_names, is_tld, parse_tld, Result, update_tld_names File "/usr/local/lib/python2.7/dist-packages/tld/utils.py", line 10, in <module> from backports.functools_lru_cache import lru_cache ImportError: No module named functools_lru_cache
Python 2.7.17
Python 3.7.5
pip 18.1 from /usr/lib/python2.7/dist-packages/pip (python 2.7)
pip 18.1 from /usr/lib/python3/dist-packages/pip (python 3.7)
which output:
/usr/local/bin/cloudflare-ddns
OS: Ubuntu 20.04 LXC VPS
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.