From d92976993d7f61e544bf7156ff61dd58aeea078d Mon Sep 17 00:00:00 2001 From: Dillan Mills Date: Fri, 27 Jan 2023 15:08:10 -0700 Subject: [PATCH 01/19] Add load balancer slupport --- cloudflare-ddns.py | 21 +++++++++++++++++++++ config-example.json | 15 ++++++++++++++- 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/cloudflare-ddns.py b/cloudflare-ddns.py index a99238b..601a4b7 100755 --- a/cloudflare-ddns.py +++ b/cloudflare-ddns.py @@ -165,6 +165,26 @@ def commitRecord(ip): return True +def updateLoadBalancer(ip): + + for option in config["load_balancer"]: + pools = cf_api('user/load_balancers/pools', 'GET', option) + + if pools: + idxr = dict((p['id'], i) for i, p in enumerate(pools['result'])) + idx = idxr.get(option['pool_id']) + + origins = pools['result'][idx]['origins'] + + idxr = dict((o['name'], i) for i, o in enumerate(origins)) + idx = idxr.get(option['origin']) + + origins[idx]['address'] = ip['ip'] + data = {'origins': origins} + + response = cf_api(f'user/load_balancers/pools/{option["pool_id"]}', 'PATCH', option, {}, data) + + def cf_api(endpoint, method, config, headers={}, data=False): api_token = config['authentication']['api_token'] if api_token != '' and api_token != 'api_token_here': @@ -201,6 +221,7 @@ def cf_api(endpoint, method, config, headers={}, data=False): def updateIPs(ips): for ip in ips.values(): commitRecord(ip) + updateLoadBalancer(ip) if __name__ == '__main__': diff --git a/config-example.json b/config-example.json index 38f1097..e2ead1f 100755 --- a/config-example.json +++ b/config-example.json @@ -21,8 +21,21 @@ ] } ], + "load_balancer": [ + { + "authentication": { + "api_token": "api_token_here", + "api_key": { + "api_key": "api_key_here", + "account_email": "your_email_here" + } + }, + "pool_id": "your_pool_id_here", + "origin": "your_origin_name_here" + } + ], "a": true, "aaaa": true, "purgeUnknownRecords": false, "ttl": 300 -} +} \ No newline at end of file From 2c2e929d175560bd5f22872467546bba331b0328 Mon Sep 17 00:00:00 2001 From: Davide Cavalca Date: Sun, 29 Jan 2023 15:19:20 -0800 Subject: [PATCH 02/19] Add systemd service and timer --- systemd/cloudflare-ddns.service | 13 +++++++++++++ systemd/cloudflare-ddns.timer | 9 +++++++++ 2 files changed, 22 insertions(+) create mode 100644 systemd/cloudflare-ddns.service create mode 100644 systemd/cloudflare-ddns.timer diff --git a/systemd/cloudflare-ddns.service b/systemd/cloudflare-ddns.service new file mode 100644 index 0000000..e5c0692 --- /dev/null +++ b/systemd/cloudflare-ddns.service @@ -0,0 +1,13 @@ +[Unit] +Description=Update DDNS on Cloudflare +ConditionPathExists=/etc/cloudflare-ddns/config.json +Wants=network-online.target +After=network-online.target + +[Service] +Type=oneshot +Environment=CONFIG_PATH=/etc/cloudflare-ddns +ExecStart=cloudflare-ddns + +[Install] +WantedBy=multi-user.target diff --git a/systemd/cloudflare-ddns.timer b/systemd/cloudflare-ddns.timer new file mode 100644 index 0000000..cc9cd61 --- /dev/null +++ b/systemd/cloudflare-ddns.timer @@ -0,0 +1,9 @@ +[Unit] +Description=Update DDNS on Cloudflare every 15 minutes + +[Timer] +OnBootSec=2min +OnUnitActiveSec=15m + +[Install] +WantedBy=timers.target From 5c909e25cd7f7bb37fdea8efd9ca1f81ba82c42e Mon Sep 17 00:00:00 2001 From: Timothy Miller <46549361+timothymiller@users.noreply.github.com> Date: Wed, 15 Feb 2023 13:25:07 -0500 Subject: [PATCH 03/19] Updated README.md --- README.md | 59 ++++--------------------------------------------------- 1 file changed, 4 insertions(+), 55 deletions(-) diff --git a/README.md b/README.md index 90df176..30970b4 100755 --- a/README.md +++ b/README.md @@ -1,9 +1,3 @@ -# 🏎️ [Find a remote job. Work from anywhere.](http://nfty.sh/SRxfA) - -100% Remote. 40 hours/week. 10+ positions available. - -[πŸ“§ Send me an email](mailto:porschejobs@timknowsbest.com) for more information. -

Cloudflare DDNS

# πŸš€ Cloudflare DDNS @@ -26,7 +20,7 @@ A small, πŸ•΅οΈ privacy centric, and ⚑ lightning fast multi-architecture Dock - πŸš€ [Deployment](https://github.com/timothymiller/cloudflare-ddns#-deploy-with-docker-compose) - 🐳 [Docker Compose](https://github.com/timothymiller/cloudflare-ddns#-deploy-with-docker-compose) - πŸ‹ [Kubernetes](https://github.com/timothymiller/cloudflare-ddns#-kubernetes) - - 🐧 [Crontab](https://github.com/timothymiller/cloudflare-ddns#-deploy-with-linux--cron) + - 🐧 [Bare Metal (Crontab)](https://github.com/timothymiller/cloudflare-ddns#-deploy-with-linux--cron) - [Building from source](https://github.com/timothymiller/cloudflare-ddns#building-from-source) - [License](https://github.com/timothymiller/cloudflare-ddns#license) - [Author](https://github.com/timothymiller/cloudflare-ddns#author) @@ -60,10 +54,10 @@ Stale, duplicate DNS records are removed for housekeeping. ## 🧰 Requirements -- [Cloudflare account](http://nfty.sh/kiUR9) -- [Domain name](http://nfty.sh/qnJji) +- [Cloudflare account](https://developers.cloudflare.com/fundamentals/account-and-billing/account-setup/create-account/) +- [Domain name](https://namecheap.pxf.io/e47gjr) -[πŸ‘‰ Click here to buy a domain name](http://nfty.sh/qnJji) and [get a free Cloudflare account](http://nfty.sh/kiUR9). +[πŸ‘‰ Click here to buy a domain name](https://namecheap.pxf.io/e47gjr) and [get a free Cloudflare account](https://developers.cloudflare.com/fundamentals/account-and-billing/account-setup/create-account/). ### Supported Platforms @@ -78,51 +72,6 @@ Stale, duplicate DNS records are removed for housekeeping. - [Cloudflare zone ID](https://support.cloudflare.com/hc/en-us/articles/200167836-Where-do-I-find-my-Cloudflare-IP-address-) - [Cloudflare zone DNS record ID](https://support.cloudflare.com/hc/en-us/articles/360019093151-Managing-DNS-records-in-Cloudflare) -## βš’οΈ Equipment - -Here is a list of equipment I use to run this script on a Raspberry Pi 4. - -### Networking - -- [Unifi Dream Machine Pro](https://amzn.to/3Fvq8OM) -- [Unifi AC Pro](https://amzn.to/3FzhdM6) - -#### Network Switches - -- [TPLink 8 Port Gigabit Switch](https://amzn.to/3h08Vm4) - -#### Network Cables - -- [2 Foot Cat 6 Ethernet Cable](https://amzn.to/3NkxyGo) -- [25 Foot Cat 6 Ethernet Cable](https://amzn.to/3FuFEu9) - -#### Storage - -- [2TB Samsung 980 PRO PCIe NVMe Gen 4 Gaming SSD](https://amzn.to/3U6tFag) -- [WD 16TB Elements Desktop External Hard Drive](https://amzn.to/3zplpdB) - -#### Battery Backup - -- [APC UPS Battery Backup Surge Protector, 425VA Backup Battery Power Supply](https://amzn.to/3U5eVZ2) - -### Recommended Setups - -Rest assured your money is well spent on any of the following platforms. Each platform provides a robust virtualization solution for running Docker containers. - -#### ARM64 - Raspberry Pi 4 - -- [Raspberry Pi 4](https://amzn.to/3Uc8gg0) -- [Raspberry Pi 4 case](https://amzn.to/3fooUtN) -- [Raspberry Pi 4 power supply](https://amzn.to/3fixzxV) -- [Raspberry Pi 4 128gb microSD card](https://amzn.to/3ztuNwL) -- [Raspberry Pi 4 heatsink](https://amzn.to/3SOK8Ps) -- [Raspberry Pi 4 cooling fan](https://amzn.to/3Doa1Qa) -- [Raspberry Pi 4 USB-C to HDMI adapter](https://amzn.to/3gVks6u) - -#### x86_64 - Intel NUC - -- [Intel NUC](https://amzn.to/3STMYT4) - ## 🚦 Getting Started First copy the example configuration file into the real one. From 2ad7e57d652f14ed95f5174c5ca621f3b9030aa6 Mon Sep 17 00:00:00 2001 From: Timothy Miller <46549361+timothymiller@users.noreply.github.com> Date: Wed, 15 Feb 2023 14:07:17 -0500 Subject: [PATCH 04/19] Added support for secondary IP checks if primary fails (Fixes #111) Updated requests module version --- cloudflare-ddns.py | 48 ++++++++++++++++++++++++++++++++++------------ requirements.txt | 2 +- 2 files changed, 37 insertions(+), 13 deletions(-) diff --git a/cloudflare-ddns.py b/cloudflare-ddns.py index a99238b..0cb3dd6 100755 --- a/cloudflare-ddns.py +++ b/cloudflare-ddns.py @@ -66,9 +66,20 @@ def getIPs(): global shown_ipv4_warning if not shown_ipv4_warning: shown_ipv4_warning = True - print("🧩 IPv4 not detected") - if purgeUnknownRecords: - deleteEntries("A") + print("🧩 IPv4 not detected via 1.1.1.1, trying 1.0.0.1") + # Try secondary IP check + try: + a = requests.get( + "https://1.0.0.1/cdn-cgi/trace").text.split("\n") + a.pop() + a = dict(s.split("=") for s in a)["ip"] + except Exception: + global shown_ipv4_warning_secondary + if not shown_ipv4_warning_secondary: + shown_ipv4_warning_secondary = True + print("🧩 IPv4 not detected via 1.0.0.1. Verify your ISP or DNS provider isn't blocking Cloudflare's IPs.") + if purgeUnknownRecords: + deleteEntries("A") if ipv6_enabled: try: aaaa = requests.get( @@ -79,16 +90,26 @@ def getIPs(): global shown_ipv6_warning if not shown_ipv6_warning: shown_ipv6_warning = True - print("🧩 IPv6 not detected") - if purgeUnknownRecords: - deleteEntries("AAAA") + print("🧩 IPv6 not detected via 1.1.1.1, trying 1.0.0.1") + try: + aaaa = requests.get( + "https://[2606:4700:4700::1001]/cdn-cgi/trace").text.split("\n") + aaaa.pop() + aaaa = dict(s.split("=") for s in aaaa)["ip"] + except Exception: + global shown_ipv6_warning_secondary + if not shown_ipv6_warning_secondary: + shown_ipv6_warning_secondary = True + print("🧩 IPv6 not detected via 1.0.0.1. Verify your ISP or DNS provider isn't blocking Cloudflare's IPs.") + if purgeUnknownRecords: + deleteEntries("AAAA") ips = {} - if(a is not None): + if (a is not None): ips["ipv4"] = { "type": "A", "ip": a } - if(aaaa is not None): + if (aaaa is not None): ips["ipv6"] = { "type": "AAAA", "ip": aaaa @@ -177,7 +198,7 @@ def cf_api(endpoint, method, config, headers={}, data=False): "X-Auth-Key": config['authentication']['api_key']['api_key'], } try: - if(data == False): + if (data == False): response = requests.request( method, "https://api.cloudflare.com/client/v4/" + endpoint, headers=headers) else: @@ -205,7 +226,9 @@ def updateIPs(ips): if __name__ == '__main__': shown_ipv4_warning = False + shown_ipv4_warning_secondary = False shown_ipv6_warning = False + shown_ipv6_warning_secondary = False ipv4_enabled = True ipv6_enabled = True purgeUnknownRecords = False @@ -244,13 +267,14 @@ if __name__ == '__main__': if ttl < 30: ttl = 30 # print("βš™οΈ TTL is too low - defaulting to 30 seconds") - if(len(sys.argv) > 1): - if(sys.argv[1] == "--repeat"): + if (len(sys.argv) > 1): + if (sys.argv[1] == "--repeat"): if ipv4_enabled and ipv6_enabled: print( "πŸ•°οΈ Updating IPv4 (A) & IPv6 (AAAA) records every " + str(ttl) + " seconds") elif ipv4_enabled and not ipv6_enabled: - print("πŸ•°οΈ Updating IPv4 (A) records every " + str(ttl) + " seconds") + print("πŸ•°οΈ Updating IPv4 (A) records every " + + str(ttl) + " seconds") elif ipv6_enabled and not ipv4_enabled: print("πŸ•°οΈ Updating IPv6 (AAAA) records every " + str(ttl) + " seconds") diff --git a/requirements.txt b/requirements.txt index 5e77405..bf0d9d4 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1 +1 @@ -requests==2.28.1 \ No newline at end of file +requests==2.28.2 \ No newline at end of file From 7212161f7b26b1cd3d4f553a835bd1f35e5dfb0d Mon Sep 17 00:00:00 2001 From: Timothy Miller <46549361+timothymiller@users.noreply.github.com> Date: Wed, 15 Feb 2023 15:01:28 -0500 Subject: [PATCH 05/19] Updated README.md --- README.md | 150 +++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 102 insertions(+), 48 deletions(-) diff --git a/README.md b/README.md index 30970b4..b7261e9 100755 --- a/README.md +++ b/README.md @@ -4,38 +4,23 @@ Access your home network remotely via a custom domain name without a static IP! -A small, πŸ•΅οΈ privacy centric, and ⚑ lightning fast multi-architecture Docker image for self hosting projects. +## ⚑ Efficiency -## πŸ“– Table of Contents +- ❀️ Easy config. List your domains and you're done. +- πŸ” The Python runtime will re-use existing HTTP connections. +- πŸ—ƒοΈ Cloudflare API responses are cached to reduce API usage. +- 🀏 The Docker image is small and efficient. +- 0️⃣ Zero dependencies. +- πŸ’ͺ Supports all platforms. +- 🏠 Enables low cost self hosting to promote a more decentralized internet. -- πŸ‡ΊπŸ‡Έ [Origin](https://github.com/timothymiller/cloudflare-ddns#-origin) -- πŸ“Š [Stats](https://github.com/timothymiller/cloudflare-ddns#-stats) -- ⁉️ [How Private & Secure Is This?](https://github.com/timothymiller/cloudflare-ddns#%EF%B8%8F-how-private--secure-is-this) -- 🧰 [Requirements](https://github.com/timothymiller/cloudflare-ddns#-requirements) -- βš’οΈ [Equipment](https://github.com/timothymiller/cloudflare-ddns#-equipment) -- 🚦 [Getting Started](https://github.com/timothymiller/cloudflare-ddns#-getting-started) - - πŸ”‘ [Authentication methods](https://github.com/timothymiller/cloudflare-ddns#-authentication-methods) - - πŸ“  [Hosting multiple subdomains on the same IP](https://github.com/timothymiller/cloudflare-ddns#-hosting-multiple-subdomains-on-the-same-ip) - - 🌐 [Hosting multiple domains (zones) on the same IP](https://github.com/timothymiller/cloudflare-ddns#-hosting-multiple-domains-zones-on-the-same-ip) -- πŸš€ [Deployment](https://github.com/timothymiller/cloudflare-ddns#-deploy-with-docker-compose) - - 🐳 [Docker Compose](https://github.com/timothymiller/cloudflare-ddns#-deploy-with-docker-compose) - - πŸ‹ [Kubernetes](https://github.com/timothymiller/cloudflare-ddns#-kubernetes) - - 🐧 [Bare Metal (Crontab)](https://github.com/timothymiller/cloudflare-ddns#-deploy-with-linux--cron) -- [Building from source](https://github.com/timothymiller/cloudflare-ddns#building-from-source) -- [License](https://github.com/timothymiller/cloudflare-ddns#license) -- [Author](https://github.com/timothymiller/cloudflare-ddns#author) +## πŸ’― Complete Support of Domain Names, Subdomains, and IPv4 & IPv6 -## πŸ‡ΊπŸ‡Έ Origin - -This script was written for the Raspberry Pi platform to enable low cost self hosting to promote a more decentralized internet. - -### 🧹 Safe for use with existing records - -`cloudflare-ddns` handles the busy work for you, so deploying web apps is less of a clickfest. Every 5 minutes, the script fetches public IPv4 and IPv6 addresses and then creates/updates DNS records for each subdomain in Cloudflare. - -#### Optional features - -Stale, duplicate DNS records are removed for housekeeping. +- 🌐 Supports multiple domains (zones) on the same IP. +- πŸ“  Supports multiple subdomains on the same IP. +- πŸ“‘ IPv4 and IPv6 support. +- 🌍 Supports all Cloudflare regions. +- πŸ‡ΊπŸ‡Έ Made in the U.S.A. ## πŸ“Š Stats @@ -59,19 +44,6 @@ Stale, duplicate DNS records are removed for housekeeping. [πŸ‘‰ Click here to buy a domain name](https://namecheap.pxf.io/e47gjr) and [get a free Cloudflare account](https://developers.cloudflare.com/fundamentals/account-and-billing/account-setup/create-account/). -### Supported Platforms - -- [Docker](https://docs.docker.com/get-docker/) -- [Docker Compose](https://docs.docker.com/compose/install/) (optional) -- [Kubernetes](https://kubernetes.io/docs/tasks/tools/) (optional) -- [Python 3](https://www.python.org/downloads/) (optional) - -### Helpful links - -- [Cloudflare API token](https://dash.cloudflare.com/profile/api-tokens) -- [Cloudflare zone ID](https://support.cloudflare.com/hc/en-us/articles/200167836-Where-do-I-find-my-Cloudflare-IP-address-) -- [Cloudflare zone DNS record ID](https://support.cloudflare.com/hc/en-us/articles/360019093151-Managing-DNS-records-in-Cloudflare) - ## 🚦 Getting Started First copy the example configuration file into the real one. @@ -122,16 +94,61 @@ Some ISP provided modems only allow port forwarding over IPv4 or IPv6. In this c ## πŸ“  Hosting multiple subdomains on the same IP? -You can save yourself some trouble when hosting multiple domains pointing to the same IP address (in the case of Traefik) by defining one A & AAAA record 'ddns.example.com' pointing to the IP of the server that will be updated by this DDNS script. For each subdomain, create a CNAME record pointing to 'ddns.example.com'. Now you don't have to manually modify the script config every time you add a new subdomain to your site! +This script can be used to update multiple subdomains on the same IP address. -## 🌐 Hosting multiple domains (zones) on the same IP? - -You can handle ddns for multiple domains (cloudflare zones) using the same docker container by separating your configs inside `config.json` like below: +For example, if you have a domain `example.com` and you want to host additional subdomains at `foo.example.com` and `bar.example.com` on the same IP address, you can use this script to update the DNS records for all subdomains. ### ⚠️ Note +Please remove the comments after `//` in the below example. They are only there to explain the config. + Do not include the base domain name in your `subdomains` config. Do not use the [FQDN](https://en.wikipedia.org/wiki/Fully_qualified_domain_name). +### πŸ‘‰ Example πŸš€ + +```bash +{ + "cloudflare": [ + { + "authentication": { + "api_token": "api_token_here", // Either api_token or api_key + "api_key": { + "api_key": "api_key_here", + "account_email": "your_email_here" + } + }, + "zone_id": "your_zone_id_here", + "subdomains": [ + { + "name": "", // Root domain (example.com) + "proxied": true + }, + { + "name": "foo", // (foo.example.com) + "proxied": true + }, + { + "name": "bar", // (bar.example.com) + "proxied": true + } + ] + } + ], + "a": true, + "aaaa": true, + "purgeUnknownRecords": false, + "ttl": 300 +} +``` + +## 🌐 Hosting multiple domains (zones) on the same IP? + +You can handle ddns for multiple domains (cloudflare zones) using the same docker container by duplicating your configs inside the `cloudflare: []` key within `config.json` like below: + +### ⚠️ Note: + +If you are using API Tokens, make sure the token used supports editing your zone ID. + ```bash { "cloudflare": [ @@ -143,7 +160,27 @@ Do not include the base domain name in your `subdomains` config. Do not use the "account_email": "your_email_here" } }, - "zone_id": "your_zone_id_here", + "zone_id": "your_first_zone_id_here", + "subdomains": [ + { + "name": "", + "proxied": false + }, + { + "name": "remove_or_replace_with_your_subdomain", + "proxied": false + } + ] + }, + { + "authentication": { + "api_token": "api_token_here", + "api_key": { + "api_key": "api_key_here", + "account_email": "your_email_here" + } + }, + "zone_id": "your_second_zone_id_here", "subdomains": [ { "name": "", @@ -162,6 +199,10 @@ Do not include the base domain name in your `subdomains` config. Do not use the } ``` +### 🧹 Optional features + +`purgeUnknownRecords` removes stale DNS records from Cloudflare. This is useful if you have a dynamic DNS record that you no longer want to use. + ### πŸ—£οΈ Call to action: Docker environment variable support I am looking for help adding Docker environment variable support to this project. If interested, check out [this comment](https://github.com/timothymiller/cloudflare-ddns/pull/35#issuecomment-974752476) and open a PR. @@ -173,7 +214,7 @@ Pre-compiled images are available via [the official docker container on DockerHu Modify the host file path of config.json inside the volumes section of docker-compose.yml. ```yml -version: '3.7' +version: '3.9' services: cloudflare-ddns: image: timothyjmiller/cloudflare-ddns:latest @@ -300,6 +341,19 @@ Recommended for production docker run -d timothyjmiller/cloudflare_ddns:latest ``` +## Supported Platforms + +- [Docker](https://docs.docker.com/get-docker/) +- [Docker Compose](https://docs.docker.com/compose/install/) (optional) +- [Kubernetes](https://kubernetes.io/docs/tasks/tools/) (optional) +- [Python 3](https://www.python.org/downloads/) (optional) + +## πŸ“œ Helpful links + +- [Cloudflare API token](https://dash.cloudflare.com/profile/api-tokens) +- [Cloudflare zone ID](https://support.cloudflare.com/hc/en-us/articles/200167836-Where-do-I-find-my-Cloudflare-IP-address-) +- [Cloudflare zone DNS record ID](https://support.cloudflare.com/hc/en-us/articles/360019093151-Managing-DNS-records-in-Cloudflare) + ## License This Template is licensed under the GNU General Public License, version 3 (GPLv3). From bd3f4a94cb2abed535575cb35088a7f67c777855 Mon Sep 17 00:00:00 2001 From: Timothy Miller <46549361+timothymiller@users.noreply.github.com> Date: Wed, 15 Feb 2023 15:05:59 -0500 Subject: [PATCH 06/19] Updated README.md --- README.md | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index b7261e9..7755c26 100755 --- a/README.md +++ b/README.md @@ -13,6 +13,8 @@ Access your home network remotely via a custom domain name without a static IP! - 0️⃣ Zero dependencies. - πŸ’ͺ Supports all platforms. - 🏠 Enables low cost self hosting to promote a more decentralized internet. +- πŸ”’ Zero-log IP provider ([cdn-cgi/trace](https://www.cloudflare.com/cdn-cgi/trace)) +- πŸ‘ GPL-3.0 License. Open source for open audits. ## πŸ’― Complete Support of Domain Names, Subdomains, and IPv4 & IPv6 @@ -28,15 +30,6 @@ Access your home network remotely via a custom domain name without a static IP! | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![cloudflare-ddns docker image size](https://img.shields.io/docker/image-size/timothyjmiller/cloudflare-ddns?style=flat-square)](https://hub.docker.com/r/timothyjmiller/cloudflare-ddns 'cloudflare-ddns docker image size') | [![Total DockerHub pulls](https://img.shields.io/docker/pulls/timothyjmiller/cloudflare-ddns?style=flat-square)](https://hub.docker.com/r/timothyjmiller/cloudflare-ddns 'Total DockerHub pulls') | [![Official Discord Server](https://img.shields.io/discord/785778163887112192?style=flat-square)](https://discord.gg/UgGmwMvNxm 'Official Discord Server') | -## ⁉️ How Private & Secure Is This? - -1. Uses zero-log external IPv4 & IPv6 provider ([cdn-cgi/trace](https://www.cloudflare.com/cdn-cgi/trace)) -2. Alpine Linux base image -3. HTTPS only via Python Software Foundation requests module -4. Docker runtime -5. Open source for open audits -6. Regular updates - ## 🧰 Requirements - [Cloudflare account](https://developers.cloudflare.com/fundamentals/account-and-billing/account-setup/create-account/) @@ -344,9 +337,9 @@ docker run -d timothyjmiller/cloudflare_ddns:latest ## Supported Platforms - [Docker](https://docs.docker.com/get-docker/) -- [Docker Compose](https://docs.docker.com/compose/install/) (optional) -- [Kubernetes](https://kubernetes.io/docs/tasks/tools/) (optional) -- [Python 3](https://www.python.org/downloads/) (optional) +- [Docker Compose](https://docs.docker.com/compose/install/) +- [Kubernetes](https://kubernetes.io/docs/tasks/tools/) +- [Python 3](https://www.python.org/downloads/) ## πŸ“œ Helpful links From f22ec89f3ee6fada9f455caf9814104a2aaeefe0 Mon Sep 17 00:00:00 2001 From: Timothy Miller <46549361+timothymiller@users.noreply.github.com> Date: Wed, 15 Feb 2023 15:15:23 -0500 Subject: [PATCH 07/19] Updated README.md --- README.md | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 7755c26..e443060 100755 --- a/README.md +++ b/README.md @@ -30,13 +30,6 @@ Access your home network remotely via a custom domain name without a static IP! | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![cloudflare-ddns docker image size](https://img.shields.io/docker/image-size/timothyjmiller/cloudflare-ddns?style=flat-square)](https://hub.docker.com/r/timothyjmiller/cloudflare-ddns 'cloudflare-ddns docker image size') | [![Total DockerHub pulls](https://img.shields.io/docker/pulls/timothyjmiller/cloudflare-ddns?style=flat-square)](https://hub.docker.com/r/timothyjmiller/cloudflare-ddns 'Total DockerHub pulls') | [![Official Discord Server](https://img.shields.io/discord/785778163887112192?style=flat-square)](https://discord.gg/UgGmwMvNxm 'Official Discord Server') | -## 🧰 Requirements - -- [Cloudflare account](https://developers.cloudflare.com/fundamentals/account-and-billing/account-setup/create-account/) -- [Domain name](https://namecheap.pxf.io/e47gjr) - -[πŸ‘‰ Click here to buy a domain name](https://namecheap.pxf.io/e47gjr) and [get a free Cloudflare account](https://developers.cloudflare.com/fundamentals/account-and-billing/account-setup/create-account/). - ## 🚦 Getting Started First copy the example configuration file into the real one. @@ -67,7 +60,7 @@ Alternatively, you can use the traditional API keys by setting appropriate value "account_email": "The email address you use to sign in to cloudflare", ``` -### Enable or disable IPv4 or IPv6 +### πŸ“ Enable or disable IPv4 or IPv6 Some ISP provided modems only allow port forwarding over IPv4 or IPv6. In this case, you would want to disable any interface not accessible via port forward. @@ -76,7 +69,7 @@ Some ISP provided modems only allow port forwarding over IPv4 or IPv6. In this c "aaaa": true ``` -### Other values explained +### πŸŽ›οΈ Other values explained ```json "zone_id": "The ID of the zone that will get the records. From your dashboard click into the zone. Under the overview tab, scroll down and the zone ID is listed in the right rail", From 83fa74831e68ed9aeeee73f7e1811ef775b90337 Mon Sep 17 00:00:00 2001 From: Timothy Miller <46549361+timothymiller@users.noreply.github.com> Date: Wed, 15 Feb 2023 15:17:08 -0500 Subject: [PATCH 08/19] Updated README.md --- README.md | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/README.md b/README.md index e443060..920ea65 100755 --- a/README.md +++ b/README.md @@ -187,11 +187,7 @@ If you are using API Tokens, make sure the token used supports editing your zone ### 🧹 Optional features -`purgeUnknownRecords` removes stale DNS records from Cloudflare. This is useful if you have a dynamic DNS record that you no longer want to use. - -### πŸ—£οΈ Call to action: Docker environment variable support - -I am looking for help adding Docker environment variable support to this project. If interested, check out [this comment](https://github.com/timothymiller/cloudflare-ddns/pull/35#issuecomment-974752476) and open a PR. +`purgeUnknownRecords` removes stale DNS records from Cloudflare. This is useful if you have a dynamic DNS record that you no longer want to use. If you have a dynamic DNS record that you no longer want to use, you can set `purgeUnknownRecords` to `true` and the script will remove the stale DNS record from Cloudflare. ## 🐳 Deploy with Docker Compose From e913d94eb86eb141acde3279788cc969c79e8f86 Mon Sep 17 00:00:00 2001 From: Timothy Miller <46549361+timothymiller@users.noreply.github.com> Date: Wed, 15 Feb 2023 15:22:06 -0500 Subject: [PATCH 09/19] Fix wildcard subdomain support --- cloudflare-ddns.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cloudflare-ddns.py b/cloudflare-ddns.py index 0cb3dd6..0bb0000 100755 --- a/cloudflare-ddns.py +++ b/cloudflare-ddns.py @@ -135,7 +135,7 @@ def commitRecord(ip): proxied = option["proxied"] fqdn = base_domain_name # Check if name provided is a reference to the root domain - if name != '' and name != '*' and name != '@': + if name != '' and name != '@': fqdn = name + "." + base_domain_name record = { "type": ip["type"], From 0dbd2f7c2b80c9226264fd4ad409cf1f022a2cb4 Mon Sep 17 00:00:00 2001 From: Timothy Miller <46549361+timothymiller@users.noreply.github.com> Date: Wed, 15 Feb 2023 15:28:58 -0500 Subject: [PATCH 10/19] Added dependabot --- .github/dependabot.yml | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 .github/dependabot.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..3f2cf30 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,11 @@ +version: 2 +updates: + - package-ecosystem: 'pip' + directory: '/' + schedule: + interval: 'daily' + + - package-ecosystem: 'docker' + directory: '/' + schedule: + interval: 'daily' From 9824815e12d424c7c3a99569b4ef9c9b542cbe66 Mon Sep 17 00:00:00 2001 From: Timothy Miller <46549361+timothymiller@users.noreply.github.com> Date: Wed, 15 Feb 2023 15:30:36 -0500 Subject: [PATCH 11/19] Git history folder added to gitignore --- .gitignore | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 843b7e0..c6b8a6d 100644 --- a/.gitignore +++ b/.gitignore @@ -57,4 +57,7 @@ env/ venv/ ENV/ env.bak/ -venv.bak/ \ No newline at end of file +venv.bak/ + +# Git History +**/.history/* From af347f89b963b03c84a4ca520f626ef4c0b73825 Mon Sep 17 00:00:00 2001 From: Timothy Miller <46549361+timothymiller@users.noreply.github.com> Date: Wed, 15 Feb 2023 15:37:06 -0500 Subject: [PATCH 12/19] Update interpreter of shebang to python3 --- cloudflare-ddns.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cloudflare-ddns.py b/cloudflare-ddns.py index 0bb0000..0f01142 100755 --- a/cloudflare-ddns.py +++ b/cloudflare-ddns.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # cloudflare-ddns.py # Summary: Access your home network remotely via a custom domain name without a static IP! # Description: Access your home network remotely via a custom domain From cb7b1804cf896627546c280d92ea0690274f7c58 Mon Sep 17 00:00:00 2001 From: Timothy Miller <46549361+timothymiller@users.noreply.github.com> Date: Wed, 15 Feb 2023 16:14:22 -0500 Subject: [PATCH 13/19] [feature] Extract IP address from netif credit: @comicchang --- README.md | 24 +++++++++-- cloudflare-ddns.py | 99 ++++++++++++++++++++++++++++++--------------- config-example.json | 4 +- requirements.txt | 3 +- 4 files changed, 92 insertions(+), 38 deletions(-) diff --git a/README.md b/README.md index 920ea65..a37a93f 100755 --- a/README.md +++ b/README.md @@ -10,10 +10,12 @@ Access your home network remotely via a custom domain name without a static IP! - πŸ” The Python runtime will re-use existing HTTP connections. - πŸ—ƒοΈ Cloudflare API responses are cached to reduce API usage. - 🀏 The Docker image is small and efficient. -- 0️⃣ Zero dependencies. +- 0️⃣ Zero weirdo dependencies. - πŸ’ͺ Supports all platforms. - 🏠 Enables low cost self hosting to promote a more decentralized internet. -- πŸ”’ Zero-log IP provider ([cdn-cgi/trace](https://www.cloudflare.com/cdn-cgi/trace)) +- πŸ§‘β€πŸš€ Supports NAT and multiple network interfaces. + - πŸ”’ HTTP [(Zero-log IP provider)](https://www.cloudflare.com/cdn-cgi/trace) + - πŸ’» [Netif](https://pypi.org/project/netifaces/): `ppp0`, `eth0`, `wlan0`, etc. - πŸ‘ GPL-3.0 License. Open source for open audits. ## πŸ’― Complete Support of Domain Names, Subdomains, and IPv4 & IPv6 @@ -69,6 +71,15 @@ Some ISP provided modems only allow port forwarding over IPv4 or IPv6. In this c "aaaa": true ``` +### πŸ§‘β€πŸš€ Method used for getting IP address + +If you have multiple network interfaces, or if you are behind nat, you may need to enable alternative way to determine your IP address(es). + +```json +"method": "netif", +"interface": "ppp0" +``` + ### πŸŽ›οΈ Other values explained ```json @@ -123,7 +134,9 @@ Do not include the base domain name in your `subdomains` config. Do not use the "a": true, "aaaa": true, "purgeUnknownRecords": false, - "ttl": 300 + "ttl": 300, + "method": "http", + "interface": "" } ``` @@ -181,7 +194,10 @@ If you are using API Tokens, make sure the token used supports editing your zone ], "a": true, "aaaa": true, - "purgeUnknownRecords": false + "purgeUnknownRecords": false, + "ttl": 300, + "method": "http", + "interface": "" } ``` diff --git a/cloudflare-ddns.py b/cloudflare-ddns.py index 0f01142..6a0a955 100755 --- a/cloudflare-ddns.py +++ b/cloudflare-ddns.py @@ -6,7 +6,7 @@ # A small, πŸ•΅οΈ privacy centric, and ⚑ # lightning fast multi-architecture Docker image for self hosting projects. -__version__ = "1.0.2" +__version__ = "1.0.3" import json import os @@ -15,6 +15,7 @@ import sys import threading import time import requests +import netifaces as ni CONFIG_PATH = os.environ.get('CONFIG_PATH', os.getcwd()) @@ -56,53 +57,80 @@ def getIPs(): global ipv4_enabled global ipv6_enabled global purgeUnknownRecords + global method + global interfaces + if ipv4_enabled: - try: - a = requests.get( - "https://1.1.1.1/cdn-cgi/trace").text.split("\n") - a.pop() - a = dict(s.split("=") for s in a)["ip"] - except Exception: - global shown_ipv4_warning - if not shown_ipv4_warning: - shown_ipv4_warning = True - print("🧩 IPv4 not detected via 1.1.1.1, trying 1.0.0.1") - # Try secondary IP check + if method == 'http': try: a = requests.get( - "https://1.0.0.1/cdn-cgi/trace").text.split("\n") + "https://1.1.1.1/cdn-cgi/trace").text.split("\n") a.pop() a = dict(s.split("=") for s in a)["ip"] except Exception: - global shown_ipv4_warning_secondary - if not shown_ipv4_warning_secondary: - shown_ipv4_warning_secondary = True - print("🧩 IPv4 not detected via 1.0.0.1. Verify your ISP or DNS provider isn't blocking Cloudflare's IPs.") + global shown_ipv4_warning + if not shown_ipv4_warning: + shown_ipv4_warning = True + print("🧩 IPv4 not detected via 1.1.1.1, trying 1.0.0.1") + # Try secondary IP check + try: + a = requests.get( + "https://1.0.0.1/cdn-cgi/trace").text.split("\n") + a.pop() + a = dict(s.split("=") for s in a)["ip"] + except Exception: + global shown_ipv4_warning_secondary + if not shown_ipv4_warning_secondary: + shown_ipv4_warning_secondary = True + print("🧩 IPv4 not detected via 1.0.0.1. Verify your ISP or DNS provider isn't blocking Cloudflare's IPs.") + if purgeUnknownRecords: + deleteEntries("A") + else: + try: + a = ni.ifaddresses(interface)[ni.AF_INET][0]['addr'] + except Exception: + global shown_ipv4_warning + if not shown_ipv4_warning: + shown_ipv4_warning = True + print("🧩 IPv4 not detected via " + interface + ". Verify your interface is up and has an IPv4 address.") if purgeUnknownRecords: deleteEntries("A") + if ipv6_enabled: - try: - aaaa = requests.get( - "https://[2606:4700:4700::1111]/cdn-cgi/trace").text.split("\n") - aaaa.pop() - aaaa = dict(s.split("=") for s in aaaa)["ip"] - except Exception: - global shown_ipv6_warning - if not shown_ipv6_warning: - shown_ipv6_warning = True - print("🧩 IPv6 not detected via 1.1.1.1, trying 1.0.0.1") + if method == 'http': try: aaaa = requests.get( - "https://[2606:4700:4700::1001]/cdn-cgi/trace").text.split("\n") + "https://[2606:4700:4700::1111]/cdn-cgi/trace").text.split("\n") aaaa.pop() aaaa = dict(s.split("=") for s in aaaa)["ip"] except Exception: - global shown_ipv6_warning_secondary - if not shown_ipv6_warning_secondary: - shown_ipv6_warning_secondary = True - print("🧩 IPv6 not detected via 1.0.0.1. Verify your ISP or DNS provider isn't blocking Cloudflare's IPs.") + global shown_ipv6_warning + if not shown_ipv6_warning: + shown_ipv6_warning = True + print("🧩 IPv6 not detected via 1.1.1.1, trying 1.0.0.1") + try: + aaaa = requests.get( + "https://[2606:4700:4700::1001]/cdn-cgi/trace").text.split("\n") + aaaa.pop() + aaaa = dict(s.split("=") for s in aaaa)["ip"] + except Exception: + global shown_ipv6_warning_secondary + if not shown_ipv6_warning_secondary: + shown_ipv6_warning_secondary = True + print("🧩 IPv6 not detected via 1.0.0.1. Verify your ISP or DNS provider isn't blocking Cloudflare's IPs.") + if purgeUnknownRecords: + deleteEntries("AAAA") + else: + try: + aaaa = ni.ifaddresses(interface)[ni.AF_INET6][0]['addr'] + except Exception: + global shown_ipv6_warning + if not shown_ipv6_warning: + shown_ipv6_warning = True + print("🧩 IPv6 not detected via " + interface + ". Verify your interface is up and has an IPv6 address.") if purgeUnknownRecords: deleteEntries("AAAA") + ips = {} if (a is not None): ips["ipv4"] = { @@ -232,6 +260,8 @@ if __name__ == '__main__': ipv4_enabled = True ipv6_enabled = True purgeUnknownRecords = False + method = 'http' + interface = '' if sys.version_info < (3, 5): raise Exception("🐍 This script requires Python 3.5+") @@ -253,6 +283,11 @@ if __name__ == '__main__': ipv4_enabled = True ipv6_enabled = True print("βš™οΈ Individually disable IPv4 or IPv6 with new config.json options. Read more about it here: https://github.com/timothymiller/cloudflare-ddns/blob/master/README.md") + try: + method = config["method"] + interface = config["interface"] + except: + print("βš™οΈ No config detected for 'method' - defaulting to 'http'") try: purgeUnknownRecords = config["purgeUnknownRecords"] except: diff --git a/config-example.json b/config-example.json index 38f1097..945d8e4 100755 --- a/config-example.json +++ b/config-example.json @@ -24,5 +24,7 @@ "a": true, "aaaa": true, "purgeUnknownRecords": false, - "ttl": 300 + "ttl": 300, + "method": "http", + "interface": "" } diff --git a/requirements.txt b/requirements.txt index bf0d9d4..cc0f731 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1 +1,2 @@ -requests==2.28.2 \ No newline at end of file +requests==2.28.2 +netifaces==0.11.0 \ No newline at end of file From ed65aff55f6200a39063c88c91114a5c15f54d3e Mon Sep 17 00:00:00 2001 From: Timothy Miller <46549361+timothymiller@users.noreply.github.com> Date: Wed, 15 Feb 2023 17:05:00 -0500 Subject: [PATCH 14/19] Revert netif changes for now --- README.md | 24 ++--------- cloudflare-ddns.py | 101 +++++++++++++++------------------------------ requirements.txt | 3 +- 3 files changed, 38 insertions(+), 90 deletions(-) diff --git a/README.md b/README.md index a37a93f..920ea65 100755 --- a/README.md +++ b/README.md @@ -10,12 +10,10 @@ Access your home network remotely via a custom domain name without a static IP! - πŸ” The Python runtime will re-use existing HTTP connections. - πŸ—ƒοΈ Cloudflare API responses are cached to reduce API usage. - 🀏 The Docker image is small and efficient. -- 0️⃣ Zero weirdo dependencies. +- 0️⃣ Zero dependencies. - πŸ’ͺ Supports all platforms. - 🏠 Enables low cost self hosting to promote a more decentralized internet. -- πŸ§‘β€πŸš€ Supports NAT and multiple network interfaces. - - πŸ”’ HTTP [(Zero-log IP provider)](https://www.cloudflare.com/cdn-cgi/trace) - - πŸ’» [Netif](https://pypi.org/project/netifaces/): `ppp0`, `eth0`, `wlan0`, etc. +- πŸ”’ Zero-log IP provider ([cdn-cgi/trace](https://www.cloudflare.com/cdn-cgi/trace)) - πŸ‘ GPL-3.0 License. Open source for open audits. ## πŸ’― Complete Support of Domain Names, Subdomains, and IPv4 & IPv6 @@ -71,15 +69,6 @@ Some ISP provided modems only allow port forwarding over IPv4 or IPv6. In this c "aaaa": true ``` -### πŸ§‘β€πŸš€ Method used for getting IP address - -If you have multiple network interfaces, or if you are behind nat, you may need to enable alternative way to determine your IP address(es). - -```json -"method": "netif", -"interface": "ppp0" -``` - ### πŸŽ›οΈ Other values explained ```json @@ -134,9 +123,7 @@ Do not include the base domain name in your `subdomains` config. Do not use the "a": true, "aaaa": true, "purgeUnknownRecords": false, - "ttl": 300, - "method": "http", - "interface": "" + "ttl": 300 } ``` @@ -194,10 +181,7 @@ If you are using API Tokens, make sure the token used supports editing your zone ], "a": true, "aaaa": true, - "purgeUnknownRecords": false, - "ttl": 300, - "method": "http", - "interface": "" + "purgeUnknownRecords": false } ``` diff --git a/cloudflare-ddns.py b/cloudflare-ddns.py index 6a0a955..ebf74e1 100755 --- a/cloudflare-ddns.py +++ b/cloudflare-ddns.py @@ -6,7 +6,7 @@ # A small, πŸ•΅οΈ privacy centric, and ⚑ # lightning fast multi-architecture Docker image for self hosting projects. -__version__ = "1.0.3" +__version__ = "1.0.2" import json import os @@ -15,7 +15,6 @@ import sys import threading import time import requests -import netifaces as ni CONFIG_PATH = os.environ.get('CONFIG_PATH', os.getcwd()) @@ -57,80 +56,53 @@ def getIPs(): global ipv4_enabled global ipv6_enabled global purgeUnknownRecords - global method - global interfaces - if ipv4_enabled: - if method == 'http': + try: + a = requests.get( + "https://1.1.1.1/cdn-cgi/trace").text.split("\n") + a.pop() + a = dict(s.split("=") for s in a)["ip"] + except Exception: + global shown_ipv4_warning + if not shown_ipv4_warning: + shown_ipv4_warning = True + print("🧩 IPv4 not detected via 1.1.1.1, trying 1.0.0.1") + # Try secondary IP check try: a = requests.get( - "https://1.1.1.1/cdn-cgi/trace").text.split("\n") + "https://1.0.0.1/cdn-cgi/trace").text.split("\n") a.pop() a = dict(s.split("=") for s in a)["ip"] except Exception: - global shown_ipv4_warning - if not shown_ipv4_warning: - shown_ipv4_warning = True - print("🧩 IPv4 not detected via 1.1.1.1, trying 1.0.0.1") - # Try secondary IP check - try: - a = requests.get( - "https://1.0.0.1/cdn-cgi/trace").text.split("\n") - a.pop() - a = dict(s.split("=") for s in a)["ip"] - except Exception: - global shown_ipv4_warning_secondary - if not shown_ipv4_warning_secondary: - shown_ipv4_warning_secondary = True - print("🧩 IPv4 not detected via 1.0.0.1. Verify your ISP or DNS provider isn't blocking Cloudflare's IPs.") - if purgeUnknownRecords: - deleteEntries("A") - else: - try: - a = ni.ifaddresses(interface)[ni.AF_INET][0]['addr'] - except Exception: - global shown_ipv4_warning - if not shown_ipv4_warning: - shown_ipv4_warning = True - print("🧩 IPv4 not detected via " + interface + ". Verify your interface is up and has an IPv4 address.") + global shown_ipv4_warning_secondary + if not shown_ipv4_warning_secondary: + shown_ipv4_warning_secondary = True + print("🧩 IPv4 not detected via 1.0.0.1. Verify your ISP or DNS provider isn't blocking Cloudflare's IPs.") if purgeUnknownRecords: deleteEntries("A") - if ipv6_enabled: - if method == 'http': + try: + aaaa = requests.get( + "https://[2606:4700:4700::1111]/cdn-cgi/trace").text.split("\n") + aaaa.pop() + aaaa = dict(s.split("=") for s in aaaa)["ip"] + except Exception: + global shown_ipv6_warning + if not shown_ipv6_warning: + shown_ipv6_warning = True + print("🧩 IPv6 not detected via 1.1.1.1, trying 1.0.0.1") try: aaaa = requests.get( - "https://[2606:4700:4700::1111]/cdn-cgi/trace").text.split("\n") + "https://[2606:4700:4700::1001]/cdn-cgi/trace").text.split("\n") aaaa.pop() aaaa = dict(s.split("=") for s in aaaa)["ip"] except Exception: - global shown_ipv6_warning - if not shown_ipv6_warning: - shown_ipv6_warning = True - print("🧩 IPv6 not detected via 1.1.1.1, trying 1.0.0.1") - try: - aaaa = requests.get( - "https://[2606:4700:4700::1001]/cdn-cgi/trace").text.split("\n") - aaaa.pop() - aaaa = dict(s.split("=") for s in aaaa)["ip"] - except Exception: - global shown_ipv6_warning_secondary - if not shown_ipv6_warning_secondary: - shown_ipv6_warning_secondary = True - print("🧩 IPv6 not detected via 1.0.0.1. Verify your ISP or DNS provider isn't blocking Cloudflare's IPs.") - if purgeUnknownRecords: - deleteEntries("AAAA") - else: - try: - aaaa = ni.ifaddresses(interface)[ni.AF_INET6][0]['addr'] - except Exception: - global shown_ipv6_warning - if not shown_ipv6_warning: - shown_ipv6_warning = True - print("🧩 IPv6 not detected via " + interface + ". Verify your interface is up and has an IPv6 address.") + global shown_ipv6_warning_secondary + if not shown_ipv6_warning_secondary: + shown_ipv6_warning_secondary = True + print("🧩 IPv6 not detected via 1.0.0.1. Verify your ISP or DNS provider isn't blocking Cloudflare's IPs.") if purgeUnknownRecords: deleteEntries("AAAA") - ips = {} if (a is not None): ips["ipv4"] = { @@ -260,8 +232,6 @@ if __name__ == '__main__': ipv4_enabled = True ipv6_enabled = True purgeUnknownRecords = False - method = 'http' - interface = '' if sys.version_info < (3, 5): raise Exception("🐍 This script requires Python 3.5+") @@ -283,11 +253,6 @@ if __name__ == '__main__': ipv4_enabled = True ipv6_enabled = True print("βš™οΈ Individually disable IPv4 or IPv6 with new config.json options. Read more about it here: https://github.com/timothymiller/cloudflare-ddns/blob/master/README.md") - try: - method = config["method"] - interface = config["interface"] - except: - print("βš™οΈ No config detected for 'method' - defaulting to 'http'") try: purgeUnknownRecords = config["purgeUnknownRecords"] except: @@ -324,4 +289,4 @@ if __name__ == '__main__': print("❓ Unrecognized parameter '" + sys.argv[1] + "'. Stopping now.") else: - updateIPs(getIPs()) + updateIPs(getIPs()) \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index cc0f731..bf0d9d4 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,2 +1 @@ -requests==2.28.2 -netifaces==0.11.0 \ No newline at end of file +requests==2.28.2 \ No newline at end of file From 414ef99f96215ee2d73e9e158a361733868cd528 Mon Sep 17 00:00:00 2001 From: Timothy Miller <46549361+timothymiller@users.noreply.github.com> Date: Wed, 15 Feb 2023 17:13:42 -0500 Subject: [PATCH 15/19] Updated docker compose version to 3.9 --- docker/docker-compose.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index 5721809..bb8b21c 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -1,11 +1,11 @@ -version: "3.7" +version: '3.9' services: cloudflare-ddns: image: timothyjmiller/cloudflare-ddns:latest container_name: cloudflare-ddns security_opt: - no-new-privileges:true - network_mode: "host" + network_mode: 'host' environment: - PUID=1000 - PGID=1000 From 713f0de5b0391a7e54a0d7d58ef847cf5c4ac600 Mon Sep 17 00:00:00 2001 From: Timothy Miller <46549361+timothymiller@users.noreply.github.com> Date: Wed, 15 Feb 2023 17:15:03 -0500 Subject: [PATCH 16/19] Updated README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 920ea65..810aa34 100755 --- a/README.md +++ b/README.md @@ -329,6 +329,7 @@ docker run -d timothyjmiller/cloudflare_ddns:latest - [Docker Compose](https://docs.docker.com/compose/install/) - [Kubernetes](https://kubernetes.io/docs/tasks/tools/) - [Python 3](https://www.python.org/downloads/) +- [Systemd](https://www.freedesktop.org/wiki/Software/systemd/) ## πŸ“œ Helpful links From fff882be112a7ab0b810184cdcb3003364c1769d Mon Sep 17 00:00:00 2001 From: Timothy Miller <46549361+timothymiller@users.noreply.github.com> Date: Wed, 15 Feb 2023 17:26:55 -0500 Subject: [PATCH 17/19] Revert config-example.json options for netif --- config-example.json | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/config-example.json b/config-example.json index 945d8e4..38f1097 100755 --- a/config-example.json +++ b/config-example.json @@ -24,7 +24,5 @@ "a": true, "aaaa": true, "purgeUnknownRecords": false, - "ttl": 300, - "method": "http", - "interface": "" + "ttl": 300 } From 82b97f9cda48ea747d636cd7cdc87d3c2a3c5383 Mon Sep 17 00:00:00 2001 From: Timothy Miller <46549361+timothymiller@users.noreply.github.com> Date: Wed, 15 Feb 2023 17:32:14 -0500 Subject: [PATCH 18/19] Updated Load Balancing docs --- README.md | 80 ++++++++++++++++++++++++++++++++++++++++++++- config-example.json | 15 +-------- 2 files changed, 80 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index 810aa34..c4f1250 100755 --- a/README.md +++ b/README.md @@ -16,12 +16,13 @@ Access your home network remotely via a custom domain name without a static IP! - πŸ”’ Zero-log IP provider ([cdn-cgi/trace](https://www.cloudflare.com/cdn-cgi/trace)) - πŸ‘ GPL-3.0 License. Open source for open audits. -## πŸ’― Complete Support of Domain Names, Subdomains, and IPv4 & IPv6 +## πŸ’― Complete Support of Domain Names, Subdomains, IPv4 & IPv6, and Load Balancing - 🌐 Supports multiple domains (zones) on the same IP. - πŸ“  Supports multiple subdomains on the same IP. - πŸ“‘ IPv4 and IPv6 support. - 🌍 Supports all Cloudflare regions. +- βš–οΈ Supports [Cloudflare Load Balancing](https://developers.cloudflare.com/load-balancing/understand-basics/pools/). - πŸ‡ΊπŸ‡Έ Made in the U.S.A. ## πŸ“Š Stats @@ -185,6 +186,83 @@ If you are using API Tokens, make sure the token used supports editing your zone } ``` +## βš–οΈ Load Balancing + +If you have multiple IP addresses and want to load balance between them, you can use the `loadBalancing` option. This will create a CNAME record for each subdomain that points to the subdomain with the lowest IP address. + +### πŸ“œ Example config to support load balancing + +```json +{ + "cloudflare": [ + { + "authentication": { + "api_token": "api_token_here", + "api_key": { + "api_key": "api_key_here", + "account_email": "your_email_here" + } + }, + "zone_id": "your_zone_id_here", + "subdomains": [ + { + "name": "", + "proxied": false + }, + { + "name": "remove_or_replace_with_your_subdomain", + "proxied": false + } + ] + } + ],{ + "cloudflare": [ + { + "authentication": { + "api_token": "api_token_here", + "api_key": { + "api_key": "api_key_here", + "account_email": "your_email_here" + } + }, + "zone_id": "your_zone_id_here", + "subdomains": [ + { + "name": "", + "proxied": false + }, + { + "name": "remove_or_replace_with_your_subdomain", + "proxied": false + } + ] + } + ], + "load_balancer": [ + { + "authentication": { + "api_token": "api_token_here", + "api_key": { + "api_key": "api_key_here", + "account_email": "your_email_here" + } + }, + "pool_id": "your_pool_id_here", + "origin": "your_origin_name_here" + } + ], + "a": true, + "aaaa": true, + "purgeUnknownRecords": false, + "ttl": 300 +} + "a": true, + "aaaa": true, + "purgeUnknownRecords": false, + "ttl": 300 +} +``` + ### 🧹 Optional features `purgeUnknownRecords` removes stale DNS records from Cloudflare. This is useful if you have a dynamic DNS record that you no longer want to use. If you have a dynamic DNS record that you no longer want to use, you can set `purgeUnknownRecords` to `true` and the script will remove the stale DNS record from Cloudflare. diff --git a/config-example.json b/config-example.json index e2ead1f..38f1097 100755 --- a/config-example.json +++ b/config-example.json @@ -21,21 +21,8 @@ ] } ], - "load_balancer": [ - { - "authentication": { - "api_token": "api_token_here", - "api_key": { - "api_key": "api_key_here", - "account_email": "your_email_here" - } - }, - "pool_id": "your_pool_id_here", - "origin": "your_origin_name_here" - } - ], "a": true, "aaaa": true, "purgeUnknownRecords": false, "ttl": 300 -} \ No newline at end of file +} From 6e92fc0d096eda45b62cdbc262a1a3ae3dd6be99 Mon Sep 17 00:00:00 2001 From: Timothy Miller <46549361+timothymiller@users.noreply.github.com> Date: Wed, 15 Feb 2023 19:28:08 -0500 Subject: [PATCH 19/19] Fix load balancer errors --- cloudflare-ddns.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cloudflare-ddns.py b/cloudflare-ddns.py index 6d4b4c5..d6a974a 100755 --- a/cloudflare-ddns.py +++ b/cloudflare-ddns.py @@ -242,7 +242,7 @@ def cf_api(endpoint, method, config, headers={}, data=False): def updateIPs(ips): for ip in ips.values(): commitRecord(ip) - updateLoadBalancer(ip) + #updateLoadBalancer(ip) if __name__ == '__main__':