summaryrefslogtreecommitdiff
path: root/src/conf_mode
diff options
context:
space:
mode:
Diffstat (limited to 'src/conf_mode')
-rwxr-xr-xsrc/conf_mode/dns_dynamic.py142
-rwxr-xr-xsrc/conf_mode/dynamic_dns.py156
-rwxr-xr-xsrc/conf_mode/flow_accounting_conf.py16
-rwxr-xr-xsrc/conf_mode/interfaces-bridge.py4
-rwxr-xr-xsrc/conf_mode/netns.py3
-rwxr-xr-xsrc/conf_mode/service_router-advert.py4
-rwxr-xr-xsrc/conf_mode/vpn_ipsec.py2
-rwxr-xr-xsrc/conf_mode/vpn_openconnect.py5
8 files changed, 164 insertions, 168 deletions
diff --git a/src/conf_mode/dns_dynamic.py b/src/conf_mode/dns_dynamic.py
new file mode 100755
index 000000000..67134e681
--- /dev/null
+++ b/src/conf_mode/dns_dynamic.py
@@ -0,0 +1,142 @@
+#!/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.configdict import dict_merge
+from vyos.template import render
+from vyos.util import call
+from vyos.xml import defaults
+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']
+
+# Protocols that do not require username
+username_unnecessary = ['1984', 'cloudflare', 'cloudns', 'duckdns', 'freemyip', 'hetzner', 'keysystems', 'njalla']
+
+# Protocols that support both IPv4 and IPv6
+dualstack_supported = ['cloudflare', 'dyndns2', 'freedns', 'njalla']
+
+def get_config(config=None):
+ if config:
+ conf = config
+ else:
+ conf = Config()
+
+ base_level = ['service', 'dns', 'dynamic']
+ if not conf.exists(base_level):
+ return None
+
+ dyndns = conf.get_config_dict(base_level, key_mangling=('-', '_'), get_first_key=True)
+
+ if 'address' in dyndns:
+ for address in dyndns['address']:
+ # Apply service specific defaults (svc_type = ['rfc2136', 'service'])
+ for svc_type in dyndns['address'][address]:
+ default_values = defaults(base_level + ['address', svc_type])
+ for svc_cfg in dyndns['address'][address][svc_type]:
+ dyndns['address'][address][svc_type][svc_cfg] = dict_merge(
+ default_values, dyndns['address'][address][svc_type][svc_cfg])
+
+ 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']:
+ # 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'] not in zone_allowed and 'zone' in config:
+ raise ConfigError(f'"{config["protocol"]}" does not support "zone"')
+
+ if config['protocol'] not in username_unnecessary:
+ if 'username' not in config:
+ raise ConfigError(f'"username" {error_msg}')
+
+ 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':
+ 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)
+ 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/conf_mode/dynamic_dns.py b/src/conf_mode/dynamic_dns.py
deleted file mode 100755
index 426e3d693..000000000
--- a/src/conf_mode/dynamic_dns.py
+++ /dev/null
@@ -1,156 +0,0 @@
-#!/usr/bin/env python3
-#
-# Copyright (C) 2018-2020 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.configdict import dict_merge
-from vyos.template import render
-from vyos.util import call
-from vyos.xml import defaults
-from vyos import ConfigError
-from vyos import airbag
-airbag.enable()
-
-config_file = r'/run/ddclient/ddclient.conf'
-
-# Mapping of service name to service protocol
-default_service_protocol = {
- 'afraid': 'freedns',
- 'changeip': 'changeip',
- 'cloudflare': 'cloudflare',
- 'dnspark': 'dnspark',
- 'dslreports': 'dslreports1',
- 'dyndns': 'dyndns2',
- 'easydns': 'easydns',
- 'namecheap': 'namecheap',
- 'noip': 'noip',
- 'sitelutions': 'sitelutions',
- 'zoneedit': 'zoneedit1'
-}
-
-def get_config(config=None):
- if config:
- conf = config
- else:
- conf = Config()
-
- base_level = ['service', 'dns', 'dynamic']
- if not conf.exists(base_level):
- return None
-
- dyndns = conf.get_config_dict(base_level, key_mangling=('-', '_'), get_first_key=True)
-
- # We have gathered the dict representation of the CLI, but there are default
- # options which we need to update into the dictionary retrived.
- for interface in dyndns['interface']:
- if 'service' in dyndns['interface'][interface]:
- # 'Autodetect' protocol used by DynDNS service
- for service in dyndns['interface'][interface]['service']:
- if service in default_service_protocol:
- dyndns['interface'][interface]['service'][service].update(
- {'protocol' : default_service_protocol.get(service)})
- else:
- dyndns['interface'][interface]['service'][service].update(
- {'custom': ''})
-
- if 'rfc2136' in dyndns['interface'][interface]:
- default_values = defaults(base_level + ['interface', 'rfc2136'])
- for rfc2136 in dyndns['interface'][interface]['rfc2136']:
- dyndns['interface'][interface]['rfc2136'][rfc2136] = dict_merge(
- default_values, dyndns['interface'][interface]['rfc2136'][rfc2136])
-
- return dyndns
-
-def verify(dyndns):
- # bail out early - looks like removal from running config
- if not dyndns:
- return None
-
- # A 'node' corresponds to an interface
- if 'interface' not in dyndns:
- return None
-
- for interface in dyndns['interface']:
- # RFC2136 - configuration validation
- if 'rfc2136' in dyndns['interface'][interface]:
- for rfc2136, config in dyndns['interface'][interface]['rfc2136'].items():
-
- for tmp in ['record', 'zone', 'server', 'key']:
- if tmp not in config:
- raise ConfigError(f'"{tmp}" required for rfc2136 based '
- f'DynDNS service on "{interface}"')
-
- if not os.path.isfile(config['key']):
- raise ConfigError(f'"key"-file not found for rfc2136 based '
- f'DynDNS service on "{interface}"')
-
- # DynDNS service provider - configuration validation
- if 'service' in dyndns['interface'][interface]:
- for service, config in dyndns['interface'][interface]['service'].items():
- error_msg = f'required for DynDNS service "{service}" on "{interface}"'
- if 'host_name' not in config:
- raise ConfigError(f'"host-name" {error_msg}')
-
- if 'login' not in config:
- if service != 'cloudflare' and ('protocol' not in config or config['protocol'] != 'cloudflare'):
- raise ConfigError(f'"login" (username) {error_msg}, unless using CloudFlare')
-
- if 'password' not in config:
- raise ConfigError(f'"password" {error_msg}')
-
- if 'zone' in config:
- if service != 'cloudflare' and ('protocol' not in config or config['protocol'] != 'cloudflare'):
- raise ConfigError(f'"zone" option only supported with CloudFlare')
-
- if 'custom' in config:
- if 'protocol' not in config:
- raise ConfigError(f'"protocol" {error_msg}')
-
- if 'server' not in config:
- raise ConfigError(f'"server" {error_msg}')
-
- return None
-
-def generate(dyndns):
- # bail out early - looks like removal from running config
- if not dyndns:
- return None
-
- render(config_file, 'dynamic-dns/ddclient.conf.j2', dyndns)
- return None
-
-def apply(dyndns):
- if not dyndns:
- call('systemctl stop ddclient.service')
- if os.path.exists(config_file):
- os.unlink(config_file)
- else:
- call('systemctl restart ddclient.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/conf_mode/flow_accounting_conf.py b/src/conf_mode/flow_accounting_conf.py
index f67f1710e..c36d52e05 100755
--- a/src/conf_mode/flow_accounting_conf.py
+++ b/src/conf_mode/flow_accounting_conf.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2018-2022 VyOS maintainers and contributors
+# 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
@@ -18,15 +18,13 @@ import os
import re
from sys import exit
-import ipaddress
-
from ipaddress import ip_address
from vyos.base import Warning
from vyos.config import Config
from vyos.configdict import dict_merge
+from vyos.configverify import verify_vrf
from vyos.ifconfig import Section
-from vyos.ifconfig import Interface
from vyos.template import render
from vyos.util import call
from vyos.util import cmd
@@ -194,6 +192,7 @@ def verify(flow_config):
sflow_collector_ipver = ip_address(server).version
# check if vrf is defined for Sflow
+ verify_vrf(flow_config)
sflow_vrf = None
if 'vrf' in flow_config:
sflow_vrf = flow_config['vrf']
@@ -211,7 +210,7 @@ def verify(flow_config):
if not is_addr_assigned(tmp, sflow_vrf):
raise ConfigError(f'Configured "sflow agent-address {tmp}" does not exist in the system!')
- # Check if configured netflow source-address exist in the system
+ # Check if configured sflow source-address exist in the system
if 'source_address' in flow_config['sflow']:
if not is_addr_assigned(flow_config['sflow']['source_address'], sflow_vrf):
tmp = flow_config['sflow']['source_address']
@@ -219,13 +218,18 @@ def verify(flow_config):
# check NetFlow configuration
if 'netflow' in flow_config:
+ # check if vrf is defined for netflow
+ netflow_vrf = None
+ if 'vrf' in flow_config:
+ netflow_vrf = flow_config['vrf']
+
# check if at least one NetFlow collector is configured if NetFlow configuration is presented
if 'server' not in flow_config['netflow']:
raise ConfigError('You need to configure at least one NetFlow server!')
# Check if configured netflow source-address exist in the system
if 'source_address' in flow_config['netflow']:
- if not is_addr_assigned(flow_config['netflow']['source_address']):
+ if not is_addr_assigned(flow_config['netflow']['source_address'], netflow_vrf):
tmp = flow_config['netflow']['source_address']
raise ConfigError(f'Configured "netflow source-address {tmp}" does not exist on the system!')
diff --git a/src/conf_mode/interfaces-bridge.py b/src/conf_mode/interfaces-bridge.py
index b961408db..4da3b097f 100755
--- a/src/conf_mode/interfaces-bridge.py
+++ b/src/conf_mode/interfaces-bridge.py
@@ -131,11 +131,11 @@ def verify(bridge):
raise ConfigError('Loopback interface "lo" can not be added to a bridge')
if 'is_bridge_member' in interface_config:
- tmp = interface_config['is_bridge_member']
+ tmp = next(iter(interface_config['is_bridge_member']))
raise ConfigError(error_msg + f'it is already a member of bridge "{tmp}"!')
if 'is_bond_member' in interface_config:
- tmp = interface_config['is_bond_member']
+ tmp = next(iter(interface_config['is_bond_member']))
raise ConfigError(error_msg + f'it is already a member of bond "{tmp}"!')
if 'is_source_interface' in interface_config:
diff --git a/src/conf_mode/netns.py b/src/conf_mode/netns.py
index 0924eb616..20129ce65 100755
--- a/src/conf_mode/netns.py
+++ b/src/conf_mode/netns.py
@@ -82,7 +82,8 @@ def verify(netns):
if 'name' in netns:
for name, config in netns['name'].items():
- print(name)
+ # no tests (yet)
+ pass
return None
diff --git a/src/conf_mode/service_router-advert.py b/src/conf_mode/service_router-advert.py
index 1b8377a4a..1dd973d67 100755
--- a/src/conf_mode/service_router-advert.py
+++ b/src/conf_mode/service_router-advert.py
@@ -93,6 +93,10 @@ def verify(rtradv):
if not (int(valid_lifetime) >= int(preferred_lifetime)):
raise ConfigError('Prefix valid-lifetime must be greater then or equal to preferred-lifetime')
+ if 'name_server' in interface_config:
+ if len(interface_config['name_server']) > 3:
+ raise ConfigError('No more then 3 IPv6 name-servers supported!')
+
if 'name_server_lifetime' in interface_config:
# man page states:
# The maximum duration how long the RDNSS entries are used for name
diff --git a/src/conf_mode/vpn_ipsec.py b/src/conf_mode/vpn_ipsec.py
index 63887b278..b82d90e4d 100755
--- a/src/conf_mode/vpn_ipsec.py
+++ b/src/conf_mode/vpn_ipsec.py
@@ -455,7 +455,7 @@ def verify(ipsec):
if dict_search('options.disable_route_autoinstall',
ipsec) == None:
- Warning('It\'s recommended to use ipsec vty with the next command\n[set vpn ipsec option disable-route-autoinstall]')
+ Warning('It\'s recommended to use ipsec vti with the next command\n[set vpn ipsec option disable-route-autoinstall]')
if 'bind' in peer_conf['vti']:
vti_interface = peer_conf['vti']['bind']
diff --git a/src/conf_mode/vpn_openconnect.py b/src/conf_mode/vpn_openconnect.py
index 83021a3e6..3d5dc12a4 100755
--- a/src/conf_mode/vpn_openconnect.py
+++ b/src/conf_mode/vpn_openconnect.py
@@ -30,7 +30,7 @@ from vyos.util import is_listen_port_bind_service
from vyos.util import dict_search
from vyos.xml import defaults
from vyos import ConfigError
-from crypt import crypt, mksalt, METHOD_SHA512
+from passlib.hash import sha512_crypt
from time import sleep
from vyos import airbag
@@ -45,7 +45,8 @@ radius_servers = cfg_dir + '/radius_servers'
# Generate hash from user cleartext password
def get_hash(password):
- return crypt(password, mksalt(METHOD_SHA512))
+ return sha512_crypt.hash(password)
+
def _default_dict_cleanup(origin: dict, default_values: dict) -> dict: