Skip to content

Latest commit

 

History

History
354 lines (340 loc) · 13.7 KB

README.md

File metadata and controls

354 lines (340 loc) · 13.7 KB

Python-Cloudflare-DDNS

Python AM Size

Small python script for request & update DNS Record hosting by Cloudflare via Cloudflare API.

Contents

Usage

Scheduling

  • Crontab Using Crontab for job scheduling.
#MIM HOUR DAY MONTH WEEK
*/30  *    *    *    *    root  python /script_path/script_ddns4.py
  • Schedule Alternatively, automatically execute via schedule module, scheduling examples as following.
import schedule
import sys

def SomethingAsPackage():
    # Please packing script as script

# Execute setting
schedule.every(30).minutes.do(SomethingAsPackage)
# Loop
try:
    while True:
        schedule.run_pending()
        time.sleep(1)
# Manuel exit
except KeyboardInterrupt:
  sys.exit()

Cloudflare API

- For safety reason, please using API Token instead of legacy API Keys.

For using module and script, you may need a domain registered with Cloudflare, or choice Cloudflare as DNS hosting service.

Then, logged into Cloudflare Dashboard, select the domain you hosting, find the Get your API token banner, click API Tokens options.

Modify the token’s permissions. only allowing DNS record edit, then generate API Token. The token secret is only shown once, make sure to copy the secret to a secure place.

Configuration file

Cloudflare API Token, Zone ID, DNS records ID will storage at configuration file as JSON format, the standard configuration file structure as follows:

{
   "Telegram_BOTs":{
      "TelegramToken": "",
      "TelegramChatID": ""
   },
   "CloudflareAPI":{
      "AuthToken": "",
      "AuthMail": ""
   },
  "Zone":{
      "ZoneID": ""
   },
  "RecordsID":{
      "DNSRecordIPv4ID": "",
      "DNSRecordIPv4Domain": "",
      "DNSRecordIPv4ProxyAble": true,
      "DNSRecordIPv4ProxyMode": true,
      "DNSRecordIPv6ID": "",
      "DNSRecordIPv6Domain": "",
      "DNSRecordIPv6ProxyAble": true,
      "DNSRecordIPv6ProxyMode": true
   },
   "IPAddressUpdateRecord": {
      "DNSRecordIPv4": "",
      "DNSRecordIPv4UpdateTime": "",
      "DNSRecordIPv6": "",
      "DNSRecordIPv6UpdateTime": ""
  },
  "CNAME": {
      "DNSRecordCNAMEID": "",
      "NAME": "",
      "TARGET": "",
      "DNSRecordCNAMEUpdateTime": ""
   }
}

For using configuration file, please modify the file path inside script.

ConfigPath = "/file_path/cloudflare_dynamic_dns.config.json"

Error handling

Error message store at cloudflare_dynamic_dns.log.

Telegram alert

Using Telegram Bot, contect BotFather to create new Bot accounts.

Telegram BOTs Token and Telegram Chat ID are needed. if the chat channel wasn't created, Telegram API will return HTTP 400 Bad Request, You need to start the chat channel, including that bot.

Import module

# Import as module
import cloudflare_dynamic_dns

# Import the function independently
from cloudflare_dynamic_dns import UpdateSpecifyDNSRecordCANME

Script

Verify Cloudflare API Token

Please reference to the demo script script_verify.py.

from cloudflare_dynamic_dns import Verify
# Check authorize status
def VerifyCheck(ConfigPath, FullyRespon):
    CheckResult = Verify(ConfigPath,FullyRespon)
    # Error
    if type(CheckResult) is bool:
        return ("Error occurred during connect to Cloudflare API, please check the error log.")
    # Get HTTP status code
    elif type(CheckResult) is int:
        return (f"Unable connect to Cloudflare API, HTTP Status Code: {CheckResult}.")
    # Output fully Cloudflare API Respon
    elif type(CheckResult) is dict:
        return (f"Cloudflare API Response:\r\n.{CheckResult}")
    # Print authorize status only
    elif type(CheckResult) is str:
        return (f"The token authorize status is: {CheckResult}.")
# Runtime
try:
    ConfigPath = "/file_path/cloudflare_dynamic_dns.config.json"
    # If enable FullyRespon, script will print out fully Cloudflare API respon as dictionary
    FullyRespon = None
    CheckResult = VerifyCheck(ConfigPath,FullyRespon)
    print(CheckResult)

Request all DNS records

Please reference to the demo script script_request_fully.py. Default will output as Python Dictionary, define OutputJSONFile to output JSON file.

from cloudflare_dynamic_dns import RequestFullyDNSRecord
# Download DNS record
def Download_DNS_Record(ConfigPath, OutputJSONFile):
    ResultDict = RequestFullyDNSRecord(ConfigPath, OutputJSONFile)
    # Error
    if type(ResultDict) is bool:
        return ("Error occurred during connect to Cloudflare API, please check the error log.")
    # Get HTTP status code
    elif type(ResultDict) is int:
        return (f"Unable connect to Cloudflare API, HTTP Status Code: {ResultDict}.")
    # Output fully Cloudflare API Respon
    elif type(ResultDict) is dict:
        return (f"Cloudflare API Response:\r\n.{ResultDict}")
    # Print authorize status only
    elif type(ResultDict) is list:
        JSONFilePath = ResultDict[1]
        return (f"Cloudflare API Response Storage at: {JSONFilePath}")
# Runtime
try:
    # Configuration file path
    ConfigPath = "/file_path/cloudflare_dynamic_dns.config.json"
    # Output all DNS record as JSON file or not, Disable will print out as dictionary
    OutputJSONFile = None
    AskRecordResult = Download_DNS_Record(ConfigPath,OutputJSONFile)
    print(AskRecordResult)
except Exception as ErrorStatus:
    print(f"Error occurred,\r\n{ErrorStatus}")

Request A record

import cloudflare_dynamic_dns
# Configuration file path
ConfigPath = "/file_path/cloudflare_dynamic_dns.config.json"
# Asking Cloudflare API
RecordIPv4 = cloudflare_dynamic_dns.RequestSpecifyDNSRecordIPv4(ConfigPath, FullyRespon=None)
# Print result
print(RecordIPv4)

Basic respon specify RecordsID reference to configuration. If you wish to request multiple or specify DNS A record manually, please define MultiRecord.

Request AAAA record

import cloudflare_dynamic_dns
# Configuration file path
ConfigPath = "/file_path/cloudflare_dynamic_dns.config.json"
# Asking Cloudflare API
RecordIPv6 = cloudflare_dynamic_dns.RequestSpecifyDNSRecordIPv6(ConfigPath, FullyRespon=None)
# Print result
print(RecordIPv6)

Basic respon specify RecordsID reference to configuration. If you wish to request multiple or specify DNS AAAA record manually, please define MultiRecord.

Update A record

Please reference to the DDNS demo script script_ddns4.py.

import cloudflare_dynamic_dns
# Update DNS A record
UpdateStatus = cloudflare_dynamic_dns.UpdateSpecifyDNSRecordIPv4(ConfigPath, UpdateDNSRecordIPv4=CurrentIPv4)
# Get Cloudflare API respon
if type(UpdateStatus) is dict:
  # Return success or not
  Success02Not = UpdateStatus["success"]
  print(f"Cloudflare API Responses: {Success02Not}.")
# Get HTTP status code
elif type(UpdateStatus) is int:
  print(f"Unable connect to Cloudflare API, HTTP Status Code: {UpdateStatus}.")
# Error
elif type(UpdateStatus) is bool:
  print("Error occurred during connect to Cloudflare API, please check the error log.")

Basis update using Python String, please define IP address UpdateDNSRecordIPv4. If you wish to update multiple or specify DNS AAAA record, please define MultiRecord which including RecordsID, Domain Name, Proxy Ability and Proxy Mode, packing as Python List.

# Array
UpdateArray = ["RecordsID", "Domain Name", True, True]
# Update record
cloudflare_dynamic_dns.UpdateSpecifyDNSRecordIPv4(ConfigPath, MultiRecord=UpdateArray, UpdateDNSRecordIPv4=CurrentIPv4)

Alternatively, expansion to loops for update multiple A record, in case of hostng multiple domain name at same IP address.

# Array
Arraies = [
    ["RecordsID_1", "Domain Name_1", True, True],
    ["RecordsID_2", "Domain Name_2", True, True],
    ["RecordsID_3", "Domain Name_3", True, True],
    ["RecordsID_4", "Domain Name_4", True, True],
    ["RecordsID_5", "Domain Name_5", True, True]
    ]
# Update record
for Array in Arraies:
    cloudflare_dynamic_dns.UpdateSpecifyDNSRecordIPv4(ConfigPath, MultiRecord=UpdateArray, UpdateDNSRecordIPv4=CurrentIPv4)

Update AAAA record

Please reference to the DDNS demo script script_ddns6.py.

import cloudflare_dynamic_dns
# Update DNS AAAA record
UpdateStatus = cloudflare_dynamic_dns.UpdateSpecifyDNSRecordIPv6(ConfigPath, UpdateDNSRecordIPv6=CurrentIPv6)
# Get Cloudflare API respon
if type(UpdateStatus) is dict:
  # Return success or not
  Success02Not = UpdateStatus["success"]
  print(f"Cloudflare API Responses: {Success02Not}.")
# Get HTTP status code
elif type(UpdateStatus) is int:
  print(f"Unable connect to Cloudflare API, HTTP Status Code: {UpdateStatus}.")
# Error
elif type(UpdateStatus) is bool:
  print("Error occurred during connect to Cloudflare API, please check the error log.")

Basis update using Python String, please define IP address UpdateDNSRecordIPv6. If you wish to update multiple or specify DNS AAAA record, please define MultiRecord which including RecordsID, Domain Name, Proxy Ability and Proxy Mode, packing as Python List.

# Array
UpdateArray = ["RecordsID", "Domain Name", True, True]
# Update record
cloudflare_dynamic_dns.UpdateSpecifyDNSRecordIPv6(ConfigPath, MultiRecord=UpdateArray, UpdateDNSRecordIPv4=CurrentIPv6)

Alternatively, expansion to loops for update multiple A record, in case of hostng multiple domain name at same IP address.

# Array
Arraies = [
    ["RecordsID_1", "Domain Name_1", True, True],
    ["RecordsID_2", "Domain Name_2", True, True],
    ["RecordsID_3", "Domain Name_3", True, True],
    ["RecordsID_4", "Domain Name_4", True, True],
    ["RecordsID_5", "Domain Name_5", True, True]
    ]
# Update record
for Array in Arraies:
    cloudflare_dynamic_dns.UpdateSpecifyDNSRecordIPv6(ConfigPath, MultiRecord=UpdateArray, UpdateDNSRecordIPv4=CurrentIPv6)

Update CNAME

Please reference to the DDNS demo script script_update_canme.py.

from cloudflare_dynamic_dns import UpdateSpecifyDNSRecordCANME
# Update CNAME record
def Download_DNS_Record(ConfigPath, UpdateDNSRecordCNAME):
    # Check CNAME input
    if type(UpdateDNSRecordCNAME) is list:
        ResultDict = UpdateSpecifyDNSRecordCANME(ConfigPath, UpdateDNSRecordCNAME)
        # Error
        if type(ResultDict) is bool:
            return ("Error occurred during connect to Cloudflare API, please check the error log.")
        # Get HTTP status code
        elif type(ResultDict) is int:
            return (f"Unable connect to Cloudflare API, HTTP Status Code: {ResultDict}.")
        # Output fully Cloudflare API Respon
        elif type(ResultDict) is dict:
            return ResultDict
    # Print authorize status only
    else:
        return (f"Unable running CNAME update. Please check input configuration.")
# Runtime
try:
    # Configuration file path
    ConfigPath = "/file_path/cloudflare_dynamic_dns.config.json"
    # CNAME update with list input
    UpdateDNSRecordCNAME = ["name.cloudflare.dns","target.cloudflare.dns"]
    CNAMEUpdateResult = Download_DNS_Record(ConfigPath, UpdateDNSRecordCNAME)
    if type(CNAMEUpdateResult) is dict:
        SuccessOrNot = CNAMEUpdateResult["success"]
        print(f"Cloudflare API Responses: {SuccessOrNot}.")
    else:
        print(CNAMEUpdateResult)
except Exception as ErrorStatus:
    print(f"Error occurred,\r\n{ErrorStatus}")

Basis update using Python List, please define CNAME NAME and VALUE inside UpdateDNSRecordCNAME. If you wish to update multiple or specify CNAME record, please define UpdateDNSRecordCNAME including RecordsID.

# Specify CNAME
UpdateDNSRecordCNAME = ["Cloudflare_DNS_Records_ID", "name.cloudflare.dns", "target.cloudflare.dns"]

Dependencies

Python version

  • Python 3.7.3 or above
  • Testing on the above Python version: 3.9.6 / 3.12.2

Python module

  • logging
  • datetime
  • json
  • requests

License

General Public License -3.0

Resources

My GitHub Gist

CloudFlare API

IP address request API