diff --git a/data/templates/dns-dynamic/ddclient.conf.j2 b/data/templates/dns-dynamic/ddclient.conf.j2 index b209c8c810..d3f71968af 100644 --- a/data/templates/dns-dynamic/ddclient.conf.j2 +++ b/data/templates/dns-dynamic/ddclient.conf.j2 @@ -21,11 +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 #} -use=no {% if name is vyos_defined %} {% for service, config in name.items() %} @@ -34,13 +29,9 @@ use=no # {{ 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 ['']) %} -{% set password = config.key if config.protocol == 'nsupdate' - else config.password %} + else [config.ip_version[2:]] %} {% set address = 'web' if config.address.web is vyos_defined else config.address.interface %} {% set web_options = config.address.web | default({}) %} @@ -48,7 +39,7 @@ use=no # 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/interface-definitions/include/version/dns-dynamic-version.xml.i b/interface-definitions/include/version/dns-dynamic-version.xml.i index 346385ccbd..98123035d1 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/interface-definitions/service_dns_dynamic.xml.in b/interface-definitions/service_dns_dynamic.xml.in index 99103ec736..9ed0adfea1 100644 --- a/interface-definitions/service_dns_dynamic.xml.in +++ b/interface-definitions/service_dns_dynamic.xml.in @@ -123,7 +123,7 @@ - DNS zone to be updated + DNS zone or root domain to be updated txt Name of DNS zone diff --git a/smoketest/scripts/cli/test_service_dns_dynamic.py b/smoketest/scripts/cli/test_service_dns_dynamic.py index c68b831af0..79fe378014 100755 --- a/smoketest/scripts/cli/test_service_dns_dynamic.py +++ b/smoketest/scripts/cli/test_service_dns_dynamic.py @@ -40,6 +40,7 @@ ttl = '300' interface = 'eth0' + class TestServiceDDNS(VyOSUnitTestSHIM.TestCase): @classmethod def setUpClass(cls): @@ -64,9 +65,11 @@ def tearDown(self): # IPv4 standard DDNS service configuration def test_01_dyndns_service_standard(self): - services = {'cloudflare': {'protocol': 'cloudflare'}, - 'freedns': {'protocol': 'freedns', 'username': username}, - 'zoneedit': {'protocol': 'zoneedit1', 'username': username}} + services = { + 'cloudflare': {'protocol': 'cloudflare'}, + 'freedns': {'protocol': 'freedns', 'username': username}, + 'zoneedit': {'protocol': 'zoneedit1', 'username': username}, + } for svc, details in services.items(): self.cli_set(name_path + [svc, 'address', 'interface', interface]) @@ -102,7 +105,7 @@ def test_01_dyndns_service_standard(self): ddclient_conf = cmd(f'sudo cat {DDCLIENT_CONF}') self.assertIn(f'usev4=ifv4', ddclient_conf) self.assertIn(f'ifv4={interface}', ddclient_conf) - self.assertIn(f'password=\'{password}\'', ddclient_conf) + self.assertIn(f"password='{password}'", ddclient_conf) # Check default interval of 300 seconds systemd_override = read_file(DDCLIENT_SYSTEMD_UNIT) @@ -152,7 +155,7 @@ def test_02_dyndns_service_ipv6(self): self.assertIn(f'protocol={proto}', ddclient_conf) self.assertIn(f'server={server}', ddclient_conf) self.assertIn(f'login={username}', ddclient_conf) - self.assertIn(f'password=\'{password}\'', ddclient_conf) + self.assertIn(f"password='{password}'", ddclient_conf) self.assertIn(f'min-interval={wait_time}', ddclient_conf) self.assertIn(f'max-interval={expiry_time_good}', ddclient_conf) @@ -162,9 +165,11 @@ def test_02_dyndns_service_ipv6(self): # IPv4+IPv6 dual DDNS service configuration def test_03_dyndns_service_dual_stack(self): - services = {'cloudflare': {'protocol': 'cloudflare', 'zone': zone}, - 'freedns': {'protocol': 'freedns', 'username': username}, - 'google': {'protocol': 'googledomains', 'username': username}} + services = { + 'cloudflare': {'protocol': 'cloudflare', 'zone': zone}, + 'freedns': {'protocol': 'freedns', 'username': username}, + 'changeip': {'protocol': 'changeip', 'username': username}, + } ip_version = 'both' for name, details in services.items(): @@ -174,7 +179,7 @@ def test_03_dyndns_service_dual_stack(self): for opt, value in details.items(): self.cli_set(name_path + [name, opt, value]) - # Dual stack is supported by 'cloudflare' and 'freedns' but not 'googledomains' + # Dual stack is supported by 'cloudflare' and 'freedns' but not 'changeip' # exception is raised for unsupported ones self.cli_set(name_path + [name, 'ip-version', ip_version]) if details['protocol'] not in ['cloudflare', 'freedns']: @@ -189,13 +194,15 @@ def test_03_dyndns_service_dual_stack(self): ddclient_conf = cmd(f'sudo cat {DDCLIENT_CONF}') if details['protocol'] not in ['cloudflare', 'freedns']: self.assertIn(f'usev4=ifv4', ddclient_conf) + self.assertNotIn(f'usev6=ifv6', ddclient_conf) self.assertIn(f'ifv4={interface}', ddclient_conf) + self.assertNotIn(f'ifv6={interface}', ddclient_conf) else: self.assertIn(f'usev4=ifv4', ddclient_conf) self.assertIn(f'usev6=ifv6', ddclient_conf) self.assertIn(f'ifv4={interface}', ddclient_conf) self.assertIn(f'ifv6={interface}', ddclient_conf) - self.assertIn(f'password=\'{password}\'', ddclient_conf) + self.assertIn(f"password='{password}'", ddclient_conf) for opt in details.keys(): if opt == 'username': @@ -205,6 +212,9 @@ def test_03_dyndns_service_dual_stack(self): tmp = details[opt] self.assertIn(f'{opt}={tmp}', ddclient_conf) + # cleanup for next iteration + self.cli_delete(name_path + [name]) + def test_04_dyndns_rfc2136(self): # Check if DDNS service can be configured and runs svc_path = name_path + ['vyos'] @@ -220,18 +230,21 @@ def test_04_dyndns_rfc2136(self): self.cli_set(svc_path + ['key', key_file.name]) self.cli_set(svc_path + ['ttl', ttl]) self.cli_set(svc_path + ['host-name', hostname]) + self.cli_set(svc_path + ['ip-version', 'both']) # commit changes self.cli_commit() # Check some generating config parameters ddclient_conf = cmd(f'sudo cat {DDCLIENT_CONF}') - self.assertIn(f'use=if', ddclient_conf) - self.assertIn(f'if={interface}', ddclient_conf) + self.assertIn(f'usev4=ifv4', ddclient_conf) + self.assertIn(f'usev6=ifv6', ddclient_conf) + self.assertIn(f'ifv4={interface}', ddclient_conf) + self.assertIn(f'ifv6={interface}', ddclient_conf) self.assertIn(f'protocol={proto}', ddclient_conf) self.assertIn(f'server={server}', ddclient_conf) self.assertIn(f'zone={zone}', ddclient_conf) - self.assertIn(f'password=\'{key_file.name}\'', ddclient_conf) + self.assertIn(f"password='{key_file.name}'", ddclient_conf) self.assertIn(f'ttl={ttl}', ddclient_conf) def test_05_dyndns_hostname(self): @@ -256,7 +269,7 @@ def test_05_dyndns_hostname(self): self.assertIn(f'protocol={proto}', ddclient_conf) self.assertIn(f'server={server}', ddclient_conf) self.assertIn(f'login={username}', ddclient_conf) - self.assertIn(f'password=\'{password}\'', ddclient_conf) + self.assertIn(f"password='{password}'", ddclient_conf) self.assertIn(f'{name}', ddclient_conf) def test_06_dyndns_web_options(self): @@ -294,10 +307,10 @@ def test_06_dyndns_web_options(self): ddclient_conf = cmd(f'sudo cat {DDCLIENT_CONF}') self.assertIn(f'usev4=webv4', ddclient_conf) self.assertIn(f'webv4={web_url}', ddclient_conf) - self.assertIn(f'webv4-skip=\'{web_skip}\'', ddclient_conf) + self.assertIn(f"webv4-skip='{web_skip}'", ddclient_conf) self.assertIn(f'protocol={proto}', ddclient_conf) self.assertIn(f'zone={zone}', ddclient_conf) - self.assertIn(f'password=\'{password}\'', ddclient_conf) + self.assertIn(f"password='{password}'", ddclient_conf) self.assertIn(f'{hostname}', ddclient_conf) def test_07_dyndns_dynamic_interface(self): @@ -326,7 +339,7 @@ def test_07_dyndns_dynamic_interface(self): self.assertIn(f'protocol={proto}', ddclient_conf) self.assertIn(f'server={server}', ddclient_conf) self.assertIn(f'login={username}', ddclient_conf) - self.assertIn(f'password=\'{password}\'', ddclient_conf) + self.assertIn(f"password='{password}'", ddclient_conf) self.assertIn(f'{hostname}', ddclient_conf) def test_08_dyndns_vrf(self): @@ -350,9 +363,12 @@ def test_08_dyndns_vrf(self): # Check for process in VRF systemd_override = read_file(DDCLIENT_SYSTEMD_UNIT) - self.assertIn(f'ExecStart=ip vrf exec {vrf_name} /usr/bin/ddclient ' \ - f'--file {DDCLIENT_CONF} --cache {DDCLIENT_CONF.replace("conf", "cache")} ' \ - f'--foreground --daemon {default_interval}', systemd_override) + self.assertIn( + f'ExecStart=ip vrf exec {vrf_name} /usr/bin/ddclient ' + f'--file {DDCLIENT_CONF} --cache {DDCLIENT_CONF.replace("conf", "cache")} ' + f'--foreground --daemon {default_interval}', + systemd_override, + ) # Check for process in VRF proc = cmd(f'ip vrf pids {vrf_name}') @@ -361,5 +377,6 @@ def test_08_dyndns_vrf(self): # Cleanup VRF self.cli_delete(['vrf', 'name', vrf_name]) + if __name__ == '__main__': unittest.main(verbosity=2, failfast=VyOSUnitTestSHIM.TestCase.debug_on()) diff --git a/src/completion/list_ddclient_protocols.sh b/src/completion/list_ddclient_protocols.sh index 0c8c2712de..d92239270a 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|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 b321d5f51b..53561e2469 100755 --- a/src/conf_mode/service_dns_dynamic.py +++ b/src/conf_mode/service_dns_dynamic.py @@ -27,33 +27,89 @@ from vyos.utils.network import interface_exists from vyos import ConfigError from vyos import airbag + airbag.enable() config_file = r'/run/ddclient/ddclient.conf' 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_necessary = [ + 'cloudflare', + 'digitalocean', + 'godaddy', + 'hetzner', + 'gandi', + 'nfsn', + 'nsupdate', +] +zone_supported = zone_necessary + ['dnsexit2', 'porkbun', 'zoneedit1'] # Protocols that do not require username -username_unnecessary = ['1984', 'cloudflare', 'cloudns', 'digitalocean', 'dnsexit2', - 'duckdns', 'freemyip', 'hetzner', 'keysystems', 'njalla', - 'nsupdate', 'regfishde'] +username_unnecessary = [ + '1984', + 'cloudflare', + 'cloudns', + 'ddns.fm', + 'digitalocean', + 'dnsexit2', + 'duckdns', + 'freemyip', + 'gandi', + 'he.net', + 'hetzner', + 'keysystems', + 'njalla', + 'nsupdate', + 'regfishde', +] # Protocols that support TTL -ttl_supported = ['cloudflare', 'dnsexit2', 'gandi', 'hetzner', 'godaddy', 'nfsn', - 'nsupdate'] +ttl_supported = [ + 'cloudflare', + 'dnsexit2', + 'gandi', + 'hetzner', + 'godaddy', + 'nfsn', + 'nsupdate', + 'porkbun', +] # Protocols that support both IPv4 and IPv6 -dualstack_supported = ['cloudflare', 'digitalocean', 'dnsexit2', 'duckdns', - 'dyndns2', 'easydns', 'freedns', 'hetzner', 'infomaniak', - 'njalla'] +dualstack_supported = [ + 'cloudflare', + 'ddns.fm', + 'digitalocean', + 'dnsexit2', + 'domeneshop', + 'duckdns', + 'dyndns2', + 'easydns', + 'freedns', + 'gandi', + 'godaddy', + 'he.net', + 'hetzner', + 'infomaniak', + 'inwx', + 'mythicdyn', + 'njalla', + 'noip', + 'nsupdate', + 'porkbun', + 'regfishde', +] # dyndns2 protocol in ddclient honors dual stack for selective servers # because of the way it is implemented in ddclient -dyndns_dualstack_servers = ['members.dyndns.org', 'dynv6.com'] +dyndns_dualstack_servers = [ + 'app.luadns.com', + 'dynv6.com', + 'members.dyndns.org', + 'update.dedyn.io', +] + def get_config(config=None): if config: @@ -65,14 +121,18 @@ def get_config(config=None): if not conf.exists(base): return None - dyndns = conf.get_config_dict(base, key_mangling=('-', '_'), - no_tag_node_value_mangle=True, - get_first_key=True, - with_recursive_defaults=True) + dyndns = conf.get_config_dict( + base, + key_mangling=('-', '_'), + no_tag_node_value_mangle=True, + get_first_key=True, + with_recursive_defaults=True, + ) dyndns['config_file'] = config_file return dyndns + def verify(dyndns): # bail out early - looks like removal from running config if not dyndns or 'name' not in dyndns: @@ -88,11 +148,15 @@ def verify(dyndns): raise ConfigError(f'"{field.replace("_", "-")}" {error_msg_req}') if not any(x in config['address'] for x in ['interface', 'web']): - raise ConfigError(f'Either "interface" or "web" {error_msg_req} ' - f'with protocol "{config["protocol"]}"') + raise ConfigError( + f'Either "interface" or "web" {error_msg_req} ' + f'with protocol "{config["protocol"]}"' + ) if all(x in config['address'] for x in ['interface', 'web']): - raise ConfigError(f'Both "interface" and "web" at the same time {error_msg_uns} ' - f'with protocol "{config["protocol"]}"') + raise ConfigError( + f'Both "interface" and "web" at the same time {error_msg_uns} ' + f'with protocol "{config["protocol"]}"' + ) # If dyndns address is an interface, ensure that the interface exists # and warn if a non-active dynamic interface is used @@ -101,22 +165,23 @@ def verify(dyndns): # exclude check interface for dynamic interfaces if tmp.match(config['address']['interface']): if not interface_exists(config['address']['interface']): - Warning(f'Interface "{config["address"]["interface"]}" does not exist yet and ' - f'cannot be used for Dynamic DNS service "{service}" until it is up!') + Warning( + f'Interface "{config["address"]["interface"]}" does not exist yet and ' + f'cannot be used for Dynamic DNS service "{service}" until it is up!' + ) else: verify_interface_exists(dyndns, config['address']['interface']) if 'web' in config['address']: # If 'skip' is specified, 'url' is required as well - if 'skip' in config['address']['web'] and 'url' not in config['address']['web']: - raise ConfigError(f'"url" along with "skip" {error_msg_req} ' - f'with protocol "{config["protocol"]}"') - if 'url' in config['address']['web']: - # Warn if using checkip.dyndns.org, as it does not support HTTPS - # See: https://github.com/ddclient/ddclient/issues/597 - if re.search("^(https?://)?checkip\.dyndns\.org", config['address']['web']['url']): - Warning(f'"checkip.dyndns.org" does not support HTTPS requests for IP address ' - f'lookup. Please use a different IP address lookup service.') + if ( + 'skip' in config['address']['web'] + and 'url' not in config['address']['web'] + ): + raise ConfigError( + f'"url" along with "skip" {error_msg_req} ' + f'with protocol "{config["protocol"]}"' + ) # RFC2136 uses 'key' instead of 'password' if config['protocol'] != 'nsupdate' and 'password' not in config: @@ -125,47 +190,94 @@ def verify(dyndns): # Other RFC2136 specific configuration validation if config['protocol'] == 'nsupdate': if 'password' in config: - raise ConfigError(f'"password" {error_msg_uns} with protocol "{config["protocol"]}"') + raise ConfigError( + f'"password" {error_msg_uns} with protocol "{config["protocol"]}"' + ) for field in ['server', 'key']: if field not in config: - raise ConfigError(f'"{field}" {error_msg_req} with protocol "{config["protocol"]}"') + raise ConfigError( + f'"{field}" {error_msg_req} with protocol "{config["protocol"]}"' + ) if config['protocol'] in zone_necessary and 'zone' not in config: - raise ConfigError(f'"zone" {error_msg_req} with protocol "{config["protocol"]}"') + raise ConfigError( + f'"zone" {error_msg_req} with protocol "{config["protocol"]}"' + ) if config['protocol'] not in zone_supported and 'zone' in config: - raise ConfigError(f'"zone" {error_msg_uns} with protocol "{config["protocol"]}"') + raise ConfigError( + f'"zone" {error_msg_uns} with protocol "{config["protocol"]}"' + ) if config['protocol'] not in username_unnecessary and 'username' not in config: - raise ConfigError(f'"username" {error_msg_req} with protocol "{config["protocol"]}"') + raise ConfigError( + f'"username" {error_msg_req} with protocol "{config["protocol"]}"' + ) if config['protocol'] not in ttl_supported and 'ttl' in config: - raise ConfigError(f'"ttl" {error_msg_uns} with protocol "{config["protocol"]}"') + raise ConfigError( + f'"ttl" {error_msg_uns} with protocol "{config["protocol"]}"' + ) if config['ip_version'] == 'both': if config['protocol'] not in dualstack_supported: - raise ConfigError(f'Both IPv4 and IPv6 at the same time {error_msg_uns} ' - f'with protocol "{config["protocol"]}"') + raise ConfigError( + f'Both IPv4 and IPv6 at the same time {error_msg_uns} ' + f'with protocol "{config["protocol"]}"' + ) # dyndns2 protocol in ddclient honors dual stack only for dyn.com (dyndns.org) - if config['protocol'] == 'dyndns2' and 'server' in config and config['server'] not in dyndns_dualstack_servers: - raise ConfigError(f'Both IPv4 and IPv6 at the same time {error_msg_uns} ' - f'for "{config["server"]}" with protocol "{config["protocol"]}"') - - if {'wait_time', 'expiry_time'} <= config.keys() and int(config['expiry_time']) < int(config['wait_time']): - raise ConfigError(f'"expiry-time" must be greater than "wait-time" for ' - f'Dynamic DNS service "{service}"') + if ( + config['protocol'] == 'dyndns2' + and 'server' in config + and config['server'] not in dyndns_dualstack_servers + ): + raise ConfigError( + f'Both IPv4 and IPv6 at the same time {error_msg_uns} ' + f'for "{config["server"]}" with protocol "{config["protocol"]}"' + ) + + if {'wait_time', 'expiry_time'} <= config.keys() and int( + config['expiry_time'] + ) < int(config['wait_time']): + raise ConfigError( + f'"expiry-time" must be greater than "wait-time" for ' + f'Dynamic DNS service "{service}"' + ) return None + def generate(dyndns): # bail out early - looks like removal from running config 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') + + # 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') + + # 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 + def apply(dyndns): systemd_service = 'ddclient.service' # Reload systemd manager configuration @@ -181,6 +293,7 @@ def apply(dyndns): return None + if __name__ == '__main__': try: c = get_config() 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 0000000000..3873c00afe --- /dev/null +++ b/src/migration-scripts/dns-dynamic/4-to-5 @@ -0,0 +1,53 @@ +# 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 protocol woima" +# - 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' and 'woima' + if config.exists(service_path + ['protocol']): + protocol = config.return_value(service_path + ['protocol']) + if protocol in ['googledomains', 'woima']: + Warning(f'Removing {service} using protocol "{protocol}" 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']) diff --git a/src/op_mode/dns.py b/src/op_mode/dns.py index 7c9f769f1f..da064e22f7 100755 --- a/src/op_mode/dns.py +++ b/src/op_mode/dns.py @@ -24,7 +24,6 @@ from tabulate import tabulate from vyos.configquery import ConfigTreeQuery from vyos.utils.process import cmd, rc_cmd -from vyos.template import is_ipv4, is_ipv6 _dynamic_cache_file = r'/run/ddclient/ddclient.cache' @@ -84,20 +83,9 @@ def _get_dynamic_host_records_raw() -> dict: # we pick up the ones we are interested in for kvraw in line.split(' ')[0].split(','): k, v = kvraw.split('=') - if k in list(_dynamic_status_columns.keys()) + ['ip', 'status']: # ip and status are legacy keys + if k in list(_dynamic_status_columns.keys()): props[k] = v - # Extract IPv4 and IPv6 address and status from legacy keys - # Dual-stack isn't supported in legacy format, 'ip' and 'status' are for one of IPv4 or IPv6 - if 'ip' in props: - if is_ipv4(props['ip']): - props['ipv4'] = props['ip'] - props['status-ipv4'] = props['status'] - elif is_ipv6(props['ip']): - props['ipv6'] = props['ip'] - props['status-ipv6'] = props['status'] - del props['ip'] - # Convert mtime to human readable format if 'mtime' in props: props['mtime'] = time.strftime( diff --git a/src/validators/ddclient-protocol b/src/validators/ddclient-protocol index 0d28039d3d..bcb2ab8ed2 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|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"