Merge branch 'master' into env-support

This commit is contained in:
Arul
2023-02-21 06:40:37 +05:30
8 changed files with 285 additions and 136 deletions

11
.github/dependabot.yml vendored Normal file
View File

@@ -0,0 +1,11 @@
version: 2
updates:
- package-ecosystem: 'pip'
directory: '/'
schedule:
interval: 'daily'
- package-ecosystem: 'docker'
directory: '/'
schedule:
interval: 'daily'

5
.gitignore vendored
View File

@@ -57,4 +57,7 @@ env/
venv/ venv/
ENV/ ENV/
env.bak/ env.bak/
venv.bak/ venv.bak/
# Git History
**/.history/*

302
README.md
View File

@@ -1,47 +1,29 @@
# 🏎️ [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.
<p align="center"><a href="https://timknowsbest.com/free-dynamic-dns" target="_blank" rel="noopener noreferrer"><img width="1024" src="feature-graphic.jpg" alt="Cloudflare DDNS"/></a></p> <p align="center"><a href="https://timknowsbest.com/free-dynamic-dns" target="_blank" rel="noopener noreferrer"><img width="1024" src="feature-graphic.jpg" alt="Cloudflare DDNS"/></a></p>
# 🚀 Cloudflare DDNS # 🚀 Cloudflare DDNS
Access your home network remotely via a custom domain name without a static IP! 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.
- 🔒 Zero-log IP provider ([cdn-cgi/trace](https://www.cloudflare.com/cdn-cgi/trace))
- 👐 GPL-3.0 License. Open source for open audits.
- 🇺🇸 [Origin](https://github.com/timothymiller/cloudflare-ddns#-origin) ## 💯 Complete Support of Domain Names, Subdomains, IPv4 & IPv6, and Load Balancing
- 📊 [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)
- 🐧 [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)
## 🇺🇸 Origin - 🌐 Supports multiple domains (zones) on the same IP.
- 📠 Supports multiple subdomains on the same IP.
This script was written for the Raspberry Pi platform to enable low cost self hosting to promote a more decentralized internet. - 📡 IPv4 and IPv6 support.
- 🌍 Supports all Cloudflare regions.
### 🧹 Safe for use with existing records - ⚖️ Supports [Cloudflare Load Balancing](https://developers.cloudflare.com/load-balancing/understand-basics/pools/).
- 🇺🇸 Made in the U.S.A.
`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.
## 📊 Stats ## 📊 Stats
@@ -49,80 +31,6 @@ Stale, duplicate DNS records are removed for housekeeping.
| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- | | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- |
| [![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') | | [![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](http://nfty.sh/kiUR9)
- [Domain name](http://nfty.sh/qnJji)
[👉 Click here to buy a domain name](http://nfty.sh/qnJji) and [get a free Cloudflare account](http://nfty.sh/kiUR9).
### 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)
## ⚒️ 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 ## 🚦 Getting Started
First copy the example configuration file into the real one. First copy the example configuration file into the real one.
@@ -153,7 +61,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", "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. 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.
@@ -162,7 +70,7 @@ Some ISP provided modems only allow port forwarding over IPv4 or IPv6. In this c
"aaaa": true "aaaa": true
``` ```
### Other values explained ### 🎛️ Other values explained
```json ```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", "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",
@@ -173,17 +81,118 @@ Some ISP provided modems only allow port forwarding over IPv4 or IPv6. In this c
## 📠 Hosting multiple subdomains on the same IP? ## 📠 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? 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.
You can handle ddns for multiple domains (cloudflare zones) using the same docker container by separating your configs inside `config.json` like below:
### ⚠️ Note ### ⚠️ 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). 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 ```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": [
{
"authentication": {
"api_token": "api_token_here",
"api_key": {
"api_key": "api_key_here",
"account_email": "your_email_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": "",
"proxied": false
},
{
"name": "remove_or_replace_with_your_subdomain",
"proxied": false
}
]
}
],
"a": true,
"aaaa": true,
"purgeUnknownRecords": false
}
```
## ⚖️ 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": [ "cloudflare": [
{ {
@@ -206,10 +215,51 @@ Do not include the base domain name in your `subdomains` config. Do not use the
} }
] ]
} }
],{
"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, "a": true,
"aaaa": true, "aaaa": true,
"purgeUnknownRecords": false "purgeUnknownRecords": false,
"ttl": 300
}
"a": true,
"aaaa": true,
"purgeUnknownRecords": false,
"ttl": 300
} }
``` ```
@@ -227,6 +277,10 @@ For ex:
"api_token": "${CF_DDNS_API_TOKEN}", "api_token": "${CF_DDNS_API_TOKEN}",
``` ```
### 🧹 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.
## 🐳 Deploy with Docker Compose ## 🐳 Deploy with Docker Compose
Pre-compiled images are available via [the official docker container on DockerHub](https://hub.docker.com/r/timothyjmiller/cloudflare-ddns). Pre-compiled images are available via [the official docker container on DockerHub](https://hub.docker.com/r/timothyjmiller/cloudflare-ddns).
@@ -234,7 +288,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. Modify the host file path of config.json inside the volumes section of docker-compose.yml.
```yml ```yml
version: '3.7' version: '3.9'
services: services:
cloudflare-ddns: cloudflare-ddns:
image: timothyjmiller/cloudflare-ddns:latest image: timothyjmiller/cloudflare-ddns:latest
@@ -361,6 +415,20 @@ Recommended for production
docker run -d timothyjmiller/cloudflare_ddns:latest docker run -d timothyjmiller/cloudflare_ddns:latest
``` ```
## Supported Platforms
- [Docker](https://docs.docker.com/get-docker/)
- [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
- [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 ## License
This Template is licensed under the GNU General Public License, version 3 (GPLv3). This Template is licensed under the GNU General Public License, version 3 (GPLv3).

View File

@@ -1,4 +1,4 @@
#!/usr/bin/env python #!/usr/bin/env python3
# cloudflare-ddns.py # cloudflare-ddns.py
# Summary: Access your home network remotely via a custom domain name without a static IP! # 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 # Description: Access your home network remotely via a custom domain
@@ -69,9 +69,20 @@ def getIPs():
global shown_ipv4_warning global shown_ipv4_warning
if not shown_ipv4_warning: if not shown_ipv4_warning:
shown_ipv4_warning = True shown_ipv4_warning = True
print("🧩 IPv4 not detected") print("🧩 IPv4 not detected via 1.1.1.1, trying 1.0.0.1")
if purgeUnknownRecords: # Try secondary IP check
deleteEntries("A") 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: if ipv6_enabled:
try: try:
aaaa = requests.get( aaaa = requests.get(
@@ -82,16 +93,26 @@ def getIPs():
global shown_ipv6_warning global shown_ipv6_warning
if not shown_ipv6_warning: if not shown_ipv6_warning:
shown_ipv6_warning = True shown_ipv6_warning = True
print("🧩 IPv6 not detected") print("🧩 IPv6 not detected via 1.1.1.1, trying 1.0.0.1")
if purgeUnknownRecords: try:
deleteEntries("AAAA") 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 = {} ips = {}
if(a is not None): if (a is not None):
ips["ipv4"] = { ips["ipv4"] = {
"type": "A", "type": "A",
"ip": a "ip": a
} }
if(aaaa is not None): if (aaaa is not None):
ips["ipv6"] = { ips["ipv6"] = {
"type": "AAAA", "type": "AAAA",
"ip": aaaa "ip": aaaa
@@ -117,7 +138,7 @@ def commitRecord(ip):
proxied = option["proxied"] proxied = option["proxied"]
fqdn = base_domain_name fqdn = base_domain_name
# Check if name provided is a reference to the root domain # 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 fqdn = name + "." + base_domain_name
record = { record = {
"type": ip["type"], "type": ip["type"],
@@ -168,6 +189,26 @@ def commitRecord(ip):
return True 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): def cf_api(endpoint, method, config, headers={}, data=False):
api_token = config['authentication']['api_token'] api_token = config['authentication']['api_token']
if api_token != '' and api_token != 'api_token_here': if api_token != '' and api_token != 'api_token_here':
@@ -180,7 +221,7 @@ def cf_api(endpoint, method, config, headers={}, data=False):
"X-Auth-Key": config['authentication']['api_key']['api_key'], "X-Auth-Key": config['authentication']['api_key']['api_key'],
} }
try: try:
if(data == False): if (data == False):
response = requests.request( response = requests.request(
method, "https://api.cloudflare.com/client/v4/" + endpoint, headers=headers) method, "https://api.cloudflare.com/client/v4/" + endpoint, headers=headers)
else: else:
@@ -204,11 +245,14 @@ def cf_api(endpoint, method, config, headers={}, data=False):
def updateIPs(ips): def updateIPs(ips):
for ip in ips.values(): for ip in ips.values():
commitRecord(ip) commitRecord(ip)
#updateLoadBalancer(ip)
if __name__ == '__main__': if __name__ == '__main__':
shown_ipv4_warning = False shown_ipv4_warning = False
shown_ipv4_warning_secondary = False
shown_ipv6_warning = False shown_ipv6_warning = False
shown_ipv6_warning_secondary = False
ipv4_enabled = True ipv4_enabled = True
ipv6_enabled = True ipv6_enabled = True
purgeUnknownRecords = False purgeUnknownRecords = False
@@ -247,13 +291,14 @@ if __name__ == '__main__':
if ttl < 30: if ttl < 30:
ttl = 30 # ttl = 30 #
print("⚙️ TTL is too low - defaulting to 30 seconds") print("⚙️ TTL is too low - defaulting to 30 seconds")
if(len(sys.argv) > 1): if (len(sys.argv) > 1):
if(sys.argv[1] == "--repeat"): if (sys.argv[1] == "--repeat"):
if ipv4_enabled and ipv6_enabled: if ipv4_enabled and ipv6_enabled:
print( print(
"🕰️ Updating IPv4 (A) & IPv6 (AAAA) records every " + str(ttl) + " seconds") "🕰️ Updating IPv4 (A) & IPv6 (AAAA) records every " + str(ttl) + " seconds")
elif ipv4_enabled and not ipv6_enabled: 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: elif ipv6_enabled and not ipv4_enabled:
print("🕰️ Updating IPv6 (AAAA) records every " + print("🕰️ Updating IPv6 (AAAA) records every " +
str(ttl) + " seconds") str(ttl) + " seconds")
@@ -268,4 +313,4 @@ if __name__ == '__main__':
print("❓ Unrecognized parameter '" + print("❓ Unrecognized parameter '" +
sys.argv[1] + "'. Stopping now.") sys.argv[1] + "'. Stopping now.")
else: else:
updateIPs(getIPs()) updateIPs(getIPs())

View File

@@ -1,11 +1,11 @@
version: "3.7" version: '3.9'
services: services:
cloudflare-ddns: cloudflare-ddns:
image: timothyjmiller/cloudflare-ddns:latest image: timothyjmiller/cloudflare-ddns:latest
container_name: cloudflare-ddns container_name: cloudflare-ddns
security_opt: security_opt:
- no-new-privileges:true - no-new-privileges:true
network_mode: "host" network_mode: 'host'
environment: environment:
- PUID=1000 - PUID=1000
- PGID=1000 - PGID=1000

View File

@@ -1 +1 @@
requests==2.28.1 requests==2.28.2

View File

@@ -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

View File

@@ -0,0 +1,9 @@
[Unit]
Description=Update DDNS on Cloudflare every 15 minutes
[Timer]
OnBootSec=2min
OnUnitActiveSec=15m
[Install]
WantedBy=timers.target