DynDNS Helper ============= Self-hosted python web application based on [FastAPI](https://github.com/tiangolo/fastapi) to update DNS records via API. Vendor backends are easily extensible and include currently: - [Hetzner DNS API](https://dns.hetzner.com/api-docs/) Current use-cases for this application: - Updating DNS records based on data received via DynDNS compatible requests from e.g. routers such as AVM Fritzbox - Support for IPv4, IPv6 and IPv6 LAN Prefix - IPv4 will result in updated A record - IPv6 will result in updated AAAA record - IPv6 LAN Prefix will update all AAAA records defined in an config mapping of FQDN to IPv6 Interface IDs Limitations: - No support for DNS round robin or the like - No updating a single FQDN across multiple vendors - No cleanup of DNS records - No updating data other than values of DNS records # TODO - docs - tests # Router DynDNS Examples ## AVM Fritzbox Fritzbox DynDNS Settings: ``` Update-URL: https://192.168.178.10/fqdns//dyndns?ip=&ip=&ipv6lanprefix= Domainname: test1.domain.tld Username: testuser Password: testpassword ``` App config: ```json { "fqdns": { "test1.domain.tld": { "vendor": "hetzner", "dyndns_credentials": { "testuser": "9f735e0df9a1ddc702bf0a1a7b83033f9f7153a00c29de82cedadc9957289b05" } } } } ``` # IPv6 LAN Prefix & Interface ID (IID) Providers will assign an publicly routed IPv6 Prefix for a client. Normally a `/48` or `/64`. Depending on the clients router configuration, devices behind it will use this prefix to assign themselves a public IPv6. These devices append an Interface ID (or: host part) to the prefix. How this IID is generated depends on the device configuration. It may be generated by using its MAC Address, it may be generated randomly for privacy reasons. This helper supports updating DNS records for IIDs when the IPv6 LAN Prefix changes: If a DynDNS API call contains an `ipv6lanprefix`, the FQDN settings of the given `fqdn` will be used to find other FQDNs to update, e.g.: ```json { "fqdns": { "test1.domain.tld": { "vendor": "hetzner", "dyndns_credentials": { "testuser": "9f735e0df9a1ddc702bf0a1a7b83033f9f7153a00c29de82cedadc9957289b05" }, "ipv6_lan_prefix_iid_map": { "test2.domain.tld": "::2a0:00ff:111c:1234" } }, "test2.domain.tld": { "vendor": "..." } } } ``` If for this configuration a DynDNS API call for `test1.domain.tld` is received, that contains `ipv6lanprefix`, an AAAA DNS record for `test2.domain.tld` is ensured. For the above example: An `ipv6lanprefix` of `2001:db8:85a3::/48` will result in: ``` test2.domaintld AAAA 2001:db8:85a3::2a0:00ff:111c:1234 ``` ## Caveats - Larger IIDs than a prefix allows will be shortened - e.g.: An `ipv6lanprefix` of `2001:db8:85a3:1234::/64` with an IID `::9876:2a0:00ff:111c:1234` will result in an IPv6 address `2001:db8:85a3:1234:2a0:00ff:111c:1234` # Configuration All configuration options should be supplied as JSON files in `/etc/dyndnshelper/`. Files are merged by their top level key. With multiple files with the same top level key (e.g. `server`) only the last read will be used. Example structure: ``` /etc/dyndnshelper/ fqdns.json server.json vendors.json ``` ## Server All server config is defined under a key `server`, e.g.: ```json { "server": { "host": "0.0.0.0" } } ``` The following options are available: | Option | Default | Required | Description | Example | |:-------|:--------|:---------|:------------|:--------| | host | '127.0.0.1' | false | host to listen on | '0.0.0.0' | | port | 8000 | false | port to listen on | 8080 | | app_log_level | 'INFO' | false | log level for the application | 'DEBUG' | | root_log_level | 'WARNING' | false | log level for all other applications | 'INFO' | ## Vendors All vendor config is defined under a key `vendors` and the lowercase name of the vendor, e.g.: ```json { "vendors": { "hetzner": { "enabled": true, ... } } } ``` The following common options are available for every vendor: | Option | Default | Required | Description | Example | |:-------|:--------|:---------|:------------|:--------| | enabled | false | false | if this vendor is enabled | true | | create_zone_if_missing | false | false | if a zone is created for a FQDN if its missing - otherwise raises an error | true | | default_zone_ttl | 86400 | false | default TTL to set for created zones | 7200 | | default_record_ttl | 600 | false | default TTL to set for created records - if not defined in individual FQDN settings | 300 | ### Hetzner Additional options for Hetzner: | Option | Default | Required | Description | Example | |:-------|:--------|:---------|:------------|:--------| | api_url | 'https://dns.hetzner.com/api/v1' | false | Hetzner DNS API URL | | | api_token | | true | default TTL to set for created records - if not defined in individual FQDN settings | 'secretapitoken' | # FQDNs All FQDN config is defined under a key `fqdns` and the FQDN as a key, e.g.: ```json { "fqdns": { "test1.domain.tld": { "vendor": "hetzner", ... } } } ``` The following options are available for each FQDN: | Option | Default | Required | Description | Example | |:-------|:--------|:---------|:------------|:--------| | vendor | | true | vendor to use for the FQDN | 'hetzner' | | zone | the root zone of the FQDN | false | DNS zone to handle DNS records in | 'myservers.domain.tld' | | record_ttl | | false | DNS record TTL to use, when creating records. Overrides vendor `default_record_ttl` | 600 | | dyndns_credentials | | false | Users and their SHA256 hashed passwords that are allowed to send DynDNS updates for this FQDN | {"testuser": "9f735e0df9a1ddc702bf0a1a7b83033f9f7153a00c29de82cedadc9957289b05"} | | ipv6_lan_prefix_iid_map | | false | FQDNs and their IIDs to be updates if a DynDNS update for this FQDN contains an IPv6 LAN Prefix | {"test2.domain.tld": "::2a0:00ff:111c:1234"} |