Page Menu
Home
VyOS Platform
Search
Configure Global Search
Log In
Files
F118306349
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Size
74 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/data/templates/dns-dynamic/ddclient.conf.j2 b/data/templates/dns-dynamic/ddclient.conf.j2
index 421daf1df..5905b19ea 100644
--- a/data/templates/dns-dynamic/ddclient.conf.j2
+++ b/data/templates/dns-dynamic/ddclient.conf.j2
@@ -1,72 +1,75 @@
{% macro render_config(host, address, web_options, ip_suffixes=['']) %}
{# Address: use=if, if=ethX, usev6=ifv6, ifv6=ethX, usev6=webv6, webv6=https://v6.example.com #}
{% for ipv in ip_suffixes %}
use{{ ipv }}={{ address if address == 'web' else 'if' }}{{ ipv }}, \
{% if address == 'web' %}
{% if web_options.url is vyos_defined %}
web{{ ipv }}={{ web_options.url }}, \
{% endif %}
{% if web_options.skip is vyos_defined %}
web-skip{{ ipv }}='{{ web_options.skip }}', \
{% endif %}
{% else %}
if{{ ipv }}={{ address }}, \
{% endif %}
{% endfor %}
{# Other service options #}
{% for k,v in kwargs.items() %}
{% if v is vyos_defined %}
{{ k }}={{ v }}{{ ',' if not loop.last }} \
{% endif %}
{% endfor %}
{# Actual hostname for the service #}
{{ host }}
{% endmacro %}
### Autogenerated by dns_dynamic.py ###
daemon={{ timeout }}
syslog=yes
ssl=yes
pid={{ config_file | replace('.conf', '.pid') }}
cache={{ config_file | replace('.conf', '.cache') }}
-{# Explicitly override global options for reliability #}
-web=googledomains {# ddclient default ('dyndns') doesn't support ssl and results in process lockup #}
-use=no {# ddclient default ('ip') results in confusing warning message in log #}
+{# 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 address is vyos_defined %}
{% for address, service_cfg in address.items() %}
{% if service_cfg.rfc2136 is vyos_defined %}
{% for name, config in service_cfg.rfc2136.items() %}
{% if config.description is vyos_defined %}
-# {{ config.description }}
+# {{ config.description }}
{% endif %}
{% for host in config.host_name if config.host_name is vyos_defined %}
+
# RFC2136 dynamic DNS configuration for {{ name }}: [{{ config.zone }}, {{ host }}]
{# Don't append 'new-style' compliant suffix ('usev4', 'usev6', 'ifv4', 'ifv6' etc.)
to the properties since 'nsupdate' doesn't support that yet. #}
{{ render_config(host, address, service_cfg.web_options,
protocol='nsupdate', server=config.server, zone=config.zone,
password=config.key, ttl=config.ttl) }}
{% endfor %}
{% endfor %}
{% endif %}
{% if service_cfg.service is vyos_defined %}
{% for name, config in service_cfg.service.items() %}
{% if config.description is vyos_defined %}
-# {{ config.description }}
+# {{ config.description }}
{% endif %}
{% for host in config.host_name if config.host_name is vyos_defined %}
{% set ip_suffixes = ['v4', 'v6'] if config.ip_version == 'both'
- else [config.ip_version[2:]] %} {# 'ipvX' -> 'vX' #}
+ else [config.ip_version[2:]] %}
+
# Web service dynamic DNS configuration for {{ name }}: [{{ config.protocol }}, {{ host }}]
{{ render_config(host, address, service_cfg.web_options, ip_suffixes,
protocol=config.protocol, server=config.server, zone=config.zone,
- login=config.username, password=config.password) }}
+ login=config.username, password=config.password, ttl=config.ttl) }}
{% endfor %}
{% endfor %}
{% endif %}
{% endfor %}
{% endif %}
diff --git a/data/templates/dns-dynamic/override.conf.j2 b/data/templates/dns-dynamic/override.conf.j2
index 6ca1b8a45..4a6851cef 100644
--- a/data/templates/dns-dynamic/override.conf.j2
+++ b/data/templates/dns-dynamic/override.conf.j2
@@ -1,10 +1,10 @@
{% set vrf_command = 'ip vrf exec ' ~ vrf ~ ' ' if vrf is vyos_defined else '' %}
[Unit]
ConditionPathExists={{ config_file }}
After=vyos-router.service
[Service]
PIDFile={{ config_file | replace('.conf', '.pid') }}
EnvironmentFile=
ExecStart=
-ExecStart=/usr/bin/ddclient -file {{ config_file }}
+ExecStart={{ vrf_command }}/usr/bin/ddclient -file {{ config_file }}
diff --git a/interface-definitions/dns-dynamic.xml.in b/interface-definitions/dns-dynamic.xml.in
index a0720f3aa..ba7f426c1 100644
--- a/interface-definitions/dns-dynamic.xml.in
+++ b/interface-definitions/dns-dynamic.xml.in
@@ -1,173 +1,156 @@
<?xml version="1.0"?>
<interfaceDefinition>
<node name="service">
<children>
<node name="dns">
<properties>
<help>Domain Name System (DNS) related services</help>
</properties>
<children>
<node name="dynamic" owner="${vyos_conf_scripts_dir}/dns_dynamic.py">
<properties>
<help>Dynamic DNS</help>
</properties>
<children>
<tagNode name="address">
<properties>
<help>Obtain IP address to send Dynamic DNS update for</help>
<valueHelp>
<format>txt</format>
<description>Use interface to obtain the IP address</description>
</valueHelp>
<valueHelp>
<format>web</format>
<description>Use HTTP(S) web request to obtain the IP address</description>
</valueHelp>
<completionHelp>
<script>${vyos_completion_dir}/list_interfaces</script>
<list>web</list>
</completionHelp>
<constraint>
#include <include/constraint/interface-name.xml.i>
<regex>web</regex>
</constraint>
</properties>
<children>
<node name="web-options">
<properties>
<help>Options when using HTTP(S) web request to obtain the IP address</help>
</properties>
<children>
#include <include/url.xml.i>
<leafNode name="skip">
<properties>
<help>Pattern to skip from the HTTP(S) respose</help>
<valueHelp>
<format>txt</format>
<description>Pattern to skip from the HTTP(S) respose to extract the external IP address</description>
</valueHelp>
</properties>
</leafNode>
</children>
</node>
<tagNode name="rfc2136">
<properties>
<help>RFC2136 nsupdate configuration</help>
<valueHelp>
<format>txt</format>
<description>RFC2136 nsupdate service name</description>
</valueHelp>
</properties>
<children>
#include <include/generic-description.xml.i>
#include <include/dns/dynamic-service-host-name-server.xml.i>
<leafNode name="key">
<properties>
<help>File containing the TSIG secret key shared with remote DNS server</help>
<valueHelp>
<format>filename</format>
<description>File in /config/auth directory</description>
</valueHelp>
<constraint>
<validator name="file-path" argument="--strict --parent-dir /config/auth"/>
</constraint>
</properties>
</leafNode>
#include <include/dns/time-to-live.xml.i>
- <leafNode name="zone">
- <properties>
- <help>Forwarding zone to be updated</help>
- <valueHelp>
- <format>txt</format>
- <description>RFC2136 Zone to be updated</description>
- </valueHelp>
- <constraint>
- <validator name="fqdn"/>
- </constraint>
- </properties>
- </leafNode>
+ #include <include/dns/dynamic-service-zone.xml.i>
</children>
</tagNode>
<tagNode name="service">
<properties>
<help>Dynamic DNS configuration</help>
<valueHelp>
<format>txt</format>
<description>Dynamic DNS service name</description>
</valueHelp>
</properties>
<children>
#include <include/generic-description.xml.i>
#include <include/dns/dynamic-service-host-name-server.xml.i>
#include <include/generic-username.xml.i>
#include <include/generic-password.xml.i>
+ #include <include/dns/time-to-live.xml.i>
<leafNode name="protocol">
<properties>
<help>ddclient protocol used for Dynamic DNS service</help>
<completionHelp>
<script>${vyos_completion_dir}/list_ddclient_protocols.sh</script>
</completionHelp>
<constraint>
<validator name="ddclient-protocol"/>
</constraint>
</properties>
</leafNode>
- <leafNode name="zone">
- <properties>
- <help>DNS zone to update (not used by all protocols)</help>
- <valueHelp>
- <format>txt</format>
- <description>Name of DNS zone</description>
- </valueHelp>
- </properties>
- </leafNode>
+ #include <include/dns/dynamic-service-zone.xml.i>
<leafNode name="ip-version">
<properties>
<help>IP address version to use</help>
<valueHelp>
<format>_ipv4</format>
<description>Use only IPv4 address</description>
</valueHelp>
<valueHelp>
<format>_ipv6</format>
<description>Use only IPv6 address</description>
</valueHelp>
<valueHelp>
<format>both</format>
<description>Use both IPv4 and IPv6 address</description>
</valueHelp>
<completionHelp>
<list>ipv4 ipv6 both</list>
</completionHelp>
<constraint>
<regex>(ipv[46]|both)</regex>
</constraint>
<constraintErrorMessage>IP Version must be literal 'ipv4', 'ipv6' or 'both'</constraintErrorMessage>
</properties>
<defaultValue>ipv4</defaultValue>
</leafNode>
</children>
</tagNode>
</children>
</tagNode>
<leafNode name="timeout">
<properties>
<help>Time in seconds to wait between DNS updates</help>
<valueHelp>
<format>u32:60-3600</format>
<description>Time in seconds</description>
</valueHelp>
<constraint>
<validator name="numeric" argument="--range 60-3600"/>
</constraint>
<constraintErrorMessage>Timeout must be between 60 and 3600 seconds</constraintErrorMessage>
</properties>
<defaultValue>300</defaultValue>
</leafNode>
+ #include <include/interface/vrf.xml.i>
</children>
</node>
</children>
</node>
</children>
</node>
</interfaceDefinition>
diff --git a/interface-definitions/dns-forwarding.xml.in b/interface-definitions/dns-forwarding.xml.in
index 86dc47a47..c4295317a 100644
--- a/interface-definitions/dns-forwarding.xml.in
+++ b/interface-definitions/dns-forwarding.xml.in
@@ -1,690 +1,720 @@
<?xml version="1.0"?>
<!-- DNS forwarder configuration -->
<interfaceDefinition>
<node name="service">
<children>
<node name="dns">
<properties>
<help>Domain Name System (DNS) related services</help>
</properties>
<children>
<node name="forwarding" owner="${vyos_conf_scripts_dir}/dns_forwarding.py">
<properties>
<help>DNS forwarding</help>
<priority>918</priority>
</properties>
<children>
<leafNode name="cache-size">
<properties>
<help>DNS forwarding cache size</help>
<valueHelp>
<format>u32:0-2147483647</format>
<description>DNS forwarding cache size</description>
</valueHelp>
<constraint>
<validator name="numeric" argument="--range 0-2147483647"/>
</constraint>
</properties>
<defaultValue>10000</defaultValue>
</leafNode>
<leafNode name="dhcp">
<properties>
<help>Interfaces whose DHCP client nameservers to forward requests to</help>
<completionHelp>
<script>${vyos_completion_dir}/list_interfaces</script>
</completionHelp>
<multi/>
</properties>
</leafNode>
<leafNode name="dns64-prefix">
<properties>
<help>Help to communicate between IPv6-only client and IPv4-only server</help>
<valueHelp>
<format>ipv6net</format>
<description>IPv6 address and /96 only prefix length</description>
</valueHelp>
<constraint>
<validator name="ipv6-prefix"/>
</constraint>
</properties>
</leafNode>
<leafNode name="dnssec">
<properties>
<help>DNSSEC mode</help>
<completionHelp>
<list>off process-no-validate process log-fail validate</list>
</completionHelp>
<valueHelp>
<format>off</format>
<description>No DNSSEC processing whatsoever!</description>
</valueHelp>
<valueHelp>
<format>process-no-validate</format>
<description>Respond with DNSSEC records to clients that ask for it. No validation done at all!</description>
</valueHelp>
<valueHelp>
<format>process</format>
<description>Respond with DNSSEC records to clients that ask for it. Validation for clients that request it.</description>
</valueHelp>
<valueHelp>
<format>log-fail</format>
<description>Similar behaviour to process, but validate RRSIGs on responses and log bogus responses.</description>
</valueHelp>
<valueHelp>
<format>validate</format>
<description>Full blown DNSSEC validation. Send SERVFAIL to clients on bogus responses.</description>
</valueHelp>
<constraint>
<regex>(off|process-no-validate|process|log-fail|validate)</regex>
</constraint>
</properties>
<defaultValue>process-no-validate</defaultValue>
</leafNode>
<tagNode name="domain">
<properties>
<help>Domain to forward to a custom DNS server</help>
<valueHelp>
<format>txt</format>
<description>An absolute DNS domain name</description>
</valueHelp>
<constraint>
<validator name="fqdn"/>
</constraint>
</properties>
<children>
#include <include/name-server-ipv4-ipv6-port.xml.i>
<leafNode name="addnta">
<properties>
<help>Add NTA (negative trust anchor) for this domain (must be set if the domain does not support DNSSEC)</help>
<valueless/>
</properties>
</leafNode>
<leafNode name="recursion-desired">
<properties>
<help>Set the "recursion desired" bit in requests to the upstream nameserver</help>
<valueless/>
</properties>
</leafNode>
</children>
</tagNode>
<tagNode name="authoritative-domain">
<properties>
<help>Domain to host authoritative records for</help>
<valueHelp>
<format>txt</format>
<description>An absolute DNS domain name</description>
</valueHelp>
<constraint>
<validator name="fqdn"/>
</constraint>
</properties>
<children>
<node name="records">
<properties>
<help>DNS zone records</help>
</properties>
<children>
<tagNode name="a">
<properties>
<help>A record</help>
<valueHelp>
<format>txt</format>
<description>A DNS name relative to the root record</description>
</valueHelp>
<valueHelp>
<format>@</format>
<description>Root record</description>
</valueHelp>
<valueHelp>
<format>any</format>
<description>Wildcard record (any subdomain)</description>
</valueHelp>
<constraint>
<regex>([-_a-zA-Z0-9.]{1,63}|@|any)(?<!\.)</regex>
</constraint>
</properties>
<children>
<leafNode name="address">
<properties>
<help>IPv4 address</help>
<valueHelp>
<format>ipv4</format>
<description>IPv4 address</description>
</valueHelp>
<multi/>
<constraint>
<validator name="ipv4-address"/>
</constraint>
</properties>
</leafNode>
#include <include/dns/time-to-live.xml.i>
+ <leafNode name="ttl">
+ <defaultValue>300</defaultValue>
+ </leafNode>
#include <include/generic-disable-node.xml.i>
</children>
</tagNode>
<tagNode name="aaaa">
<properties>
<help>AAAA record</help>
<valueHelp>
<format>txt</format>
<description>A DNS name relative to the root record</description>
</valueHelp>
<valueHelp>
<format>@</format>
<description>Root record</description>
</valueHelp>
<valueHelp>
<format>any</format>
<description>Wildcard record (any subdomain)</description>
</valueHelp>
<constraint>
<regex>([-_a-zA-Z0-9.]{1,63}|@|any)(?<!\.)</regex>
</constraint>
</properties>
<children>
<leafNode name="address">
<properties>
<help>IPv6 address</help>
<valueHelp>
<format>ipv6</format>
<description>IPv6 address</description>
</valueHelp>
<multi/>
<constraint>
<validator name="ipv6-address"/>
</constraint>
</properties>
</leafNode>
#include <include/dns/time-to-live.xml.i>
+ <leafNode name="ttl">
+ <defaultValue>300</defaultValue>
+ </leafNode>
#include <include/generic-disable-node.xml.i>
</children>
</tagNode>
<tagNode name="cname">
<properties>
<help>CNAME record</help>
<valueHelp>
<format>txt</format>
<description>A DNS name relative to the root record</description>
</valueHelp>
<valueHelp>
<format>@</format>
<description>Root record</description>
</valueHelp>
<constraint>
<regex>([-_a-zA-Z0-9.]{1,63}|@)(?<!\.)</regex>
</constraint>
</properties>
<children>
<leafNode name="target">
<properties>
<help>Target DNS name</help>
<valueHelp>
<format>name.example.com</format>
<description>Absolute DNS name</description>
</valueHelp>
<constraint>
<regex>[-_a-zA-Z0-9.]{1,63}(?<!\.)</regex>
</constraint>
</properties>
</leafNode>
#include <include/dns/time-to-live.xml.i>
+ <leafNode name="ttl">
+ <defaultValue>300</defaultValue>
+ </leafNode>
#include <include/generic-disable-node.xml.i>
</children>
</tagNode>
<tagNode name="mx">
<properties>
<help>MX record</help>
<valueHelp>
<format>txt</format>
<description>A DNS name relative to the root record</description>
</valueHelp>
<valueHelp>
<format>@</format>
<description>Root record</description>
</valueHelp>
<constraint>
<regex>([-_a-zA-Z0-9.]{1,63}|@)(?<!\.)</regex>
</constraint>
</properties>
<children>
<tagNode name="server">
<properties>
<help>Mail server</help>
<valueHelp>
<format>name.example.com</format>
<description>Absolute DNS name</description>
</valueHelp>
<constraint>
<regex>[-_a-zA-Z0-9.]{1,63}(?<!\.)</regex>
</constraint>
</properties>
<children>
<leafNode name="priority">
<properties>
<help>Server priority</help>
<valueHelp>
<format>u32:1-999</format>
<description>Server priority (lower numbers are higher priority)</description>
</valueHelp>
<constraint>
<validator name="numeric" argument="--range 1-999"/>
</constraint>
</properties>
<defaultValue>10</defaultValue>
</leafNode>
</children>
</tagNode>
#include <include/dns/time-to-live.xml.i>
+ <leafNode name="ttl">
+ <defaultValue>300</defaultValue>
+ </leafNode>
#include <include/generic-disable-node.xml.i>
</children>
</tagNode>
<tagNode name="ns">
<properties>
<help>NS record</help>
<valueHelp>
<format>txt</format>
<description>A DNS name relative to the root record</description>
</valueHelp>
<constraint>
<regex>([-_a-zA-Z0-9.]{1,63}|@)(?<!\.)</regex>
</constraint>
</properties>
<children>
<leafNode name="target">
<properties>
<help>Target DNS server authoritative for subdomain</help>
<valueHelp>
<format>nsXX.example.com</format>
<description>Absolute DNS name</description>
</valueHelp>
<constraint>
<regex>[-_a-zA-Z0-9.]{1,63}(?<!\.)</regex>
</constraint>
</properties>
</leafNode>
#include <include/dns/time-to-live.xml.i>
+ <leafNode name="ttl">
+ <defaultValue>300</defaultValue>
+ </leafNode>
#include <include/generic-disable-node.xml.i>
</children>
</tagNode>
<tagNode name="ptr">
<properties>
<help>PTR record</help>
<valueHelp>
<format>txt</format>
<description>A DNS name relative to the root record</description>
</valueHelp>
<valueHelp>
<format>@</format>
<description>Root record</description>
</valueHelp>
<constraint>
<regex>([-_a-zA-Z0-9.]{1,63}|@)(?<!\.)</regex>
</constraint>
</properties>
<children>
<leafNode name="target">
<properties>
<help>Target DNS name</help>
<valueHelp>
<format>name.example.com</format>
<description>Absolute DNS name</description>
</valueHelp>
<constraint>
<regex>[-_a-zA-Z0-9.]{1,63}(?<!\.)</regex>
</constraint>
</properties>
</leafNode>
#include <include/dns/time-to-live.xml.i>
+ <leafNode name="ttl">
+ <defaultValue>300</defaultValue>
+ </leafNode>
#include <include/generic-disable-node.xml.i>
</children>
</tagNode>
<tagNode name="txt">
<properties>
<help>TXT record</help>
<valueHelp>
<format>txt</format>
<description>A DNS name relative to the root record</description>
</valueHelp>
<valueHelp>
<format>@</format>
<description>Root record</description>
</valueHelp>
<constraint>
<regex>([-_a-zA-Z0-9.]{1,63}|@)(?<!\.)</regex>
</constraint>
</properties>
<children>
<leafNode name="value">
<properties>
<help>Record contents</help>
<valueHelp>
<format>txt</format>
<description>Record contents</description>
</valueHelp>
<multi/>
</properties>
</leafNode>
#include <include/dns/time-to-live.xml.i>
+ <leafNode name="ttl">
+ <defaultValue>300</defaultValue>
+ </leafNode>
#include <include/generic-disable-node.xml.i>
</children>
</tagNode>
<tagNode name="spf">
<properties>
<help>SPF record</help>
<valueHelp>
<format>txt</format>
<description>A DNS name relative to the root record</description>
</valueHelp>
<valueHelp>
<format>@</format>
<description>Root record</description>
</valueHelp>
<constraint>
<regex>([-_a-zA-Z0-9.]{1,63}|@)(?<!\.)</regex>
</constraint>
</properties>
<children>
<leafNode name="value">
<properties>
<help>Record contents</help>
<valueHelp>
<format>txt</format>
<description>Record contents</description>
</valueHelp>
</properties>
</leafNode>
#include <include/dns/time-to-live.xml.i>
+ <leafNode name="ttl">
+ <defaultValue>300</defaultValue>
+ </leafNode>
#include <include/generic-disable-node.xml.i>
</children>
</tagNode>
<tagNode name="srv">
<properties>
<help>SRV record</help>
<valueHelp>
<format>txt</format>
<description>A DNS name relative to the root record</description>
</valueHelp>
<valueHelp>
<format>@</format>
<description>Root record</description>
</valueHelp>
<constraint>
<regex>([-_a-zA-Z0-9.]{1,63}|@)(?<!\.)</regex>
</constraint>
</properties>
<children>
<tagNode name="entry">
<properties>
<help>Service entry</help>
<valueHelp>
<format>u32:0-65535</format>
<description>Entry number</description>
</valueHelp>
<constraint>
<validator name="numeric" argument="--range 0-65535"/>
</constraint>
</properties>
<children>
<leafNode name="hostname">
<properties>
<help>Server hostname</help>
<valueHelp>
<format>name.example.com</format>
<description>Absolute DNS name</description>
</valueHelp>
<constraint>
<regex>[-_a-zA-Z0-9.]{1,63}(?<!\.)</regex>
</constraint>
</properties>
</leafNode>
<leafNode name="port">
<properties>
<help>Port number</help>
<valueHelp>
<format>u32:0-65535</format>
<description>TCP/UDP port number</description>
</valueHelp>
<constraint>
<validator name="numeric" argument="--range 0-65536"/>
</constraint>
</properties>
</leafNode>
<leafNode name="priority">
<properties>
<help>Entry priority</help>
<valueHelp>
<format>u32:0-65535</format>
<description>Entry priority (lower numbers are higher priority)</description>
</valueHelp>
<constraint>
<validator name="numeric" argument="--range 0-65535"/>
</constraint>
</properties>
<defaultValue>10</defaultValue>
</leafNode>
<leafNode name="weight">
<properties>
<help>Entry weight</help>
<valueHelp>
<format>u32:0-65535</format>
<description>Entry weight</description>
</valueHelp>
<constraint>
<validator name="numeric" argument="--range 0-65535"/>
</constraint>
</properties>
<defaultValue>0</defaultValue>
</leafNode>
</children>
</tagNode>
#include <include/dns/time-to-live.xml.i>
+ <leafNode name="ttl">
+ <defaultValue>300</defaultValue>
+ </leafNode>
#include <include/generic-disable-node.xml.i>
</children>
</tagNode>
<tagNode name="naptr">
<properties>
<help>NAPTR record</help>
<valueHelp>
<format>txt</format>
<description>A DNS name relative to the root record</description>
</valueHelp>
<valueHelp>
<format>@</format>
<description>Root record</description>
</valueHelp>
<constraint>
<regex>([-_a-zA-Z0-9.]{1,63}|@)(?<!\.)</regex>
</constraint>
</properties>
<children>
<tagNode name="rule">
<properties>
<help>NAPTR rule</help>
<valueHelp>
<format>u32:0-65535</format>
<description>Rule number</description>
</valueHelp>
<constraint>
<validator name="numeric" argument="--range 0-65535"/>
</constraint>
</properties>
<children>
<leafNode name="order">
<properties>
<help>Rule order</help>
<valueHelp>
<format>u32:0-65535</format>
<description>Rule order (lower order is evaluated first)</description>
</valueHelp>
<constraint>
<validator name="numeric" argument="--range 0-65535"/>
</constraint>
</properties>
</leafNode>
<leafNode name="preference">
<properties>
<help>Rule preference</help>
<valueHelp>
<format>u32:0-65535</format>
<description>Rule preference</description>
</valueHelp>
<constraint>
<validator name="numeric" argument="--range 0-65535"/>
</constraint>
</properties>
<defaultValue>0</defaultValue>
</leafNode>
<leafNode name="lookup-srv">
<properties>
<help>S flag</help>
<valueless/>
</properties>
</leafNode>
<leafNode name="lookup-a">
<properties>
<help>A flag</help>
<valueless/>
</properties>
</leafNode>
<leafNode name="resolve-uri">
<properties>
<help>U flag</help>
<valueless/>
</properties>
</leafNode>
<leafNode name="protocol-specific">
<properties>
<help>P flag</help>
<valueless/>
</properties>
</leafNode>
<leafNode name="service">
<properties>
<help>Service type</help>
<constraint>
<regex>[a-zA-Z][a-zA-Z0-9]{0,31}(\+[a-zA-Z][a-zA-Z0-9]{0,31})?</regex>
</constraint>
</properties>
</leafNode>
<leafNode name="regexp">
<properties>
<help>Regular expression</help>
</properties>
</leafNode>
<leafNode name="replacement">
<properties>
<help>Replacement DNS name</help>
<valueHelp>
<format>name.example.com</format>
<description>Absolute DNS name</description>
</valueHelp>
<constraint>
<regex>[-_a-zA-Z0-9.]{1,63}(?<!\.)</regex>
</constraint>
</properties>
</leafNode>
</children>
</tagNode>
#include <include/dns/time-to-live.xml.i>
+ <leafNode name="ttl">
+ <defaultValue>300</defaultValue>
+ </leafNode>
#include <include/generic-disable-node.xml.i>
</children>
</tagNode>
</children>
</node>
#include <include/generic-disable-node.xml.i>
</children>
</tagNode>
<leafNode name="ignore-hosts-file">
<properties>
<help>Do not use local /etc/hosts file in name resolution</help>
<valueless/>
</properties>
</leafNode>
<leafNode name="no-serve-rfc1918">
<properties>
<help>Makes the server authoritatively not aware of RFC1918 addresses</help>
<valueless/>
</properties>
</leafNode>
<leafNode name="allow-from">
<properties>
<help>Networks allowed to query this server</help>
<valueHelp>
<format>ipv4net</format>
<description>IP address and prefix length</description>
</valueHelp>
<valueHelp>
<format>ipv6net</format>
<description>IPv6 address and prefix length</description>
</valueHelp>
<multi/>
<constraint>
<validator name="ip-prefix"/>
</constraint>
</properties>
</leafNode>
#include <include/listen-address.xml.i>
#include <include/port-number.xml.i>
<leafNode name="port">
<defaultValue>53</defaultValue>
</leafNode>
<leafNode name="negative-ttl">
<properties>
<help>Maximum amount of time negative entries are cached</help>
<valueHelp>
<format>u32:0-7200</format>
<description>Seconds to cache NXDOMAIN entries</description>
</valueHelp>
<constraint>
<validator name="numeric" argument="--range 0-7200"/>
</constraint>
</properties>
<defaultValue>3600</defaultValue>
</leafNode>
<leafNode name="timeout">
<properties>
<help>Number of milliseconds to wait for a remote authoritative server to respond</help>
<valueHelp>
<format>u32:10-60000</format>
<description>Network timeout in milliseconds</description>
</valueHelp>
<constraint>
<validator name="numeric" argument="--range 10-60000"/>
</constraint>
</properties>
<defaultValue>1500</defaultValue>
</leafNode>
#include <include/name-server-ipv4-ipv6-port.xml.i>
<leafNode name="source-address">
<properties>
<help>Local addresses from which to send DNS queries</help>
<completionHelp>
<script>${vyos_completion_dir}/list_local_ips.sh --both</script>
</completionHelp>
<valueHelp>
<format>ipv4</format>
<description>IPv4 address from which to send traffic</description>
</valueHelp>
<valueHelp>
<format>ipv6</format>
<description>IPv6 address from which to send traffic</description>
</valueHelp>
<multi/>
<constraint>
<validator name="ip-address"/>
</constraint>
</properties>
<defaultValue>0.0.0.0 ::</defaultValue>
</leafNode>
<leafNode name="system">
<properties>
<help>Use system name servers</help>
<valueless/>
</properties>
</leafNode>
</children>
</node>
</children>
</node>
</children>
</node>
</interfaceDefinition>
diff --git a/interface-definitions/include/dns/dynamic-service-host-name-server.xml.i b/interface-definitions/include/dns/dynamic-service-host-name-server.xml.i
index ee1af2a36..9dd14f97c 100644
--- a/interface-definitions/include/dns/dynamic-service-host-name-server.xml.i
+++ b/interface-definitions/include/dns/dynamic-service-host-name-server.xml.i
@@ -1,34 +1,35 @@
<!-- include start from dns/dynamic-service-host-name-server.xml.i -->
<leafNode name="host-name">
<properties>
<help>Hostname to register with Dynamic DNS service</help>
<constraint>
#include <include/constraint/host-name.xml.i>
+ <regex>(\@|\*)[-.A-Za-z0-9]*</regex>
</constraint>
- <constraintErrorMessage>Host-name must be alphanumeric and can contain hyphens</constraintErrorMessage>
+ <constraintErrorMessage>Host-name must be alphanumeric, can contain hyphens and can be prefixed with '@' or '*'</constraintErrorMessage>
<multi/>
</properties>
</leafNode>
<leafNode name="server">
<properties>
<help>Remote Dynamic DNS server to send updates to</help>
<valueHelp>
<format>ipv4</format>
<description>IPv4 address of the remote server</description>
</valueHelp>
<valueHelp>
<format>ipv6</format>
<description>IPv6 address of the remote server</description>
</valueHelp>
<valueHelp>
<format>hostname</format>
<description>Fully qualified domain name of the remote server</description>
</valueHelp>
<constraint>
<validator name="ip-address"/>
<validator name="fqdn"/>
</constraint>
<constraintErrorMessage>Remote server must be IP address or fully qualified domain name</constraintErrorMessage>
</properties>
</leafNode>
<!-- include end -->
diff --git a/interface-definitions/include/dns/dynamic-service-zone.xml.i b/interface-definitions/include/dns/dynamic-service-zone.xml.i
new file mode 100644
index 000000000..0cc00468f
--- /dev/null
+++ b/interface-definitions/include/dns/dynamic-service-zone.xml.i
@@ -0,0 +1,14 @@
+<!-- include start from dns/dynamic-service-zone.xml.i -->
+<leafNode name="zone">
+ <properties>
+ <help>DNS zone to be updated</help>
+ <valueHelp>
+ <format>txt</format>
+ <description>Name of DNS zone</description>
+ </valueHelp>
+ <constraint>
+ <validator name="fqdn"/>
+ </constraint>
+ </properties>
+</leafNode>
+<!-- include end -->
diff --git a/interface-definitions/include/dns/time-to-live.xml.i b/interface-definitions/include/dns/time-to-live.xml.i
index 5c1a1472d..000eea108 100644
--- a/interface-definitions/include/dns/time-to-live.xml.i
+++ b/interface-definitions/include/dns/time-to-live.xml.i
@@ -1,15 +1,14 @@
<!-- include start from dns/time-to-live.xml.i -->
<leafNode name="ttl">
<properties>
<help>Time-to-live (TTL)</help>
<valueHelp>
<format>u32:0-2147483647</format>
<description>TTL in seconds</description>
</valueHelp>
<constraint>
<validator name="numeric" argument="--range 0-2147483647"/>
</constraint>
</properties>
- <defaultValue>300</defaultValue>
</leafNode>
<!-- include end -->
diff --git a/smoketest/scripts/cli/test_service_dns_dynamic.py b/smoketest/scripts/cli/test_service_dns_dynamic.py
index 357c3dfb1..66dcde434 100755
--- a/smoketest/scripts/cli/test_service_dns_dynamic.py
+++ b/smoketest/scripts/cli/test_service_dns_dynamic.py
@@ -1,192 +1,264 @@
#!/usr/bin/env python3
#
# Copyright (C) 2019-2023 VyOS maintainers and contributors
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 or later as
# published by the Free Software Foundation.
#
# This program 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 General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import os
import unittest
import tempfile
+import random
+import string
from base_vyostest_shim import VyOSUnitTestSHIM
from vyos.configsession import ConfigSessionError
from vyos.utils.process import cmd
from vyos.utils.process import process_running
+DDCLIENT_SYSTEMD_UNIT = '/run/systemd/system/ddclient.service.d/override.conf'
DDCLIENT_CONF = '/run/ddclient/ddclient.conf'
DDCLIENT_PID = '/run/ddclient/ddclient.pid'
+DDCLIENT_PNAME = 'ddclient'
base_path = ['service', 'dns', 'dynamic']
+server = 'ddns.vyos.io'
hostname = 'test.ddns.vyos.io'
zone = 'vyos.io'
+username = 'vyos_user'
password = 'paSS_@4ord'
+ttl = '300'
interface = 'eth0'
class TestServiceDDNS(VyOSUnitTestSHIM.TestCase):
+ def setUp(self):
+ # Always start with a clean CLI instance
+ self.cli_delete(base_path)
+
def tearDown(self):
# Check for running process
self.assertTrue(process_running(DDCLIENT_PID))
# Delete DDNS configuration
self.cli_delete(base_path)
self.cli_commit()
# PID file must no londer exist after process exited
self.assertFalse(os.path.exists(DDCLIENT_PID))
# IPv4 standard DDNS service configuration
def test_01_dyndns_service_standard(self):
- ddns = ['address', interface, 'service']
+ svc_path = ['address', interface, 'service']
services = {'cloudflare': {'protocol': 'cloudflare'},
- 'freedns': {'protocol': 'freedns', 'username': 'vyos_user'},
- 'zoneedit': {'protocol': 'zoneedit1', 'username': 'vyos_user'}}
+ 'freedns': {'protocol': 'freedns', 'username': username},
+ 'zoneedit': {'protocol': 'zoneedit1', 'username': username}}
for svc, details in services.items():
- # Always start with a clean CLI instance
- self.cli_delete(base_path)
-
- self.cli_set(base_path + ddns + [svc, 'host-name', hostname])
- self.cli_set(base_path + ddns + [svc, 'password', password])
- self.cli_set(base_path + ddns + [svc, 'zone', zone])
+ self.cli_set(base_path + svc_path + [svc, 'host-name', hostname])
+ self.cli_set(base_path + svc_path + [svc, 'password', password])
+ self.cli_set(base_path + svc_path + [svc, 'zone', zone])
+ self.cli_set(base_path + svc_path + [svc, 'ttl', ttl])
for opt, value in details.items():
- self.cli_set(base_path + ddns + [svc, opt, value])
+ self.cli_set(base_path + svc_path + [svc, opt, value])
- # commit changes
+ # 'zone' option is supported and required by 'cloudfare', but not 'freedns' and 'zoneedit'
+ self.cli_set(base_path + svc_path + [svc, 'zone', zone])
+ if details['protocol'] == 'cloudflare':
+ pass
+ else:
+ # exception is raised for unsupported ones
+ with self.assertRaises(ConfigSessionError):
+ self.cli_commit()
+ self.cli_delete(base_path + svc_path + [svc, 'zone'])
+
+ # 'ttl' option is supported by 'cloudfare', but not 'freedns' and 'zoneedit'
+ self.cli_set(base_path + svc_path + [svc, 'ttl', ttl])
if details['protocol'] == 'cloudflare':
pass
else:
- # zone option does not work on all protocols, an exception is
- # raised for all others
+ # exception is raised for unsupported ones
with self.assertRaises(ConfigSessionError):
self.cli_commit()
- self.cli_delete(base_path + ddns + [svc, 'zone', zone])
+ self.cli_delete(base_path + svc_path + [svc, 'ttl'])
# commit changes
self.cli_commit()
# Check the generating config parameters
ddclient_conf = cmd(f'sudo cat {DDCLIENT_CONF}')
# default value 300 seconds
self.assertIn(f'daemon=300', ddclient_conf)
self.assertIn(f'usev4=ifv4', ddclient_conf)
self.assertIn(f'ifv4={interface}', ddclient_conf)
self.assertIn(f'password={password}', ddclient_conf)
for opt in details.keys():
if opt == 'username':
login = details[opt]
self.assertIn(f'login={login}', ddclient_conf)
else:
tmp = details[opt]
self.assertIn(f'{opt}={tmp}', ddclient_conf)
# IPv6 only DDNS service configuration
def test_02_dyndns_service_ipv6(self):
timeout = '60'
- ddns = ['address', interface, 'service', 'dynv6']
+ svc_path = ['address', interface, 'service', 'dynv6']
proto = 'dyndns2'
- user = 'none'
- password = 'paSS_4ord'
- srv = 'ddns.vyos.io'
ip_version = 'ipv6'
self.cli_set(base_path + ['timeout', timeout])
- self.cli_set(base_path + ddns + ['ip-version', ip_version])
- self.cli_set(base_path + ddns + ['protocol', proto])
- self.cli_set(base_path + ddns + ['server', srv])
- self.cli_set(base_path + ddns + ['username', user])
- self.cli_set(base_path + ddns + ['password', password])
- self.cli_set(base_path + ddns + ['host-name', hostname])
+ self.cli_set(base_path + svc_path + ['ip-version', ip_version])
+ self.cli_set(base_path + svc_path + ['protocol', proto])
+ self.cli_set(base_path + svc_path + ['server', server])
+ self.cli_set(base_path + svc_path + ['username', username])
+ self.cli_set(base_path + svc_path + ['password', password])
+ self.cli_set(base_path + svc_path + ['host-name', hostname])
# commit changes
self.cli_commit()
# Check the generating config parameters
ddclient_conf = cmd(f'sudo cat {DDCLIENT_CONF}')
self.assertIn(f'daemon={timeout}', ddclient_conf)
self.assertIn(f'usev6=ifv6', ddclient_conf)
self.assertIn(f'ifv6={interface}', ddclient_conf)
self.assertIn(f'protocol={proto}', ddclient_conf)
- self.assertIn(f'server={srv}', ddclient_conf)
- self.assertIn(f'login={user}', ddclient_conf)
+ self.assertIn(f'server={server}', ddclient_conf)
+ self.assertIn(f'login={username}', ddclient_conf)
self.assertIn(f'password={password}', ddclient_conf)
# IPv4+IPv6 dual DDNS service configuration
def test_03_dyndns_service_dual_stack(self):
- ddns = ['address', interface, 'service']
- services = {'cloudflare': {'protocol': 'cloudflare', 'zone': 'vyos.io'},
- 'freedns': {'protocol': 'freedns', 'username': 'vyos_user'}}
- password = 'vyos_pass'
+ svc_path = ['address', interface, 'service']
+ services = {'cloudflare': {'protocol': 'cloudflare', 'zone': zone},
+ 'freedns': {'protocol': 'freedns', 'username': username},
+ 'google': {'protocol': 'googledomains', 'username': username}}
ip_version = 'both'
- for svc, details in services.items():
- # Always start with a clean CLI instance
- self.cli_delete(base_path)
-
- self.cli_set(base_path + ddns + [svc, 'host-name', hostname])
- self.cli_set(base_path + ddns + [svc, 'password', password])
- self.cli_set(base_path + ddns + [svc, 'ip-version', ip_version])
+ for name, details in services.items():
+ self.cli_set(base_path + svc_path + [name, 'host-name', hostname])
+ self.cli_set(base_path + svc_path + [name, 'password', password])
for opt, value in details.items():
- self.cli_set(base_path + ddns + [svc, opt, value])
+ self.cli_set(base_path + svc_path + [name, opt, value])
+
+ # Dual stack is supported by 'cloudfare' and 'freedns' but not 'googledomains'
+ # exception is raised for unsupported ones
+ self.cli_set(base_path + svc_path + [name, 'ip-version', ip_version])
+ if details['protocol'] not in ['cloudflare', 'freedns']:
+ with self.assertRaises(ConfigSessionError):
+ self.cli_commit()
+ self.cli_delete(base_path + svc_path + [name, 'ip-version'])
# commit changes
self.cli_commit()
# Check the generating config parameters
ddclient_conf = cmd(f'sudo cat {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)
+ if details['protocol'] not in ['cloudflare', 'freedns']:
+ self.assertIn(f'usev4=ifv4', ddclient_conf)
+ self.assertIn(f'ifv4={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)
for opt in details.keys():
if opt == 'username':
login = details[opt]
self.assertIn(f'login={login}', ddclient_conf)
else:
tmp = details[opt]
self.assertIn(f'{opt}={tmp}', ddclient_conf)
def test_04_dyndns_rfc2136(self):
# Check if DDNS service can be configured and runs
- ddns = ['address', interface, 'rfc2136', 'vyos']
- srv = 'ns1.vyos.io'
- zone = 'vyos.io'
- ttl = '300'
+ svc_path = ['address', interface, 'rfc2136', 'vyos']
with tempfile.NamedTemporaryFile(prefix='/config/auth/') as key_file:
key_file.write(b'S3cretKey')
- self.cli_set(base_path + ddns + ['server', srv])
- self.cli_set(base_path + ddns + ['zone', zone])
- self.cli_set(base_path + ddns + ['key', key_file.name])
- self.cli_set(base_path + ddns + ['ttl', ttl])
- self.cli_set(base_path + ddns + ['host-name', hostname])
+ self.cli_set(base_path + svc_path + ['server', server])
+ self.cli_set(base_path + svc_path + ['zone', zone])
+ self.cli_set(base_path + svc_path + ['key', key_file.name])
+ self.cli_set(base_path + svc_path + ['ttl', ttl])
+ self.cli_set(base_path + svc_path + ['host-name', hostname])
# 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'protocol=nsupdate', ddclient_conf)
- self.assertIn(f'server={srv}', 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'ttl={ttl}', ddclient_conf)
+ def test_05_dyndns_hostname(self):
+ # Check if DDNS service can be configured and runs
+ svc_path = ['address', interface, 'service', 'namecheap']
+ proto = 'namecheap'
+ hostnames = ['@', 'www', hostname, f'@.{hostname}']
+
+ for name in hostnames:
+ self.cli_set(base_path + svc_path + ['protocol', proto])
+ self.cli_set(base_path + svc_path + ['server', server])
+ self.cli_set(base_path + svc_path + ['username', username])
+ self.cli_set(base_path + svc_path + ['password', password])
+ self.cli_set(base_path + svc_path + ['host-name', name])
+
+ # commit changes
+ self.cli_commit()
+
+ # Check the generating config parameters
+ ddclient_conf = cmd(f'sudo cat {DDCLIENT_CONF}')
+ 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'{name}', ddclient_conf)
+
+ def test_06_dyndns_vrf(self):
+ vrf_name = f'vyos-test-{"".join(random.choices(string.ascii_letters + string.digits, k=5))}'
+ svc_path = ['address', interface, 'service', 'cloudflare']
+
+ self.cli_set(['vrf', 'name', vrf_name, 'table', '12345'])
+ self.cli_set(base_path + ['vrf', vrf_name])
+
+ self.cli_set(base_path + svc_path + ['protocol', 'cloudflare'])
+ self.cli_set(base_path + svc_path + ['host-name', hostname])
+ self.cli_set(base_path + svc_path + ['zone', zone])
+ self.cli_set(base_path + svc_path + ['password', password])
+
+ # commit changes
+ self.cli_commit()
+
+ # Check for process in VRF
+ systemd_override = cmd(f'cat {DDCLIENT_SYSTEMD_UNIT}')
+ self.assertIn(f'ExecStart=ip vrf exec {vrf_name} /usr/bin/ddclient -file {DDCLIENT_CONF}',
+ systemd_override)
+
+ # Check for process in VRF
+ proc = cmd(f'ip vrf pids {vrf_name}')
+ self.assertIn(DDCLIENT_PNAME, proc)
+
+ # Cleanup VRF
+ self.cli_delete(['vrf', 'name', vrf_name])
+
if __name__ == '__main__':
unittest.main(verbosity=2)
diff --git a/src/completion/list_ddclient_protocols.sh b/src/completion/list_ddclient_protocols.sh
index 75fb0cf44..3b4eff4d6 100755
--- a/src/completion/list_ddclient_protocols.sh
+++ b/src/completion/list_ddclient_protocols.sh
@@ -1,17 +1,17 @@
#!/bin/sh
#
# Copyright (C) 2023 VyOS maintainers and contributors
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 or later as
# published by the Free Software Foundation.
#
# This program 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 General Public License for more details.
#
# 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)
+echo -n $(ddclient -list-protocols | grep -vE 'nsupdate|cloudns')
diff --git a/src/conf_mode/dns_dynamic.py b/src/conf_mode/dns_dynamic.py
index 4b1aed742..8a438cf6f 100755
--- a/src/conf_mode/dns_dynamic.py
+++ b/src/conf_mode/dns_dynamic.py
@@ -1,134 +1,148 @@
#!/usr/bin/env python3
#
# Copyright (C) 2018-2023 VyOS maintainers and contributors
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 or later as
# published by the Free Software Foundation.
#
# This program 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 General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import os
from sys import exit
from vyos.config import Config
+from vyos.configverify import verify_interface_exists
from vyos.template import render
from vyos.utils.process import call
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_allowed = ['cloudflare', 'godaddy', 'hetzner', 'gandi', 'nfsn']
+zone_necessary = ['cloudflare', 'godaddy', 'hetzner', 'gandi', 'nfsn']
# Protocols that do not require username
username_unnecessary = ['1984', 'cloudflare', 'cloudns', 'duckdns', 'freemyip', 'hetzner', 'keysystems', 'njalla']
+# Protocols that support TTL
+ttl_supported = ['cloudflare', 'gandi', 'hetzner', 'dnsexit', 'godaddy', 'nfsn']
+
# Protocols that support both IPv4 and IPv6
dualstack_supported = ['cloudflare', 'dyndns2', 'freedns', 'njalla']
+# 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']
+
def get_config(config=None):
if config:
conf = config
else:
conf = Config()
- base_level = ['service', 'dns', 'dynamic']
- if not conf.exists(base_level):
+ base = ['service', 'dns', 'dynamic']
+ if not conf.exists(base):
return None
- dyndns = conf.get_config_dict(base_level, key_mangling=('-', '_'),
+ 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 'address' not in dyndns:
return None
for address in dyndns['address']:
+ # If dyndns address is an interface, ensure it exists
+ if address != 'web':
+ verify_interface_exists(address)
+
# RFC2136 - configuration validation
if 'rfc2136' in dyndns['address'][address]:
for config in dyndns['address'][address]['rfc2136'].values():
for field in ['host_name', 'zone', 'server', 'key']:
if field not in config:
raise ConfigError(f'"{field.replace("_", "-")}" is required for RFC2136 '
f'based Dynamic DNS service on "{address}"')
# 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}"'
for field in ['host_name', 'password', 'protocol']:
if field not in config:
raise ConfigError(f'"{field.replace("_", "-")}" {error_msg}')
- if config['protocol'] in zone_allowed and 'zone' not in config:
- raise ConfigError(f'"zone" {error_msg}')
+ if config['protocol'] in zone_necessary and 'zone' not in config:
+ raise ConfigError(f'"zone" {error_msg}')
+
+ 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_allowed and 'zone' in config:
- raise ConfigError(f'"{config["protocol"]}" does not support "zone"')
+ if config['protocol'] not in username_unnecessary and 'username' not in config:
+ raise ConfigError(f'"username" {error_msg}')
- if config['protocol'] not in username_unnecessary:
- if 'username' not in config:
- raise ConfigError(f'"username" {error_msg}')
+ if config['protocol'] not in ttl_supported and 'ttl' in config:
+ raise ConfigError(f'"{config["protocol"]}" does not support "ttl"')
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')
# dyndns2 protocol in ddclient honors dual stack only for dyn.com (dyndns.org)
- if config['protocol'] == 'dyndns2' and 'server' in config and config['server'] != 'members.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"]}"')
return None
def generate(dyndns):
# bail out early - looks like removal from running config
if not dyndns or 'address' not in dyndns:
return None
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
call('systemctl daemon-reload')
# bail out early - looks like removal from running config
if not dyndns or 'address' not in dyndns:
call(f'systemctl stop {systemd_service}')
if os.path.exists(config_file):
os.unlink(config_file)
else:
call(f'systemctl reload-or-restart {systemd_service}')
return None
if __name__ == '__main__':
try:
c = get_config()
verify(c)
generate(c)
apply(c)
except ConfigError as e:
print(e)
exit(1)
diff --git a/src/validators/ddclient-protocol b/src/validators/ddclient-protocol
index 6f927927b..bc6826120 100755
--- a/src/validators/ddclient-protocol
+++ b/src/validators/ddclient-protocol
@@ -1,24 +1,24 @@
#!/bin/sh
#
# Copyright (C) 2023 VyOS maintainers and contributors
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 or later as
# published by the Free Software Foundation.
#
# This program 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 General Public License for more details.
#
# 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 -qw $1
+ddclient -list-protocols | grep -vE 'nsupdate|cloudns' | grep -qw $1
if [ $? -gt 0 ]; then
echo "Error: $1 is not a valid protocol, please choose from the supported list of protocols"
exit 1
fi
exit 0
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Sat, Oct 17, 12:14 AM (1 d, 13 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
4416484
Default Alt Text
(74 KB)
Attached To
Mode
rVYOSONEX vyos-1x
Attached
Detach File
Event Timeline
Log In to Comment