summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--data/templates/firewall/nftables-nat66.j24
-rw-r--r--data/templates/ipsec/swanctl/profile.j24
-rw-r--r--data/templates/ocserv/ocserv_config.j234
-rw-r--r--debian/control2
-rw-r--r--interface-definitions/include/accel-ppp/radius-additions.xml.i13
-rw-r--r--interface-definitions/include/nat-exclude.xml.i8
-rw-r--r--interface-definitions/include/nat-rule.xml.i7
-rw-r--r--interface-definitions/include/radius-nas-ip-address.xml.i14
-rw-r--r--interface-definitions/nat66.xml.in2
-rw-r--r--interface-definitions/vpn-l2tp.xml.in1
-rw-r--r--interface-definitions/vpn-openconnect.xml.in13
-rw-r--r--op-mode-definitions/container.xml.in2
-rwxr-xr-xsmoketest/scripts/cli/test_nat66.py68
-rwxr-xr-xsmoketest/scripts/cli/test_vpn_openconnect.py79
-rwxr-xr-xsrc/conf_mode/nat66.py3
-rwxr-xr-xsrc/op_mode/restart_dhcp_relay.py4
16 files changed, 168 insertions, 90 deletions
diff --git a/data/templates/firewall/nftables-nat66.j2 b/data/templates/firewall/nftables-nat66.j2
index ca19506f2..2fe04b4ff 100644
--- a/data/templates/firewall/nftables-nat66.j2
+++ b/data/templates/firewall/nftables-nat66.j2
@@ -63,6 +63,10 @@
{% if dest_address is vyos_defined %}
{% set output = output ~ ' ' ~ dest_address %}
{% endif %}
+{% if config.exclude is vyos_defined %}
+{# rule has been marked as 'exclude' thus we simply return here #}
+{% set trns_address = 'return' %}
+{% endif %}
{% if trns_address is vyos_defined %}
{% set output = output ~ ' ' ~ trns_address %}
{% endif %}
diff --git a/data/templates/ipsec/swanctl/profile.j2 b/data/templates/ipsec/swanctl/profile.j2
index d4f417378..8519a84f8 100644
--- a/data/templates/ipsec/swanctl/profile.j2
+++ b/data/templates/ipsec/swanctl/profile.j2
@@ -9,6 +9,10 @@
version = {{ ike.key_exchange[4:] if ike.key_exchange is vyos_defined else "0" }}
rekey_time = {{ ike.lifetime }}s
keyingtries = 0
+{% if ike.dead_peer_detection is vyos_defined %}
+ dpd_timeout = {{ ike.dead_peer_detection.timeout }}
+ dpd_delay = {{ ike.dead_peer_detection.interval }}
+{% endif %}
{% if profile_conf.authentication.mode is vyos_defined('pre-shared-secret') %}
local {
auth = psk
diff --git a/data/templates/ocserv/ocserv_config.j2 b/data/templates/ocserv/ocserv_config.j2
index 8418a2185..e0cad5181 100644
--- a/data/templates/ocserv/ocserv_config.j2
+++ b/data/templates/ocserv/ocserv_config.j2
@@ -56,36 +56,32 @@ ban-reset-time = 300
# The name to use for the tun device
device = sslvpn
-# An alternative way of specifying the network:
-{% if network_settings %}
# DNS settings
-{% if network_settings.name_server is string %}
-dns = {{ network_settings.name_server }}
-{% else %}
-{% for dns in network_settings.name_server %}
+{% if network_settings.name_server is vyos_defined %}
+{% for dns in network_settings.name_server %}
dns = {{ dns }}
-{% endfor %}
-{% endif %}
+{% endfor %}
+{% endif %}
+
# IPv4 network pool
-{% if network_settings.client_ip_settings %}
-{% if network_settings.client_ip_settings.subnet %}
+{% if network_settings.client_ip_settings.subnet is vyos_defined %}
ipv4-network = {{ network_settings.client_ip_settings.subnet }}
-{% endif %}
-{% endif %}
+{% endif %}
+
# IPv6 network pool
-{% if network_settings.client_ipv6_pool %}
-{% if network_settings.client_ipv6_pool.prefix %}
+{% if network_settings.client_ipv6_pool.prefix is vyos_defined %}
ipv6-network = {{ network_settings.client_ipv6_pool.prefix }}
ipv6-subnet-prefix = {{ network_settings.client_ipv6_pool.mask }}
-{% endif %}
-{% endif %}
{% endif %}
-{% if network_settings.push_route is string %}
-route = {{ network_settings.push_route }}
-{% else %}
+{% if network_settings.push_route is vyos_defined %}
{% for route in network_settings.push_route %}
route = {{ route }}
{% endfor %}
{% endif %}
+{% if network_settings.split_dns is vyos_defined %}
+{% for tmp in network_settings.split_dns %}
+split-dns = {{ tmp }}
+{% endfor %}
+{% endif %}
diff --git a/debian/control b/debian/control
index 6a6ccf602..0db098be6 100644
--- a/debian/control
+++ b/debian/control
@@ -59,7 +59,7 @@ Depends:
frr-rpki-rtrlib,
frr-snmp,
grc,
- hostapd (>= 0.6.8),
+ hostapd,
hvinfo,
igmpproxy,
ipaddrcheck,
diff --git a/interface-definitions/include/accel-ppp/radius-additions.xml.i b/interface-definitions/include/accel-ppp/radius-additions.xml.i
index 441c9dda5..15ff5165f 100644
--- a/interface-definitions/include/accel-ppp/radius-additions.xml.i
+++ b/interface-definitions/include/accel-ppp/radius-additions.xml.i
@@ -89,18 +89,7 @@
<defaultValue>3</defaultValue>
</leafNode>
#include <include/radius-nas-identifier.xml.i>
- <leafNode name="nas-ip-address">
- <properties>
- <help>NAS-IP-Address attribute sent to RADIUS</help>
- <constraint>
- <validator name="ipv4-address"/>
- </constraint>
- <valueHelp>
- <format>ipv4</format>
- <description>NAS-IP-Address attribute</description>
- </valueHelp>
- </properties>
- </leafNode>
+ #include <include/radius-nas-ip-address.xml.i>
<leafNode name="preallocate-vif">
<properties>
<help>Enable attribute NAS-Port-Id in Access-Request</help>
diff --git a/interface-definitions/include/nat-exclude.xml.i b/interface-definitions/include/nat-exclude.xml.i
new file mode 100644
index 000000000..4d53cf844
--- /dev/null
+++ b/interface-definitions/include/nat-exclude.xml.i
@@ -0,0 +1,8 @@
+<!-- include start from nat-exclude.xml.i -->
+<leafNode name="exclude">
+ <properties>
+ <help>Exclude packets matching this rule from NAT</help>
+ <valueless/>
+ </properties>
+</leafNode>
+<!-- include end -->
diff --git a/interface-definitions/include/nat-rule.xml.i b/interface-definitions/include/nat-rule.xml.i
index bdb86ed9b..84941aa6a 100644
--- a/interface-definitions/include/nat-rule.xml.i
+++ b/interface-definitions/include/nat-rule.xml.i
@@ -23,12 +23,7 @@
</children>
</node>
#include <include/generic-disable-node.xml.i>
- <leafNode name="exclude">
- <properties>
- <help>Exclude packets matching this rule from NAT</help>
- <valueless/>
- </properties>
- </leafNode>
+ #include <include/nat-exclude.xml.i>
<leafNode name="log">
<properties>
<help>NAT rule logging</help>
diff --git a/interface-definitions/include/radius-nas-ip-address.xml.i b/interface-definitions/include/radius-nas-ip-address.xml.i
new file mode 100644
index 000000000..8d0a3fd6a
--- /dev/null
+++ b/interface-definitions/include/radius-nas-ip-address.xml.i
@@ -0,0 +1,14 @@
+<!-- include start from radius-nas-ip-address.xml.i -->
+<leafNode name="nas-ip-address">
+ <properties>
+ <help>NAS-IP-Address attribute sent to RADIUS</help>
+ <constraint>
+ <validator name="ipv4-address"/>
+ </constraint>
+ <valueHelp>
+ <format>ipv4</format>
+ <description>NAS-IP-Address attribute</description>
+ </valueHelp>
+ </properties>
+</leafNode>
+<!-- include end -->
diff --git a/interface-definitions/nat66.xml.in b/interface-definitions/nat66.xml.in
index ac3198f45..bde1a6f8d 100644
--- a/interface-definitions/nat66.xml.in
+++ b/interface-definitions/nat66.xml.in
@@ -35,6 +35,7 @@
<valueless/>
</properties>
</leafNode>
+ #include <include/nat-exclude.xml.i>
<leafNode name="log">
<properties>
<help>NAT66 rule logging</help>
@@ -162,6 +163,7 @@
<valueless/>
</properties>
</leafNode>
+ #include <include/nat-exclude.xml.i>
<leafNode name="log">
<properties>
<help>NAT66 rule logging</help>
diff --git a/interface-definitions/vpn-l2tp.xml.in b/interface-definitions/vpn-l2tp.xml.in
index f734283e7..fd70a76dc 100644
--- a/interface-definitions/vpn-l2tp.xml.in
+++ b/interface-definitions/vpn-l2tp.xml.in
@@ -215,6 +215,7 @@
</properties>
</leafNode>
#include <include/radius-nas-identifier.xml.i>
+ #include <include/radius-nas-ip-address.xml.i>
<node name="dae-server">
<properties>
<help>IPv4 address and port to bind Dynamic Authorization Extension server (DM/CoA)</help>
diff --git a/interface-definitions/vpn-openconnect.xml.in b/interface-definitions/vpn-openconnect.xml.in
index 21b47125d..6309863c5 100644
--- a/interface-definitions/vpn-openconnect.xml.in
+++ b/interface-definitions/vpn-openconnect.xml.in
@@ -265,6 +265,19 @@
</children>
</node>
#include <include/name-server-ipv4-ipv6.xml.i>
+ <leafNode name="split-dns">
+ <properties>
+ <help>Domains over which the provided DNS should be used</help>
+ <valueHelp>
+ <format>txt</format>
+ <description>Client prefix length</description>
+ </valueHelp>
+ <constraint>
+ <validator name="fqdn"/>
+ </constraint>
+ <multi/>
+ </properties>
+ </leafNode>
</children>
</node>
</children>
diff --git a/op-mode-definitions/container.xml.in b/op-mode-definitions/container.xml.in
index a7048e5ed..97a087ce2 100644
--- a/op-mode-definitions/container.xml.in
+++ b/op-mode-definitions/container.xml.in
@@ -149,7 +149,7 @@
<path>container name</path>
</completionHelp>
</properties>
- <command>sudo ${vyos_op_scripts_dir}/container.py restart name="$3"</command>
+ <command>sudo ${vyos_op_scripts_dir}/container.py restart --name="$3"</command>
</tagNode>
</children>
</node>
diff --git a/smoketest/scripts/cli/test_nat66.py b/smoketest/scripts/cli/test_nat66.py
index aac6a30f9..4b5625569 100755
--- a/smoketest/scripts/cli/test_nat66.py
+++ b/smoketest/scripts/cli/test_nat66.py
@@ -42,6 +42,17 @@ class TestNAT66(VyOSUnitTestSHIM.TestCase):
self.cli_delete(base_path)
self.cli_commit()
+ def verify_nftables(self, nftables_search, table, inverse=False, args=''):
+ nftables_output = cmd(f'sudo nft {args} list table {table}')
+
+ for search in nftables_search:
+ matched = False
+ for line in nftables_output.split("\n"):
+ if all(item in line for item in search):
+ matched = True
+ break
+ self.assertTrue(not matched if inverse else matched, msg=search)
+
def test_source_nat66(self):
source_prefix = 'fc00::/64'
translation_prefix = 'fc01::/64'
@@ -49,29 +60,23 @@ class TestNAT66(VyOSUnitTestSHIM.TestCase):
self.cli_set(src_path + ['rule', '1', 'source', 'prefix', source_prefix])
self.cli_set(src_path + ['rule', '1', 'translation', 'address', translation_prefix])
- # check validate() - outbound-interface must be defined
- self.cli_commit()
+ self.cli_set(src_path + ['rule', '2', 'outbound-interface', 'eth1'])
+ self.cli_set(src_path + ['rule', '2', 'source', 'prefix', source_prefix])
+ self.cli_set(src_path + ['rule', '2', 'translation', 'address', 'masquerade'])
- tmp = cmd('sudo nft -j list table ip6 nat')
- data_json = jmespath.search('nftables[?rule].rule[?chain]', json.loads(tmp))
+ self.cli_set(src_path + ['rule', '3', 'outbound-interface', 'eth1'])
+ self.cli_set(src_path + ['rule', '3', 'source', 'prefix', source_prefix])
+ self.cli_set(src_path + ['rule', '3', 'exclude'])
- for idx in range(0, len(data_json)):
- data = data_json[idx]
+ self.cli_commit()
- self.assertEqual(data['chain'], 'POSTROUTING')
- self.assertEqual(data['family'], 'ip6')
- self.assertEqual(data['table'], 'nat')
+ nftables_search = [
+ ['oifname "eth1"', 'ip6 saddr fc00::/64', 'snat prefix to fc01::/64'],
+ ['oifname "eth1"', 'ip6 saddr fc00::/64', 'masquerade'],
+ ['oifname "eth1"', 'ip6 saddr fc00::/64', 'return']
+ ]
- iface = dict_search('match.right', data['expr'][0])
- address = dict_search('match.right.prefix.addr', data['expr'][2])
- mask = dict_search('match.right.prefix.len', data['expr'][2])
- translation_address = dict_search('snat.addr.prefix.addr', data['expr'][3])
- translation_mask = dict_search('snat.addr.prefix.len', data['expr'][3])
-
- self.assertEqual(iface, 'eth1')
- # check for translation address
- self.assertEqual(f'{translation_address}/{translation_mask}', translation_prefix)
- self.assertEqual(f'{address}/{mask}', source_prefix)
+ self.verify_nftables(nftables_search, 'ip6 nat')
def test_source_nat66_address(self):
source_prefix = 'fc00::/64'
@@ -106,28 +111,25 @@ class TestNAT66(VyOSUnitTestSHIM.TestCase):
def test_destination_nat66(self):
destination_address = 'fc00::1'
translation_address = 'fc01::1'
+ source_address = 'fc02::1'
self.cli_set(dst_path + ['rule', '1', 'inbound-interface', 'eth1'])
self.cli_set(dst_path + ['rule', '1', 'destination', 'address', destination_address])
self.cli_set(dst_path + ['rule', '1', 'translation', 'address', translation_address])
+ self.cli_set(dst_path + ['rule', '2', 'inbound-interface', 'eth1'])
+ self.cli_set(dst_path + ['rule', '2', 'destination', 'address', destination_address])
+ self.cli_set(dst_path + ['rule', '2', 'source', 'address', source_address])
+ self.cli_set(dst_path + ['rule', '2', 'exclude'])
+
# check validate() - outbound-interface must be defined
self.cli_commit()
- tmp = cmd('sudo nft -j list table ip6 nat')
- data_json = jmespath.search('nftables[?rule].rule[?chain]', json.loads(tmp))
-
- for idx in range(0, len(data_json)):
- data = data_json[idx]
-
- self.assertEqual(data['chain'], 'PREROUTING')
- self.assertEqual(data['family'], 'ip6')
- self.assertEqual(data['table'], 'nat')
-
- iface = dict_search('match.right', data['expr'][0])
- dnat_addr = dict_search('dnat.addr', data['expr'][3])
+ nftables_search = [
+ ['iifname "eth1"', 'ip6 daddr fc00::1', 'dnat to fc01::1'],
+ ['iifname "eth1"', 'ip6 saddr fc02::1', 'ip6 daddr fc00::1', 'return']
+ ]
- self.assertEqual(dnat_addr, translation_address)
- self.assertEqual(iface, 'eth1')
+ self.verify_nftables(nftables_search, 'ip6 nat')
def test_destination_nat66_prefix(self):
destination_prefix = 'fc00::/64'
diff --git a/smoketest/scripts/cli/test_vpn_openconnect.py b/smoketest/scripts/cli/test_vpn_openconnect.py
index bda279342..8572d6d66 100755
--- a/smoketest/scripts/cli/test_vpn_openconnect.py
+++ b/smoketest/scripts/cli/test_vpn_openconnect.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2020 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
@@ -19,6 +19,7 @@ import unittest
from base_vyostest_shim import VyOSUnitTestSHIM
from vyos.util import process_named_running
+from vyos.util import read_file
OCSERV_CONF = '/run/ocserv/ocserv.conf'
base_path = ['vpn', 'openconnect']
@@ -46,36 +47,84 @@ MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgPLpD0Ohhoq0g4nhx
u8/3jHMM7sDwL3aWzW/zp54/LhCWUoLMjDdDEEigK4fal4ZF9aA9F0Ww
"""
-class TestVpnOpenconnect(VyOSUnitTestSHIM.TestCase):
+PROCESS_NAME = 'ocserv-main'
+config_file = '/run/ocserv/ocserv.conf'
+auth_file = '/run/ocserv/ocpasswd'
+otp_file = '/run/ocserv/users.oath'
+
+class TestVPNOpenConnect(VyOSUnitTestSHIM.TestCase):
+ @classmethod
+ def setUpClass(cls):
+ super(TestVPNOpenConnect, cls).setUpClass()
+
+ # ensure we can also run this test on a live system - so lets clean
+ # out the current configuration :)
+ cls.cli_delete(cls, base_path)
+
+ cls.cli_set(cls, pki_path + ['ca', 'openconnect', 'certificate', cert_data.replace('\n','')])
+ cls.cli_set(cls, pki_path + ['certificate', 'openconnect', 'certificate', cert_data.replace('\n','')])
+ cls.cli_set(cls, pki_path + ['certificate', 'openconnect', 'private', 'key', key_data.replace('\n','')])
+
+ @classmethod
+ def tearDownClass(cls):
+ cls.cli_delete(cls, pki_path)
+ super(TestVPNOpenConnect, cls).tearDownClass()
+
def tearDown(self):
- # Delete vpn openconnect configuration
- self.cli_delete(pki_path)
+ self.assertTrue(process_named_running(PROCESS_NAME))
+
self.cli_delete(base_path)
self.cli_commit()
- def test_vpn(self):
+ self.assertFalse(process_named_running(PROCESS_NAME))
+
+ def test_ocserv(self):
user = 'vyos_user'
password = 'vyos_pass'
otp = '37500000026900000000200000000000'
-
- self.cli_delete(pki_path)
- self.cli_delete(base_path)
-
- self.cli_set(pki_path + ['ca', 'openconnect', 'certificate', cert_data.replace('\n','')])
- self.cli_set(pki_path + ['certificate', 'openconnect', 'certificate', cert_data.replace('\n','')])
- self.cli_set(pki_path + ['certificate', 'openconnect', 'private', 'key', key_data.replace('\n','')])
+ v4_subnet = '192.0.2.0/24'
+ v6_prefix = '2001:db8:1000::/64'
+ v6_len = '126'
+ name_server = ['1.2.3.4', '1.2.3.5', '2001:db8::1']
+ split_dns = ['vyos.net', 'vyos.io']
self.cli_set(base_path + ['authentication', 'local-users', 'username', user, 'password', password])
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 + ['network-settings', 'client-ip-settings', 'subnet', v4_subnet])
+ self.cli_set(base_path + ['network-settings', 'client-ipv6-pool', 'prefix', v6_prefix])
+ self.cli_set(base_path + ['network-settings', 'client-ipv6-pool', 'mask', v6_len])
+
+ for ns in name_server:
+ self.cli_set(base_path + ['network-settings', 'name-server', ns])
+ for domain in split_dns:
+ self.cli_set(base_path + ['network-settings', 'split-dns', domain])
+
self.cli_set(base_path + ['ssl', 'ca-certificate', 'openconnect'])
self.cli_set(base_path + ['ssl', 'certificate', 'openconnect'])
self.cli_commit()
- # Check for running process
- self.assertTrue(process_named_running('ocserv-main'))
+ # Verify configuration
+ daemon_config = read_file(config_file)
+
+ # authentication mode local password-otp
+ self.assertIn(f'auth = "plain[passwd=/run/ocserv/ocpasswd,otp=/run/ocserv/users.oath]"', daemon_config)
+ self.assertIn(f'ipv4-network = {v4_subnet}', daemon_config)
+ self.assertIn(f'ipv6-network = {v6_prefix}', daemon_config)
+ self.assertIn(f'ipv6-subnet-prefix = {v6_len}', daemon_config)
+
+ for ns in name_server:
+ self.assertIn(f'dns = {ns}', daemon_config)
+ for domain in split_dns:
+ self.assertIn(f'split-dns = {domain}', daemon_config)
+
+ auth_config = read_file(auth_file)
+ self.assertIn(f'{user}:*:$', auth_config)
+
+ otp_config = read_file(otp_file)
+ self.assertIn(f'HOTP/T30/6 {user} - {otp}', otp_config)
if __name__ == '__main__':
unittest.main(verbosity=2)
diff --git a/src/conf_mode/nat66.py b/src/conf_mode/nat66.py
index 0972151a0..f64102d88 100755
--- a/src/conf_mode/nat66.py
+++ b/src/conf_mode/nat66.py
@@ -125,7 +125,8 @@ def verify(nat):
if addr != 'masquerade' and not is_ipv6(addr):
raise ConfigError(f'IPv6 address {addr} is not a valid address')
else:
- raise ConfigError(f'{err_msg} translation address not specified')
+ if 'exclude' not in config:
+ raise ConfigError(f'{err_msg} translation address not specified')
prefix = dict_search('source.prefix', config)
if prefix != None:
diff --git a/src/op_mode/restart_dhcp_relay.py b/src/op_mode/restart_dhcp_relay.py
index db5a48970..9203c009f 100755
--- a/src/op_mode/restart_dhcp_relay.py
+++ b/src/op_mode/restart_dhcp_relay.py
@@ -43,7 +43,7 @@ if __name__ == '__main__':
if commit_in_progress():
print('Cannot restart DHCP relay while a commit is in progress')
exit(1)
- call('systemctl restart isc-dhcp-server.service')
+ call('systemctl restart isc-dhcp-relay.service')
sys.exit(0)
elif args.ipv6:
@@ -54,7 +54,7 @@ if __name__ == '__main__':
if commit_in_progress():
print('Cannot restart DHCPv6 relay while commit is in progress')
exit(1)
- call('systemctl restart isc-dhcp-server6.service')
+ call('systemctl restart isc-dhcp-relay6.service')
sys.exit(0)
else: