diff options
author | goodNETnick <33053932+goodNETnick@users.noreply.github.com> | 2022-04-01 12:09:56 +1000 |
---|---|---|
committer | goodNETnick <pknet@ya.ru> | 2022-04-09 01:33:25 -0400 |
commit | 1da9cc02d7c83898c267070618e2cc91e16eb1cf (patch) | |
tree | bfe672212ef22b525420428d3f36ff02d6cd5aa0 /src | |
parent | aa5b35b68c1170bfd0b9661bafa72bb10fe6ca95 (diff) | |
parent | 53e20097d227ebf4bdb4dc6c85427ec9c5ec3982 (diff) | |
download | vyos-1x-1da9cc02d7c83898c267070618e2cc91e16eb1cf.tar.gz vyos-1x-1da9cc02d7c83898c267070618e2cc91e16eb1cf.zip |
ocserv: T4231: Added OTP support for Openconnect 2FA
Diffstat (limited to 'src')
-rwxr-xr-x | src/conf_mode/vpn_openconnect.py | 30 | ||||
-rwxr-xr-x | src/conf_mode/vrf.py | 8 | ||||
-rwxr-xr-x | src/migration-scripts/openconnect/1-to-2 | 14 | ||||
-rw-r--r-- | src/tests/test_util.py | 15 |
4 files changed, 46 insertions, 21 deletions
diff --git a/src/conf_mode/vpn_openconnect.py b/src/conf_mode/vpn_openconnect.py index 8e100d9f9..84d31f9a5 100755 --- a/src/conf_mode/vpn_openconnect.py +++ b/src/conf_mode/vpn_openconnect.py @@ -24,6 +24,7 @@ from vyos.pki import wrap_private_key from vyos.template import render from vyos.util import call from vyos.util import is_systemd_service_running +from vyos.util import dict_search from vyos.xml import defaults from vyos import ConfigError from crypt import crypt, mksalt, METHOD_SHA512 @@ -55,6 +56,16 @@ def get_config(): default_values = defaults(base) ocserv = dict_merge(default_values, ocserv) + # workaround a "know limitation" - https://phabricator.vyos.net/T2665 + del ocserv['authentication']['local_users']['username']['otp'] + if not ocserv["authentication"]["local_users"]["username"]: + raise ConfigError('openconnect mode local required at least one user') + default_ocserv_usr_values = default_values['authentication']['local_users']['username']['otp'] + for user, params in ocserv['authentication']['local_users']['username'].items(): + # Not every configuration requires OTP settings + if ocserv['authentication']['local_users']['username'][user].get('otp'): + ocserv['authentication']['local_users']['username'][user]['otp'] = dict_merge(default_ocserv_usr_values, ocserv['authentication']['local_users']['username'][user]['otp']) + if ocserv: ocserv['pki'] = conf.get_config_dict(['pki'], key_mangling=('-', '_'), get_first_key=True, no_tag_node_value_mangle=True) @@ -69,19 +80,18 @@ def verify(ocserv): if "mode" in ocserv["authentication"]: if "local" in ocserv["authentication"]["mode"]: if "radius" in ocserv["authentication"]["mode"]: - raise ConfigError('openconnect supports only one authentication mode. Currently configured \'local\' and \'radius\' modes') + raise ConfigError('OpenConnect authentication modes are mutually-exclusive, remove either local or radius from your configuration') if not ocserv["authentication"]["local_users"]: - raise ConfigError('openconnect mode local required at leat one user') + raise ConfigError('openconnect mode local required at least one user') if not ocserv["authentication"]["local_users"]["username"]: - raise ConfigError('openconnect mode local required at leat one user') + raise ConfigError('openconnect mode local required at least one user') else: # For OTP mode: verify that each local user has an OTP key if "otp" in ocserv["authentication"]["mode"]["local"]: users_wo_key = [] - for user in ocserv["authentication"]["local_users"]["username"]: - if not ocserv["authentication"]["local_users"]["username"][user].get("otp"): - users_wo_key.append(user) - elif not ocserv["authentication"]["local_users"]["username"][user]["otp"].get("key"): + for user, user_config in ocserv["authentication"]["local_users"]["username"].items(): + # User has no OTP key defined + if dict_search('otp.key', user_config) == None: users_wo_key.append(user) if users_wo_key: raise ConfigError(f'OTP enabled, but no OTP key is configured for these users:\n{users_wo_key}') @@ -156,9 +166,9 @@ def generate(ocserv): if "local_users" in ocserv["authentication"]: for user in ocserv["authentication"]["local_users"]["username"]: # OTP token type from CLI parameters: - otp_interval = ocserv["authentication"]["local_users"]["username"][user]["otp"].get("interval", "30") - token_type = ocserv["authentication"]["local_users"]["username"][user]["otp"].get("token-type", "hotp-time") - otp_length = ocserv["authentication"]["local_users"]["username"][user]["otp"].get("otp-length", "6") + otp_interval = str(ocserv["authentication"]["local_users"]["username"][user]["otp"].get("interval")) + token_type = ocserv["authentication"]["local_users"]["username"][user]["otp"].get("token_type") + otp_length = str(ocserv["authentication"]["local_users"]["username"][user]["otp"].get("otp_length")) if token_type == "hotp-time": otp_type = "HOTP/T" + otp_interval elif token_type == "hotp-event": diff --git a/src/conf_mode/vrf.py b/src/conf_mode/vrf.py index 6a521a0dd..c3e2d8efd 100755 --- a/src/conf_mode/vrf.py +++ b/src/conf_mode/vrf.py @@ -30,6 +30,7 @@ from vyos.util import get_interface_config from vyos.util import popen from vyos.util import run from vyos.util import sysctl_write +from vyos.util import is_ipv6_enabled from vyos import ConfigError from vyos import frr from vyos import airbag @@ -215,10 +216,11 @@ def apply(vrf): # set VRF description for e.g. SNMP monitoring vrf_if = Interface(name) - # We also should add proper loopback IP addresses to the newly - # created VRFs for services bound to the loopback address (SNMP, NTP) + # We also should add proper loopback IP addresses to the newly added + # VRF for services bound to the loopback address (SNMP, NTP) vrf_if.add_addr('127.0.0.1/8') - vrf_if.add_addr('::1/128') + if is_ipv6_enabled(): + vrf_if.add_addr('::1/128') # add VRF description if available vrf_if.set_alias(config.get('description', '')) diff --git a/src/migration-scripts/openconnect/1-to-2 b/src/migration-scripts/openconnect/1-to-2 index 36d807a3c..7031fb252 100755 --- a/src/migration-scripts/openconnect/1-to-2 +++ b/src/migration-scripts/openconnect/1-to-2 @@ -37,15 +37,15 @@ if not config.exists(cfg_base): # Nothing to do sys.exit(0) else: - if config.exists(cfg_base + ['authentication'] + ['mode']): - if config.return_value(cfg_base + ['authentication'] + ['mode']) == 'radius': + if config.exists(cfg_base + ['authentication', 'mode']): + if config.return_value(cfg_base + ['authentication', 'mode']) == 'radius': # if "mode value radius", change to "tag node mode + valueless node radius" - config.delete(cfg_base + ['authentication'] + ['mode'] + ['radius']) - config.set(cfg_base + ['authentication'] + ['mode'] + ['radius'], value=None, replace=True) - elif not config.exists(cfg_base + ['authentication'] + ['mode'] + ['local']): + config.delete(cfg_base + ['authentication','mode', 'radius']) + config.set(cfg_base + ['authentication', 'mode', 'radius'], value=None, replace=True) + elif not config.exists(cfg_base + ['authentication', 'mode', 'local']): # if "mode local", change to "tag node mode + node local value password" - config.delete(cfg_base + ['authentication'] + ['mode'] + ['local']) - config.set(cfg_base + ['authentication'] + ['mode'] + ['local'], value='password', replace=True) + config.delete(cfg_base + ['authentication', 'mode', 'local']) + config.set(cfg_base + ['authentication', 'mode', 'local'], value='password', replace=True) try: with open(file_name, 'w') as f: f.write(config.to_string()) diff --git a/src/tests/test_util.py b/src/tests/test_util.py index 9bd27adc0..91890262c 100644 --- a/src/tests/test_util.py +++ b/src/tests/test_util.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2020-2021 VyOS maintainers and contributors +# Copyright (C) 2020-2022 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 @@ -23,3 +23,16 @@ class TestVyOSUtil(TestCase): expected_data = {"foo_bar": {"baz_quux": None}} new_data = mangle_dict_keys(data, '-', '_') self.assertEqual(new_data, expected_data) + + def test_sysctl_read(self): + self.assertEqual(sysctl_read('net.ipv4.conf.lo.forwarding'), '1') + + def test_ipv6_enabled(self): + tmp = sysctl_read('net.ipv6.conf.all.disable_ipv6') + # We need to test for both variants as this depends on how the + # Docker container is started (with or without IPv6 support) - so we + # will simply check both cases to not make the users life miserable. + if tmp == '0': + self.assertTrue(is_ipv6_enabled()) + else: + self.assertFalse(is_ipv6_enabled()) |