summaryrefslogtreecommitdiff
path: root/src/conf_mode/vpn_ipsec.py
diff options
context:
space:
mode:
Diffstat (limited to 'src/conf_mode/vpn_ipsec.py')
-rwxr-xr-xsrc/conf_mode/vpn_ipsec.py173
1 files changed, 85 insertions, 88 deletions
diff --git a/src/conf_mode/vpn_ipsec.py b/src/conf_mode/vpn_ipsec.py
index e59f20a5d..4632310ca 100755
--- a/src/conf_mode/vpn_ipsec.py
+++ b/src/conf_mode/vpn_ipsec.py
@@ -22,9 +22,10 @@ from sys import exit
from time import sleep
from vyos.config import Config
-from vyos.configdiff import ConfigDiff
+from vyos.configdiff import get_config_diff
from vyos.template import render
from vyos.util import call
+from vyos.util import dict_search
from vyos.util import get_interface_address
from vyos.util import process_named_running
from vyos.util import run
@@ -82,24 +83,7 @@ DHCP_BASE = "/var/lib/dhcp/dhclient"
LOCAL_KEY_PATHS = ['/config/auth/', '/config/ipsec.d/rsa-keys/']
X509_PATH = '/config/auth/'
-conf = None
-
-def resync_l2tp(conf):
- if not conf.exists('vpn l2tp remote-access ipsec-settings '):
- return
-
- tmp = run('/usr/libexec/vyos/conf_mode/ipsec-settings.py')
- if tmp > 0:
- print('ERROR: failed to reapply L2TP IPSec settings!')
-
-def resync_nhrp(conf):
- if not conf.exists('protocols nhrp tunnel'):
- return
-
- run('/opt/vyatta/sbin/vyos-update-nhrp.pl --set_ipsec')
-
def get_config(config=None):
- global conf
if config:
conf = config
else:
@@ -109,7 +93,13 @@ def get_config(config=None):
return None
# retrieve common dictionary keys
- ipsec = conf.get_config_dict(base, key_mangling=('-', '_'), get_first_key=True, no_tag_node_value_mangle=True)
+ ipsec = conf.get_config_dict(base, key_mangling=('-', '_'),
+ get_first_key=True, no_tag_node_value_mangle=True)
+
+ ipsec['l2tp_exists'] = conf.exists('vpn l2tp remote-access ipsec-settings ')
+ ipsec['nhrp_exists'] = conf.exists('protocols nhrp tunnel')
+ ipsec['rsa_keys'] = conf.get_config_dict(['vpn', 'rsa-keys'], key_mangling=('-', '_'),
+ get_first_key=True, no_tag_node_value_mangle=True)
default_ike_pfs = None
@@ -150,8 +140,33 @@ def get_config(config=None):
ciphers.append(f"{enc}-{hash}-{pfs_translate[pfs]}" if pfs else f"{enc}-{hash}")
esp_ciphers[group] = ','.join(ciphers) + '!'
+ diff = get_config_diff(conf, key_mangling=('-', '_'))
+ diff.set_level(base)
+
+ new_if, old_if = diff.get_value_diff(['ipsec-interfaces', 'interface'])
+ ipsec['interface_change'] = (old_if != new_if)
+
return ipsec
+def get_rsa_local_key(ipsec):
+ return dict_search('local_key.file', ipsec['rsa_keys'])
+
+def verify_rsa_local_key(ipsec):
+ file = get_rsa_local_key(ipsec)
+
+ if not file:
+ return False
+
+ for path in LOCAL_KEY_PATHS:
+ full_path = os.path.join(path, file)
+ if os.path.exists(full_path):
+ return full_path
+
+ return False
+
+def verify_rsa_key(ipsec, key_name):
+ return dict_search(f'rsa_key_name.{key_name}.rsa_key', ipsec['rsa_keys'])
+
def verify(ipsec):
if not ipsec:
return None
@@ -194,30 +209,33 @@ def verify(ipsec):
if 'x509' not in peer_conf['authentication']:
raise ConfigError(f"Missing x509 settings on site-to-site peer {peer}")
- if 'key' not in peer_conf['authentication']['x509'] or 'ca_cert_file' not in peer_conf['authentication']['x509'] or 'cert_file' not in peer_conf['authentication']['x509']:
+ if 'key' not in peer_conf['authentication']['x509']:
+ raise ConfigError(f"Missing x509 key on site-to-site peer {peer}")
+
+ if 'ca_cert_file' not in peer_conf['authentication']['x509'] or 'cert_file' not in peer_conf['authentication']['x509']:
raise ConfigError(f"Missing x509 settings on site-to-site peer {peer}")
if 'file' not in peer_conf['authentication']['x509']['key']:
- raise ConfigError(f"Missing x509 settings on site-to-site peer {peer}")
+ raise ConfigError(f"Missing x509 key file on site-to-site peer {peer}")
for key in ['ca_cert_file', 'cert_file', 'crl_file']:
if key in peer_conf['authentication']['x509']:
- path = peer_conf['authentication']['x509'][key]
- if not os.path.exists(path if path.startswith(X509_PATH) else (X509_PATH + path)):
+ path = os.path.join(X509_PATH, peer_conf['authentication']['x509'][key])
+ if not os.path.exists(path):
raise ConfigError(f"File not found for {key} on site-to-site peer {peer}")
- key_path = peer_conf['authentication']['x509']['key']['file']
- if not os.path.exists(key_path if key_path.startswith(X509_PATH) else (X509_PATH + key_path)):
+ key_path = os.path.join(X509_PATH, peer_conf['authentication']['x509']['key']['file'])
+ if not os.path.exists(key_path):
raise ConfigError(f"Private key not found on site-to-site peer {peer}")
if peer_conf['authentication']['mode'] == 'rsa':
- if not verify_rsa_local_key():
+ if not verify_rsa_local_key(ipsec):
raise ConfigError(f"Invalid key on rsa-keys local-key")
if 'rsa_key_name' not in peer_conf['authentication']:
raise ConfigError(f"Missing rsa-key-name on site-to-site peer {peer}")
- if not verify_rsa_key(peer_conf['authentication']['rsa_key_name']):
+ if not verify_rsa_key(ipsec, peer_conf['authentication']['rsa_key_name']):
raise ConfigError(f"Invalid rsa-key-name on site-to-site peer {peer}")
if 'local_address' not in peer_conf and 'dhcp_interface' not in peer_conf:
@@ -228,6 +246,9 @@ def verify(ipsec):
if not os.path.exists(f'{DHCP_BASE}_{dhcp_interface}.conf'):
raise ConfigError(f"Invalid dhcp-interface on site-to-site peer {peer}")
+ if not get_dhcp_address(dhcp_interface):
+ raise ConfigError(f"Failed to get address from dhcp-interface on site-to-site peer {peer}")
+
if 'vti' in peer_conf:
if 'local_address' in peer_conf and 'dhcp_interface' in peer_conf:
raise ConfigError(f"A single local-address or dhcp-interface is required when using VTI on site-to-site peer {peer}")
@@ -238,7 +259,7 @@ def verify(ipsec):
raise ConfigError(f'VTI interface {vti_interface} for site-to-site peer {peer} does not exist!')
if 'vti' not in peer_conf and 'tunnel' not in peer_conf:
- raise ConfigError(f"No vti or tunnels specified on site-to-site peer {peer}")
+ raise ConfigError(f"No VTI or tunnel specified on site-to-site peer {peer}")
if 'tunnel' in peer_conf:
for tunnel, tunnel_conf in peer_conf['tunnel'].items():
@@ -259,33 +280,6 @@ def verify(ipsec):
if ('local' in tunnel_conf and 'prefix' in tunnel_conf['local']) or ('remote' in tunnel_conf and 'prefix' in tunnel_conf['remote']):
raise ConfigError(f"Local/remote prefix cannot be used with ESP transport mode on tunnel {tunnel} for site-to-site peer {peer}")
-def get_rsa_local_key():
- global conf
- base = ['vpn', 'rsa-keys']
- if not conf.exists(base + ['local-key', 'file']):
- return False
-
- return conf.return_value(base + ['local-key', 'file'])
-
-def verify_rsa_local_key():
- file = get_rsa_local_key()
-
- if not file:
- return False
-
- for path in LOCAL_KEY_PATHS:
- if os.path.exists(path + file):
- return path + file
-
- return False
-
-def verify_rsa_key(key_name):
- global conf
- base = ['vpn', 'rsa-keys']
- if not conf.exists(base):
- return False
- return conf.exists(base + ['rsa-key-name', key_name, 'rsa-key'])
-
def generate(ipsec):
data = {}
@@ -294,7 +288,7 @@ def generate(ipsec):
data['authby'] = authby_translate
data['ciphers'] = {'ike': ike_ciphers, 'esp': esp_ciphers}
data['marks'] = {}
- data['rsa_local_key'] = verify_rsa_local_key()
+ data['rsa_local_key'] = verify_rsa_local_key(ipsec)
data['x509_path'] = X509_PATH
if 'site_to_site' in data and 'peer' in data['site_to_site']:
@@ -320,10 +314,12 @@ def generate(ipsec):
data['marks'][vti_interface] = get_mark(vti_interface)
else:
for tunnel, tunnel_conf in peer_conf['tunnel'].items():
- if ('local' not in tunnel_conf or 'prefix' not in tunnel_conf['local']) or ('remote' not in tunnel_conf or 'prefix' not in tunnel_conf['remote']):
+ local_prefix = dict_search('local.prefix', tunnel_conf['local']['prefix'])
+ remote_prefix = dict_search('remote.prefix', tunnel_conf['remote']['prefix'])
+
+ if not local_prefix or not remote_prefix:
continue
- local_prefix = tunnel_conf['local']['prefix']
- remote_prefix = tunnel_conf['remote']['prefix']
+
passthrough = cidr_fit(local_prefix, remote_prefix)
data['site_to_site']['peer'][peer]['tunnel'][tunnel]['passthrough'] = passthrough
@@ -340,49 +336,48 @@ def generate(ipsec):
render("/etc/ipsec.secrets", "ipsec/ipsec.secrets.tmpl", data)
render("/etc/swanctl/swanctl.conf", "ipsec/swanctl.conf.tmpl", data)
+def resync_l2tp(ipsec):
+ if not ipsec['l2tp_exists']:
+ return
+
+ tmp = run('/usr/libexec/vyos/conf_mode/ipsec-settings.py')
+ if tmp > 0:
+ print('ERROR: failed to reapply L2TP IPSec settings!')
+
+def resync_nhrp(ipsec):
+ if not ipsec['nhrp_exists']:
+ return
+
+ run('/opt/vyatta/sbin/vyos-update-nhrp.pl --set_ipsec')
+
def apply(ipsec):
if not ipsec:
- if conf.exists('vpn l2tp '):
+ if ipsec['l2tp_exists']:
call('sudo /usr/sbin/ipsec rereadall')
call('sudo /usr/sbin/ipsec reload')
call('sudo /usr/sbin/swanctl -q')
else:
call('sudo /usr/sbin/ipsec stop')
+ else:
+ should_start = ('profile' in ipsec or dict_search('site_to_site.peer', ipsec))
- resync_l2tp(conf)
- resync_nhrp(conf)
- return
-
- diff = ConfigDiff(conf, key_mangling=('-', '_'))
- diff.set_level(['vpn', 'ipsec'])
-
- old_if, new_if = diff.get_value_diff(['ipsec-interfaces', 'interface'])
- interface_change = (old_if != new_if)
-
- should_start = ('profile' in ipsec or ('site_to_site' in ipsec and 'peer' in ipsec['site_to_site']))
-
- if not process_named_running('charon'):
- args = ''
- if 'auto_update' in ipsec:
- args = f'--auto-update {ipsec["auto_update"]}'
-
- if should_start:
+ 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}')
- else:
- if not should_start:
- call('sudo /usr/sbin/ipsec stop')
- elif interface_change:
+ elif not should_start:
+ 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')
+ if should_start:
+ sleep(2) # Give charon enough time to start
+ call('sudo /usr/sbin/swanctl -q')
- resync_l2tp(conf)
- resync_nhrp(conf)
+ resync_l2tp(ipsec)
+ resync_nhrp(ipsec)
def get_mark(vti_interface):
vti_num = int(vti_interface.lstrip('vti'))
@@ -390,10 +385,12 @@ def get_mark(vti_interface):
def get_dhcp_address(interface):
addr = get_interface_address(interface)
- if not addr:
+ if not addr or 'addr_info' not in addr:
return None
if len(addr['addr_info']) == 0:
return None
+ if 'local' not in addr['addr_info'][0]:
+ return None
return addr['addr_info'][0]['local']
if __name__ == '__main__':