VPN: IPsec: Virtual Tunnel Interfaces - optionally hook vti tunnel configuration to connection up event in order to support dynamic dns scenarios. closes https://github.com/opnsense/core/issues/6781

Simplify ipsec_configure_vti() to make sure we only drop interfaces when not required anymore (tunnel address cleanup is unconditional) and only set local/remote address when configured.
This commit is contained in:
Ad Schellevis 2024-02-12 17:46:47 +01:00
parent fb0f9764e0
commit 7182c0455f
5 changed files with 83 additions and 62 deletions

View File

@ -1731,26 +1731,7 @@ function ipsec_configure_vti($verbose = false, $device = null)
continue;
}
$local_configured = null;
$remote_configured = null;
if (!empty($configured_intf[$intf])) {
if (!is_ipaddr($configured_intf[$intf]['local'])) {
$local_configured = ipsec_resolve($configured_intf[$intf]['local']);
} else {
$local_configured = $configured_intf[$intf]['local'];
}
if (!is_ipaddr($configured_intf[$intf]['remote'])) {
$remote_configured = ipsec_resolve($configured_intf[$intf]['remote']);
} else {
$remote_configured = $configured_intf[$intf]['remote'];
}
}
if (
empty($configured_intf[$intf])
|| empty($intf_details['tunnel'])
|| $local_configured != $intf_details['tunnel']['src_addr']
|| $remote_configured != $intf_details['tunnel']['dest_addr']
) {
if (empty($configured_intf[$intf])) {
log_msg(sprintf("destroy interface %s", $intf), LOG_DEBUG);
legacy_interface_destroy($intf);
unset($current_interfaces[$intf]);
@ -1797,7 +1778,10 @@ function ipsec_configure_vti($verbose = false, $device = null)
if (legacy_interface_create('ipsec', $intf) === null) {
break;
}
mwexecf('/sbin/ifconfig %s reqid %s', [$intf, $intf_details['reqid']]);
}
// [re]initialise if_ipsec
mwexecf('/sbin/ifconfig %s reqid %s', [$intf, $intf_details['reqid']]);
if (!empty($intf_details['local']) && !empty($intf_details['remote'])) {
mwexecf('/sbin/ifconfig %s %s tunnel %s %s up', [
$intf,
is_ipaddrv6($intf_details['local']) ? 'inet6' : 'inet',

View File

@ -92,6 +92,18 @@ class Swanctl extends BaseModel
}
}
if (
(empty((string)$node->local) && !empty((string)$node->remote)) ||
(!empty((string)$node->local) && empty((string)$node->remote))
) {
$messages->appendMessage(
new Message(
gettext("A local and remote address should be provided or both should be left empty"),
$key . ".local"
)
);
}
if ($vti_inets['local'] != $vti_inets['remote']) {
$messages->appendMessage(new Message(gettext("Protocol families should match"), $key . ".local"));
}

View File

@ -365,13 +365,11 @@
<local type="NetworkField">
<NetMaskAllowed>N</NetMaskAllowed>
<WildcardEnabled>N</WildcardEnabled>
<Required>Y</Required>
<ValidationMessage>Please specify a valid address.</ValidationMessage>
</local>
<remote type="NetworkField">
<NetMaskAllowed>N</NetMaskAllowed>
<WildcardEnabled>N</WildcardEnabled>
<Required>Y</Required>
<ValidationMessage>Please specify a valid address.</ValidationMessage>
</remote>
<tunnel_local type="NetworkField">

View File

@ -29,7 +29,6 @@
handle swanctl.conf updown event
"""
import os
import json
import subprocess
import argparse
import tempfile
@ -37,7 +36,7 @@ import syslog
from configparser import ConfigParser
from lib import list_spds
spd_filename = '/usr/local/etc/swanctl/reqid_events.conf'
events_filename = '/usr/local/etc/swanctl/reqid_events.conf'
spd_add_cmd = 'spdadd -%(ipproto)s %(source)s %(destination)s any ' \
'-P out ipsec %(protocol)s/tunnel/%(local)s-%(remote)s/unique:%(reqid)s;'
@ -54,47 +53,64 @@ if __name__ == '__main__':
if cmd_args.action and cmd_args.action.startswith('up'):
syslog.openlog('charon', facility=syslog.LOG_LOCAL4)
syslog.syslog(syslog.LOG_NOTICE, '[UPDOWN] received %s event for reqid %s' % (cmd_args.action, cmd_args.reqid))
if os.path.exists(spd_filename):
if os.path.exists(events_filename):
cnf = ConfigParser()
cnf.read(spd_filename)
cnf.read(events_filename)
spds = []
vtis = []
for section in cnf.sections():
if cnf.get(section, 'reqid') == cmd_args.reqid \
or cnf.get(section, 'connection_child') == cmd_args.connection_child:
spds.append({
'reqid': cmd_args.reqid,
'local' : cmd_args.local,
'remote' : cmd_args.remote,
'destination': os.environ.get('PLUTO_PEER_CLIENT')
})
for opt in cnf.options(section):
if cnf.get(section, opt).strip() != '':
spds[-1][opt] = cnf.get(section, opt).strip()
if section.startswith('spd_'):
spds.append({
'reqid': cmd_args.reqid,
'local' : cmd_args.local,
'remote' : cmd_args.remote,
'destination': os.environ.get('PLUTO_PEER_CLIENT')
})
for opt in cnf.options(section):
if cnf.get(section, opt).strip() != '':
spds[-1][opt] = cnf.get(section, opt).strip()
elif section.startswith('vti_'):
vtis.append({
'reqid': cmd_args.reqid,
'local' : cmd_args.local,
'remote' : cmd_args.remote
})
# (re)apply manual policies if specified
cur_spds = list_spds(automatic=False)
set_key = []
for spd in cur_spds:
policy_found = False
for mspd in spds:
if mspd['source'] == spd['src'] and mspd['destination'] == spd['dst']:
policy_found = True
if policy_found or spd['reqid'] == cmd_args.reqid:
set_key.append('spddelete -n %(src)s %(dst)s any -P %(direction)s;' % spd)
for vti in vtis:
if None in vti.values():
# incomplete, skip
continue
intf = 'ipsec%s' % vti['reqid']
proto = 'inet6' if vti['local'].find(':') > -1 else 'inet'
subprocess.run(['/sbin/ifconfig', intf, 'reqid', vti['reqid']])
subprocess.run(['/sbin/ifconfig', intf, proto, 'tunnel', vti['local'], vti['remote']])
for spd in spds:
if None in spd.values():
# incomplete, skip
continue
spd['ipproto'] = '4' if spd.get('source', '').find(':') == -1 else '6'
syslog.syslog(
syslog.LOG_NOTICE,
'[UPDOWN] add manual policy : %s' % (spd_add_cmd % spd)[7:]
)
set_key.append(spd_add_cmd % spd)
if len(set_key) > 0:
f = tempfile.NamedTemporaryFile(mode='wt', delete=False)
f.write('\n'.join(set_key))
f.close()
subprocess.run(['/sbin/setkey', '-f', f.name], capture_output=True, text=True)
os.unlink(f.name)
# (re)apply manual policies if specified
cur_spds = list_spds(automatic=False)
set_key = []
for spd in cur_spds:
policy_found = False
for mspd in spds:
if mspd['source'] == spd['src'] and mspd['destination'] == spd['dst']:
policy_found = True
if policy_found or spd['reqid'] == cmd_args.reqid:
set_key.append('spddelete -n %(src)s %(dst)s any -P %(direction)s;' % spd)
for spd in spds:
if None in spd.values():
# incomplete, skip
continue
spd['ipproto'] = '4' if spd.get('source', '').find(':') == -1 else '6'
syslog.syslog(
syslog.LOG_NOTICE,
'[UPDOWN] add manual policy : %s' % (spd_add_cmd % spd)[7:]
)
set_key.append(spd_add_cmd % spd)
if len(set_key) > 0:
f = tempfile.NamedTemporaryFile(mode='wt', delete=False)
f.write('\n'.join(set_key))
f.close()
subprocess.run(['/sbin/setkey', '-f', f.name], capture_output=True, text=True)
os.unlink(f.name)

View File

@ -16,3 +16,14 @@ description = {{spd.description}}
uuid = {{spd['@uuid']}}
{% endif %}
{% endfor %}
{% for vti in helpers.toList('OPNsense.Swanctl.VTIs.VTI') %}
{% if vti.enabled == '1' and vti.local|default('') == '' and vti.remote|default('') == '' %}
[vti_{{vti['@uuid']|replace('-', '')}}]
reqid = {{vti.reqid}}
description = {{vti.description}}
uuid = {{vti['@uuid']}}
{% endif %}
{% endfor %}