Skip to content

Commit

Permalink
ddclient: T5708: Migration to 3.11.1 and related improvements
Browse files Browse the repository at this point in the history
- Migrate to ddclient 3.11.1 and enforce debian/control dependency
- Add dual stack support for additional protocols
- Restrict usage of `porkbun` protocol, VyOS configuration structure
  isn't compatible with porkbun yet
- Improve and cleanup error messages
  • Loading branch information
indrajitr committed Nov 5, 2023
1 parent 3f79724 commit 47c3db0
Show file tree
Hide file tree
Showing 5 changed files with 33 additions and 18 deletions.
2 changes: 1 addition & 1 deletion debian/control
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ Depends:
# For "set service aws glb"
aws-gwlbtun,
# For "service dns dynamic"
ddclient (>= 3.9.1),
ddclient (>= 3.11.1),
# End "service dns dynamic"
# # For "service ids"
fastnetmon [amd64],
Expand Down
2 changes: 1 addition & 1 deletion src/completion/list_ddclient_protocols.sh
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,4 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.

echo -n $(ddclient -list-protocols | grep -vE 'nsupdate|cloudns')
echo -n $(ddclient -list-protocols | grep -vE 'nsupdate|cloudns|porkbun')
34 changes: 19 additions & 15 deletions src/conf_mode/dns_dynamic.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,16 +30,21 @@
systemd_override = r'/run/systemd/system/ddclient.service.d/override.conf'

# Protocols that require zone
zone_necessary = ['cloudflare', 'godaddy', 'hetzner', 'gandi', 'nfsn']
zone_necessary = ['cloudflare', 'digitalocean', 'godaddy', 'hetzner', 'gandi', 'nfsn']
zone_supported = zone_necessary + ['dnsexit2', 'zoneedit1']

# Protocols that do not require username
username_unnecessary = ['1984', 'cloudflare', 'cloudns', 'duckdns', 'freemyip', 'hetzner', 'keysystems', 'njalla']
username_unnecessary = ['1984', 'cloudflare', 'cloudns', 'digitalocean', 'dnsexit2',
'duckdns', 'freemyip', 'hetzner', 'keysystems', 'njalla',
'regfishde']

# Protocols that support TTL
ttl_supported = ['cloudflare', 'gandi', 'hetzner', 'dnsexit', 'godaddy', 'nfsn']
ttl_supported = ['cloudflare', 'dnsexit2', 'gandi', 'hetzner', 'godaddy', 'nfsn']

# Protocols that support both IPv4 and IPv6
dualstack_supported = ['cloudflare', 'dyndns2', 'freedns', 'njalla']
dualstack_supported = ['cloudflare', 'digitalocean', 'dnsexit2', 'duckdns',
'dyndns2', 'easydns', 'freedns', 'hetzner', 'infomaniak',
'njalla']

# dyndns2 protocol in ddclient honors dual stack for selective servers
# because of the way it is implemented in ddclient
Expand Down Expand Up @@ -88,32 +93,31 @@ def verify(dyndns):
# Dynamic DNS service provider - configuration validation
if 'service' in dyndns['address'][address]:
for service, config in dyndns['address'][address]['service'].items():
error_msg = f'is required for Dynamic DNS service "{service}" on "{address}"'
error_msg_req = f'is required for Dynamic DNS service "{service}" on "{address}" with protocol "{config["protocol"]}"'
error_msg_uns = f'is not supported for Dynamic DNS service "{service}" on "{address}" with protocol "{config["protocol"]}"'

for field in ['host_name', 'password', 'protocol']:
if field not in config:
raise ConfigError(f'"{field.replace("_", "-")}" {error_msg}')
raise ConfigError(f'"{field.replace("_", "-")}" {error_msg_req}')

if config['protocol'] in zone_necessary and 'zone' not in config:
raise ConfigError(f'"zone" {error_msg}')
raise ConfigError(f'"zone" {error_msg_req}')

if config['protocol'] not in zone_necessary and 'zone' in config:
raise ConfigError(f'"{config["protocol"]}" does not support "zone"')
if config['protocol'] not in zone_supported and 'zone' in config:
raise ConfigError(f'"zone" {error_msg_uns}')

if config['protocol'] not in username_unnecessary and 'username' not in config:
raise ConfigError(f'"username" {error_msg}')
raise ConfigError(f'"username" {error_msg_req}')

if config['protocol'] not in ttl_supported and 'ttl' in config:
raise ConfigError(f'"{config["protocol"]}" does not support "ttl"')
raise ConfigError(f'"ttl" {error_msg_uns}')

if config['ip_version'] == 'both':
if config['protocol'] not in dualstack_supported:
raise ConfigError(f'"{config["protocol"]}" does not support '
f'both IPv4 and IPv6 at the same time')
raise ConfigError(f'Both IPv4 and IPv6 at the same time {error_msg_uns}')
# 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'"{config["protocol"]}" does not support '
f'both IPv4 and IPv6 at the same time for "{config["server"]}"')
raise ConfigError(f'Both IPv4 and IPv6 at the same time {error_msg_uns} for "{config["server"]}"')

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"')
Expand Down
11 changes: 11 additions & 0 deletions src/migration-scripts/dns-dynamic/1-to-2
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
# - migrate "service dns dynamic timeout ..."
# to "service dns dynamic interval ..."
# - remove "service dns dynamic address <interface> web-options ..." when <interface> != "web"
# - migrate "service dns dynamic address <interface> service <service> protocol dnsexit"
# to "service dns dynamic address <interface> service <service> protocol dnsexit2"

import sys
from vyos.configtree import ConfigTree
Expand Down Expand Up @@ -51,6 +53,15 @@ for address in config.list_nodes(address_path):
if address != 'web':
config.delete(address_path, ['web-options'])

# Migrate "service dns dynamic address <interface> service <service> protocol dnsexit"
# to "service dns dynamic address <interface> service <service> protocol dnsexit2"
for address in config.list_nodes(address_path):
for svc_cfg in config.list_nodes(address_path + [address, 'service']):
if config.exists(address_path + [address, 'service', svc_cfg, 'protocol']):
protocol = config.return_value(address_path + [address, 'service', svc_cfg, 'protocol'])
if protocol == 'dnsexit':
config.set(address_path + [address, 'service', svc_cfg, 'protocol'], 'dnsexit2')

try:
with open(file_name, 'w') as f:
f.write(config.to_string())
Expand Down
2 changes: 1 addition & 1 deletion src/validators/ddclient-protocol
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.

ddclient -list-protocols | grep -vE 'nsupdate|cloudns' | grep -qw $1
ddclient -list-protocols | grep -vE 'nsupdate|cloudns|porkbun' | grep -qw $1

if [ $? -gt 0 ]; then
echo "Error: $1 is not a valid protocol, please choose from the supported list of protocols"
Expand Down

0 comments on commit 47c3db0

Please sign in to comment.