mirror of
https://github.com/KevinMidboe/cloudflare-ddns.git
synced 2025-10-29 09:30:17 +00:00
Moved all source files to src/
This commit is contained in:
12
README.md
12
README.md
@@ -29,7 +29,7 @@ virtualenv
|
|||||||
source -p $(which python3) env/bin/activate
|
source -p $(which python3) env/bin/activate
|
||||||
|
|
||||||
pip install -r requirements.txt
|
pip install -r requirements.txt
|
||||||
python main.py
|
python src/main.py
|
||||||
```
|
```
|
||||||
|
|
||||||
## Kubernetes
|
## Kubernetes
|
||||||
@@ -46,8 +46,14 @@ metadata:
|
|||||||
namespace: cloudflare-ddns
|
namespace: cloudflare-ddns
|
||||||
type: Opaque
|
type: Opaque
|
||||||
data:
|
data:
|
||||||
API_KEY: BASE_64_ENCODED_CLOUDFLARE_API_KEY
|
API_KEY: CLOUDFLARE_API_KEY
|
||||||
DDNS_ZONE: BASE64_ENCODED_CLOUDFLARE_ZONE_ID
|
DDNS_ZONE: CLOUDFLARE_ZONE_ID
|
||||||
|
```
|
||||||
|
|
||||||
|
(Optional: receive a SMS from gateway API by appending to secrets data)
|
||||||
|
```yaml
|
||||||
|
SMS_API_KEY: GATEWAY_API_API_KEY
|
||||||
|
SMS_RECIPIENT: PHONE_NUMBER_TO_RECEIVE
|
||||||
```
|
```
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|||||||
65
main.py
65
main.py
@@ -1,65 +0,0 @@
|
|||||||
import os
|
|
||||||
import re
|
|
||||||
import requests
|
|
||||||
from bulk_dns_update import updateAllZones, setAPIKey, getDDNSAddresszoneId
|
|
||||||
from notify import notify
|
|
||||||
from dotenv import load_dotenv
|
|
||||||
from logger import logger
|
|
||||||
|
|
||||||
load_dotenv()
|
|
||||||
|
|
||||||
currentIP = None
|
|
||||||
recordedIP = None
|
|
||||||
DDNS_ZONE = os.getenv('DDNS_ZONE')
|
|
||||||
|
|
||||||
|
|
||||||
def validIP(ipString):
|
|
||||||
ipRegex = '^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$'
|
|
||||||
return re.search(ipRegex, ipString)
|
|
||||||
|
|
||||||
def publicAddress():
|
|
||||||
global currentIP
|
|
||||||
logger.info('Getting public IP from ifconfg.me...')
|
|
||||||
|
|
||||||
r = requests.get('https://ifconfig.me')
|
|
||||||
if r.status_code != 200 or not validIP(r.text):
|
|
||||||
return
|
|
||||||
|
|
||||||
currentIP = r.text
|
|
||||||
logger.info('Public IP: {}'.format(currentIP))
|
|
||||||
|
|
||||||
|
|
||||||
def cloudflareDDNS():
|
|
||||||
global recordedIP
|
|
||||||
logger.info('Checking IP recorded in Cloudflare...')
|
|
||||||
ddnsRecord = getDDNSAddresszoneId(DDNS_ZONE)
|
|
||||||
recordedIP = ddnsRecord['content']
|
|
||||||
logger.info('Found ddns recorded IP: {}'.format(recordedIP))
|
|
||||||
|
|
||||||
if currentIP != recordedIP and validIP(recordedIP):
|
|
||||||
logger.info('Public IP has changed, updating all A records.')
|
|
||||||
return True
|
|
||||||
else:
|
|
||||||
logger.info('is same, exiting')
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
|
||||||
apiKey = os.getenv('API_KEY')
|
|
||||||
if apiKey is None:
|
|
||||||
raise Exception('In .env file or environment set Cloudflare variable: API_KEY')
|
|
||||||
if DDNS_ZONE is None:
|
|
||||||
raise Exception('In .env file or environment; set Cloudflare zone where addr. points to current IP.')
|
|
||||||
|
|
||||||
setAPIKey(apiKey)
|
|
||||||
|
|
||||||
publicAddress()
|
|
||||||
changed = cloudflareDDNS()
|
|
||||||
|
|
||||||
if changed is True:
|
|
||||||
notify("IP changed to: {}. Updating all cloudflare zones!".format(currentIP))
|
|
||||||
updateAllZones(recordedIP, currentIP)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
main()
|
|
||||||
7
src/__init__.py
Normal file
7
src/__init__.py
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
import os
|
||||||
|
import sys
|
||||||
|
PROJECT_PATH = os.getcwd()
|
||||||
|
SOURCE_PATH = os.path.join(
|
||||||
|
PROJECT_PATH, "src"
|
||||||
|
)
|
||||||
|
sys.path.append(SOURCE_PATH)
|
||||||
@@ -63,6 +63,11 @@ def getZones():
|
|||||||
url = 'https://api.cloudflare.com/client/v4/zones'
|
url = 'https://api.cloudflare.com/client/v4/zones'
|
||||||
data = cloudflareRequest(url)
|
data = cloudflareRequest(url)
|
||||||
|
|
||||||
|
if 'success' not in data and 'errors' not in data:
|
||||||
|
logger.info("Unexpected cloudflare error when getting zones, no response!")
|
||||||
|
logger.info("data:" + str(data))
|
||||||
|
raise Exception('Unexpected Cloudflare error, missing response! Check logs.')
|
||||||
|
|
||||||
if data['success'] is False:
|
if data['success'] is False:
|
||||||
logger.info('Request to cloudflare was unsuccessful, error:')
|
logger.info('Request to cloudflare was unsuccessful, error:')
|
||||||
logger.info(data['errors'])
|
logger.info(data['errors'])
|
||||||
@@ -77,9 +82,15 @@ def getZones():
|
|||||||
|
|
||||||
|
|
||||||
def getRecordsForZone(zoneId):
|
def getRecordsForZone(zoneId):
|
||||||
url = 'https://api.cloudflare.com/client/v4/zones/{}/dns_records?type=A'.format(zoneId)
|
url = 'https://api.cloudflare.com/client/v4/zones/{}/dns_records?type=A'.format(
|
||||||
|
zoneId)
|
||||||
data = cloudflareRequest(url)
|
data = cloudflareRequest(url)
|
||||||
|
|
||||||
|
if 'success' not in data and 'errors' not in data:
|
||||||
|
logger.info("Unexpected cloudflare error when getting records, no response!")
|
||||||
|
logger.info("data:" + str(data))
|
||||||
|
raise Exception('Unexpected Cloudflare error, missing response! Check logs.')
|
||||||
|
|
||||||
if data['success'] is False:
|
if data['success'] is False:
|
||||||
logger.info('Request from cloudflare was unsuccessful, error:')
|
logger.info('Request from cloudflare was unsuccessful, error:')
|
||||||
logger.info(data['errors'])
|
logger.info(data['errors'])
|
||||||
@@ -105,7 +116,8 @@ def getDDNSAddresszoneId(ddnsZone):
|
|||||||
|
|
||||||
|
|
||||||
def updateRecord(zoneId, recordId, name, newIP, ttl, proxied):
|
def updateRecord(zoneId, recordId, name, newIP, ttl, proxied):
|
||||||
url = 'https://api.cloudflare.com/client/v4/zones/{}/dns_records/{}'.format(zoneId, recordId)
|
url = 'https://api.cloudflare.com/client/v4/zones/{}/dns_records/{}'.format(
|
||||||
|
zoneId, recordId)
|
||||||
data = {
|
data = {
|
||||||
'type': 'A',
|
'type': 'A',
|
||||||
'name': name,
|
'name': name,
|
||||||
@@ -115,7 +127,8 @@ def updateRecord(zoneId, recordId, name, newIP, ttl, proxied):
|
|||||||
}
|
}
|
||||||
|
|
||||||
response = cloudflareUpdateRequest(url, data)
|
response = cloudflareUpdateRequest(url, data)
|
||||||
logger.info('\tRecord updated: {}'.format('✅' if response['success'] is True else '❌'))
|
logger.info('\tRecord updated: {}'.format(
|
||||||
|
'✅' if response['success'] is True else '❌'))
|
||||||
|
|
||||||
|
|
||||||
def getMatchingRecordsForZone(zoneId, oldIP):
|
def getMatchingRecordsForZone(zoneId, oldIP):
|
||||||
@@ -131,8 +144,10 @@ def updateZone(zone, oldIP, newIP):
|
|||||||
return
|
return
|
||||||
|
|
||||||
for record in records:
|
for record in records:
|
||||||
logger.info('\tRecord {}: {} -> {}'.format(record['name'], record['content'], newIP))
|
logger.info(
|
||||||
updateRecord(zone['id'], record['id'], record['name'], newIP, record['ttl'], record['proxied'])
|
'\tRecord {}: {} -> {}'.format(record['name'], record['content'], newIP))
|
||||||
|
updateRecord(zone['id'], record['id'], record['name'],
|
||||||
|
newIP, record['ttl'], record['proxied'])
|
||||||
|
|
||||||
|
|
||||||
def updateAllZones(oldIP, newIP):
|
def updateAllZones(oldIP, newIP):
|
||||||
70
src/main.py
Normal file
70
src/main.py
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
import os
|
||||||
|
import re
|
||||||
|
import requests
|
||||||
|
from bulk_dns_update import updateAllZones, setAPIKey, getDDNSAddresszoneId
|
||||||
|
from notify import notify
|
||||||
|
from dotenv import load_dotenv
|
||||||
|
from logger import logger
|
||||||
|
|
||||||
|
load_dotenv()
|
||||||
|
|
||||||
|
DDNS_ZONE = os.getenv('DDNS_ZONE')
|
||||||
|
|
||||||
|
|
||||||
|
def validIP(ipString):
|
||||||
|
ipRegex = '^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$'
|
||||||
|
return bool(re.search(ipRegex, str(ipString)))
|
||||||
|
|
||||||
|
|
||||||
|
def publicAddress():
|
||||||
|
logger.info('Getting public IP from ifconfg.me...')
|
||||||
|
|
||||||
|
r = requests.get('https://ifconfig.me')
|
||||||
|
if r.status_code != 200:
|
||||||
|
return
|
||||||
|
|
||||||
|
ip = r.text
|
||||||
|
if not validIP(ip):
|
||||||
|
return
|
||||||
|
|
||||||
|
logger.info('Public IP: {}'.format(ip))
|
||||||
|
return ip
|
||||||
|
|
||||||
|
|
||||||
|
def cloudflareDDNS():
|
||||||
|
logger.info('Checking IP recorded in Cloudflare...')
|
||||||
|
ddnsRecord = getDDNSAddresszoneId(DDNS_ZONE)
|
||||||
|
ip = ddnsRecord['content']
|
||||||
|
logger.info('Found ddns recorded IP: {}'.format(ip))
|
||||||
|
|
||||||
|
if not validIP(ip):
|
||||||
|
return
|
||||||
|
|
||||||
|
return ip
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
apiKey = os.getenv('API_KEY')
|
||||||
|
if apiKey is None:
|
||||||
|
raise Exception(
|
||||||
|
'In .env file or environment set Cloudflare variable: API_KEY')
|
||||||
|
if DDNS_ZONE is None:
|
||||||
|
raise Exception(
|
||||||
|
'In .env file or environment; set Cloudflare zone where addr. points to current IP.')
|
||||||
|
|
||||||
|
setAPIKey(apiKey)
|
||||||
|
|
||||||
|
currentIP = publicAddress()
|
||||||
|
recordedIP = cloudflareDDNS()
|
||||||
|
|
||||||
|
if currentIP == recordedIP or None in [currentIP, recordedIP]:
|
||||||
|
logger.info('is same, exiting')
|
||||||
|
return
|
||||||
|
|
||||||
|
logger.info('Public IP has changed, updating all A records.')
|
||||||
|
notify("IP changed to: {}. Updating all cloudflare zones!".format(currentIP))
|
||||||
|
updateAllZones(recordedIP, currentIP)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
||||||
Reference in New Issue
Block a user