From 9e937b6fbca2be93ef3d5e9d1673707cd6b000eb Mon Sep 17 00:00:00 2001 From: Indrajit Raychaudhuri Date: Wed, 25 Dec 2024 22:37:03 -0600 Subject: ddclient: T6981: No need for setting up ssl explicitly In ddclient v4, tls (ssl) is enabled by default, there is no need to set it up explicitly in the configuration file. --- data/templates/dns-dynamic/ddclient.conf.j2 | 1 - 1 file changed, 1 deletion(-) (limited to 'data') diff --git a/data/templates/dns-dynamic/ddclient.conf.j2 b/data/templates/dns-dynamic/ddclient.conf.j2 index b209c8c81..595f3c0a3 100644 --- a/data/templates/dns-dynamic/ddclient.conf.j2 +++ b/data/templates/dns-dynamic/ddclient.conf.j2 @@ -21,7 +21,6 @@ if{{ ipv }}={{ address }}, \ {{ host }} {% endmacro %} ### Autogenerated by service_dns_dynamic.py ### -ssl=yes {# ddclient default (web=dyndns) doesn't support ssl and results in process lockup #} web=googledomains {# ddclient default (use=ip) results in confusing warning message in log #} -- cgit v1.2.3 From 7cf47a389edecc6c05590468a10b6aa916881cc5 Mon Sep 17 00:00:00 2001 From: Indrajit Raychaudhuri Date: Wed, 25 Dec 2024 22:01:13 -0600 Subject: ddclient: T6981: Remove defunct googledomains Since googledomains (domains.google.com) has been shut down, we need to remove any configuration path that refers to googledomains. 1. Remove 'googledomains' as the default web resolver in ddclient.conf.j2 2. Apply migration to remove googledomains from dynamic DNS service configurations. The migration is also necessary to undo a previous migration where we added 'googledomains' as default web resolver to work around lack of tls (ssl) support with default web resolver 'dyndns' (checkip.dyndns.org) in ddclient v3.x. --- data/templates/dns-dynamic/ddclient.conf.j2 | 2 - .../include/version/dns-dynamic-version.xml.i | 2 +- src/migration-scripts/dns-dynamic/4-to-5 | 52 ++++++++++++++++++++++ 3 files changed, 53 insertions(+), 3 deletions(-) create mode 100644 src/migration-scripts/dns-dynamic/4-to-5 (limited to 'data') diff --git a/data/templates/dns-dynamic/ddclient.conf.j2 b/data/templates/dns-dynamic/ddclient.conf.j2 index 595f3c0a3..5fc9582c7 100644 --- a/data/templates/dns-dynamic/ddclient.conf.j2 +++ b/data/templates/dns-dynamic/ddclient.conf.j2 @@ -21,8 +21,6 @@ if{{ ipv }}={{ address }}, \ {{ host }} {% endmacro %} ### Autogenerated by service_dns_dynamic.py ### -{# ddclient default (web=dyndns) doesn't support ssl and results in process lockup #} -web=googledomains {# ddclient default (use=ip) results in confusing warning message in log #} use=no diff --git a/interface-definitions/include/version/dns-dynamic-version.xml.i b/interface-definitions/include/version/dns-dynamic-version.xml.i index 346385ccb..98123035d 100644 --- a/interface-definitions/include/version/dns-dynamic-version.xml.i +++ b/interface-definitions/include/version/dns-dynamic-version.xml.i @@ -1,3 +1,3 @@ - + diff --git a/src/migration-scripts/dns-dynamic/4-to-5 b/src/migration-scripts/dns-dynamic/4-to-5 new file mode 100644 index 000000000..3a055a122 --- /dev/null +++ b/src/migration-scripts/dns-dynamic/4-to-5 @@ -0,0 +1,52 @@ +# Copyright VyOS maintainers and contributors +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this library. If not, see . + +# T6981: +# - remove "service dns dynamic name protocol googledomains" +# - remove "service dns dynamic name address web url ..." +# when url is https://domains.google.com/* + +import re +from vyos.base import Warning +from vyos.configtree import ConfigTree + +base_path = ['service', 'dns', 'dynamic', 'name'] + +def migrate(config: ConfigTree) -> None: + if not config.exists(base_path): + # Nothing to do + return + + for service in config.list_nodes(base_path): + + service_path = base_path + [service] + + # Remove configurations using protocol 'googledomains' + if config.exists(service_path + ['protocol']): + protocol = config.return_value(service_path + ['protocol']) + if protocol == 'googledomains': + Warning(f'Removing {service} using protocol "googledomains" because the service has been shutdown') + config.delete(service_path) + + # Look for 'address web' with 'url' set to 'googledomains' or 'https://domains.google.com' + # and remove them so that ddclient defaults apply for 'address web' + if config.exists(service_path + ['address', 'web']): + web_addr_path = service_path + ['address', 'web'] + if config.exists(web_addr_path + ['url']): + url = config.return_value(web_addr_path + ['url']) + if url == 'googledomains' or re.search(r'^(https?://)?domains\.google\.com', url): + config.delete(web_addr_path + ['url']) + if config.exists(web_addr_path + ['skip']): + config.delete(web_addr_path + ['skip']) -- cgit v1.2.3 From b5389bed1f3ab3f46322988adad72e6e795b94bf Mon Sep 17 00:00:00 2001 From: Indrajit Raychaudhuri Date: Sun, 29 Dec 2024 18:41:53 -0600 Subject: ddclient: T6981: Remove non-global config properties In ddclient v4, 'use' is not a global scope property anymore. Besides 'use' had been deprecated in favor of 'usev4'/'usev6' in ddclient v3.10. --- data/templates/dns-dynamic/ddclient.conf.j2 | 2 -- 1 file changed, 2 deletions(-) (limited to 'data') diff --git a/data/templates/dns-dynamic/ddclient.conf.j2 b/data/templates/dns-dynamic/ddclient.conf.j2 index 5fc9582c7..3805b175f 100644 --- a/data/templates/dns-dynamic/ddclient.conf.j2 +++ b/data/templates/dns-dynamic/ddclient.conf.j2 @@ -21,8 +21,6 @@ if{{ ipv }}={{ address }}, \ {{ host }} {% endmacro %} ### Autogenerated by service_dns_dynamic.py ### -{# ddclient default (use=ip) results in confusing warning message in log #} -use=no {% if name is vyos_defined %} {% for service, config in name.items() %} -- cgit v1.2.3 From 282e1c4d27543bf7941b10685e633056dd04b2be Mon Sep 17 00:00:00 2001 From: Indrajit Raychaudhuri Date: Wed, 25 Dec 2024 22:42:27 -0600 Subject: ddclient: T6981: No need for special treatment for nsupdate Protocol 'nsupdate' (rfc2136) now supports dual-stack ('usev4', 'usev6' options), so there is no need for special treatment for 'nsupdate' in the config template. --- data/templates/dns-dynamic/ddclient.conf.j2 | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'data') diff --git a/data/templates/dns-dynamic/ddclient.conf.j2 b/data/templates/dns-dynamic/ddclient.conf.j2 index 3805b175f..98d1584a1 100644 --- a/data/templates/dns-dynamic/ddclient.conf.j2 +++ b/data/templates/dns-dynamic/ddclient.conf.j2 @@ -29,11 +29,9 @@ if{{ ipv }}={{ address }}, \ # {{ config.description }} {% endif %} {% for host in config.host_name if config.host_name is vyos_defined %} -{# ip_suffixes can be either of ['v4'], ['v6'], ['v4', 'v6'] for all protocols except 'nsupdate' - ip_suffixes must be [''] for nsupdate since it doesn't support usevX/wantipvX yet #} +{# ip_suffixes can be either of ['v4'], ['v6'], ['v4', 'v6'] for all protocols #} {% set ip_suffixes = ['v4', 'v6'] if config.ip_version == 'both' - else ([config.ip_version[2:]] if config.protocol != 'nsupdate' - else ['']) %} + else [config.ip_version[2:]] %} {% set password = config.key if config.protocol == 'nsupdate' else config.password %} {% set address = 'web' if config.address.web is vyos_defined -- cgit v1.2.3 From 65c8d1538d0fa68d4e7306aa152cdc39ba082b6b Mon Sep 17 00:00:00 2001 From: Indrajit Raychaudhuri Date: Mon, 30 Dec 2024 19:03:34 -0600 Subject: ddclient: T6981: Move nsupdate key mapping to Python Relocate key mapping for 'nsupdate' (rfc2136) from Jinja template to Python. This keeps the template simpler and allows for more complex dict handling in Python. --- data/templates/dns-dynamic/ddclient.conf.j2 | 10 ++++------ src/conf_mode/service_dns_dynamic.py | 7 +++++++ 2 files changed, 11 insertions(+), 6 deletions(-) (limited to 'data') diff --git a/data/templates/dns-dynamic/ddclient.conf.j2 b/data/templates/dns-dynamic/ddclient.conf.j2 index 98d1584a1..fd50a529d 100644 --- a/data/templates/dns-dynamic/ddclient.conf.j2 +++ b/data/templates/dns-dynamic/ddclient.conf.j2 @@ -29,19 +29,17 @@ if{{ ipv }}={{ address }}, \ # {{ config.description }} {% endif %} {% for host in config.host_name if config.host_name is vyos_defined %} -{# ip_suffixes can be either of ['v4'], ['v6'], ['v4', 'v6'] for all protocols #} -{% set ip_suffixes = ['v4', 'v6'] if config.ip_version == 'both' - else [config.ip_version[2:]] %} -{% set password = config.key if config.protocol == 'nsupdate' - else config.password %} {% set address = 'web' if config.address.web is vyos_defined else config.address.interface %} {% set web_options = config.address.web | default({}) %} +{# ip_suffixes can be either of ['v4'], ['v6'], ['v4', 'v6'] for all protocols #} +{% set ip_suffixes = ['v4', 'v6'] if config.ip_version == 'both' + else [config.ip_version[2:]] %} # Web service dynamic DNS configuration for {{ service }}: [{{ config.protocol }}, {{ host }}] {{ render_config(host, address, web_options, ip_suffixes, protocol=config.protocol, server=config.server, zone=config.zone, - login=config.username, password=password, ttl=config.ttl, + login=config.username, password=config.password, ttl=config.ttl, min_interval=config.wait_time, max_interval=config.expiry_time) }} {% endfor %} {% endfor %} diff --git a/src/conf_mode/service_dns_dynamic.py b/src/conf_mode/service_dns_dynamic.py index 08ee80a6b..682daa8a2 100755 --- a/src/conf_mode/service_dns_dynamic.py +++ b/src/conf_mode/service_dns_dynamic.py @@ -156,6 +156,13 @@ def generate(dyndns): if not dyndns or 'name' not in dyndns: return None + # Adjust protocol specific keys + for name in dyndns['name']: + # nsupdate (RFC2136) uses: + # - 'password' in ddclient.conf instead of 'key' in vyos conf + if dyndns['name'][name]['protocol'] == 'nsupdate': + dyndns['name'][name]['password'] = dyndns['name'][name].pop('key') + render(config_file, 'dns-dynamic/ddclient.conf.j2', dyndns, permission=0o600) render(systemd_override, 'dns-dynamic/override.conf.j2', dyndns) return None -- cgit v1.2.3 From 381869d06314bbe98c8f29f37548fd729cb58262 Mon Sep 17 00:00:00 2001 From: Indrajit Raychaudhuri Date: Mon, 30 Dec 2024 19:01:53 -0600 Subject: ddclient: T6981: Enable support for porkbun In ddclient, 'porkbun' protocol uses properties that are different from the ones used by VyOS configuration. Support for 'porkbun' is enabled by adding the necessary property remaping before applying them to the template to render the final ddclient config. --- data/templates/dns-dynamic/ddclient.conf.j2 | 11 +++++++---- interface-definitions/service_dns_dynamic.xml.in | 4 ++-- src/completion/list_ddclient_protocols.sh | 2 +- src/conf_mode/service_dns_dynamic.py | 16 +++++++++++++--- src/validators/ddclient-protocol | 2 +- 5 files changed, 24 insertions(+), 11 deletions(-) (limited to 'data') diff --git a/data/templates/dns-dynamic/ddclient.conf.j2 b/data/templates/dns-dynamic/ddclient.conf.j2 index fd50a529d..41aae729e 100644 --- a/data/templates/dns-dynamic/ddclient.conf.j2 +++ b/data/templates/dns-dynamic/ddclient.conf.j2 @@ -15,7 +15,7 @@ if{{ ipv }}={{ address }}, \ {% endfor %} {# Other service options with special treatment for password #} {% for k,v in kwargs.items() if v is vyos_defined %} -{{ k | replace('_', '-') }}={{ "'%s'" % (v) if k == 'password' else v }}{{ ',' if not loop.last }} \ +{{ k | replace('_', '-') }}={{ "'%s'" % (v) if k in ['password', 'secretapikey'] else v }}{{ ',' if not loop.last }} \ {% endfor %} {# Actual hostname for the service #} {{ host }} @@ -38,9 +38,12 @@ if{{ ipv }}={{ address }}, \ # Web service dynamic DNS configuration for {{ service }}: [{{ config.protocol }}, {{ host }}] {{ render_config(host, address, web_options, ip_suffixes, - protocol=config.protocol, server=config.server, zone=config.zone, - login=config.username, password=config.password, ttl=config.ttl, - min_interval=config.wait_time, max_interval=config.expiry_time) }} + protocol=config.protocol, server=config.server, + zone=config.zone, root_domain=config.root_domain, + login=config.username, apikey=config.apikey, + password=config.password, secretapikey=config.secretapikey, + ttl=config.ttl, min_interval=config.wait_time, + max_interval=config.expiry_time) }} {% endfor %} {% endfor %} {% endif %} diff --git a/interface-definitions/service_dns_dynamic.xml.in b/interface-definitions/service_dns_dynamic.xml.in index 99103ec73..e352f88e4 100644 --- a/interface-definitions/service_dns_dynamic.xml.in +++ b/interface-definitions/service_dns_dynamic.xml.in @@ -123,10 +123,10 @@ - DNS zone to be updated + DNS zone or root domain to be updated txt - Name of DNS zone + Name of DNS zone or root domain diff --git a/src/completion/list_ddclient_protocols.sh b/src/completion/list_ddclient_protocols.sh index 0e66b006a..d92239270 100755 --- a/src/completion/list_ddclient_protocols.sh +++ b/src/completion/list_ddclient_protocols.sh @@ -14,4 +14,4 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -echo -n $(ddclient --list-protocols | grep -vE 'cloudns|directnic|emailonly|porkbun') +echo -n $(ddclient --list-protocols | grep -vE 'cloudns|directnic|emailonly') diff --git a/src/conf_mode/service_dns_dynamic.py b/src/conf_mode/service_dns_dynamic.py index 167c67a6a..27b7e0690 100755 --- a/src/conf_mode/service_dns_dynamic.py +++ b/src/conf_mode/service_dns_dynamic.py @@ -35,7 +35,7 @@ systemd_override = r'/run/systemd/system/ddclient.service.d/override.conf' # Protocols that require zone zone_necessary = ['cloudflare', 'digitalocean', 'godaddy', 'hetzner', 'gandi', 'nfsn', 'nsupdate'] -zone_supported = zone_necessary + ['dnsexit2', 'zoneedit1'] +zone_supported = zone_necessary + ['dnsexit2', 'porkbun', 'zoneedit1'] # Protocols that do not require username username_unnecessary = ['1984', 'cloudflare', 'cloudns', 'ddns.fm', 'digitalocean', @@ -44,14 +44,14 @@ username_unnecessary = ['1984', 'cloudflare', 'cloudns', 'ddns.fm', 'digitalocea # Protocols that support TTL ttl_supported = ['cloudflare', 'dnsexit2', 'gandi', 'hetzner', 'godaddy', 'nfsn', - 'nsupdate'] + 'nsupdate', 'porkbun'] # Protocols that support both IPv4 and IPv6 dualstack_supported = ['cloudflare', 'ddns.fm', 'digitalocean', 'dnsexit2', 'domeneshop', 'duckdns', 'dyndns2', 'easydns', 'freedns', 'gandi', 'godaddy', 'he.net', 'hetzner', 'infomaniak', 'inwx', 'mythicdyn', 'njalla', 'noip', 'nsupdate', - 'regfishde'] + 'porkbun', 'regfishde'] # dyndns2 protocol in ddclient honors dual stack for selective servers # because of the way it is implemented in ddclient @@ -166,6 +166,16 @@ def generate(dyndns): if dyndns['name'][name]['protocol'] == 'nsupdate': dyndns['name'][name]['password'] = dyndns['name'][name].pop('key') + # porkbun uses: + # - 'root-domain' in ddclient.conf instead of 'zone' in vyos conf + # - 'apikey' in ddclient.conf instead of 'username' in vyos conf + # - 'secretapikey' in ddclient.conf instead of 'password' in vyos conf + if dyndns['name'][name]['protocol'] == 'porkbun': + dyndns['name'][name]['apikey'] = dyndns['name'][name].pop('username') + dyndns['name'][name]['secretapikey'] = dyndns['name'][name].pop('password') + if 'zone' in dyndns['name'][name]: + dyndns['name'][name]['root_domain'] = dyndns['name'][name].pop('zone') + render(config_file, 'dns-dynamic/ddclient.conf.j2', dyndns, permission=0o600) render(systemd_override, 'dns-dynamic/override.conf.j2', dyndns) return None diff --git a/src/validators/ddclient-protocol b/src/validators/ddclient-protocol index 217853939..bcb2ab8ed 100755 --- a/src/validators/ddclient-protocol +++ b/src/validators/ddclient-protocol @@ -14,7 +14,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -ddclient --list-protocols | grep -vE 'cloudns|directnic|emailonly|porkbun' | grep -qw $1 +ddclient --list-protocols | grep -vE 'cloudns|directnic|emailonly' | grep -qw $1 if [ $? -gt 0 ]; then echo "Error: $1 is not a valid protocol, please choose from the supported list of protocols" -- cgit v1.2.3 From 98f83eb047c994c30cc254ea7a415b1135765d8b Mon Sep 17 00:00:00 2001 From: Indrajit Raychaudhuri Date: Mon, 30 Dec 2024 19:18:23 -0600 Subject: ddclient: T6981: Enforce using Gandi personal access token Since the API key has been deprecated by Gandi, enforce using personal access token for 'gandi' protocol. --- data/templates/dns-dynamic/ddclient.conf.j2 | 1 + src/conf_mode/service_dns_dynamic.py | 4 ++++ 2 files changed, 5 insertions(+) (limited to 'data') diff --git a/data/templates/dns-dynamic/ddclient.conf.j2 b/data/templates/dns-dynamic/ddclient.conf.j2 index 41aae729e..ef7432433 100644 --- a/data/templates/dns-dynamic/ddclient.conf.j2 +++ b/data/templates/dns-dynamic/ddclient.conf.j2 @@ -42,6 +42,7 @@ if{{ ipv }}={{ address }}, \ zone=config.zone, root_domain=config.root_domain, login=config.username, apikey=config.apikey, password=config.password, secretapikey=config.secretapikey, + use_personal_access_token=config.use_personal_access_token, ttl=config.ttl, min_interval=config.wait_time, max_interval=config.expiry_time) }} {% endfor %} diff --git a/src/conf_mode/service_dns_dynamic.py b/src/conf_mode/service_dns_dynamic.py index 27b7e0690..92b067dcc 100755 --- a/src/conf_mode/service_dns_dynamic.py +++ b/src/conf_mode/service_dns_dynamic.py @@ -176,6 +176,10 @@ def generate(dyndns): if 'zone' in dyndns['name'][name]: dyndns['name'][name]['root_domain'] = dyndns['name'][name].pop('zone') + # Gandi API key is deprecated, enforce using personal access token + if dyndns['name'][name]['protocol'] == 'gandi': + dyndns['name'][name]['use_personal_access_token'] = 'yes' + render(config_file, 'dns-dynamic/ddclient.conf.j2', dyndns, permission=0o600) render(systemd_override, 'dns-dynamic/override.conf.j2', dyndns) return None -- cgit v1.2.3