diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7952bae --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +.venv +.env +develop.py +.idea diff --git a/README.md b/README.md new file mode 100644 index 0000000..4c631a6 --- /dev/null +++ b/README.md @@ -0,0 +1,62 @@ +# Cloudflare Python DynDNS + +This script provides a way to implement a self-hosted Dynamic DNS (DynDNS) service that automatically updates your Cloudflare DNS record with your current public IP address whenever it changes. + +## Setup + +1. **Create a Subdomain**: Set up a subdomain like `dyndns.your-domain.com` in your Cloudflare account. All other DNS records should be `CNAME` records that resolve to `dyndns.your-domain.com`. + +2. **Retrieve Cloudflare Details**: In your Cloudflare dashboard, find the following details for the subdomain you created: + - **Zone ID** + - **DNS Record Name** + - **DNS Record ID** + + Set these values in the script configuration. + +3. **How It Works**: The script checks your current public IP address. If it has changed, it updates the DNS record for `dyndns.your-domain.com` to reflect the new IP address. + +## Deployment + +### Step 1: Create Virtual Environment + +```bash +python3 -m venv /opt/cloudflare-python-dyndns/venv +/opt/cloudflare-python-dyndns/venv/bin/activate +``` + +### Step 2: Install Dependencies + +Clone the repository and install required Python packages: +```bash +pip install -r /path/to/requirements.txt +``` +### Step 3: Set Up Cron Job + +To run the script periodically, open your crontab and add the following line to check for IP address changes every 15 minutes: +```bash +crontab -e +``` +Then add this line: +```bash +*/15 * * * * /opt/cloudflare-python-dyndns/venv/bin/python /opt/cloudflare-python-dyndns/cloudflare-python-dyndns/main.py >/dev/null 2>&1 +``` +## Environment Variables + +Make sure to create a .env file in the project directory and add the following environment variables: + + CLOUDFLARE_API_TOKEN: Your Cloudflare API token with permission to edit DNS records.` + CLOUDFLARE_ZONE_ID: The Zone ID of your domain in Cloudflare. + CLOUDFLARE_DNS_RECORD_NAME: The DNS record name (e.g., dyndns.your-domain.com). + CLOUDFLARE_DNS_RECORD_ID: The DNS record ID for the dyndns record. + +## Roadmap + + Support for IPv6 addresses. + Automate the process of fetching the DNS Record Name and ID. + +## Authors + +- [@lucashahmann](https://www.github.com/lucashahmann) +## License + +[MIT](https://choosealicense.com/licenses/mit/) \ No newline at end of file diff --git a/main.py b/main.py new file mode 100644 index 0000000..eee8a7a --- /dev/null +++ b/main.py @@ -0,0 +1,61 @@ +# Imports +import os +from cloudflare import Cloudflare +from dotenv import load_dotenv +from requests import get + +# Function to retrieve public IPv4 address +def get_public_ipv4(): + ip_v4 = get('https://api.ipify.org').content.decode('utf8') + return ip_v4 + +# Function to set the dynamic DNS record in Cloudflare +def set_dyndns_record(client, new_public_ip): + response = client.dns.records.edit( + zone_id=zone_id, + dns_record_id=record_id, + name=record_name, + content=new_public_ip, + type="A" + ) + return response + +# Load environment variables from .env file +load_dotenv() + +# Retrieve required environment variables +api_token = os.environ.get("CLOUDFLARE_API_TOKEN") +if not api_token: + raise ValueError("Cloudflare API token is not set in environment variables.") + +zone_id = os.environ.get("CLOUDFLARE_ZONE_ID") +record_name = os.environ.get("CLOUDFLARE_DNS_RECORD_NAME") +record_id = os.environ.get("CLOUDFLARE_DNS_RECORD_ID") + +# Set the headers for Cloudflare API request +headers = { + "Authorization": f"Bearer {api_token}", + "Content-Type": "application/json" +} + +# Main execution +if __name__ == "__main__": + # Initialize Cloudflare client with the API token and headers + client = Cloudflare(default_headers=headers) + + # Get the current DNS record for dynamic DNS + dyndns_record = client.dns.records.get( + dns_record_id=record_id, + zone_id=zone_id + ) + + # Retrieve the current public IP address + public_ip = get_public_ipv4() + + # Check if the current public IP matches the existing DNS record + if public_ip == dyndns_record.content: + # Exit if the IP addresses are the same + exit() + else: + # Update the DNS record with the new public IP + set_dyndns_record(client, public_ip) \ No newline at end of file diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..aca4250 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,3 @@ +requests +cloudflare +python-dotenv \ No newline at end of file