Public release

Added removal of stable, duplicate records per subdomain.
This commit is contained in:
Tim Miller 2019-09-29 15:33:25 -04:00
parent b687c40314
commit 9487784876
4 changed files with 181 additions and 0 deletions

59
README.md Executable file
View File

@ -0,0 +1,59 @@
# :rocket: Cloudflare DDNS
Dynamic DNS service based on Cloudflare! Access your home network remotely via a custom domain name without a static IP!
## :us: Origin
This script was written for the Raspberry Pi platform to enable low cost, simple self hosting to promote a more decentralized internet. On execution, the script fetches public IPv4 and IPv6 addresses and creates/updates DNS records for the subdomains in Cloudflare. Stale, duplicate DNS records are removed for housekeeping.
## :vertical_traffic_light: Getting Started
Edit config.json and replace the values with your own.
Values explained:
```json
"api_key": "Your cloudflare API Key",
"account_email": "The email address you use to sign in to cloudflare",
"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",
"subdomains": "Array of subdomaind you want to assign the IP(s) to (e.g. pi.example.com)",
"proxied": false (defaults to false. Make it true if you want CDN/SSL benefits from cloudflare. This usually disables SSH)
```
## :running: Running
This script requires Python 3.5+, which comes preinstalled on the latest version of Raspbian. Download/clone this repo and execute `./sync`, which will set up a virtualenv, pull in any dependencies, and fire the script.
## :alarm_clock: Scheduling
This script was written with the intention of cron managing it.
## :penguin: Linux instructions (all distros)
1. Upload the cloudflare-ddns folder to your home directory /home/your_username_here/
2. Run the following code in terminal
```bash
crontab -e
```
3. Add the following lines to sync your DNS records every 15 minutes
```bash
*/15 * * * * /home/your_username_here/cloudflare-ddns/sync
```
## License
This Template is licensed under the GNU General Public License, version 3 (GPLv3) and is distributed free of charge.
## Author
Timothy Miller
GitHub: https://github.com/timothymiller 💡
Website: https://timknowsbest.com 💻
Donation: https://timknowsbest.com/donate 💸

98
cloudflare-ddns.py Executable file
View File

@ -0,0 +1,98 @@
import requests
import json
import sys
import os
PATH = os.getcwd() + "/"
version = float(str(sys.version_info[0]) + "." + str(sys.version_info[1]))
if(version < 3.5):
raise Exception("This script requires Python 3.5+")
with open(PATH + "config.json") as config_file:
config = json.loads(config_file.read())
def getIPs():
a = requests.get("https://api.ipify.org?format=json").json().get("ip")
aaaa = requests.get("https://api6.ipify.org?format=json").json().get("ip")
ips = []
if(a.find(".") > -1):
ips.append({
"type": "A",
"ip": a
})
if(aaaa.find(":") > -1):
ips.append({
"type": "AAAA",
"ip": aaaa
})
return ips
def commitRecord(ip):
stale_record_ids = []
for c in config["cloudflare"]:
subdomains = c["subdomains"]
response = cf_api("zones/" + c['zone_id'], "GET", c)
base_domain_name = response["result"]["name"]
for subdomain in subdomains:
exists = False
record = {
"type": ip["type"],
"name": subdomain,
"content": ip["ip"],
"proxied": c["proxied"]
}
list = cf_api("zones/" + c['zone_id'] + "/dns_records&per_page=100?type=" + ip["type"], "GET", c)
full_subdomain = subdomain + "." + base_domain_name
dns_id = ""
for r in list["result"]:
if (r["name"] == full_subdomain):
exists = True
if (r["content"] != ip["ip"]):
if (dns_id == ""):
dns_id = r["id"]
else:
stale_record_ids.append(r["id"])
if(exists == False):
print("Adding new record " + str(record))
response = cf_api(
"zones/" + c['zone_id'] + "/dns_records", "POST", c, {}, record)
elif(dns_id != ""):
# Only update if the record content is different
print("Updating record " + str(record))
response = cf_api(
"zones/" + c['zone_id'] + "/dns_records/" + dns_id, "PUT", c, {}, record)
# Delete duplicate, stale records
for identifier in stale_record_ids:
print("Deleting stale record " + str(identifier))
response = cf_api("zones/" + c['zone_id'] + "/dns_records/" + identifier, "DELETE", c)
return True
def cf_api(endpoint, method, config, headers={}, data=False):
headers = {
"X-Auth-Email": config['account_email'],
"X-Auth-Key": config['api_key'],
**headers
}
if(data == False):
response = requests.request(
method, "https://api.cloudflare.com/client/v4/" + endpoint, headers=headers)
else:
response = requests.request(
method, "https://api.cloudflare.com/client/v4/" + endpoint, headers=headers, json=data)
return response.json()
for ip in getIPs():
print("Checking " + ip["type"] + " records")
commitRecord(ip)

14
config.json Executable file
View File

@ -0,0 +1,14 @@
{
"cloudflare": [
{
"api_key": "api_key_here",
"account_email": "your_email_here",
"zone_id": "your_zone_id_here",
"subdomains": [
"one.example.com",
"two.example.com"
],
"proxied": false
}
]
}

10
sync Executable file
View File

@ -0,0 +1,10 @@
#!/bin/bash
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
python3 -m venv venv
source ./venv/bin/activate
pip install requests
cd $DIR
python cloudflare-ddns.py