GithubHelp home page GithubHelp logo

dness's Introduction

dness

Finesse with dness: a dynamic dns client

ci


Motivation

When one has a server that is subjected to unpredictable IP address changes, such as at home or elsewhere, a change in IP address causes unexpected downtime. Instead of paying for a static IP address, one can employ a dynamic dns client on said server, which will update the WAN IP address on the dns server.

There are plenty of dynamic dns clients, including the venerable ddclient, but troublesome installation + perl system dependency resolution, and cache format errors have left much to be desired. Other solutions fall short, so dness was created with the following goals:

Features:

  • ✔ Cross platform (Windows, Mac, Linux, ARM, BSD)
  • ✔ Zero dependencies (one can opt to dynamically link openssl when compiling from source)
  • ✔ A standard configuration (TOML) that is similar to ddclient's
  • ✔ Support for multiple Dynamic DNS Services:
  • ✔ Permissively licensed

Installation

To maximize initial flexibility, dness is not a daemon. Instead it relies on the host's scheduling (cron, systemd timers, windows scheduler).

Ubuntu / Debian (systemd + deb)

dpkg -i dness<version>_amd64.deb

# ensure it is working
/usr/bin/dness

# enable systemd timer
systemctl daemon-reload
systemctl start dness.timer
systemctl enable dness.timer

# update configuration
${EDITOR:-vi} /etc/dness/dness.conf

# create the environment variables with sensitive info
(umask 077; touch /etc/dness/dness.env)
${EDITOR:-vi} /etc/dness/dness.env

Windows

  • Download the latest zip with "windows" in its name
  • Unzip
  • Create configuration file (dness.conf)
  • Execute dness.exe -c dness.conf to verify behavior
  • If desired, use windows task scheduler to execute command at specific times

Other

Download the latest appropriate target

Configuration

No configuration file is necessary when only the WAN IP is desired.

./dness

Sample output:

[INFO  trust_dns_proto::xfer::dns_exchange] sending message via: UDP(208.67.220.220:53)
[INFO  dness] resolved address to 256.256.256.256 in 23ms
[INFO  dness] processed all: (updated: 0, already current: 0, missing: 0) in 29ms

Simple Configuration

But dness can do more than resolve one's WAN IP. Below is a simple configuration file (conventionally named dness.conf) that will update cloudflare records.

[[domains]]
type = "cloudflare"
token = "dec0de"
zone = "example.com"
records = [
    "n.example.com"
]

Execute dness with the configuration:

./dness -c dness.conf

Substitute Sensitive Values

Dness will substitute in values from the environment into the configuration so that sensitive values don't need to be specified in the config:

[[domains]]
type = "cloudflare"
token = "{{MY_CLOUDFLARE_TOKEN}}"
zone = "example.com"
records = [
    "n.example.com"
]

This is a great way to run dness in an unprivileged account but still have access to sensitive values.

Annotated Configuration

Below are the configuration options, but they've been annotated with comments.

[log]
# How verbose the log is. Common values: Error, Warn, Info, Debug, Trace
# The default level is info
level = "Debug"

[[domains]]
# We denote that our domain is managed by cloudflare
type = "cloudflare"

# Create Cloudflare token by using the use "Edit zone DNS" API token template.
# Alternatively one can use email + key fields but the token is recommended as
# it is more secure
token = "dec0de"

# The email address registered in cloudflare that is authorized to update dns
# records. Only required when not using the token field
# email = "[email protected]"

# The cloudflare key can be found in the domain overview, in "Get your API key"
# and view "Global API Key". Required when "email" is used
# key = "deadbeef"

# The zone is the domain name
zone = "example.com"

# List of A records found under the DNS tab that should be updated
records = [
    "n.example.com"
]

# More than one domain can be specified in a config!
[[domains]]
type = "cloudflare"
email = "[email protected]"
key = "deadbeef"
zone = "example2.com"
records = [
    "n.example2.com",
    "n2.example2.com"
]

Supported Dynamic DNS Services

Cloudflare

[[domains]]
# We denote that our domain is managed by cloudflare
type = "cloudflare"

# The email address registered in cloudflare that is authorized to update dns
# records
email = "[email protected]"

# The cloudflare key can be found in the domain overview, in "Get your API key"
# and view "Global API Key" (or another key as appropriate)
key = "deadbeef"

# The zone is the domain name
zone = "example.com"

# List of A records found under the DNS tab that should be updated
records = [
    "n.example.com"
]

Cloudflare dynamic dns service works in three steps:

  1. Send GET to translate the zone (example.com) to cloudflare's id
  2. Send GET to find all the domains under the zone and their sub-ids
    • Cloudflare paginates the response to handle many subdomains
    • It is possible to query for individual domains but as long as more than one desired domain in each page -- this methods cuts down requests
  3. Each desired domain in the config is checked to ensure that it is set to our address. In this way cloudflare is our cache (to guard against nefarious users updating out of band)

GoDaddy

[[domains]]
# denote that the domain is managed by godaddy
type = "godaddy"

# The GoDaddy domain: https://dcc.godaddy.com/domains/
domain = "example.com"

# This is the api key, you can create one here:
# https://developer.godaddy.com/keys
key = "abc123"

# The password for the key, top secret!
secret = "ef"

# The records to update. "@" = "example.com", "a" = "a.example.com"
records = [ "@", "a" ]

GoDaddy dynamic dns service works as the following:

  1. Send a GET request to find all records in the domain
  2. Find all the expected records (and log those that are missing) and check their current IP
  3. Update the remote IP as needed, ensuring that original properties are preserved in the upload, so that we don't overwrite a property like TTL.

Namecheap

[[domains]]
# Namecheap requires that dynamic dns is enabled in their UI!
type = "namecheap"
domain = "test-dness-1.xyz"
ddns_password = "super_secret_password"

# The records to update. Make sure they are listed as A + Dynamic DNS
# "@" = "test-dness-1.xyz"
# "* = "<any-sub-domain>.test-dness-1.xyz"
# "sub = "sub.test-dness-1.xyz"
records = [ "@", "*", "sub" ]

The namecheap services requires dynamic dns enabled in their UI.

Updating the dns entry works as follows:

  • A dns query is sent to cloudflare to check the IP of the record
  • If the IP is different than WAN then a request is sent to namecheap to update it
  • If the IP is the same, no action is taken

This method suffers from natural flow of dns propagation. When namecheap receives the update, it may take up to an hour for cloudflare to see the new record. In the meantime, dness will keep updating namecheap servers with the WAN. This has no consequential side effects other than momentary confusion why updates are being sent to namecheap every 5 minutes. Future revisions of this provider may use another method (like API integration) if the current method proves deficient enough.

He.net

[[domains]]
type = "he"
hostname = "test-dness-1.xyz"
password = "super_secret_password"
records = [ "@", "sub" ]

he.net follows the same flow as Namecheap (check the current record via DNS and update if necessary).

No-IP

[[domains]]
type = "noip"
hostname = "dnesstest.hopto.org"
username = "[email protected]"
password = "super_secret_password"

Dynu

[[domains]]
type = "dynu"
hostname = "test-dness.camdvr.org"
username = "MyUserName"

# ip update password:
# https://www.dynu.com/en-US/ControlPanel/ManageCredentials
password = "IpUpdatePassword"

# The records to update.
# "@" = "test-dness.camdvr.org"
# "sub = "sub.test-dness.camdvr.org"
records = [ "@", "sub" ]

Porkbun

[[domains]]
# denote that the domain is managed by porkbun
type = "porkbun"

# The Porkbun domain: https://porkbun.com/account/domainsSpeedy
# IMPORTANT: You must enable API Access for the domain at the above url.
domain = "example.com"

# This is the api key, you can create one here:
# https://porkbun.com/account/api
key = "abc123"

# The password for the key, top secret! Only visible once when you create the key.
secret = "ef"

# The records to update. "@" = "example.com", "a" = "a.example.com" "*" = "*.example.com"
# Both "@" and "" are valid to configure root domain.
records = [ "@", "a" ]

Porkbun dynamic dns service works similar to GoDaddy:

  1. Send a POST request to find all records in the domain
  2. Find all the expected records (and log those that are missing) and check their current IP
  3. Update the remote IP as needed, ensuring that original properties are preserved in the upload, so that we don't overwrite a property like TTL.

Supported WAN IP Resolvers

There are a couple different methods for dness to resolve the WAN IP address.

OpenDNS

The default WAN IP address resolver queries OpenDNS. It resolves IPv4 addresses by querying "myip.opendns.com" against resolver1.opendns.com and resolver2.opendns.com.

While it is the default, it can explicitly be specified by appending snippet below to the top of the config:

ip_resolver = "opendns"

Ipify

OpenDNS may not be available to all networks, so one can configure dness to use Ipify. Instead of using DNS, an HTTPs request will be sent. To opt into using Ipify, append the snippet below to the top of the config:

ip_resolver = "ipify"

dness's People

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

dness's Issues

Can't install: conffile name 'etc/dness/dness.conf' is not an absolute pathname

Hi

Fresh Ubuntu 21.04 LXC container under Proxmox and when I try to install (dpkg -i *.deb) the Dness package, I see the following:

Preparing to unpack latest.deb ...
dpkg: error processing archive latest.deb (--install):
 conffile name 'etc/dness/dness.conf' is not an absolute pathname
Errors were encountered while processing:
 latest.deb
root@dness:~# dpkg -i latest.deb 
(Reading database ... 17822 files and directories currently installed.)
Preparing to unpack latest.deb ...
dpkg: error processing archive latest.deb (--install):
 conffile name 'etc/dness/dness.conf' is not an absolute pathname
Errors were encountered while processing:
 latest.deb

Thank you

namecheap support

[[domains]]
type = "namecheap"
domain = "deadbeef"
zone = "example2.com"
records = [ "mysubdomain" ]

Would be:
https://dynamicdns.park-your-domain.com/update?host=mysubdomain&domain=example2.com&password=deadbeef&ip=[your_ip]

Docs:
https://dynamicdns.park-your-domain.com/update?host=[host]&domain=[domain_name]&password=[ddns_password]&ip=[your_ip]

IP Address = optional value. If you don't specify any IP, the IP from which you are accessing this URL will be set for the domain.

https://www.namecheap.com/support/knowledgebase/article.aspx/29/11/how-do-i-use-a-browser-to-dynamically-update-the-hosts-ip

ERROR: Could not resolve via dns

When trying to update IP address to cloudflare, it returns with error "could not successfully resolve IP caused by: could not resolve via dns"

Trace level output is:

C:\Apps\dness>C:\Apps\dness\dness-v0.2.1.exe -c C:\Apps\dness\dness.conf
[DEBUG trust_dns_resolver::async_resolver] trust-dns resolver running
[TRACE trust_dns_resolver::async_resolver] handle passed back
[DEBUG trust_dns_proto::xfer::dns_handle] querying: myip.opendns.com. A
[DEBUG trust_dns_resolver::name_server::name_server_pool] sending request: [Query { name: Name { is_fqdn: true, labels: [myip, opendns, com] }, query_type: A, query_class: IN }]
[DEBUG trust_dns_resolver::name_server::name_server] reconnecting: NameServerConfig { socket_addr: V4(208.67.222.222:53), protocol: Udp, tls_dns_name: None }
[DEBUG trust_dns_proto::xfer] enqueueing message: [Query { name: Name { is_fqdn: true, labels: [myip, opendns, com] }, query_type: A, query_class: IN }]
[TRACE trust_dns_resolver::name_server::connection_provider] polling response inner
[DEBUG trust_dns_resolver::name_server::name_server] reconnecting: NameServerConfig { socket_addr: V4(208.67.220.220:53), protocol: Udp, tls_dns_name: None }
[DEBUG trust_dns_proto::xfer] enqueueing message: [Query { name: Name { is_fqdn: true, labels: [myip, opendns, com] }, query_type: A, query_class: IN }]
[TRACE trust_dns_resolver::name_server::connection_provider] polling response inner
[TRACE trust_dns_resolver::name_server::connection_provider] polling response inner
[TRACE trust_dns_resolver::name_server::connection_provider] polling response inner
[TRACE mio::sys::windows::selector] select; timeout=Some(0ns)
[TRACE mio::sys::windows::selector] polling IOCP
[TRACE mio::sys::windows::selector] returning
[TRACE trust_dns_resolver::name_server::connection_provider] polling response inner
[TRACE mio::poll] registering with poller
[TRACE mio::sys::windows::selector] register Token(0) Readable | Writable
[TRACE mio::sys::windows::selector] set readiness to (empty)
[TRACE mio::sys::windows::udp] scheduling a read
[TRACE mio::sys::windows::selector] set readiness to Writable
[DEBUG trust_dns_proto::udp::udp_stream] created socket successfully
[TRACE trust_dns_resolver::name_server::connection_provider] polling response inner
[TRACE mio::poll] registering with poller
[TRACE mio::sys::windows::selector] register Token(1) Readable | Writable
[TRACE mio::sys::windows::selector] set readiness to (empty)
[TRACE mio::sys::windows::udp] scheduling a read
[TRACE mio::sys::windows::selector] set readiness to Writable
[DEBUG trust_dns_proto::udp::udp_stream] created socket successfully
[TRACE mio::sys::windows::selector] select; timeout=Some(0ns)
[TRACE mio::sys::windows::selector] polling IOCP
[TRACE mio::sys::windows::selector] returning
[TRACE trust_dns_resolver::name_server::connection_provider] polling response inner
[TRACE mio::sys::windows::selector] set readiness to (empty)
[TRACE mio::sys::windows::udp] scheduling a send
[TRACE trust_dns_resolver::name_server::connection_provider] polling response inner
[TRACE mio::sys::windows::selector] set readiness to (empty)
[TRACE mio::sys::windows::udp] scheduling a send
[TRACE mio::sys::windows::selector] select; timeout=Some(0ns)
[TRACE mio::sys::windows::selector] polling IOCP
[TRACE mio::sys::windows::selector] select; -> got overlapped
[TRACE mio::sys::windows::udp] finished a send 45
[TRACE mio::sys::windows::selector] set readiness to Writable
[TRACE mio::sys::windows::selector] select; -> got overlapped
[TRACE mio::sys::windows::udp] finished a send 45
[TRACE mio::sys::windows::selector] set readiness to Writable
[TRACE mio::sys::windows::selector] returning
[TRACE trust_dns_resolver::name_server::connection_provider] polling response inner
[TRACE trust_dns_resolver::name_server::connection_provider] polling response inner
[TRACE mio::sys::windows::selector] select; timeout=Some(0ns)
[TRACE mio::sys::windows::selector] polling IOCP
[TRACE mio::sys::windows::selector] returning
[TRACE trust_dns_resolver::name_server::connection_provider] polling response inner
[TRACE trust_dns_resolver::name_server::connection_provider] polling response inner
[TRACE mio::sys::windows::selector] select; timeout=Some(4.0762395s)
[TRACE mio::sys::windows::selector] polling IOCP
[TRACE mio::sys::windows::selector] select; -> got overlapped
[TRACE mio::sys::windows::udp] finished a recv 91
[TRACE mio::sys::windows::selector] set readiness to Readable | Writable
[TRACE mio::sys::windows::selector] returning
[TRACE trust_dns_resolver::name_server::connection_provider] polling response inner
[TRACE trust_dns_resolver::name_server::connection_provider] polling response inner
[TRACE mio::sys::windows::selector] set readiness to Writable
[TRACE mio::sys::windows::udp] scheduling a read
[DEBUG trust_dns_proto::rr::record_data] reading SOA
[DEBUG trust_dns_proto::udp::udp_client_stream] received message id: 24875
[TRACE mio::poll] deregistering handle with poller
[TRACE mio::sys::windows::selector] deregistering
[TRACE mio::poll] deregistering handle with poller
[TRACE mio::sys::windows::selector] deregistering
[DEBUG trust_dns_resolver::name_server::name_server_pool] mDNS responsed for query: NXDomain
[ERROR dness] could not successfully resolve IP
caused by: could not resolve via dns

Support dynu.com

Please consider adding support for Dynu who offer free DDNS service with complete control of your domain name at all times.

They use an open format, based on HTTP requests, similar to no-ip, also deployed by other service providers. The protocol is described here: IP Update Protocol.

Unable to resolve domain IP but still tries to update records

Hi

Minor feature suggestion. I noticed while I was unable to resolve my WAN IP that the log still showed it was connecting to NameCheap and trying to update the records anyway.

If there is an error with resolving the WAN IP, perhaps the process should halt without trying to update the DNS provider since it will fail.

Thank you

Support for dns.he.net

https://dns.he.net is a well known vendor that provides free DNS hosting.

Documentation is their service is available here

An existing rustlang implementation is additionally available here

Registration should be free and available for anyone to test.

Error when updating dynu.nu records

First I want to thank you for implementing support for dynu #290. 😄

I have detected an issue when the DDNS record is out of sync and needs updating. I see the following (real IP and domain has been altered below):

root@srv:~# dness -c /etc/dness/dness.conf
[INFO  dness] resolved address to 100.100.100.100 in 2ms
[ERROR dness] could not update example.com (dynu)
        caused by: expected zero errors, but received: badauth
[INFO  dness] processed all: (updated: 0, already current: 3, missing: 0) in 2ms
[ERROR dness] at least one update failed, so exiting with non-zero status code

It can be worth mentioning that, using the identical credentials updating works using ddclient.

Support noip.com

Please consider adding support for No-IP who offer free DDNS service with 30 million users.

They use an open format, based on HTTP requests, also deployed by other service providers. The API is described here: Request Method.

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.