Giter Club home page Giter Club logo

cloudflare-ddns-client's Introduction

GitHub Release Docker Tag

Tests Code Coverage

Platforms

License

Better Dynamic DNS for Your Home Lab or Self-Hosted Cloud

This is a cross-platform DDNS client for CloudFlare, written in Go. It makes multiple attemps to retrieve your public IP, first using DNS and then several public APIs. It is well tested with a full end-to-end test suite that actually verifies functionality against CloudFlare. It also has good logging so you can see it working in your system logs.

All it requires from you are your domain name, e.g. mydomain.com, your DNS record name, e.g. mydomain.com or sub.mydomain.com, and a CloudFlare API token with the permissions of Zone:Zone:Read and Zone:DNS:Edit.

cloudflare-ddns will attempt to resolve your public IP in the following order:

  1. Using OpenDNS
  2. Using Google DNS
  3. Using http://whatismyip.akamai.com
  4. Using https://ipecho.net/plain
  5. Using https://wtfismyip.com/text

Running It

By passing in all required arguments:

$ cloudflare-ddns --domain mydomain.com --record sub.mydomain.com --token <cloudflare-api-token>
11:16PM INF Found external IP '97.113.235.123'
11:16PM INF DNS record 'sub.mydomain.com' is already set to IP '97.113.235.123'

If you have a config file set up (see below), no arguments are needed:

$ cloudflare-ddns
11:16PM INF Found external IP '97.113.235.123'
11:16PM INF DNS record 'sub.mydomain.com' is already set to IP '97.113.235.123'

With environment variables:

DOMAIN=mydomain.com RECORD=sub.mydomain.com TOKEN=<your-cloudflare-api-token> cloudflare-ddns
11:16PM INF Found external IP '97.113.235.123'
11:16PM INF DNS record 'sub.mydomain.com' is already set to IP '97.113.235.123'

Running with Docker

With a configuration file:

docker run --rm -v /path/to/cloudflare-ddns.conf:/etc/cloudflare-ddns.conf mattolenik/cloudflare-ddns-client

With environment variables:

docker run --rm -e DOMAIN=mydomain.com -e RECORD=sub.mydomain.com -e TOKEN=<your-cloudflare-api-token> mattolenik/cloudflare-ddns-client

Installation

As a Single Binary

cloudflare-ddns is distributed as a single binary with no dependencies. Simply download the correct binary for your OS under releases, rename it to cloudflare-ddns and place it in a convenient location such as /usr/local/bin.

If on Linux:

curl -sSLo /usr/local/bin/cloudflare-ddns $(curl -s https://api.github.com/repos/mattolenik/cloudflare-ddns-client/releases/latest | awk -F'"' '/browser_download_url.*linux-amd64/ {print $4}') && chmod +x /usr/local/bin/cloudflare-ddns

If on macOS:

curl -sSLo /usr/local/bin/cloudflare-ddns $(curl -s https://api.github.com/repos/mattolenik/cloudflare-ddns-client/releases/latest | awk -F'"' '/browser_download_url.*darwin-amd64/ {print $4}') && chmod +x /usr/local/bin/cloudflare-ddns

Docker

docker pull mattolenik/cloudflare-ddns-client

Ubuntu PPA

Coming soon.

MacOS HomeBrew

Coming soon.

Configuration

cloudflare-ddns can take configuration either through config file, command-line arguments, or environment variable. Use whichever method you feel is easiest for your use case. Be careful when passing in your CloudFlare API token as a CLI argument, it may be visible in logs if you are running the program from a cron job, systemd, etc.

Configuration File

Configuration can be provided in any of the formats supported by the viper configuration library, including JSON, YAML, and TOML.

Example TOML configuration file:

# Configuration for cloudflare-ddns, TOML format.

# The domain name that you own
domain = "example.com"

# The DNS record to update, may be a subdomain or the domain name itself
record = "subdomain.example.com"

# Your CloudFlare API token, must have permissions Zone:Zone:Read, Zone:DNS:Edit
token = "your-cloudflare-api-token-here"

Running Periodically with Cron

TBD

Running Periodically with systemd

TBD

Logging

cloudflare-ddns has decent informational logging. It will output logs to stderr. A verbose option, --verbose or -v can be set to print additional debugging logs.

It can also print logs in JSON for consumption by logging tools that process JSON. Just use the --json flag.

Command-Line Usage

A dynamic DNS client for CloudFlare. Automatically detects your public IP and
creates/updates a DNS record in CloudFlare.

Configuration flags can be set by defining an environment variable of the same name.
For example:
DOMAIN=mydomain.com RECORD=sub.mydomain.com TOKEN=<api-token> cloudflare-ddns

Usage:
  cloudflare-ddns [flags]

Flags:
      --config string   Path to config file (default is /etc/cloudflare-ddns.toml)
      --domain string   Domain name in CloudFlare, e.g. example.com
  -h, --help            help for cloudflare-ddns
      --json            Log format, either pretty or json, defaults to pretty
      --record string   DNS record name in CloudFlare, may be subdomain or same as domain
      --token string    CloudFlare API token with permissions Zone:Zone:Read and Zone:DNS:Edit
  -v, --verbose         Verbose logging, prints additional log output
      --version         version for cloudflare-ddns

Development

Running Tests

cloudflare-ddns has a suite of end-to-end tests that actually run the real program against a real CloudFlare account to verify functionality.

To run these tests, the following environment variables must be present at the time that make test is run:

  • CLOUDFLARE_TOKEN an API token with the usual Zone:Zone:Read and Zone:DNS:Edit permissions
  • TEST_DOMAIN the domain name in your CloudFlare account where the test records will be placed

When forking this repo you should store these as GitHub Secrets within your repository. The Actions test job will retrieve those secrets and populate the environment variables for you.

The tests will create new records and clean them up when finished.

cloudflare-ddns-client's People

Contributors

mattolenik avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar

Forkers

d4z3x autumncn

cloudflare-ddns-client's Issues

Add support for other DNS providers

Consider adding support for other DNS/DDNS providers such as Route53, DuckDNS, etc.

Acceptance Criteria

  • Project is renamed (new name TBD)
  • Support for Route53
  • Support for DuckDNS
  • Support Azure
  • Support Namecheap

Write integration tests that actually use CloudFlare

Acceptance Criteria

  • An actual CloudFlare account with a real domain has its DNS records updated
  • Can be run through GitHub Actions, retrieving the CloudFlare domain name, record name, and API token with GitHub secrets, which allows anyone to run the tests in their own fork
  • Covers case of changing IP as well as no-op when IP is already up to date

Publish to Ubuntu PPA

Acceptance Criteria

  • The deb file currently built by fpm gets uploaded to an Ubuntu PPA automatically upon each new tagged release
  • PPA PGP key is stored in GitHub secrets

Add daemon mode

Add a daemon mode that keeps the program running and periodically updating DNS. A new flag, --daemon, will specify how often DNS should refresh. It will take a required argument of a Go-style duration, e.g. 10m, 1hr, etc, as defined here. The env var DAEMON can be set to achieve the same effect, with its value set to the duration, e.g. DAEMON=15m.

already exists check doesn't work with bare hostname as record

rac@r2d2:~$ bin/updateCF
cloudflare-ddns --domain anttirytsola.fi --record r2d2 --token ...  --verbose
12:15PM INF Found public IP '34.75.222.'
12:15PM DBG Examining DNS record ID '9512c5261a71d4334357bf4c4dd58390' with name 'anttirytsola.fi'
12:15PM DBG Examining DNS record ID '0b2c47be8026ccead6ea06ab7197c5d1' with name 'horizon.anttirytsola.fi'
12:15PM DBG Examining DNS record ID '86588276cf8bce7116a0915aeafa8cd6' with name 'r2d2.anttirytsola.fi'
12:15PM DBG Examining DNS record ID '1e7f2a3a91b9d4dce5e10ba776b1f3a6' with name 'rytsolafi.anttirytsola.fi'
12:15PM DBG Examining DNS record ID 'c746198fe7c0bd772f02cb1018fab1ab' with name 'www.anttirytsola.fi'
12:15PM INF No DNS record 'r2d2' found for domain 'anttirytsola.fi', creating now
Error: failed to create DNS record 'r2d2' on domain 'anttirytsola.fi': HTTP status 400: Record already exists. (81057)
12:15PM ERR HTTP status 400: Record already exists. (81057)
cloudflare/cloudflare.go:75: failed to create DNS record 'r2d2' on domain 'anttirytsola.fi'

Seems to be cloudflare.go 58.. 85

in this case record is hostname r2d2 and when checking r.Name is with domain, r2d2.anttirytsola.fi

    }
    // Find the specific record
    var recordID string
    for _, r := range records {
        log.Debug().Msgf("Examining DNS record ID '%s' with name '%s'", r.ID, r.Name)
        if r.Name == record {         <-  This check fails. 
            recordID = r.ID
            if r.Content == ip {
                log.Info().Msgf("DNS record '%s' is already set to IP '%s'", record, ip)
                return nil
            }
            break
        }
    }
    // Create the record if it's not already there
    if recordID == "" {
        log.Info().Msgf("No DNS record '%s' found for domain '%s', creating now", record, domain)
        resp, err := p.client.CreateDNSRecord(p.ctx, zoneID, cloudflare.DNSRecord{
            Content: ip,
            Type:    "A",
            Name:    record,
        })
        if err != nil {
            return errors.Annotatef(err, "failed to create DNS record '%s' on domain '%s'", record, domain)
        }
        recordID = resp.Result.ID
    }

Host with both IPv6 and IPv4 tries to use IPv6 to update A record

I have a host that has a static IPv6 address but its IPv4 address is dynamic. I'd like to update the IPv4 when it changes, but cloudflare-ddns-client obtains the IPv6 and tries to set it for the A record which of course fails:

10:37PM INF Found public IP '2600:1900:...
...
Error: failed to update DNS record '<redacted>' to IP address '2600:1900:...': HTTP status 400: Content for A record must be a valid IPv4 address. (9005)

The DNS and URL queries need to force an IPv4 connection, the equivalent of:
dig -4 myip.opendns.com A @resolver1.opendns.com

Separate out IP lookup code into new repo

The IP lookup code is useful on its own, factor it into a new Go library and consume that library from this project.

Acceptance Criteria

  • There exists a new GitHub repo for the new public IP package
  • The new package also has a very simple command-line interface (no flags needed other than -version and -help)
  • The command outputs the public IP to stdout and any errors or warnings to stderr

Config not found in /etc

The help says:

--config string Path to config file. If not specified will look for cloudflare-ddns.toml in the program dir (), $HOME/.config, or /etc, in that order

But running it without --config it does not find the file in /etc/cloudflare-ddns.toml even though it exists and it's readable. Invoking with an explicit --config /etc/cloudflare-ddns.toml works.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.