Skip to content

Commit

Permalink
Change DynDNS Provider to Cloudflare (#7)
Browse files Browse the repository at this point in the history
* Changed DynDNS registrar to Cloudflare
Updated some functions
Rewrite to better add multiple domains
New Logo

* Updated deps

* Updated Readme.md

* Update README.md

Added section on how to obtain Cloudflare credentials and IDs
  • Loading branch information
simonl169 authored Mar 22, 2024
1 parent 48829d2 commit 3fec2ec
Show file tree
Hide file tree
Showing 8 changed files with 150 additions and 106 deletions.
2 changes: 1 addition & 1 deletion .dockerignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
environment
venv
__pycache__
.idea
.gitignore
Expand Down
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
config.json
docker-compose.yml
environment
venv
__pycache__
.idea
33 changes: 22 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,18 +1,29 @@
![GitHub release (with filter)](https://img.shields.io/github/v/release/simonl169/dns-owl) ![GitHub all releases](https://img.shields.io/github/downloads/simonl169/dns-owl/total)
![Static Badge](https://img.shields.io/badge/Python-3.11-blue)





# dns-owl
This is a little Python script that updates your IP to the DynDNS servers of Strato
This is a little Python script that updates your IP to the DynDNS servers of Cloudflare

# Use
You can deploy this container on your server and update your public IP to the Strato DynDNS Servers. You can specify the update intervall in the environment variables using standard cron notation.

In the config.json you can enter the domains you want to update by a comma-separated list
You can deploy this container on your server and update your public IP to the Cloudflare DynDNS Servers. You can specify the update intervall in the environment variables using standard cron notation.

In the config.json you can enter the domains you want to update by a comma-separated list.
New for Cloudflare: you have to add your Zone ID, Record IDs and API Key, as well as the Mail to access the Cloudflare API and Update your records.

# Record ID and Zone ID, API Key
You can get your Zone ID and API Key form your Cloudflare Profile. If you are unsure, refer to the Cloudflare docs.
With Zone ID, Key and Mail, you can run
~~~
curl --request GET \
--url https://api.cloudflare.com/client/v4/zones/{YOUR_ZONE_ID}/dns_records \
--header 'Content-Type: application/json' \
--header 'X-Auth-Email: [email protected]' \
--header 'X-Auth-Key: {YOUR_API_KEY}'
~~~
The result contains a list of your subdomains for this record and their corresponding RECORD_ID.

# Careful
The script waits the specified interval before doing its first update. If you chose a long one (eg. 1 day) it takes as much time for the first update. During this, nothing is written to the log, so it can seems as the container is hung up, which is not the case.
I try to remove this and have it run an update right at the start of the container.
I will try to make the logs more verbose in future versions.

# Strato
Support for Strato is deprecated, since I no longer host my domains there and cannot test any changes.
With the next update I will tr to make the script supporting both.
18 changes: 14 additions & 4 deletions config.example.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,17 @@
{

"ZONE_ID": "your_zone_id",
"USER_EMAIL": "[email protected]",
"API_KEY": "your_CF_API_key",
"domains": [
"domain1.example.com",
"domain2.example.com",
"domain3.example.com"
{
"RECORD_ID": "record_key_1",
"RECORD_NAME": "sub1.domain.com"
},
{
"RECORD_ID": "record_key_2",
"RECORD_NAME": "sub2.domain.com"
}
]
}

}
132 changes: 101 additions & 31 deletions dns_functions.py
Original file line number Diff line number Diff line change
@@ -1,21 +1,31 @@
import requests
import pydig
import json

def get_current_public_ip():
ip = requests.get('https://ident.me')
print('My public IP address is: {}'.format(ip.text))
return ip.text

def load_config(filename: str):
with open(filename, 'r') as f:
data = json.load(f)
return data

def resolve_current_server_ip(url):
resolver = pydig.Resolver(nameservers=['8.8.8.8'])

def get_current_public_ip() -> str:
ip_address = requests.get('https://ident.me')
print(f'\tMy public IP address is: {ip_address.text}')
return ip_address.text


def resolve_current_server_ip(url, nameservers='8.8.8.8') -> [str, str]:
nameservers = [nameservers]
resolver = pydig.Resolver(nameservers=nameservers)
local_ip = pydig.query(url, 'A')
public_ip = resolver.query(url, 'A')
print(f'\tLocal IP address for {url} is: {local_ip[0]}')
print(f'\tPublic IP address for {url} is: {public_ip[0]}')
return local_ip[0], public_ip[0]

def compare_ip(ip1, ip2):

def compare_ip(ip1, ip2) -> bool:
print('\tComparing addresses:')
print(f'\tIP address 1: {ip1}')
print(f'\tIP address 2: {ip2}')
Expand All @@ -24,32 +34,92 @@ def compare_ip(ip1, ip2):
else:
return False

def update_dns_ip(url, password):

update_url = 'https://dyndns.strato.com/nic/update?hostname=' + url + '&thisipv4=1'
#print(update_url)
r = requests.get(update_url, auth=(url, password))
#print(r.status_code)
#print(r.headers)
#print(r.text)
if 'nochg' in r.text:
print('\tNo update needed!')
print(f'\tFeedback from Strato: {r.text}')
elif 'good' in r.text:
print('\tSuccess!')
print(f'\tFeedback from Strato: {r.text}')
elif 'badauth' in r.text:
print('\tBadauth!')
print('\tYou provided a wrong password or the subdomain does not exist')
elif 'abuse' in r.text:
print('\tAbuse! You probaby update too often')
print(f'\tFeedback from Strato: {r.text}')
else:
print('\tSome other error')
print(f'\tFeedback from Strato: {r.text}')

def set_ip(cloudflare, domain, current_ip: str):
"""
sets the ip in via cloudflare api
"""
zone_id = cloudflare['ZONE_ID']
api_key = cloudflare['API_KEY']
user_email = cloudflare['USER_EMAIL']

record_id = domain['RECORD_ID']
record_name = domain['RECORD_NAME']

print(f"\tCloudflare Zone ID is: {zone_id}")
print(f"\tCloudflare API Key is: {api_key}")
print(f"\tRecord ID is: {record_id}")
print(f"\tRecord Name is: {record_name}")

url = (
"https://api.cloudflare.com/client/v4/zones/%(zone_id)s/dns_records/%(record_id)s"
% {"zone_id": zone_id, "record_id": record_id}
)

headers = {
"X-Auth-Email": user_email,
"X-Auth-Key": api_key,
"Content-Type": "application/json",
}

payload = {"type": "A", "name": record_name, "content": current_ip}
response = requests.put(url, headers=headers, data=json.dumps(payload))
# print(response.status_code)
# print(response.json()['success'])

return response


def print_owl():
print(r"""
# _____ _ _ _____ ____ __ __ _ ___ ___
# | __ \ | \ | | / ____| / __ \\ \ / /| | / _ \ |__ \
# | | | || \| || (___ | | | |\ \ /\ / / | | __ __| | | | ) |
# | | | || . ` | \___ \ | | | | \ \/ \/ / | | \ \ / /| | | | / /
# | |__| || |\ | ____) | | |__| | \ /\ / | |____ \ V / | |_| |_ / /_
# |_____/ |_| \_||_____/ \____/ \/ \/ |______| \_/ \___/(_)|____|
__________-------____ ____-------__________
\------____-------___--__---------__--___-------____------/
\//////// / / / / / \ _-------_ / \ \ \ \ \ \\\\\\\\/
\////-/-/------/_/_| /___ ___\ |_\_\------\-\-\\\\/
--//// / / / //|| (O)\ /(O) ||\\ \ \ \ \\\\--
---__/ // /| \_ /V\ _/ |\ \\ \__---
-// / /\_ ------- _/\ \ \\-
\_/_/ /\---------/\ \_\_/
----\ | /----
| -|- |
/ | \
---- \___|
# by Simon169
""")


def update_all_ip(current_ip):
data = load_config('config.json')

cf = data

for domain in data['domains']:
print(f"{'':#<40}")
print(f"\tUpdating DynDNS IP for domain: {domain['RECORD_NAME']}...")
response = set_ip(cf, domain, current_ip)

if response.json()['success']:
print(f"\tIP was set successfully!")
else:
print(f"\tThere was an error, see below for more details")
print(f"\tResponse code was: {response.status_code}")

print('\tDone!')
print(f"{'':#<40}")


if __name__ == '__main__':
print_owl()
print(f"{'':#<40}")
ip = get_current_public_ip()

print('Test')
update_all_ip(ip)
3 changes: 1 addition & 2 deletions docker-compose.example.yml
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
version: '3.8'
services:
dns-owl:
image: ghcr.io/simonl169/dns-owl:v0.1.8
image: ghcr.io/simonl169/dns-owl:v0.2
environment:
- STRATO_DYNDNS_PASSWORD=yourpw
- TZ=Europe/Berlin
- CRONVARS2=*/1 * * * *
volumes:
Expand Down
58 changes: 6 additions & 52 deletions main.py
Original file line number Diff line number Diff line change
@@ -1,55 +1,9 @@
# This is a sample Python script.
import dns_functions as dns

# Press Shift+F10 to execute it or replace it with your code.
# Press Double Shift to search everywhere for classes, files, tool windows, actions, and settings.
if __name__ == "__main__":

import requests
import json
import os
import datetime
from subprocess import call


from dns_functions import *

def dns_main():
print('###############-------------------------------------')
print(f'Current Time: {datetime.datetime.now().strftime("%d/%m/%Y %H:%M:%S")}')
print('Getting current public IP address...')
public_server_ip = get_current_public_ip()
print('###############-------------------------------------')
print('Reading config.json...')
with open('config.json', 'r') as f:
data = json.load(f)

print('###############-------------------------------------')
for url in data['domains']:
print(f'\tChecking IP address for domain {url}')
local_dns_ip, public_dns_ip = resolve_current_server_ip(url)
print('\tCompare to current public IP address...')

decide = compare_ip(public_server_ip, public_dns_ip)

if not decide:
print('\tDetecting different DNS IP, update needed')
password = os.environ.get('STRATO_DYNDNS_PASSWORD', 'default')
update_dns_ip(url, password)
else:
print('\tIP has not changed, no further action')

print('###############-------------------------------------')


# Press the green button in the gutter to run the script.
if __name__ == '__main__':

print('Running DNS Owl\n')
print(' {o,o} ')
print('./)_)')
print(' ""\n')
print('by Simon169\n')

dns_main()

print('done! Next repetition set by crontab variables!')
dns.print_owl()
print(f"{'':#<40}")
ip = dns.get_current_public_ip()

dns.update_all_ip(ip)
8 changes: 4 additions & 4 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
certifi==2023.5.7
charset-normalizer==3.1.0
idna==3.4
certifi==2024.2.2
charset-normalizer==3.3.2
idna==3.6
pydig==0.4.0
requests==2.31.0
urllib3==2.0.3
urllib3==2.2.1

0 comments on commit 3fec2ec

Please sign in to comment.