Skip to content

ab-a/cf-ddns

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

10 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Cloudflare Dynamic DNS Updater (Go)

A lightweight, self-contained Dynamic DNS client for Cloudflare written in Go.
It automatically detects your public IPv4 address and updates a Cloudflare DNS A record, ideal for home servers, dynamic IP connections, or lightweight edge nodes.

For a detailed breakdown of how it works, check out the full blog post.


🧠 Overview

This tool periodically:

  1. Fetches your current public IPv4 using a configurable API (e.g. https://api.ipify.org).
  2. Compares it with the A record on Cloudflare.
  3. Creates or updates the DNS record if the IP has changed.

It’s designed to:

  • Run as a simple daemon or systemd service.
  • Use a specific network interface for outbound requests (supports SO_BINDTODEVICE).
  • Require no external dependencies — just Go and a Cloudflare API token.

⬇️ Install script examples

One liner:

curl -fsSL https://raw.githubusercontent.com/ab-a/cf-ddns/main/install.sh | sudo bash -s -- \
  --arch amd64 --iface eth1 --token <TOKEN> --zone example.com --record ddns.example.com

Auto-detect architecture (recommended):

curl -fsSL https://raw.githubusercontent.com/ab-a/cf-ddns/main/install.sh
sudo bash install.sh \
  --iface eth1 \
  --token <CLOUDFLARE_TOKEN> \
  --zone example.com \
  --record ddns.example.com

Force architecture:

curl -fsSL https://raw.githubusercontent.com/ab-a/cf-ddns/main/install.sh
sudo bash install.sh --arch amd64 --iface eth1 --token <TOKEN> --zone example.com --record ddns.example.com
sudo bash install.sh --arch arm64 --iface eth1   --token <TOKEN> --zone example.com --record ddns.example.com

Skip checksum (not recommended):

curl -fsSL https://raw.githubusercontent.com/ab-a/cf-ddns/main/install.sh
sudo bash install.sh --no-check --iface wlp4s0 --token <TOKEN> --zone example.com --record ddns.example.com

⚙️ Configuration

All settings are defined in a YAML file (default: /etc/cf-ddns/config.yaml).

# Network interface to use for outbound requests (Linux only)
interface: "eth2"

# Public IP API — must return the IPv4 in plain text
ip_api: "https://api.ipify.org"

# How often to refresh the DNS record
interval: "5m"

cloudflare:
  # Cloudflare API token with "Zone:Read" and "DNS:Edit" permissions
  api_token: "your-api-token-here"

  # Your Cloudflare zone and record names
  zone_name: "example.com"
  record_name: "ddns.example.com"

  # TTL (1 = auto)
  ttl: 1

  # Whether to enable Cloudflare proxy (orange cloud)
  proxied: false

You can override the default config path with the environment variable:

export CF_DDNS_CONFIG=/path/to/config.yaml

🚀 Usage

Build

⚙️ Installation & Build

   git clone https://github.com/ab-a/cf-ddns.git
   cd cf-ddns
   go get gopkg.in/yaml.v3
   go build -o cf-ddns .
   sha256sum cf-ddns > cf-ddns.sha256
   sha256sum -c cf-ddns.sha256
   go build -o cf-ddns .

Run

sudo ./cf-ddns

Requires root or CAP_NET_RAW privileges when using SO_BINDTODEVICE (binding to a network interface).
If you don’t need interface binding, you can simplify the code by removing that part.


🛠 Example Systemd Service

You can set it up as a service for continuous operation:

[Unit]
Description=Cloudflare DDNS updater (interface-bound)
Wants=network-online.target
After=network-online.target

[Service]
Type=simple
ExecStart=/usr/local/bin/cf-ddns
Environment=CF_DDNS_CONFIG=/etc/cf-ddns/config.yaml
# Needs root to use SO_BINDTODEVICE; you can also add AmbientCapabilities if you want to drop full root.
User=root
Group=root
# Hardening (adjust if needed)
NoNewPrivileges=true
ProtectSystem=strict
ProtectHome=true
PrivateTmp=true
PrivateDevices=true
CapabilityBoundingSet=CAP_NET_RAW CAP_NET_BIND_SERVICE
AmbientCapabilities=CAP_NET_RAW
ReadWritePaths=/etc/cf-ddns

Restart=always
RestartSec=5s

[Install]
WantedBy=multi-user.target

Enable and start:

sudo systemctl enable cf-ddns
sudo systemctl start cf-ddns

🧩 Notes

  • Timeouts: Default HTTP timeout is 10 seconds.
  • API compatibility: Works with any Cloudflare zone and a valid A record.
  • Safety: Only writes to Cloudflare when a change is detected.
  • Platform: Tested on Linux; other OSes may need adaptation since it uses SO_BINDTODEVICE.

🧪 Example Output

ddns.example.com set to 11.22.33.44

On subsequent runs, if the IP hasn’t changed, it will silently skip the update.


🔒 Security

Store your Cloudflare API token securely. The token should have:

  • Zone:Read
  • DNS:Edit
    permissions, scoped only to the zone you’re updating.

🧰 Troubleshooting

Issue Possible Cause Fix
permission denied on SO_BINDTODEVICE Insufficient privileges Run as root or set CAP_NET_RAW
non-2xx from IP API IP service unreachable Try another API endpoint
zone lookup failed Wrong zone_name or invalid token Double-check your Cloudflare credentials
Cloudflare write failed Invalid record config Ensure the record exists or allow creation

📄 License

MIT — free to use, modify, and share.
Credit is appreciated if you build on it.

About

Cloudflare Dynamic DNS Updater

Resources

Stars

Watchers

Forks

Packages

No packages published