summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--data/templates/accel-ppp/config_chap_secrets_radius.j236
-rw-r--r--data/templates/accel-ppp/config_ipv6_pool.j24
-rw-r--r--data/templates/accel-ppp/config_modules_auth_mode.j25
-rw-r--r--data/templates/accel-ppp/config_modules_auth_protocols.j210
-rw-r--r--data/templates/accel-ppp/config_modules_ipv6.j25
-rw-r--r--data/templates/accel-ppp/config_shaper_radius.j210
-rw-r--r--data/templates/accel-ppp/pppoe.config.tmpl66
-rw-r--r--data/templates/accel-ppp/sstp.config.tmpl133
-rw-r--r--interface-definitions/include/accel-radius-additions-rate-limit.xml.i1
-rw-r--r--python/vyos/configdict.py83
-rw-r--r--python/vyos/configverify.py57
-rwxr-xr-xsmoketest/scripts/cli/test_service_pppoe-server.py2
-rwxr-xr-xsrc/conf_mode/service_pppoe-server.py98
-rwxr-xr-xsrc/conf_mode/vpn_sstp.py346
14 files changed, 283 insertions, 573 deletions
diff --git a/data/templates/accel-ppp/config_chap_secrets_radius.j2 b/data/templates/accel-ppp/config_chap_secrets_radius.j2
new file mode 100644
index 000000000..c94e75a23
--- /dev/null
+++ b/data/templates/accel-ppp/config_chap_secrets_radius.j2
@@ -0,0 +1,36 @@
+{% if authentication.mode is defined and authentication.mode == 'local' %}
+[chap-secrets]
+chap-secrets={{ chap_secrets_file }}
+{% elif authentication.mode is defined and authentication.mode == 'radius' %}
+[radius]
+verbose=1
+{% for server, options in authentication.radius.server.items() if not options.disable is defined %}
+server={{ server }},{{ options.key }},auth-port={{ options.port }},acct-port={{ options.acct_port }},req-limit=0,fail-time={{ options.fail_time }}
+{% endfor %}
+{% if authentication.radius.acct_interim_jitter is defined and authentication.radius.acct_interim_jitter is not none %}
+acct-interim-jitter={{ authentication.radius.acct_interim_jitter }}
+{% endif %}
+acct-timeout={{ authentication.radius.acct_timeout }}
+timeout={{ authentication.radius.timeout }}
+max-try={{ authentication.radius.max_try }}
+{% if authentication.radius.nas_identifier is defined and authentication.radius.nas_identifier is not none %}
+nas-identifier={{ authentication.radius.nas_identifier }}
+{% endif %}
+{% if authentication.radius.nas_ip_address is defined and authentication.radius.nas_ip_address is not none %}
+nas-ip-address={{ authentication.radius.nas_ip_address }}
+{% endif %}
+{% if authentication.radius.source_address is defined and authentication.radius.source_address is not none %}
+bind={{ authentication.radius.source_address }}
+{% endif %}
+{% if authentication.radius.called_sid_format is defined and authentication.radius.called_sid_format is not none %}
+called-sid={{ authentication.radius.called_sid_format }}
+{% endif %}
+{% if authentication.radius.dynamic_author.server is defined and authentication.radius.dynamic_author.server is not none %}
+dae-server={{ authentication.radius.dynamic_author.server }}:{{ authentication.radius.dynamic_author.port }},{{ authentication.radius.dynamic_author.key }}
+{% endif -%}
+{% endif %}
+{# Both chap-secrets and radius block required the gw-ip-address #}
+{% if gateway_address is defined and gateway_address is not none %}
+gw-ip-address={{ gateway_address }}
+{% endif %}
+
diff --git a/data/templates/accel-ppp/config_ipv6_pool.j2 b/data/templates/accel-ppp/config_ipv6_pool.j2
index b764fc6f0..f45bf9442 100644
--- a/data/templates/accel-ppp/config_ipv6_pool.j2
+++ b/data/templates/accel-ppp/config_ipv6_pool.j2
@@ -13,4 +13,8 @@ delegate={{ prefix }},{{ options.delegation_prefix }}
{% endfor %}
{% endif %}
{% endif %}
+{% if client_ipv6_pool.delegate is defined and client_ipv6_pool.delegate is not none %}
+[ipv6-dhcp]
+verbose=1
+{% endif %}
{% endif %}
diff --git a/data/templates/accel-ppp/config_modules_auth_mode.j2 b/data/templates/accel-ppp/config_modules_auth_mode.j2
new file mode 100644
index 000000000..5eca76f91
--- /dev/null
+++ b/data/templates/accel-ppp/config_modules_auth_mode.j2
@@ -0,0 +1,5 @@
+{% if authentication is defined and authentication.mode is defined and authentication.mode == 'local' %}
+chap-secrets
+{% elif authentication is defined and authentication.mode is defined and authentication.mode == 'radius' %}
+radius
+{% endif %}
diff --git a/data/templates/accel-ppp/config_modules_auth_protocols.j2 b/data/templates/accel-ppp/config_modules_auth_protocols.j2
new file mode 100644
index 000000000..e122d6c48
--- /dev/null
+++ b/data/templates/accel-ppp/config_modules_auth_protocols.j2
@@ -0,0 +1,10 @@
+{% for protocol in authentication.protocols %}
+{# this should be fixed in the CLI by a migrator #}
+{% if protocol == 'chap' %}
+auth_chap_md5
+{% elif protocol == 'mschap' %}
+auth_mschap_v1
+{% else %}
+auth_{{ protocol.replace('-', '_') }}
+{% endif %}
+{% endfor %}
diff --git a/data/templates/accel-ppp/config_modules_ipv6.j2 b/data/templates/accel-ppp/config_modules_ipv6.j2
new file mode 100644
index 000000000..e9ea4924b
--- /dev/null
+++ b/data/templates/accel-ppp/config_modules_ipv6.j2
@@ -0,0 +1,5 @@
+{% if ppp_options.ipv6 is defined and ppp_options.ipv6 != 'deny' %}
+ipv6pool
+ipv6_nd
+ipv6_dhcp
+{% endif %}
diff --git a/data/templates/accel-ppp/config_shaper_radius.j2 b/data/templates/accel-ppp/config_shaper_radius.j2
new file mode 100644
index 000000000..2a6641245
--- /dev/null
+++ b/data/templates/accel-ppp/config_shaper_radius.j2
@@ -0,0 +1,10 @@
+{% if authentication is defined and authentication.mode is defined and authentication.mode == 'radius' %}
+{% if authentication is defined and authentication.radius is defined and authentication.radius.rate_limit is defined and authentication.radius.rate_limit.enable is defined %}
+[shaper]
+verbose=1
+attr={{ authentication.radius.rate_limit.attribute }}
+{% if authentication.radius.rate_limit.vendor is defined and authentication.radius.rate_limit.vendor is not none %}
+vendor={{ authentication.radius.rate_limit.vendor }}
+{% endif %}
+{% endif %}
+{% endif %}
diff --git a/data/templates/accel-ppp/pppoe.config.tmpl b/data/templates/accel-ppp/pppoe.config.tmpl
index 8f1b9e7c5..19adbc890 100644
--- a/data/templates/accel-ppp/pppoe.config.tmpl
+++ b/data/templates/accel-ppp/pppoe.config.tmpl
@@ -2,25 +2,15 @@
[modules]
log_syslog
pppoe
-{{ "radius" if authentication.mode is defined and authentication.mode == 'radius' }}
-chap-secrets
-ippool
-{% if ppp_options.ipv6 is defined and ppp_options.ipv6 != 'deny' %}
-ipv6pool
-ipv6_nd
-ipv6_dhcp
-{% endif %}
-{% for protocol in authentication.protocols %}
-{# this should be fixed in the CLI by a migrator #}
-{% if protocol == 'chap' %}
-auth_chap_md5
-{% elif protocol == 'mschap' %}
-auth_mschap_v1
-{% else %}
-auth_{{ protocol.replace('-', '_') }}
-{% endif %}
-{% endfor %}
shaper
+{# Common authentication backend definitions #}
+{% include 'accel-ppp/config_modules_auth_mode.j2' %}
+ippool
+{# Common IPv6 definitions #}
+{% include 'accel-ppp/config_modules_ipv6.j2' %}
+{# Common authentication protocols (pap, chap ...) #}
+{% include 'accel-ppp/config_modules_auth_protocols.j2' %}
+
{% if snmp is defined %}
net-snmp
{% endif %}
@@ -60,41 +50,8 @@ wins{{ loop.index }}={{ server }}
{% endfor %}
{% endif %}
-{% if authentication.mode is defined and authentication.mode == 'local' %}
-[chap-secrets]
-chap-secrets={{ chap_secrets_file }}
-{% elif authentication.mode is defined and authentication.mode == 'radius' %}
-[radius]
-verbose=1
-{% for server, options in authentication.radius.server.items() if not options.disable is defined %}
-server={{ server }},{{ options.key }},auth-port={{ options.port }},acct-port={{ options.acct_port }},req-limit=0,fail-time={{ options.fail_time }}
-{% endfor %}
-{% if authentication.radius.acct_interim_jitter is defined and authentication.radius.acct_interim_jitter is not none %}
-acct-interim-jitter={{ authentication.radius.acct_interim_jitter }}
-{% endif %}
-acct-timeout={{ authentication.radius.acct_timeout }}
-timeout={{ authentication.radius.timeout }}
-max-try={{ authentication.radius.max_try }}
-{% if authentication.radius.nas_identifier is defined and authentication.radius.nas_identifier is not none %}
-nas-identifier={{ authentication.radius.nas_identifier }}
-{% endif %}
-{% if authentication.radius.nas_ip_address is defined and authentication.radius.nas_ip_address is not none %}
-nas-ip-address={{ authentication.radius.nas_ip_address }}
-{% endif %}
-{% if authentication.radius.source_address is defined and authentication.radius.source_address is not none %}
-bind={{ authentication.radius.source_address }}
-{% endif %}
-{% if authentication.radius.called_sid_format is defined and authentication.radius.called_sid_format is not none %}
-called-sid={{ authentication.radius.called_sid_format }}
-{% endif %}
-{% if authentication.radius.dynamic_author.server is defined and authentication.radius.dynamic_author.server is not none %}
-dae-server={{ authentication.radius.dynamic_author.server }}:{{ authentication.radius.dynamic_author.port }},{{ authentication.radius.dynamic_author.key }}
-{% endif -%}
-{% endif %}
-
-{% if gateway_address is defined and gateway_address is not none %}
-gw-ip-address={{ gateway_address }}
-{% endif %}
+{# Common chap-secrets and RADIUS server/option definitions #}
+{% include 'accel-ppp/config_chap_secrets_radius.j2' %}
{% if session_control is defined and session_control != 'disable' %}
[common]
@@ -170,5 +127,8 @@ timeout={{ limits.timeout }}
{% endif %}
{% endif %}
+{# Common RADIUS shaper configuration #}
+{% include 'accel-ppp/config_shaper_radius.j2' %}
+
[cli]
tcp=127.0.0.1:2001
diff --git a/data/templates/accel-ppp/sstp.config.tmpl b/data/templates/accel-ppp/sstp.config.tmpl
index c9e4a1d7d..7ca7b1c1e 100644
--- a/data/templates/accel-ppp/sstp.config.tmpl
+++ b/data/templates/accel-ppp/sstp.config.tmpl
@@ -3,22 +3,16 @@
log_syslog
sstp
shaper
-{% if auth_mode == 'local' %}
-chap-secrets
-{% elif auth_mode == 'radius' %}
-radius
-{% endif -%}
+{# Common authentication backend definitions #}
+{% include 'accel-ppp/config_modules_auth_mode.j2' %}
ippool
-ipv6pool
-ipv6_nd
-ipv6_dhcp
-
-{% for proto in auth_proto %}
-{{proto}}
-{% endfor %}
+{# Common IPv6 definitions #}
+{% include 'accel-ppp/config_modules_ipv6.j2' %}
+{# Common authentication protocols (pap, chap ...) #}
+{% include 'accel-ppp/config_modules_auth_protocols.j2' %}
[core]
-thread-count={{thread_cnt}}
+thread-count={{ thread_count }}
[common]
single-session=replace
@@ -35,112 +29,37 @@ disable
verbose=1
ifname=sstp%d
accept=ssl
-ssl-ca-file={{ ssl_ca }}
-ssl-pemfile={{ ssl_cert }}
-ssl-keyfile={{ ssl_key }}
-
-{% if client_ip_pool %}
-[ip-pool]
-gw-ip-address={{ client_gateway }}
-{% for subnet in client_ip_pool %}
-{{ subnet }}
-{% endfor %}
-{% endif %}
+ssl-ca-file={{ ssl.ca_cert_file }}
+ssl-pemfile={{ ssl.cert_file }}
+ssl-keyfile={{ ssl.key_file }}
-{% if dnsv4 %}
-[dns]
-{% for dns in dnsv4 -%}
-dns{{ loop.index }}={{ dns }}
-{% endfor -%}
-{% endif %}
+{# Common IP pool definitions #}
+{% include 'accel-ppp/config_ip_pool.j2' %}
-{% if dnsv6 %}
-[ipv6-dns]
-{% for dns in dnsv6 -%}
-{{ dns }}
-{% endfor -%}
-{% endif %}
+{# Common IPv6 pool definitions #}
+{% include 'accel-ppp/config_ipv6_pool.j2' %}
+{# Common DNS name-server definition #}
+{% include 'accel-ppp/config_name_server.j2' %}
-{% if auth_mode == 'local' %}
-[chap-secrets]
-chap-secrets={{ chap_secrets_file }}
-{% elif auth_mode == 'radius' %}
-[radius]
-verbose=1
-{% for r in radius_server %}
-server={{ r.server }},{{ r.key }},auth-port={{ r.port }},acct-port={{ r.acct_port }},req-limit=0,fail-time={{ r.fail_time }}
-{% endfor -%}
-
-acct-timeout={{ radius_acct_tmo }}
-timeout={{ radius_timeout }}
-max-try={{ radius_max_try }}
-
-{% if radius_nas_id %}
-nas-identifier={{ radius_nas_id }}
-{% endif -%}
-{% if radius_nas_ip %}
-nas-ip-address={{ radius_nas_ip }}
-{% endif -%}
-{% if radius_source_address %}
-bind={{ radius_source_address }}
-{% endif -%}
-
-
-{% if radius_dynamic_author %}
-dae-server={{ radius_dynamic_author.server }}:{{ radius_dynamic_author.port }},{{ radius_dynamic_author.key }}
-{% endif -%}
-{% endif %}
-{% if client_gateway %}
-gw-ip-address={{ client_gateway }}
-{% endif %}
+{# Common chap-secrets and RADIUS server/option definitions #}
+{% include 'accel-ppp/config_chap_secrets_radius.j2' %}
[ppp]
verbose=1
check-ip=1
-{% if mtu %}
+{# MTU #}
mtu={{ mtu }}
-{% endif -%}
-{% if client_ipv6_pool %}
+{% if client_ipv6_pool is defined %}
ipv6=allow
{% endif %}
+mppe={{ ppp_options.mppe }}
+lcp-echo-interval={{ ppp_options.lcp_echo_interval }}
+lcp-echo-timeout={{ ppp_options.lcp_echo_timeout }}
+lcp-echo-failure={{ ppp_options.lcp_echo_failure }}
-{% if ppp_mppe %}
-mppe={{ ppp_mppe }}
-{% endif -%}
-{% if ppp_echo_interval %}
-lcp-echo-interval={{ ppp_echo_interval }}
-{% endif -%}
-{% if ppp_echo_failure %}
-lcp-echo-failure={{ ppp_echo_failure }}
-{% endif -%}
-{% if ppp_echo_timeout %}
-lcp-echo-timeout={{ ppp_echo_timeout }}
-{% endif %}
-
-{% if client_ipv6_pool %}
-[ipv6-pool]
-{% for p in client_ipv6_pool %}
-{{ p.prefix }},{{ p.mask }}
-{% endfor %}
-{% for p in client_ipv6_delegate_prefix %}
-delegate={{ p.prefix }},{{ p.mask }}
-{% endfor %}
-{% endif %}
-
-{% if client_ipv6_delegate_prefix %}
-[ipv6-dhcp]
-verbose=1
-{% endif %}
-
-{% if radius_shaper_attr %}
-[shaper]
-verbose=1
-attr={{ radius_shaper_attr }}
-{% if radius_shaper_vendor %}
-vendor={{ radius_shaper_vendor }}
-{% endif -%}
-{% endif %}
+{# Common RADIUS shaper configuration #}
+{% include 'accel-ppp/config_shaper_radius.j2' %}
[cli]
tcp=127.0.0.1:2005
diff --git a/interface-definitions/include/accel-radius-additions-rate-limit.xml.i b/interface-definitions/include/accel-radius-additions-rate-limit.xml.i
index deab40e03..23a4a51cf 100644
--- a/interface-definitions/include/accel-radius-additions-rate-limit.xml.i
+++ b/interface-definitions/include/accel-radius-additions-rate-limit.xml.i
@@ -8,6 +8,7 @@
<properties>
<help>Specifies which radius attribute contains rate information. (default is Filter-Id)</help>
</properties>
+ <defaultValue>Filter-Id</defaultValue>
</leafNode>
<leafNode name="vendor">
<properties>
diff --git a/python/vyos/configdict.py b/python/vyos/configdict.py
index ce6d58693..99072a1b9 100644
--- a/python/vyos/configdict.py
+++ b/python/vyos/configdict.py
@@ -18,12 +18,8 @@ A library for retrieving value dicts from VyOS configs in a declarative fashion.
"""
import os
-from copy import deepcopy
-
from vyos.util import vyos_dict_search
from vyos.xml import defaults
-from vyos.xml import is_tag
-from vyos.xml import is_leaf
from vyos import ConfigError
def retrieve_config(path_hash, base_path, config):
@@ -198,6 +194,9 @@ def is_member(conf, interface, intftype=None):
interface name -> Interface is a member of this interface
False -> interface type cannot have members
"""
+ from vyos.xml import is_tag
+ from vyos.xml import is_leaf
+
ret_val = None
intftypes = ['bonding', 'bridge']
if intftype not in intftypes + [None]:
@@ -265,11 +264,12 @@ def is_source_interface(conf, interface, intftype=None):
def get_interface_dict(config, base, ifname=''):
"""
- Common utility function to retrieve and mandgle the interfaces available
- in CLI configuration. All interfaces have a common base ground where the
- value retrival is identical - so it can and should be reused
+ Common utility function to retrieve and mangle the interfaces configuration
+ from the CLI input nodes. All interfaces have a common base where value
+ retrival is identical. This function must be used whenever possible when
+ working on the interfaces node!
- Will return a dictionary with the necessary interface configuration
+ Return a dictionary with the necessary interface config keys.
"""
if not ifname:
# determine tagNode instance
@@ -405,3 +405,70 @@ def get_interface_dict(config, base, ifname=''):
# Check vif, vif-s/vif-c VLAN interfaces for removal
dict = get_removed_vlans(config, dict)
return dict
+
+
+def get_accel_dict(config, base, chap_secrets):
+ """
+ Common utility function to retrieve and mangle the Accel-PPP configuration
+ from different CLI input nodes. All Accel-PPP services have a common base
+ where value retrival is identical. This function must be used whenever
+ possible when working with Accel-PPP services!
+
+ Return a dictionary with the necessary interface config keys.
+ """
+ from vyos.util import get_half_cpus
+ from vyos.validate import is_ipv4
+
+ dict = config.get_config_dict(base, 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.
+ default_values = defaults(base)
+
+ # defaults include RADIUS server specifics per TAG node which need to be
+ # added to individual RADIUS servers instead - so we can simply delete them
+ if vyos_dict_search('authentication.radius.server', default_values):
+ del default_values['authentication']['radius']['server']
+
+ # defaults include static-ip address per TAG node which need to be added to
+ # individual local users instead - so we can simply delete them
+ if vyos_dict_search('authentication.local_users.username', default_values):
+ del default_values['authentication']['local_users']['username']
+
+ dict = dict_merge(default_values, dict)
+
+ # set CPUs cores to process requests
+ dict.update({'thread_count' : get_half_cpus()})
+ # we need to store the path to the secrets file
+ dict.update({'chap_secrets_file' : chap_secrets})
+
+ # We can only have two IPv4 and three IPv6 nameservers - also they are
+ # configured in a different way in the configuration, this is why we split
+ # the configuration
+ if 'name_server' in dict:
+ ns_v4 = []
+ ns_v6 = []
+ for ns in dict['name_server']:
+ if is_ipv4(ns): ns_v4.append(ns)
+ else: ns_v6.append(ns)
+
+ dict.update({'name_server_ipv4' : ns_v4, 'name_server_ipv6' : ns_v6})
+ del dict['name_server']
+
+ # Add individual RADIUS server default values
+ if vyos_dict_search('authentication.radius.server', dict):
+ default_values = defaults(base + ['authentication', 'radius', 'server'])
+
+ for server in vyos_dict_search('authentication.radius.server', dict):
+ dict['authentication']['radius']['server'][server] = dict_merge(
+ default_values, dict['authentication']['radius']['server'][server])
+
+ # Add individual local-user default values
+ if vyos_dict_search('authentication.local_users.username', dict):
+ default_values = defaults(base + ['authentication', 'local_users', 'username'])
+
+ for username in vyos_dict_search('authentication.local_users.username', dict):
+ dict['authentication']['local_users']['username'][username] = dict_merge(
+ default_values, dict['authentication']['local_users']['username'][username])
+
+ return dict
diff --git a/python/vyos/configverify.py b/python/vyos/configverify.py
index 944fc4294..f970ca6de 100644
--- a/python/vyos/configverify.py
+++ b/python/vyos/configverify.py
@@ -22,6 +22,7 @@
# makes use of it!
from vyos import ConfigError
+from vyos.util import vyos_dict_search
def verify_mtu(config):
"""
@@ -51,7 +52,6 @@ def verify_mtu_ipv6(config):
configured on the interface. IPv6 requires a 1280 bytes MTU.
"""
from vyos.validate import is_ipv6
- from vyos.util import vyos_dict_search
# IPv6 minimum required link mtu
min_mtu = 1280
@@ -204,3 +204,58 @@ def verify_vlan_config(config):
verify_dhcpv6(vlan)
verify_address(vlan)
verify_vrf(vlan)
+
+def verify_accel_ppp_base_service(config):
+ """
+ Common helper function which must be used by all Accel-PPP services based
+ on get_config_dict()
+ """
+ # vertify auth settings
+ if vyos_dict_search('authentication.mode', config) == 'local':
+ if not vyos_dict_search('authentication.local_users', config):
+ raise ConfigError('PPPoE local auth mode requires local users to be configured!')
+
+ for user in vyos_dict_search('authentication.local_users.username', config):
+ user_config = config['authentication']['local_users']['username'][user]
+
+ if 'password' not in user_config:
+ raise ConfigError(f'Password required for local user "{user}"')
+
+ if 'rate_limit' in user_config:
+ # if up/download is set, check that both have a value
+ if not {'upload', 'download'} <= set(user_config['rate_limit']):
+ raise ConfigError(f'User "{user}" has rate-limit configured for only one ' \
+ 'direction but both upload and download must be given!')
+
+ elif vyos_dict_search('authentication.mode', config) == 'radius':
+ if not vyos_dict_search('authentication.radius.server', config):
+ raise ConfigError('RADIUS authentication requires at least one server')
+
+ for server in vyos_dict_search('authentication.radius.server', config):
+ radius_config = config['authentication']['radius']['server'][server]
+ if 'key' not in radius_config:
+ raise ConfigError(f'Missing RADIUS secret key for server "{server}"')
+
+ if 'gateway_address' not in config:
+ raise ConfigError('PPPoE server requires gateway-address to be configured!')
+
+ if 'name_server_ipv4' in config:
+ if len(config['name_server_ipv4']) > 2:
+ raise ConfigError('Not more then two IPv4 DNS name-servers ' \
+ 'can be configured')
+
+ if 'name_server_ipv6' in config:
+ if len(config['name_server_ipv6']) > 3:
+ raise ConfigError('Not more then three IPv6 DNS name-servers ' \
+ 'can be configured')
+
+ if 'client_ipv6_pool' in config:
+ ipv6_pool = config['client_ipv6_pool']
+ if 'delegate' in ipv6_pool:
+ if 'prefix' not in ipv6_pool:
+ raise ConfigError('IPv6 "delegate" also requires "prefix" to be defined!')
+
+ for delegate in ipv6_pool['delegate']:
+ if 'delegation_prefix' not in ipv6_pool['delegate'][delegate]:
+ raise ConfigError('delegation-prefix length required!')
+
diff --git a/smoketest/scripts/cli/test_service_pppoe-server.py b/smoketest/scripts/cli/test_service_pppoe-server.py
index 8db002b57..f0c71e2de 100755
--- a/smoketest/scripts/cli/test_service_pppoe-server.py
+++ b/smoketest/scripts/cli/test_service_pppoe-server.py
@@ -47,7 +47,7 @@ class TestServicePPPoEServer(BasicAccelPPPTest.BaseTest):
mtu = '1492'
# validate some common values in the configuration
- for tmp in ['log_syslog', 'pppoe', 'chap-secrets', 'ippool',
+ for tmp in ['log_syslog', 'pppoe', 'ippool',
'auth_mschap_v2', 'auth_mschap_v1', 'auth_chap_md5',
'auth_pap', 'shaper']:
# Settings without values provide None
diff --git a/src/conf_mode/service_pppoe-server.py b/src/conf_mode/service_pppoe-server.py
index 30b382c69..a520120f8 100755
--- a/src/conf_mode/service_pppoe-server.py
+++ b/src/conf_mode/service_pppoe-server.py
@@ -19,13 +19,11 @@ import os
from sys import exit
from vyos.config import Config
-from vyos.configdict import dict_merge
-from vyos.validate import is_ipv4
+from vyos.configdict import get_accel_dict
+from vyos.configverify import verify_accel_ppp_base_service
from vyos.template import render
from vyos.util import call
-from vyos.util import get_half_cpus
from vyos.util import vyos_dict_search
-from vyos.xml import defaults
from vyos import ConfigError
from vyos import airbag
airbag.enable()
@@ -42,108 +40,22 @@ def get_config(config=None):
if not conf.exists(base):
return None
- pppoe = conf.get_config_dict(base, 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.
- default_values = defaults(base)
-
- # defaults include RADIUS server specifics per TAG node which need to be
- # added to individual RADIUS servers instead - so we can simply delete them
- if vyos_dict_search('authentication.radius.server', default_values):
- del default_values['authentication']['radius']['server']
- # defaults include static-ip address per TAG node which need to be added to
- # individual local users instead - so we can simply delete them
- if vyos_dict_search('authentication.local_users.username', default_values):
- del default_values['authentication']['local_users']['username']
-
- pppoe = dict_merge(default_values, pppoe)
-
- # set CPUs cores to process requests
- pppoe.update({'thread_count' : get_half_cpus()})
- # we need to store the path to the secrets file
- pppoe.update({'chap_secrets_file' : pppoe_chap_secrets})
-
- # We can only have two IPv4 and three IPv6 nameservers - also they are
- # configured in a different way in the configuration, this is why we split
- # the configuration
- if 'name_server' in pppoe:
- ns_v4 = []
- ns_v6 = []
- for ns in pppoe['name_server']:
- if is_ipv4(ns): ns_v4.append(ns)
- else: ns_v6.append(ns)
-
- pppoe.update({'name_server_ipv4' : ns_v4, 'name_server_ipv6' : ns_v6})
- del pppoe['name_server']
-
- # Add individual RADIUS server default values
- if vyos_dict_search('authentication.radius.server', pppoe):
- default_values = defaults(base + ['authentication', 'radius', 'server'])
-
- for server in vyos_dict_search('authentication.radius.server', pppoe):
- pppoe['authentication']['radius']['server'][server] = dict_merge(
- default_values, pppoe['authentication']['radius']['server'][server])
-
- # Add individual local-user default values
- if vyos_dict_search('authentication.local_users.username', pppoe):
- default_values = defaults(base + ['authentication', 'local_users', 'username'])
-
- for username in vyos_dict_search('authentication.local_users.username', pppoe):
- pppoe['authentication']['local_users']['username'][username] = dict_merge(
- default_values, pppoe['authentication']['local_users']['username'][username])
-
+ # retrieve common dictionary keys
+ pppoe = get_accel_dict(conf, base, pppoe_chap_secrets)
return pppoe
-
def verify(pppoe):
if not pppoe:
return None
- # vertify auth settings
- if vyos_dict_search('authentication.mode', pppoe) == 'local':
- if not vyos_dict_search('authentication.local_users', pppoe):
- raise ConfigError('PPPoE local auth mode requires local users to be configured!')
-
- for user in vyos_dict_search('authentication.local_users.username', pppoe):
- user_config = pppoe['authentication']['local_users']['username'][user]
-
- if 'password' not in user_config:
- raise ConfigError(f'Password required for local user "{user}"')
-
- if 'rate_limit' in user_config:
- # if up/download is set, check that both have a value
- if not {'upload', 'download'} <= set(user_config['rate_limit']):
- raise ConfigError(f'User "{user}" has rate-limit configured for only one ' \
- 'direction but both upload and download must be given!')
-
- elif vyos_dict_search('authentication.mode', pppoe) == 'radius':
- if not vyos_dict_search('authentication.radius.server', pppoe):
- raise ConfigError('RADIUS authentication requires at least one server')
-
- for server in vyos_dict_search('authentication.radius.server', pppoe):
- radius_config = pppoe['authentication']['radius']['server'][server]
- if 'key' not in radius_config:
- raise ConfigError(f'Missing RADIUS secret key for server "{server}"')
+ verify_accel_ppp_base_service(pppoe)
if 'wins_server' in pppoe and len(pppoe['wins_server']) > 2:
raise ConfigError('Not more then two IPv4 WINS name-servers can be configured')
- if 'name_server_ipv4' in pppoe:
- if len(pppoe['name_server_ipv4']) > 2:
- raise ConfigError('Not more then two IPv4 DNS name-servers ' \
- 'can be configured')
-
- if 'name_server_ipv6' in pppoe:
- if len(pppoe['name_server_ipv6']) > 3:
- raise ConfigError('Not more then three IPv6 DNS name-servers ' \
- 'can be configured')
-
if 'interface' not in pppoe:
raise ConfigError('At least one listen interface must be defined!')
- if 'gateway_address' not in pppoe:
- raise ConfigError('PPPoE server requires gateway-address to be configured!')
-
# local ippool and gateway settings config checks
if not (vyos_dict_search('client_ip_pool.subnet', pppoe) or
(vyos_dict_search('client_ip_pool.start', pppoe) and
diff --git a/src/conf_mode/vpn_sstp.py b/src/conf_mode/vpn_sstp.py
index 3eece1922..2597ba42f 100755
--- a/src/conf_mode/vpn_sstp.py
+++ b/src/conf_mode/vpn_sstp.py
@@ -16,340 +16,66 @@
import os
-from time import sleep
from sys import exit
-from copy import deepcopy
-from stat import S_IRUSR, S_IWUSR, S_IRGRP
from vyos.config import Config
+from vyos.configdict import get_accel_dict
+from vyos.configverify import verify_accel_ppp_base_service
from vyos.template import render
-from vyos.util import call, run, get_half_cpus
-from vyos.validate import is_ipv4
+from vyos.util import call
+from vyos.util import vyos_dict_search
from vyos import ConfigError
-
from vyos import airbag
airbag.enable()
sstp_conf = '/run/accel-pppd/sstp.conf'
sstp_chap_secrets = '/run/accel-pppd/sstp.chap-secrets'
-default_config_data = {
- 'local_users' : [],
- 'auth_mode' : 'local',
- 'auth_proto' : ['auth_mschap_v2'],
- 'chap_secrets_file': sstp_chap_secrets, # used in Jinja2 template
- 'client_ip_pool' : [],
- 'client_ipv6_pool': [],
- 'client_ipv6_delegate_prefix': [],
- 'client_gateway': '',
- 'dnsv4' : [],
- 'dnsv6' : [],
- 'radius_server' : [],
- 'radius_acct_tmo' : '3',
- 'radius_max_try' : '3',
- 'radius_timeout' : '3',
- 'radius_nas_id' : '',
- 'radius_nas_ip' : '',
- 'radius_source_address' : '',
- 'radius_shaper_attr' : '',
- 'radius_shaper_vendor': '',
- 'radius_dynamic_author' : '',
- 'ssl_ca' : '',
- 'ssl_cert' : '',
- 'ssl_key' : '',
- 'mtu' : '',
- 'ppp_mppe' : 'prefer',
- 'ppp_echo_failure' : '',
- 'ppp_echo_interval' : '',
- 'ppp_echo_timeout' : '',
- 'thread_cnt' : get_half_cpus()
-}
-
def get_config(config=None):
- sstp = deepcopy(default_config_data)
- base_path = ['vpn', 'sstp']
if config:
conf = config
else:
conf = Config()
- if not conf.exists(base_path):
+ base = ['vpn', 'sstp']
+ if not conf.exists(base):
return None
- conf.set_level(base_path)
-
- if conf.exists(['authentication', 'mode']):
- sstp['auth_mode'] = conf.return_value(['authentication', 'mode'])
-
- #
- # local auth
- if conf.exists(['authentication', 'local-users']):
- for username in conf.list_nodes(['authentication', 'local-users', 'username']):
- user = {
- 'name' : username,
- 'password' : '',
- 'state' : 'enabled',
- 'ip' : '*',
- 'upload' : None,
- 'download' : None
- }
-
- conf.set_level(base_path + ['authentication', 'local-users', 'username', username])
-
- if conf.exists(['password']):
- user['password'] = conf.return_value(['password'])
-
- if conf.exists(['disable']):
- user['state'] = 'disable'
-
- if conf.exists(['static-ip']):
- user['ip'] = conf.return_value(['static-ip'])
-
- if conf.exists(['rate-limit', 'download']):
- user['download'] = conf.return_value(['rate-limit', 'download'])
-
- if conf.exists(['rate-limit', 'upload']):
- user['upload'] = conf.return_value(['rate-limit', 'upload'])
-
- sstp['local_users'].append(user)
-
- #
- # RADIUS auth and settings
- conf.set_level(base_path + ['authentication', 'radius'])
- if conf.exists(['server']):
- for server in conf.list_nodes(['server']):
- radius = {
- 'server' : server,
- 'key' : '',
- 'fail_time' : 0,
- 'port' : '1812',
- 'acct_port' : '1813'
- }
-
- conf.set_level(base_path + ['authentication', 'radius', 'server', server])
-
- if conf.exists(['fail-time']):
- radius['fail_time'] = conf.return_value(['fail-time'])
-
- if conf.exists(['port']):
- radius['port'] = conf.return_value(['port'])
-
- if conf.exists(['acct-port']):
- radius['acct_port'] = conf.return_value(['acct-port'])
-
- if conf.exists(['key']):
- radius['key'] = conf.return_value(['key'])
-
- if not conf.exists(['disable']):
- sstp['radius_server'].append(radius)
-
- #
- # advanced radius-setting
- conf.set_level(base_path + ['authentication', 'radius'])
-
- if conf.exists(['acct-timeout']):
- sstp['radius_acct_tmo'] = conf.return_value(['acct-timeout'])
-
- if conf.exists(['max-try']):
- sstp['radius_max_try'] = conf.return_value(['max-try'])
-
- if conf.exists(['timeout']):
- sstp['radius_timeout'] = conf.return_value(['timeout'])
-
- if conf.exists(['nas-identifier']):
- sstp['radius_nas_id'] = conf.return_value(['nas-identifier'])
-
- if conf.exists(['nas-ip-address']):
- sstp['radius_nas_ip'] = conf.return_value(['nas-ip-address'])
-
- if conf.exists(['source-address']):
- sstp['radius_source_address'] = conf.return_value(['source-address'])
-
- # Dynamic Authorization Extensions (DOA)/Change Of Authentication (COA)
- if conf.exists(['dynamic-author']):
- dae = {
- 'port' : '1700',
- 'server' : '',
- 'key' : ''
- }
-
- if conf.exists(['dynamic-author', 'server']):
- dae['server'] = conf.return_value(['dynamic-author', 'server'])
-
- if conf.exists(['dynamic-author', 'port']):
- dae['port'] = conf.return_value(['dynamic-author', 'port'])
-
- if conf.exists(['dynamic-author', 'key']):
- dae['key'] = conf.return_value(['dynamic-author', 'key'])
-
- sstp['radius_dynamic_author'] = dae
-
- if conf.exists(['rate-limit', 'enable']):
- sstp['radius_shaper_attr'] = 'Filter-Id'
- c_attr = ['rate-limit', 'enable', 'attribute']
- if conf.exists(c_attr):
- sstp['radius_shaper_attr'] = conf.return_value(c_attr)
-
- c_vendor = ['rate-limit', 'enable', 'vendor']
- if conf.exists(c_vendor):
- sstp['radius_shaper_vendor'] = conf.return_value(c_vendor)
-
- #
- # authentication protocols
- conf.set_level(base_path + ['authentication'])
- if conf.exists(['protocols']):
- # clear default list content, now populate with actual CLI values
- sstp['auth_proto'] = []
- auth_mods = {
- 'pap': 'auth_pap',
- 'chap': 'auth_chap_md5',
- 'mschap': 'auth_mschap_v1',
- 'mschap-v2': 'auth_mschap_v2'
- }
-
- for proto in conf.return_values(['protocols']):
- sstp['auth_proto'].append(auth_mods[proto])
-
- #
- # read in SSL certs
- conf.set_level(base_path + ['ssl'])
- if conf.exists(['ca-cert-file']):
- sstp['ssl_ca'] = conf.return_value(['ca-cert-file'])
-
- if conf.exists(['cert-file']):
- sstp['ssl_cert'] = conf.return_value(['cert-file'])
-
- if conf.exists(['key-file']):
- sstp['ssl_key'] = conf.return_value(['key-file'])
-
-
- #
- # read in client IPv4 pool
- conf.set_level(base_path + ['client-ip-pool'])
- if conf.exists(['subnet']):
- sstp['client_ip_pool'] = conf.return_values(['subnet'])
-
- #
- # read in client IPv6 pool
- conf.set_level(base_path + ['client-ipv6-pool'])
- if conf.exists(['prefix']):
- for prefix in conf.list_nodes(['prefix']):
- tmp = {
- 'prefix': prefix,
- 'mask': '64'
- }
-
- if conf.exists(['prefix', prefix, 'mask']):
- tmp['mask'] = conf.return_value(['prefix', prefix, 'mask'])
-
- sstp['client_ipv6_pool'].append(tmp)
-
- if conf.exists(['delegate']):
- for prefix in conf.list_nodes(['delegate']):
- tmp = {
- 'prefix': prefix,
- 'mask': ''
- }
-
- if conf.exists(['delegate', prefix, 'delegation-prefix']):
- tmp['mask'] = conf.return_value(['delegate', prefix, 'delegation-prefix'])
-
- sstp['client_ipv6_delegate_prefix'].append(tmp)
-
- #
- # read in network settings
- conf.set_level(base_path)
- if conf.exists(['gateway-address']):
- sstp['client_gateway'] = conf.return_value(['gateway-address'])
-
- if conf.exists(['name-server']):
- for name_server in conf.return_values(['name-server']):
- if is_ipv4(name_server):
- sstp['dnsv4'].append(name_server)
- else:
- sstp['dnsv6'].append(name_server)
-
- if conf.exists(['mtu']):
- sstp['mtu'] = conf.return_value(['mtu'])
-
- #
- # read in PPP stuff
- conf.set_level(base_path + ['ppp-options'])
- if conf.exists('mppe'):
- sstp['ppp_mppe'] = conf.return_value(['mppe'])
-
- if conf.exists(['lcp-echo-failure']):
- sstp['ppp_echo_failure'] = conf.return_value(['lcp-echo-failure'])
-
- if conf.exists(['lcp-echo-interval']):
- sstp['ppp_echo_interval'] = conf.return_value(['lcp-echo-interval'])
-
- if conf.exists(['lcp-echo-timeout']):
- sstp['ppp_echo_timeout'] = conf.return_value(['lcp-echo-timeout'])
-
+ # retrieve common dictionary keys
+ sstp = get_accel_dict(conf, base, sstp_chap_secrets)
return sstp
-
def verify(sstp):
- if sstp is None:
+ if not sstp:
return None
- # vertify auth settings
- if sstp['auth_mode'] == 'local':
- if not sstp['local_users']:
- raise ConfigError('SSTP local auth mode requires local users to be configured!')
-
- for user in sstp['local_users']:
- username = user['name']
- if not user['password']:
- raise ConfigError(f'Password required for local user "{username}"')
-
- # if up/download is set, check that both have a value
- if user['upload'] and not user['download']:
- raise ConfigError(f'Download speed value required for local user "{username}"')
-
- if user['download'] and not user['upload']:
- raise ConfigError(f'Upload speed value required for local user "{username}"')
-
- if not sstp['client_ip_pool']:
- raise ConfigError('Client IP subnet required')
-
- if not sstp['client_gateway']:
- raise ConfigError('Client gateway IP address required')
-
- if len(sstp['dnsv4']) > 2:
- raise ConfigError('Not more then two IPv4 DNS name-servers can be configured')
+ verify_accel_ppp_base_service(sstp)
- # check ipv6
- if sstp['client_ipv6_delegate_prefix'] and not sstp['client_ipv6_pool']:
- raise ConfigError('IPv6 prefix delegation requires client-ipv6-pool prefix')
+ if not sstp['client_ip_pool']:
+ raise ConfigError('Client IP subnet required')
- for prefix in sstp['client_ipv6_delegate_prefix']:
- if not prefix['mask']:
- raise ConfigError('Delegation-prefix required for individual delegated networks')
-
- if not sstp['ssl_ca'] or not sstp['ssl_cert'] or not sstp['ssl_key']:
- raise ConfigError('One or more SSL certificates missing')
-
- if not os.path.exists(sstp['ssl_ca']):
- file = sstp['ssl_ca']
- raise ConfigError(f'SSL CA certificate file "{file}" does not exist')
-
- if not os.path.exists(sstp['ssl_cert']):
- file = sstp['ssl_cert']
- raise ConfigError(f'SSL public key file "{file}" does not exist')
-
- if not os.path.exists(sstp['ssl_key']):
- file = sstp['ssl_key']
- raise ConfigError(f'SSL private key file "{file}" does not exist')
+ #
+ # SSL certificate checks
+ #
+ tmp = vyos_dict_search('ssl.ca_cert_file', sstp)
+ if not tmp:
+ raise ConfigError(f'SSL CA certificate file required!')
+ else:
+ if not os.path.isfile(tmp):
+ raise ConfigError(f'SSL CA certificate "{tmp}" does not exist!')
- if sstp['auth_mode'] == 'radius':
- if len(sstp['radius_server']) == 0:
- raise ConfigError('RADIUS authentication requires at least one server')
+ tmp = vyos_dict_search('ssl.cert_file', sstp)
+ if not tmp:
+ raise ConfigError(f'SSL public key file required!')
+ else:
+ if not os.path.isfile(tmp):
+ raise ConfigError(f'SSL public key "{tmp}" does not exist!')
- for radius in sstp['radius_server']:
- if not radius['key']:
- server = radius['server']
- raise ConfigError(f'Missing RADIUS secret key for server "{ server }"')
+ tmp = vyos_dict_search('ssl.key_file', sstp)
+ if not tmp:
+ raise ConfigError(f'SSL private key file required!')
+ else:
+ if not os.path.isfile(tmp):
+ raise ConfigError(f'SSL private key "{tmp}" does not exist!')
def generate(sstp):
if not sstp:
@@ -358,9 +84,9 @@ def generate(sstp):
# accel-cmd reload doesn't work so any change results in a restart of the daemon
render(sstp_conf, 'accel-ppp/sstp.config.tmpl', sstp, trim_blocks=True)
- if sstp['local_users']:
- render(sstp_chap_secrets, 'accel-ppp/chap-secrets.tmpl', sstp, trim_blocks=True)
- os.chmod(sstp_chap_secrets, S_IRUSR | S_IWUSR | S_IRGRP)
+ if vyos_dict_search('authentication.mode', sstp) == 'local':
+ render(sstp_chap_secrets, 'accel-ppp/chap-secrets.config_dict.tmpl',
+ sstp, trim_blocks=True, permission=0o640)
else:
if os.path.exists(sstp_chap_secrets):
os.unlink(sstp_chap_secrets)