dynamicdns

package module
v0.0.0-...-a0a2581 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 11, 2024 License: Apache-2.0 Imports: 18 Imported by: 2

README

Dynamic DNS app for Caddy

This is a simple Caddy app that keeps your DNS pointed to your machine; especially useful if your IP address is not static.

It simply queries a service (an "IP source") for your public IP address every so often and if it changes, it updates the DNS records with your configured provider. It supports multiple IPs, including IPv4 and IPv6, as well as redundant IP sources.

IP sources and DNS providers are modular. This app comes with IP source modules. However, you'll need to plug in a DNS provider module from caddy-dns so that your DNS records can be updated.

[!NOTE] This is not an official repository of the Caddy Web Server organization.

Sample configurations
Minimal example config

Caddyfile config (global options):

{
	dynamic_dns {
		provider cloudflare {env.CLOUDFLARE_API_TOKEN}
		domains {
			example.com
		}
	}
}

Equivalent JSON config:

{
	"apps": {
		"dynamic_dns": {
			"domains": {
				"example.com": ["@"]
			},
			"dns_provider": {
				"name": "cloudflare",
				"api_token": "{env.CLOUDFLARE_API_TOKEN}"
			}
		}
	}
}

This updates DNS records for example.com via Cloudflare's API. (Notice how the DNS zone is separate from record names/subdomains.)

Complex example config

Here's a more filled-out config, will all the options used.

This config prefers to get the IP address locally via UPnP (if edge router has UPnP enabled, of course), but if that fails, will fall back to querying icanhazip.com for the IP address. If those fail, it will try to find the IP address from the eth0 interface. It then updates records for example.com, www.example.com, and subdomain.example.net. Notice how the zones and subdomains are separate; this eliminates ambiguity since we don't have to try to be clever and figure out the zone via recursive, authoritative DNS lookups. We also check every 5 minutes instead of 30 minutes (default), and set a TTL of 1 hour for the records.

The interface option will return at most 1 IPv4 and 1 IPv6 address. It will only return addresses that are not private according to RFC 1918 (IPv4) and RFC 4193 (IPv6), and is a global unicast address according to RFC 1122, RFC 4632, and RFC 4291 with the exception of IPv4 directed broadcast addresses.

Note that it's redundant to specify both IP versions in the config, since the default is to enable both IPv4 and IPv6. It's purpose is to allow disabling one or the other if your server is only reachable via one of the versions. It's included in this config example for posterity.

Caddyfile config (global options):

{
	dynamic_dns {
		provider cloudflare {env.CLOUDFLARE_API_TOKEN}
		domains {
			example.com @ www
			example.net subdomain
		}
		ip_source upnp
		ip_source simple_http https://icanhazip.com
		ip_source simple_http https://api64.ipify.org
		ip_source interface eth0
		check_interval 5m
		versions ipv4 ipv6
		ttl 1h
	}
}

Equivalent JSON config:

{
	"apps": {
		"dynamic_dns": {
			"dns_provider": {
				"name": "cloudflare",
				"api_token": "{env.CLOUDFLARE_API_TOKEN}"
			},
			"domains": {
				"example.com": ["@", "www"],
				"example.net": ["subdomain"]
			},
			"ip_sources": [
				{
					"source": "upnp"
				},
				{
					"source": "simple_http",
					"endpoints": ["https://icanhazip.com", "https://api64.ipify.org"]
				},
				{
					"source": "interface",
					"name": "eth0"
				}
			],
			"check_interval": "5m",
			"versions": {
				"ipv4": true,
				"ipv6": true
			},
			"ttl": "1h",
			"dynamic_domains": false
		}
	}
}
Disabling IPv4

To disable IPv4 lookups and use your unique global unicast IPv6 address, specify only IPv6 as the version you want enabled and use your machine interface to get the IP address:

Caddyfile config:

{
	dynamic_dns {
		provider cloudflare {env.CLOUDFLARE_API_TOKEN}
		domains {
			example.com
		}
		versions ipv6
		ip_source interface eth0
	}
}

Equivalent JSON config; you may omit the other version you want to keep enabled (omission is assumed to mean enabled):

{
	"apps": {
		"dynamic_dns": {
			"domains": {
				"example.com": ["@"]
			},
			"dns_provider": {
				"name": "cloudflare",
				"api_token": "{env.CLOUDFLARE_API_TOKEN}"
			},
			"ip_sources": [
				{
					"source": "interface",
					"name": "eth0"
				}
			],
			"versions": {
				"ipv4": false
			}
		}
	}
}
Disabling IPv6

To disable IPv6 lookups, specify only IPv4 as the version you want enabled:

Caddyfile config:

{
	dynamic_dns {
		provider cloudflare {env.CLOUDFLARE_API_TOKEN}
		domains {
			example.com
		}
		versions ipv4
	}
}

Equivalent JSON config; you may omit the other version you want to keep enabled (omission is assumed to mean enabled):

{
	"apps": {
		"dynamic_dns": {
			"domains": {
				"example.com": ["@"]
			},
			"dns_provider": {
				"name": "cloudflare",
				"api_token": "{env.CLOUDFLARE_API_TOKEN}"
			},
			"versions": {
				"ipv6": false
			}
		}
	}
}
Dynamic Domains

There is an option dynamic_domains that can scan through the configured domains configured in this Caddy instance and will try to manage the DNS of those domains.

Note:

  • Only host matchers at the top-level of server routes will get managed.
  • on_demand is not supported because the hostname isn't known at config time.

Caddyfile config:

{
	dynamic_dns {
		provider cloudflare {env.CLOUDFLARE_API_TOKEN}
		domains {
			example.com @ www
			example.net subdomain
		}
		dynamic_domains
	}
}

# This domain will be managed.
cool.example.com {
	redir http://google.com
}

# This domain will *NOT* be managed because it's not configured in dynamic_dns.
another.host.com {
	redir http://youtube.com
}

Equivalent JSON config:

{
	"apps": {
		"dynamic_dns": {
			"domains": {
				"example.com": ["@", "www"],
				"example.net": ["subdomain"]
			},
			"dynamic_domains": true,
			"dns_provider": {
				"name": "cloudflare",
				"api_token": "topsecret"
			},
		},
		"servers": {
			"srv0": {
				"routes": [{
					// omitted
					"match": [{
						"host": [
							// This domain will be managed.
							"cool.example.com"
						]
					}]
				}, {
					// omitted
					"match": [{
						"host": [
							// This domain will *NOT* be managed because it's not configured in dynamic_dns.
							"another.host.com"
						]
					}]
				}]
			}
		}
	}
}

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type App

type App struct {
	// The sources from which to get the server's public IP address.
	// Multiple sources can be specified for redundancy.
	// Default: simple_http
	IPSourcesRaw []json.RawMessage `json:"ip_sources,omitempty" caddy:"namespace=dynamic_dns.ip_sources inline_key=source"`

	// The configuration for the DNS provider with which the DNS
	// records will be updated.
	DNSProviderRaw json.RawMessage `json:"dns_provider,omitempty" caddy:"namespace=dns.providers inline_key=name"`

	// The record names, keyed by DNS zone, for which to update the A/AAAA records.
	// Record names are relative to the zone. The zone is usually your registered
	// domain name. To refer to the zone itself, use the record name of "@".
	//
	// For example, assuming your zone is example.com, and you want to update A/AAAA
	// records for "example.com" and "www.example.com" so that they resolve to this
	// Caddy instance, configure like so: `"example.com": ["@", "www"]`
	Domains map[string][]string `json:"domains,omitempty"`

	// If enabled, no new DNS records will be created. Only existing records will be updated.
	// This means that the A or AAAA records need to be created manually ahead of time.
	UpdateOnly bool `json:"update_only,omitempty"`

	// If enabled, the "http" app's config will be scanned to assemble the list
	// of domains for which to enable dynamic DNS updates.
	DynamicDomains bool `json:"dynamic_domains,omitempty"`

	// The IP versions to enable. By default, both "ipv4" and "ipv6" will be enabled.
	// To disable IPv6, specify {"ipv6": false}.
	Versions IPVersions `json:"versions,omitempty"`

	// How frequently to check the public IP address. Default: 30m
	CheckInterval caddy.Duration `json:"check_interval,omitempty"`

	// The TTL to set on DNS records.
	TTL caddy.Duration `json:"ttl,omitempty"`
	// contains filtered or unexported fields
}

App is a Caddy app that keeps your DNS records updated with the public IP address of your instance. It updates A and AAAA records.

func (App) CaddyModule

func (App) CaddyModule() caddy.ModuleInfo

CaddyModule returns the Caddy module information.

func (*App) Provision

func (a *App) Provision(ctx caddy.Context) error

Provision sets up the app module.

func (App) Start

func (a App) Start() error

Start starts the app module.

func (App) Stop

func (a App) Stop() error

Stop stops the app module.

type IPSource

type IPSource interface {
	GetIPs(context.Context, IPVersions) ([]net.IP, error)
}

IPSource is a type that can get IP addresses.

type IPVersions

type IPVersions struct {
	IPv4 *bool `json:"ipv4,omitempty"`
	IPv6 *bool `json:"ipv6,omitempty"`
}

IPVersions is the IP versions to enable for dynamic DNS. Versions are enabled if true or nil, set to false to disable.

func (IPVersions) V4Enabled

func (ip IPVersions) V4Enabled() bool

V4Enabled returns true if IPv4 is enabled.

func (IPVersions) V6Enabled

func (ip IPVersions) V6Enabled() bool

V6Enabled returns true if IPv6 is enabled.

type NetInterface

type NetInterface struct {
	// The name of the network interface.
	Name string `json:"name,omitempty"`
	// contains filtered or unexported fields
}

NetInterface gets the public IP address(es) (at most 1 IPv4 and 1 IPv6) from a network interface by name.

func (NetInterface) CaddyModule

func (NetInterface) CaddyModule() caddy.ModuleInfo

CaddyModule returns the Caddy module information.

func (NetInterface) GetIPs

func (u NetInterface) GetIPs(ctx context.Context, versions IPVersions) ([]net.IP, error)

GetIPs gets the public address of from the network interface.

func (*NetInterface) Provision

func (u *NetInterface) Provision(ctx caddy.Context) error

Provision sets up the module.

func (*NetInterface) UnmarshalCaddyfile

func (u *NetInterface) UnmarshalCaddyfile(d *caddyfile.Dispenser) error

type SimpleHTTP

type SimpleHTTP struct {
	// The list of endpoints to query. If empty, a default list will
	// be used:
	//
	// - https://api64.ipify.org
	// - https://myip.addr.space
	// - https://ifconfig.me
	// - https://icanhazip.com
	// - https://ident.me
	// - https://ipecho.net/plain
	Endpoints []string `json:"endpoints,omitempty"`
	// contains filtered or unexported fields
}

SimpleHTTP is an IP source that looks up the public IP addresses by making HTTP(S) requests to the specified endpoints; it will try each endpoint with IPv4 and IPv6 until at least one returns a valid value. It is OK if an endpoint doesn't support both IP versions; returning a single valid IP address is sufficient.

The endpoints must return HTTP status 200 and the response body must contain only the IP address in plain text.

func (SimpleHTTP) CaddyModule

func (SimpleHTTP) CaddyModule() caddy.ModuleInfo

CaddyModule returns the Caddy module information.

func (SimpleHTTP) GetIPs

func (sh SimpleHTTP) GetIPs(ctx context.Context, versions IPVersions) ([]net.IP, error)

GetIPs gets the public addresses of this machine.

func (*SimpleHTTP) Provision

func (sh *SimpleHTTP) Provision(ctx caddy.Context) error

Provision sets up the module.

func (*SimpleHTTP) UnmarshalCaddyfile

func (sh *SimpleHTTP) UnmarshalCaddyfile(d *caddyfile.Dispenser) error

type Static

type Static struct {
	IPs []net.IP `json:"ips,omitempty"`
	// contains filtered or unexported fields
}

func (Static) CaddyModule

func (Static) CaddyModule() caddy.ModuleInfo

func (Static) GetIPs

func (s Static) GetIPs(ctx context.Context, versions IPVersions) ([]net.IP, error)

func (*Static) Provision

func (s *Static) Provision(ctx caddy.Context) error

func (*Static) UnmarshalCaddyfile

func (s *Static) UnmarshalCaddyfile(d *caddyfile.Dispenser) error

type UPnP

type UPnP struct {
	// The UPnP endpoint to query. If empty, the default UPnP
	// discovery will be used.
	Endpoint string `json:"endpoint,omitempty"`
	// contains filtered or unexported fields
}

UPnP gets the IP address from UPnP device.

func (UPnP) CaddyModule

func (UPnP) CaddyModule() caddy.ModuleInfo

CaddyModule returns the Caddy module information.

func (UPnP) GetIPs

func (u UPnP) GetIPs(ctx context.Context, _ IPVersions) ([]net.IP, error)

GetIPs gets the public address(es) of this machine. This implementation ignores the configured IP versions, since we can't really choose whether we're looking for IPv4 or IPv6 with UPnP, we just get what we get.

func (*UPnP) Provision

func (u *UPnP) Provision(ctx caddy.Context) error

Provision sets up the module.

func (*UPnP) UnmarshalCaddyfile

func (u *UPnP) UnmarshalCaddyfile(d *caddyfile.Dispenser) error

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL