summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorgoodNETnick <33053932+goodNETnick@users.noreply.github.com>2022-04-01 12:09:56 +1000
committergoodNETnick <pknet@ya.ru>2022-04-09 01:33:25 -0400
commit1da9cc02d7c83898c267070618e2cc91e16eb1cf (patch)
treebfe672212ef22b525420428d3f36ff02d6cd5aa0
parentaa5b35b68c1170bfd0b9661bafa72bb10fe6ca95 (diff)
parent53e20097d227ebf4bdb4dc6c85427ec9c5ec3982 (diff)
downloadvyos-1x-1da9cc02d7c83898c267070618e2cc91e16eb1cf.tar.gz
vyos-1x-1da9cc02d7c83898c267070618e2cc91e16eb1cf.zip
ocserv: T4231: Added OTP support for Openconnect 2FA
-rw-r--r--data/templates/frr/bgpd.frr.tmpl3
-rw-r--r--data/templates/ocserv/ocserv_otp_usr.tmpl2
-rw-r--r--interface-definitions/include/auth-local-users.xml.i19
-rw-r--r--interface-definitions/include/bgp/protocol-common-config.xml.i6
-rw-r--r--interface-definitions/vpn_openconnect.xml.in4
-rw-r--r--op-mode-definitions/show-log.xml.in73
-rwxr-xr-xpython/vyos/ifconfig/interface.py16
-rw-r--r--python/vyos/ifconfig/loopback.py13
-rw-r--r--python/vyos/util.py4
-rwxr-xr-xsmoketest/scripts/cli/test_protocols_bgp.py2
-rwxr-xr-xsmoketest/scripts/cli/test_system_ipv6.py31
-rwxr-xr-xsmoketest/scripts/cli/test_vpn_openconnect.py5
-rwxr-xr-xsmoketest/scripts/cli/test_vrf.py54
-rwxr-xr-xsrc/conf_mode/vpn_openconnect.py30
-rwxr-xr-xsrc/conf_mode/vrf.py8
-rwxr-xr-xsrc/migration-scripts/openconnect/1-to-214
-rw-r--r--src/tests/test_util.py15
17 files changed, 246 insertions, 53 deletions
diff --git a/data/templates/frr/bgpd.frr.tmpl b/data/templates/frr/bgpd.frr.tmpl
index 45e0544b7..0bc0fd36e 100644
--- a/data/templates/frr/bgpd.frr.tmpl
+++ b/data/templates/frr/bgpd.frr.tmpl
@@ -545,6 +545,9 @@ router bgp {{ local_as }} {{ 'vrf ' ~ vrf if vrf is defined and vrf is not none
{% if parameters.no_fast_external_failover is defined %}
no bgp fast-external-failover
{% endif %}
+{% if parameters.no_suppress_duplicates is defined %}
+ no bgp suppress-duplicates
+{% endif %}
{% if parameters.reject_as_sets is defined %}
bgp reject-as-sets
{% endif %}
diff --git a/data/templates/ocserv/ocserv_otp_usr.tmpl b/data/templates/ocserv/ocserv_otp_usr.tmpl
index db8893ae8..fea9af5d5 100644
--- a/data/templates/ocserv/ocserv_otp_usr.tmpl
+++ b/data/templates/ocserv/ocserv_otp_usr.tmpl
@@ -1,6 +1,6 @@
#<token_type> <username> <pin> <secret_hex_key> <counter> <lastpass> <time>
{% if username is defined %}
-{% for user, user_config in username.itmes() %}
+{% for user, user_config in username.items() %}
{% if user_config.disable is not defined and user_config.otp is defined and user_config.otp is not none %}
{{ user_config.otp.token_tmpl }} {{ user }} {{ user_config.otp.pin | default("-", true) }} {{ user_config.otp.key }}
{% endif %}
diff --git a/interface-definitions/include/auth-local-users.xml.i b/interface-definitions/include/auth-local-users.xml.i
index add2fc8e1..cb456eecf 100644
--- a/interface-definitions/include/auth-local-users.xml.i
+++ b/interface-definitions/include/auth-local-users.xml.i
@@ -34,45 +34,47 @@
<constraint>
<regex>[a-fA-F0-9]{20,10000}</regex>
</constraint>
- <constraintErrorMessage>Key name must in hex be alphanumerical only (min. 20 hex characters)</constraintErrorMessage>
+ <constraintErrorMessage>Key name must only include hex characters and be at least 20 characters long</constraintErrorMessage>
</properties>
</leafNode>
<leafNode name="otp-length">
<properties>
- <help>Optional. Number of digits in OTP code (default: 6)</help>
+ <help>Number of digits in OTP code</help>
<valueHelp>
<format>u32:6-8</format>
- <description>Number of digits in OTP code (default: 6)</description>
+ <description>Number of digits in OTP code</description>
</valueHelp>
<constraint>
<validator name="numeric" argument="--range 6-8"/>
</constraint>
<constraintErrorMessage>Number of digits in OTP code must be between 6 and 8</constraintErrorMessage>
</properties>
+ <defaultValue>6</defaultValue>
</leafNode>
<leafNode name="interval">
<properties>
- <help>Optional. Time tokens interval in seconds (for time tokens) (default: 30)</help>
+ <help>Time tokens interval in seconds</help>
<valueHelp>
<format>u32:5-86400</format>
- <description>Time tokens interval in seconds (for time tokens). (default: 30)</description>
+ <description>Time tokens interval in seconds.</description>
</valueHelp>
<constraint>
<validator name="numeric" argument="--range 5-86400"/>
</constraint>
<constraintErrorMessage>Time token interval must be between 5 and 86400 seconds</constraintErrorMessage>
</properties>
+ <defaultValue>30</defaultValue>
</leafNode>
<leafNode name="token-type">
<properties>
- <help>Optional. Token type (default: hotp-time)</help>
+ <help>Token type</help>
<valueHelp>
<format>hotp-time</format>
- <description>time-based OTP algorithm</description>
+ <description>Time-based OTP algorithm</description>
</valueHelp>
<valueHelp>
<format>hotp-event</format>
- <description>event-based OTP algorithm</description>
+ <description>Event-based OTP algorithm</description>
</valueHelp>
<constraint>
<regex>(hotp-time|hotp-event)</regex>
@@ -81,6 +83,7 @@
<list>hotp-time hotp-event</list>
</completionHelp>
</properties>
+ <defaultValue>hotp-time</defaultValue>
</leafNode>
</children>
</node>
diff --git a/interface-definitions/include/bgp/protocol-common-config.xml.i b/interface-definitions/include/bgp/protocol-common-config.xml.i
index 38337b032..b59ff0287 100644
--- a/interface-definitions/include/bgp/protocol-common-config.xml.i
+++ b/interface-definitions/include/bgp/protocol-common-config.xml.i
@@ -1430,6 +1430,12 @@
<valueless/>
</properties>
</leafNode>
+ <leafNode name="no-suppress-duplicates">
+ <properties>
+ <help>Disable suppress duplicate updates if the route actually not changed</help>
+ <valueless/>
+ </properties>
+ </leafNode>
<leafNode name="reject-as-sets">
<properties>
<help>Reject routes with AS_SET or AS_CONFED_SET flag</help>
diff --git a/interface-definitions/vpn_openconnect.xml.in b/interface-definitions/vpn_openconnect.xml.in
index 631c3b739..05458ed34 100644
--- a/interface-definitions/vpn_openconnect.xml.in
+++ b/interface-definitions/vpn_openconnect.xml.in
@@ -23,7 +23,7 @@
<help>Use local username/password configuration (OTP supported)</help>
<valueHelp>
<format>password</format>
- <description>Password-only local authentication (default)</description>
+ <description>Password-only local authentication</description>
</valueHelp>
<valueHelp>
<format>otp</format>
@@ -36,7 +36,7 @@
<constraint>
<regex>^(password|otp|password-otp)$</regex>
</constraint>
- <constraintErrorMessage>Invalid authentication mode</constraintErrorMessage>
+ <constraintErrorMessage>Invalid authentication mode. Must be one of: password, otp or password-otp </constraintErrorMessage>
<completionHelp>
<list>otp password password-otp</list>
</completionHelp>
diff --git a/op-mode-definitions/show-log.xml.in b/op-mode-definitions/show-log.xml.in
index 4c0a7913b..2d75f119d 100644
--- a/op-mode-definitions/show-log.xml.in
+++ b/op-mode-definitions/show-log.xml.in
@@ -212,6 +212,79 @@
</tagNode>
</children>
</node>
+ <node name="protocol">
+ <properties>
+ <help>Show log for Routing Protocols</help>
+ </properties>
+ <children>
+ <leafNode name="ospf">
+ <properties>
+ <help>Show log for OSPF</help>
+ </properties>
+ <command>journalctl -b /usr/lib/frr/ospfd</command>
+ </leafNode>
+ <leafNode name="ospfv3">
+ <properties>
+ <help>Show log for OSPF for IPv6</help>
+ </properties>
+ <command>journalctl -b /usr/lib/frr/ospf6d</command>
+ </leafNode>
+ <leafNode name="bgp">
+ <properties>
+ <help>Show log for BGP</help>
+ </properties>
+ <command>journalctl -b /usr/lib/frr/bgpd</command>
+ </leafNode>
+ <leafNode name="rip">
+ <properties>
+ <help>Show log for RIP</help>
+ </properties>
+ <command>journalctl -b /usr/lib/frr/ripd</command>
+ </leafNode>
+ <leafNode name="ripng">
+ <properties>
+ <help>Show log for RIPng</help>
+ </properties>
+ <command>journalctl -b /usr/lib/frr/ripngd</command>
+ </leafNode>
+ <leafNode name="static">
+ <properties>
+ <help>Show log for static route</help>
+ </properties>
+ <command>journalctl -b /usr/lib/frr/staticd</command>
+ </leafNode>
+ <leafNode name="multicast">
+ <properties>
+ <help>Show log for Multicast protocol</help>
+ </properties>
+ <command>journalctl -b /usr/lib/frr/pimd</command>
+ </leafNode>
+ <leafNode name="isis">
+ <properties>
+ <help>Show log for ISIS</help>
+ </properties>
+ <command>journalctl -b /usr/lib/frr/isisd</command>
+ </leafNode>
+ <leafNode name="nhrp">
+ <properties>
+ <help>Show log for NHRP</help>
+ </properties>
+ <command>journalctl -b /usr/lib/frr/nhrpd</command>
+ </leafNode>
+ <leafNode name="bfd">
+ <properties>
+ <help>Show log for BFD</help>
+ </properties>
+ <command>journalctl -b /usr/lib/frr/bfdd</command>
+ </leafNode>
+ <leafNode name="mpls">
+ <properties>
+ <help>Show log for MPLS</help>
+ </properties>
+ <command>journalctl -b /usr/lib/frr/ldpd</command>
+ </leafNode>
+ </children>
+ </node>
<leafNode name="snmp">
<properties>
<help>Show log for Simple Network Monitoring Protocol (SNMP)</help>
diff --git a/python/vyos/ifconfig/interface.py b/python/vyos/ifconfig/interface.py
index 4fda1c0a9..f39da90e4 100755
--- a/python/vyos/ifconfig/interface.py
+++ b/python/vyos/ifconfig/interface.py
@@ -39,7 +39,7 @@ from vyos.util import read_file
from vyos.util import get_interface_config
from vyos.util import get_interface_namespace
from vyos.util import is_systemd_service_active
-from vyos.util import sysctl_read
+from vyos.util import is_ipv6_enabled
from vyos.template import is_ipv4
from vyos.template import is_ipv6
from vyos.validate import is_intf_addr_assigned
@@ -1083,6 +1083,10 @@ class Interface(Control):
addr_is_v4 = is_ipv4(addr)
+ # Failsave - do not add IPv6 address if IPv6 is disabled
+ if is_ipv6(addr) and not is_ipv6_enabled():
+ return False
+
# add to interface
if addr == 'dhcp':
self.set_dhcp(True)
@@ -1498,7 +1502,7 @@ class Interface(Control):
self.set_ipv4_source_validation(value)
# Only change IPv6 parameters if IPv6 was not explicitly disabled
- if sysctl_read('net.ipv6.conf.all.disable_ipv6') == '0':
+ if is_ipv6_enabled():
# Configure MSS value for IPv6 TCP connections
tmp = dict_search('ipv6.adjust_mss', config)
value = tmp if (tmp != None) else '0'
@@ -1526,10 +1530,6 @@ class Interface(Control):
value = tmp if (tmp != None) else '1'
self.set_ipv6_dad_messages(value)
- # MTU - Maximum Transfer Unit
- if 'mtu' in config:
- self.set_mtu(config.get('mtu'))
-
# Delete old IPv6 EUI64 addresses before changing MAC
for addr in (dict_search('ipv6.address.eui64_old', config) or []):
self.del_ipv6_eui64_address(addr)
@@ -1546,6 +1546,10 @@ class Interface(Control):
for addr in tmp:
self.add_ipv6_eui64_address(addr)
+ # MTU - Maximum Transfer Unit
+ if 'mtu' in config:
+ self.set_mtu(config.get('mtu'))
+
# re-add ourselves to any bridge we might have fallen out of
if 'is_bridge_member' in config:
bridge_dict = config.get('is_bridge_member')
diff --git a/python/vyos/ifconfig/loopback.py b/python/vyos/ifconfig/loopback.py
index de554ef44..30c890fdf 100644
--- a/python/vyos/ifconfig/loopback.py
+++ b/python/vyos/ifconfig/loopback.py
@@ -13,9 +13,8 @@
# You should have received a copy of the GNU Lesser General Public
# License along with this library. If not, see <http://www.gnu.org/licenses/>.
-import vyos.util
-
from vyos.ifconfig.interface import Interface
+from vyos.util import is_ipv6_enabled
@Interface.register
class LoopbackIf(Interface):
@@ -34,8 +33,6 @@ class LoopbackIf(Interface):
}
}
- name = 'loopback'
-
def remove(self):
"""
Loopback interface can not be deleted from operating system. We can
@@ -62,11 +59,11 @@ class LoopbackIf(Interface):
on any interface. """
addr = config.get('address', [])
- # We must ensure that the loopback addresses are never deleted from the system
- addr += ['127.0.0.1/8']
- if (vyos.util.sysctl_read('net.ipv6.conf.all.disable_ipv6') == '0'):
- addr += ['::1/128']
+ # We must ensure that the loopback addresses are never deleted from the system
+ addr.append('127.0.0.1/8')
+ if is_ipv6_enabled():
+ addr.append('::1/128')
# Update IP address entry in our dictionary
config.update({'address' : addr})
diff --git a/python/vyos/util.py b/python/vyos/util.py
index f46775490..f3f323c34 100644
--- a/python/vyos/util.py
+++ b/python/vyos/util.py
@@ -1019,3 +1019,7 @@ def sysctl_write(name, value):
call(f'sysctl -wq {name}={value}')
return True
return False
+
+def is_ipv6_enabled() -> bool:
+ """ Check if IPv6 support on the system is enabled or not """
+ return (sysctl_read('net.ipv6.conf.all.disable_ipv6') == '0')
diff --git a/smoketest/scripts/cli/test_protocols_bgp.py b/smoketest/scripts/cli/test_protocols_bgp.py
index d7230baf4..db1587ba7 100755
--- a/smoketest/scripts/cli/test_protocols_bgp.py
+++ b/smoketest/scripts/cli/test_protocols_bgp.py
@@ -274,6 +274,7 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase):
self.cli_set(base_path + ['parameters', 'conditional-advertisement', 'timer', cond_adv_timer])
self.cli_set(base_path + ['parameters', 'fast-convergence'])
self.cli_set(base_path + ['parameters', 'minimum-holdtime', min_hold_time])
+ self.cli_set(base_path + ['parameters', 'no-suppress-duplicates'])
self.cli_set(base_path + ['parameters', 'reject-as-sets'])
self.cli_set(base_path + ['parameters', 'shutdown'])
self.cli_set(base_path + ['parameters', 'suppress-fib-pending'])
@@ -305,6 +306,7 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase):
self.assertIn(f' bgp shutdown', frrconfig)
self.assertIn(f' bgp suppress-fib-pending', frrconfig)
self.assertNotIn(f'bgp ebgp-requires-policy', frrconfig)
+ self.assertIn(f' no bgp suppress-duplicates', frrconfig)
afiv4_config = self.getFRRconfig(' address-family ipv4 unicast')
self.assertIn(f' maximum-paths {max_path_v4}', afiv4_config)
diff --git a/smoketest/scripts/cli/test_system_ipv6.py b/smoketest/scripts/cli/test_system_ipv6.py
index 3112d2e46..837d1dc12 100755
--- a/smoketest/scripts/cli/test_system_ipv6.py
+++ b/smoketest/scripts/cli/test_system_ipv6.py
@@ -17,7 +17,12 @@
import unittest
from base_vyostest_shim import VyOSUnitTestSHIM
+
+from vyos.template import is_ipv4
from vyos.util import read_file
+from vyos.util import is_ipv6_enabled
+from vyos.util import get_interface_config
+from vyos.validate import is_intf_addr_assigned
base_path = ['system', 'ipv6']
@@ -42,6 +47,14 @@ class TestSystemIPv6(VyOSUnitTestSHIM.TestCase):
self.assertEqual(read_file(file_forwarding), '0')
def test_system_ipv6_disable(self):
+ # Verify previous "enable" state
+ self.assertEqual(read_file(file_disable), '0')
+ self.assertTrue(is_ipv6_enabled())
+
+ loopbacks = ['127.0.0.1', '::1']
+ for addr in loopbacks:
+ self.assertTrue(is_intf_addr_assigned('lo', addr))
+
# Do not assign any IPv6 address on interfaces, this requires a reboot
# which can not be tested, but we can read the config file :)
self.cli_set(base_path + ['disable'])
@@ -49,6 +62,24 @@ class TestSystemIPv6(VyOSUnitTestSHIM.TestCase):
# Verify configuration file
self.assertEqual(read_file(file_disable), '1')
+ self.assertFalse(is_ipv6_enabled())
+
+ for addr in loopbacks:
+ if is_ipv4(addr):
+ self.assertTrue(is_intf_addr_assigned('lo', addr))
+ else:
+ self.assertFalse(is_intf_addr_assigned('lo', addr))
+
+ # T4330: Verify MTU can be changed with IPv6 disabled
+ mtu = '1600'
+ eth_if = 'eth0'
+ self.cli_set(['interfaces', 'ethernet', eth_if, 'mtu', mtu])
+ self.cli_commit()
+
+ tmp = get_interface_config(eth_if)
+ self.assertEqual(tmp['mtu'], int(mtu))
+
+ self.cli_delete(['interfaces', 'ethernet', eth_if, 'mtu'])
def test_system_ipv6_strict_dad(self):
# This defaults to 1
diff --git a/smoketest/scripts/cli/test_vpn_openconnect.py b/smoketest/scripts/cli/test_vpn_openconnect.py
index b0e859b5c..1f2c36f0d 100755
--- a/smoketest/scripts/cli/test_vpn_openconnect.py
+++ b/smoketest/scripts/cli/test_vpn_openconnect.py
@@ -37,6 +37,8 @@ class TestVpnOpenconnect(VyOSUnitTestSHIM.TestCase):
def test_vpn(self):
user = 'vyos_user'
password = 'vyos_pass'
+ otp = '37500000026900000000200000000000'
+
self.cli_delete(pki_path)
self.cli_delete(base_path)
@@ -45,7 +47,8 @@ class TestVpnOpenconnect(VyOSUnitTestSHIM.TestCase):
self.cli_set(pki_path + ['certificate', 'openconnect', 'private', 'key', key_data])
self.cli_set(base_path + ["authentication", "local-users", "username", user, "password", password])
- self.cli_set(base_path + ["authentication", "mode", "local"])
+ self.cli_set(base_path + ["authentication", "local-users", "username", user, "otp", "key", otp])
+ self.cli_set(base_path + ["authentication", "mode", "local", "password-otp"])
self.cli_set(base_path + ["network-settings", "client-ip-settings", "subnet", "192.0.2.0/24"])
self.cli_set(base_path + ["ssl", "ca-certificate", 'openconnect'])
self.cli_set(base_path + ["ssl", "certificate", 'openconnect'])
diff --git a/smoketest/scripts/cli/test_vrf.py b/smoketest/scripts/cli/test_vrf.py
index 5ffa9c086..5daea589c 100755
--- a/smoketest/scripts/cli/test_vrf.py
+++ b/smoketest/scripts/cli/test_vrf.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
@@ -25,9 +25,10 @@ from base_vyostest_shim import VyOSUnitTestSHIM
from vyos.configsession import ConfigSessionError
from vyos.ifconfig import Interface
from vyos.ifconfig import Section
-from vyos.template import is_ipv6
+from vyos.template import is_ipv4
from vyos.util import cmd
from vyos.util import read_file
+from vyos.util import get_interface_config
from vyos.validate import is_intf_addr_assigned
base_path = ['vrf']
@@ -105,10 +106,13 @@ class VRFTest(VyOSUnitTestSHIM.TestCase):
frrconfig = self.getFRRconfig(f'vrf {vrf}')
self.assertIn(f' vni {table}', frrconfig)
+ tmp = get_interface_config(vrf)
+ self.assertEqual(int(table), tmp['linkinfo']['info_data']['table'])
+
# Increment table ID for the next run
table = str(int(table) + 1)
- def test_vrf_loopback_ips(self):
+ def test_vrf_loopbacks_ips(self):
table = '2000'
for vrf in vrfs:
base = base_path + ['name', vrf]
@@ -119,10 +123,48 @@ class VRFTest(VyOSUnitTestSHIM.TestCase):
self.cli_commit()
# Verify VRF configuration
+ loopbacks = ['127.0.0.1', '::1']
for vrf in vrfs:
- self.assertTrue(vrf in interfaces())
- self.assertTrue(is_intf_addr_assigned(vrf, '127.0.0.1'))
- self.assertTrue(is_intf_addr_assigned(vrf, '::1'))
+ # Ensure VRF was created
+ self.assertIn(vrf, interfaces())
+ # Test for proper loopback IP assignment
+ for addr in loopbacks:
+ self.assertTrue(is_intf_addr_assigned(vrf, addr))
+
+ def test_vrf_loopbacks_no_ipv6(self):
+ table = '2002'
+ for vrf in vrfs:
+ base = base_path + ['name', vrf]
+ self.cli_set(base + ['table', str(table)])
+ table = str(int(table) + 1)
+
+ # Globally disable IPv6 - this will remove all IPv6 interface addresses
+ self.cli_set(['system', 'ipv6', 'disable'])
+
+ # commit changes
+ self.cli_commit()
+
+ # Verify VRF configuration
+ table = '2002'
+ loopbacks = ['127.0.0.1', '::1']
+ for vrf in vrfs:
+ # Ensure VRF was created
+ self.assertIn(vrf, interfaces())
+
+ # Verify VRF table ID
+ tmp = get_interface_config(vrf)
+ self.assertEqual(int(table), tmp['linkinfo']['info_data']['table'])
+
+ # Test for proper loopback IP assignment
+ for addr in loopbacks:
+ if is_ipv4(addr):
+ self.assertTrue(is_intf_addr_assigned(vrf, addr))
+ else:
+ self.assertFalse(is_intf_addr_assigned(vrf, addr))
+
+ table = str(int(table) + 1)
+
+ self.cli_delete(['system', 'ipv6'])
def test_vrf_bind_all(self):
table = '2000'
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())