From feb030fb7d285fbb50723560bc3f88a9ba1970cc Mon Sep 17 00:00:00 2001 From: Florian Eckert Date: Tue, 22 Sep 2026 12:44:17 +0200 Subject: [PATCH] luci-app-strongswan-swanctl: add local and remote configuration tabs This syncs the LuCI strongswan configuration with the swanctl initscript change that moved the local and remote authentication options into their own uci sections, referenced from the connection via the 'list local' and 'list remote' options. This allows modelling of multiple IKEv2 authentication rounds. Add new 'local' and 'remote' GridSection tabs with the options moved out of the connection section: - local: auth, id, certs, key - remote: auth, id, cacerts, eap_id The connection section now references the created sections through new 'Local Authentication' and 'Remote Authentication' options. The obsolete flat options (authentication_method, local_identifier, local_cert, local_key, remote_identifier, remote_ca_certs) are removed. The section name validation is extended so that local and remote sections cannot share names with other section types. Signed-off-by: Florian Eckert --- .../view/strongswan-swanctl/swanctl.js | 150 ++++++++++++------ 1 file changed, 102 insertions(+), 48 deletions(-) diff --git a/applications/luci-app-strongswan-swanctl/htdocs/luci-static/resources/view/strongswan-swanctl/swanctl.js b/applications/luci-app-strongswan-swanctl/htdocs/luci-static/resources/view/strongswan-swanctl/swanctl.js index a9fc68dfcb..16f11db17f 100644 --- a/applications/luci-app-strongswan-swanctl/htdocs/luci-static/resources/view/strongswan-swanctl/swanctl.js +++ b/applications/luci-app-strongswan-swanctl/htdocs/luci-static/resources/view/strongswan-swanctl/swanctl.js @@ -39,11 +39,13 @@ function sectionNameCheck(extra_class) { ...uci.sections('ipsec', 'connection'), ...uci.sections('ipsec', 'child'), ...uci.sections('ipsec', 'crypto_proposal'), + ...uci.sections('ipsec', 'local'), + ...uci.sections('ipsec', 'remote'), ]; if (sections.find(function(s) { return s['.name'] == v; })) { - return _('Connections, Encryption Proposals and Children may not share the same names.') + ' ' + + return _('Connections, Encryption Proposals, Children, Locals and Remotes may not share the same names.') + ' ' + _('Use combinations like child1_phase1.'); } return true; @@ -148,55 +150,44 @@ return view.extend({ }; o.rmempty = false; - o = s.taboption('authentication', form.ListValue, 'authentication_method', - _('Authentication Method'), _('IKE authentication (phase 1)')); - o.modalonly = true; - o.value('psk', 'Pre-shared Key'); - o.value('pubkey', 'Public Key'); + o = s.taboption('authentication', form.MultiValue, 'local', _('Local Authentication'), + _('Local authentication sections used for this connection.') + ' ' + + _('Each section corresponds to one authentication round.')); + o.load = function (section_id) { + this.keylist = []; + this.vallist = []; - o = s.taboption('authentication', form.Value, 'local_identifier', _('Local Identifier'), - _('Local identifier for IKE (phase 1)')); - o.datatype = 'string'; - o.placeholder = 'C=US, O=Acme Corporation, CN=headquarters'; - o.modalonly = true; + var sections = uci.sections('ipsec', 'local'); + if (sections.length == 0) { + this.value('', _('Please create a Local section first')); + } else { + sections.forEach(L.bind(function (section) { + this.value(section['.name']); + }, this)); + } - o = s.taboption('authentication', form.Value, 'remote_identifier', _('Remote Identifier'), - _('Remote identifier for IKE (phase 1)')); - o.datatype = 'string'; - o.placeholder = 'C=US, O=Acme Corporation, CN=soho'; - o.modalonly = true; + return this.super('load', [section_id]); + }; - o = s.taboption('authentication', form.Value, 'pre_shared_key', _('Pre-Shared Key'), - _('The pre-shared key for the tunnel')); - o.datatype = 'string'; - o.password = true; - o.modalonly = true; + o = s.taboption('authentication', form.MultiValue, 'remote', _('Remote Authentication'), + _('Remote authentication sections used for this connection.') + ' ' + + _('Each section corresponds to one authentication round.')); + o.load = function (section_id) { + this.keylist = []; + this.vallist = []; + + var sections = uci.sections('ipsec', 'remote'); + if (sections.length == 0) { + this.value('', _('Please create a Remote section first')); + } else { + sections.forEach(L.bind(function (section) { + this.value(section['.name']); + }, this)); + } + + return this.super('load', [section_id]); + }; o.rmempty = false; - o.depends('authentication_method', 'psk'); - - o = s.taboption('authentication', form.Value, 'local_cert', _('Local Certificate'), - _('Certificate pathname to use for authentication')); - o.datatype = 'file'; - o.depends('authentication_method', 'pubkey'); - o.modalonly = true; - - o = s.taboption('authentication', form.Value, 'local_key', _('Local Key'), - _('Private key pathname to use with above certificate')); - o.datatype = 'file'; - o.modalonly = true; - - o = s.taboption('authentication', form.Value, 'ca_cert', _('CA Certificate'), - _("CA certificate that need to lie in remote peer's certificate's path of trust")); - o.datatype = 'file'; - o.depends('authentication_method', 'pubkey'); - o.modalonly = true; - - o = s.taboption('authentication', form.DynamicList, 'remote_ca_certs', _('Remote CA Certificates'), - _('Restrict the remote peer\'s certificate to be issued by one of these CAs')); - o.datatype = 'file'; - o.depends('authentication_method', 'pubkey'); - o.optional = true; - o.modalonly = true; o = s.taboption('authentication', form.ListValue, 'send_cert', _('Send Certificate'), _('Whether to send our own certificate to the remote peer')); @@ -204,14 +195,12 @@ return view.extend({ o.value('ifasked'); o.value('never'); o.default = 'ifasked'; - o.depends('authentication_method', 'pubkey'); o.optional = true; o.modalonly = true; o = s.taboption('authentication', form.Flag, 'send_certreq', _('Send Certificate Request'), _('Send certificate request payloads to offer trusted root CA certificates to the peer')); o.default = '1'; - o.depends('authentication_method', 'pubkey'); o.modalonly = true; o = s.taboption('advanced', form.Flag, 'mobike', _('MOBIKE'), @@ -274,6 +263,71 @@ return view.extend({ o.default = 'ikev2'; o.modalonly = true; + // Local Configuration + s = m.section(form.GridSection, 'local', _('Local'), + _('Define local IKE authentication rounds referenced from connections.')); + s.addremove = true; + s.nodescriptions = true; + s.renderSectionAdd = sectionNameCheck; + + o = s.option(form.ListValue, 'auth', _('Authentication Method'), + _('IKE authentication (phase 1)')); + o.value('psk', 'Pre-shared Key'); + o.value('pubkey', 'Public Key'); + o.default = 'psk'; + o.rmempty = false; + + o = s.option(form.Value, 'id', _('Local Identifier'), + _('Local identifier for IKE (phase 1)')); + o.datatype = 'string'; + o.placeholder = 'C=US, O=Acme Corporation, CN=headquarters'; + o.modalonly = true; + + o = s.option(form.Value, 'certs', _('Local Certificate'), + _('Certificate to use for authentication, relative to /etc/swanctl/x509.')); + o.datatype = 'file'; + o.modalonly = true; + + o = s.option(form.Value, 'key', _('Local Key'), + _('Private key to use with the certificate, relative to /etc/swanctl/private.')); + o.datatype = 'file'; + o.modalonly = true; + + // Remote Configuration + s = m.section(form.GridSection, 'remote', _('Remote'), + _('Define remote IKE authentication rounds referenced from connections.')); + s.addremove = true; + s.nodescriptions = true; + s.renderSectionAdd = sectionNameCheck; + + o = s.option(form.ListValue, 'auth', _('Authentication Method'), + _('IKE authentication (phase 1)')); + o.value('psk', 'Pre-shared Key'); + o.value('pubkey', 'Public Key'); + o.value('eap-mschapv2', 'EAP MSCHAPv2'); + o.value('eap-tls', 'EAP TLS'); + o.default = 'psk'; + o.rmempty = false; + + o = s.option(form.Value, 'id', _('Remote Identifier'), + _('Remote identifier for IKE (phase 1)')); + o.datatype = 'string'; + o.placeholder = 'C=US, O=Acme Corporation, CN=soho'; + o.modalonly = true; + + o = s.option(form.DynamicList, 'cacerts', _('Remote CA Certificates'), + _('Restrict the remote peer\'s certificate to be issued by one of these CAs')); + o.datatype = 'file'; + o.modalonly = true; + + o = s.option(form.Value, 'eap_id', _('EAP ID'), + _('EAP identity to use with the remote peer')); + o.datatype = 'string'; + o.default = '%any'; + o.depends('auth', 'eap-mschapv2'); + o.depends('auth', 'eap-tls'); + o.modalonly = true; + // Children Configuration s = m.section(form.GridSection, 'child', _('Children'), _('Define Connection Children to be used in Remote Configurations.'));