summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristian Poessinger <christian@poessinger.com>2021-07-03 15:52:26 +0200
committerChristian Poessinger <christian@poessinger.com>2021-07-04 11:57:15 +0200
commitb2bf1592189fb9298f2a68272418a132a73f37bf (patch)
tree20599766a0c4d23bc0defb1add6e28221669836a
parentce3847239493d76bd0462e2a7b1f5cca41c57457 (diff)
downloadvyos-1x-b2bf1592189fb9298f2a68272418a132a73f37bf.tar.gz
vyos-1x-b2bf1592189fb9298f2a68272418a132a73f37bf.zip
ipsec: T1210: T1251: IKEv2 road-warrior support
set vpn ipsec esp-group ESP-RW compression 'disable' set vpn ipsec esp-group ESP-RW lifetime '3600' set vpn ipsec esp-group ESP-RW pfs 'disable' set vpn ipsec esp-group ESP-RW proposal 10 encryption 'aes256' set vpn ipsec esp-group ESP-RW proposal 10 hash 'sha256' set vpn ipsec esp-group ESP-RW proposal 20 encryption 'aes256' set vpn ipsec esp-group ESP-RW proposal 20 hash 'sha1' set vpn ipsec ike-group IKE-RW key-exchange 'ikev2' set vpn ipsec ike-group IKE-RW lifetime '10800' set vpn ipsec ike-group IKE-RW mobike 'enable' set vpn ipsec ike-group IKE-RW proposal 10 dh-group '2' set vpn ipsec ike-group IKE-RW proposal 10 encryption 'aes256' set vpn ipsec ike-group IKE-RW proposal 10 hash 'sha1' set vpn ipsec ike-group IKE-RW proposal 20 dh-group '2' set vpn ipsec ike-group IKE-RW proposal 20 encryption 'aes128' set vpn ipsec ike-group IKE-RW proposal 20 hash 'sha1' set vpn ipsec ipsec-interfaces interface 'dum0' set vpn ipsec remote-access rw authentication id 'vyos' set vpn ipsec remote-access rw authentication local-users username vyos password vyos set vpn ipsec remote-access rw authentication x509 ca-certificate 'peer_172-18-254-202' set vpn ipsec remote-access rw authentication x509 certificate 'peer_172-18-254-202' set vpn ipsec remote-access rw description 'asdf' set vpn ipsec remote-access rw esp-group 'ESP-RW' set vpn ipsec remote-access rw ike-group 'IKE-RW'
-rw-r--r--data/templates/ipsec/swanctl.conf.tmpl18
-rw-r--r--data/templates/ipsec/swanctl/remote_access.tmpl37
-rw-r--r--interface-definitions/vpn_ipsec.xml.in53
-rwxr-xr-xsrc/conf_mode/vpn_ipsec.py42
4 files changed, 129 insertions, 21 deletions
diff --git a/data/templates/ipsec/swanctl.conf.tmpl b/data/templates/ipsec/swanctl.conf.tmpl
index cafe52e78..15c035688 100644
--- a/data/templates/ipsec/swanctl.conf.tmpl
+++ b/data/templates/ipsec/swanctl.conf.tmpl
@@ -1,6 +1,7 @@
### Autogenerated by vpn_ipsec.py ###
{% import 'ipsec/swanctl/profile.tmpl' as profile_tmpl %}
{% import 'ipsec/swanctl/peer.tmpl' as peer_tmpl %}
+{% import 'ipsec/swanctl/remote_access.tmpl' as remote_access_tmpl %}
connections {
{% if profile is defined %}
@@ -13,6 +14,11 @@ connections {
{{ peer_tmpl.conn(peer, peer_conf, ike_group, esp_group) }}
{% endfor %}
{% endif %}
+{% if remote_access is defined and remote_access is not none %}
+{% for rw, rw_conf in remote_access.items() if rw_conf.disable is not defined %}
+{{ remote_access_tmpl.conn(rw, rw_conf, ike_group, esp_group) }}
+{% endfor %}
+{% endif %}
}
secrets {
@@ -60,5 +66,17 @@ secrets {
{% endif %}
{% endfor %}
{% endif %}
+{% if remote_access is defined %}
+{% for ra, ra_conf in remote_access.items() if remote_access is defined %}
+{% if ra_conf.authentication is defined and ra_conf.authentication.local_users is defined and ra_conf.authentication.local_users.username is defined %}
+{% for user, user_conf in ra_conf.authentication.local_users.username.items() if user_conf.disable is not defined %}
+ eap-{{ ra }}-{{ user }} {
+ secret = "{{ user_conf.password }}"
+ id-{{ ra }}-{{ user }} = "{{ user }}"
+ }
+{% endfor %}
+{% endif %}
+{% endfor %}
+{% endif %}
}
diff --git a/data/templates/ipsec/swanctl/remote_access.tmpl b/data/templates/ipsec/swanctl/remote_access.tmpl
new file mode 100644
index 000000000..89f6e343e
--- /dev/null
+++ b/data/templates/ipsec/swanctl/remote_access.tmpl
@@ -0,0 +1,37 @@
+{% macro conn(name, rw_conf, ike_group, esp_group) %}
+{# peer needs to reference the global IKE configuration for certain values #}
+{% set ike = ike_group[rw_conf.ike_group] %}
+{% set esp = esp_group[rw_conf.esp_group] %}
+ ra-{{ name }} {
+ remote_addrs = %any
+ local_addrs = %any
+ proposals = {{ ike_group[rw_conf.ike_group] | get_esp_ike_cipher | join(',') }}
+ version = {{ ike.key_exchange[4:] if ike is defined and ike.key_exchange is defined else "0" }}
+ send_certreq = no
+ rekey_time = {{ ike.lifetime }}s
+ keyingtries = 0
+ local {
+ auth = pubkey
+{% if rw_conf.authentication is defined and rw_conf.authentication.id is defined and rw_conf.authentication.use_x509_id is not defined %}
+ id = "{{ rw_conf.authentication.id }}"
+{% endif %}
+{% if rw_conf.authentication is defined and rw_conf.authentication.x509 is defined and rw_conf.authentication.x509.certificate is defined %}
+ certs = {{ rw_conf.authentication.x509.certificate }}.pem
+{% endif %}
+ }
+ remote {
+ auth = eap-mschapv2
+ id = %any
+ eap_id = %any
+ }
+ children {
+ ikev2-vpn {
+ esp_proposals = {{ esp | get_esp_ike_cipher | join(',') }}
+ rekey_time = {{ esp.lifetime }}s
+ rand_time = 540s
+ local_ts = 0.0.0.0/0
+ dpd_action = clear
+ }
+ }
+ }
+{% endmacro %}
diff --git a/interface-definitions/vpn_ipsec.xml.in b/interface-definitions/vpn_ipsec.xml.in
index ff60bb82f..ef3b05e29 100644
--- a/interface-definitions/vpn_ipsec.xml.in
+++ b/interface-definitions/vpn_ipsec.xml.in
@@ -705,6 +705,59 @@
#include <include/ipsec/ike-group.xml.i>
</children>
</tagNode>
+ <tagNode name="remote-access">
+ <properties>
+ <help>Remote access IKEv2 VPN </help>
+ </properties>
+ <children>
+ <node name="authentication">
+ <properties>
+ <help>Authentication for remote access</help>
+ </properties>
+ <children>
+ #include <include/ipsec/authentication-id.xml.i>
+ #include <include/ipsec/authentication-x509.xml.i>
+ <node name="local-users">
+ <properties>
+ <help>Local user authentication for PPPoE server</help>
+ </properties>
+ <children>
+ <tagNode name="username">
+ <properties>
+ <help>User name for authentication</help>
+ </properties>
+ <children>
+ #include <include/generic-disable-node.xml.i>
+ <leafNode name="password">
+ <properties>
+ <help>Password for authentication</help>
+ </properties>
+ </leafNode>
+ </children>
+ </tagNode>
+ </children>
+ </node>
+ </children>
+ </node>
+ #include <include/generic-description.xml.i>
+ #include <include/generic-disable-node.xml.i>
+ #include <include/ipsec/esp-group.xml.i>
+ #include <include/ipsec/ike-group.xml.i>
+ <leafNode name="timeout">
+ <properties>
+ <help>Timeout to close connection if no data is transmitted</help>
+ <valueHelp>
+ <format>u32:10-86400</format>
+ <description>Timeout in seconds (default 28800)</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 10-86400"/>
+ </constraint>
+ </properties>
+ <defaultValue>28800</defaultValue>
+ </leafNode>
+ </children>
+ </tagNode>
<node name="site-to-site">
<properties>
<help>Site-to-site VPN</help>
diff --git a/src/conf_mode/vpn_ipsec.py b/src/conf_mode/vpn_ipsec.py
index d1b29ee9a..50223320d 100755
--- a/src/conf_mode/vpn_ipsec.py
+++ b/src/conf_mode/vpn_ipsec.py
@@ -34,7 +34,6 @@ from vyos.template import render
from vyos.validate import is_ipv6_link_local
from vyos.util import call
from vyos.util import dict_search
-from vyos.util import process_named_running
from vyos.util import run
from vyos.xml import defaults
from vyos import ConfigError
@@ -81,6 +80,7 @@ def get_config(config=None):
# added in a more fine grained way later on
del default_values['esp_group']
del default_values['ike_group']
+ del default_values['remote_access']
ipsec = dict_merge(default_values, ipsec)
if 'esp_group' in ipsec:
@@ -88,12 +88,16 @@ def get_config(config=None):
for group in ipsec['esp_group']:
ipsec['esp_group'][group] = dict_merge(default_values,
ipsec['esp_group'][group])
-
if 'ike_group' in ipsec:
default_values = defaults(base + ['ike-group'])
for group in ipsec['ike_group']:
ipsec['ike_group'][group] = dict_merge(default_values,
ipsec['ike_group'][group])
+ if 'remote_access' in ipsec:
+ default_values = defaults(base + ['remote-access'])
+ for rw in ipsec['remote_access']:
+ ipsec['remote_access'][rw] = dict_merge(default_values,
+ ipsec['remote_access'][rw])
ipsec['dhcp_no_address'] = {}
ipsec['interface_change'] = leaf_node_changed(conf, base + ['ipsec-interfaces',
@@ -109,8 +113,6 @@ def get_config(config=None):
get_first_key=True,
no_tag_node_value_mangle=True)
- import pprint
- pprint.pprint(ipsec)
return ipsec
def get_rsa_local_key(ipsec):
@@ -326,6 +328,11 @@ def generate(ipsec):
if not os.path.exists(KEY_PATH):
os.mkdir(KEY_PATH, mode=0o700)
+ if 'remote_access' in ipsec:
+ for rw, rw_conf in ipsec['remote_access'].items():
+ if 'authentication' in rw_conf and 'x509' in rw_conf['authentication']:
+ generate_pki_files(ipsec['pki'], rw_conf['authentication']['x509'])
+
if 'site_to_site' in data and 'peer' in data['site_to_site']:
for peer, peer_conf in ipsec['site_to_site']['peer'].items():
if peer in ipsec['dhcp_no_address']:
@@ -385,24 +392,17 @@ def resync_nhrp(ipsec):
def apply(ipsec):
if not ipsec:
- call('sudo /usr/sbin/ipsec stop')
+ call('sudo ipsec stop')
else:
- should_start = 'profile' in ipsec or dict_search('site_to_site.peer', ipsec)
-
- if not process_named_running('charon') and should_start:
- args = f'--auto-update {ipsec["auto_update"]}' if 'auto_update' in ipsec else ''
- call(f'sudo /usr/sbin/ipsec start {args}')
- elif not should_start:
- call('sudo /usr/sbin/ipsec stop')
- elif ipsec['interface_change']:
- call('sudo /usr/sbin/ipsec restart')
- else:
- call('sudo /usr/sbin/ipsec rereadall')
- call('sudo /usr/sbin/ipsec reload')
-
- if should_start:
- sleep(2) # Give charon enough time to start
- call('sudo /usr/sbin/swanctl -q')
+ args = ''
+ if 'auto_update' in ipsec:
+ args = '--auto-update ' + ipsec['auto_update']
+ call(f'sudo ipsec restart {args}')
+ call('sudo ipsec rereadall')
+ call('sudo ipsec reload')
+
+ sleep(5) # Give charon enough time to start
+ call('sudo swanctl -q')
resync_l2tp(ipsec)
resync_nhrp(ipsec)