luci-app-strongswan-swanctl: add legacy ipsec config migration overlay

The strongswan-swanctl view now checks on load whether a legacy ipsec
configuration exists in '/etc/config/ipsec'. If no uci globals section is
present, a blocking overlay is shown on the connection page which offers
to run the command '/etc/init.d/swanctl migrate'.

While the migration is pending, the page action footer (Save & Apply,
Apply and Revert buttons) is disabled and the main content area is not
clickable, so that no changes can be made to the ipsec configuration
before the migration has finished. A successful migration reloads the page,
while a failed migration is reported with the option to retry.

If a legacy configuration can be found and to trigger the migration, the rpc
ACLs are extended with the need  permissions.

Signed-off-by: Florian Eckert <fe@dev.tdt.de>
This commit is contained in:
Florian Eckert authored and Florian Eckert committed 2026-09-30 11:58:57 +02:00
1 parent 8afbea7e0c
commit 6cbaf78428
2 files changed
+78 -1

No files matched your search

@@ -4,6 +4,7 @@
'require rpc';
'require uci';
'require ui';
'require fs';
const callListAlgorithms = rpc.declare({
object: 'luci.swanctl',
@@ -53,20 +54,90 @@ function sectionNameCheck(extra_class) {
return el;
};
let migrationOverlay = null;
function renderMigrationContent(errorMessage) {
const dialog = migrationOverlay.firstElementChild;
const migrateButton = E('button', {
'class': 'btn cbi-button cbi-button-apply',
'click': handleMigrate
}, [_('Migrate')]);
const content = E([], [
E('h4', _('Migrate IPsec configuration')),
E('p', _('A legacy IPsec configuration was found.') + ' ' +
_('It must be migrated to the new swanctl uci schema in ' +
'\'/etc/config/ipsec\' before it can be managed here.')),
E('p', _('Before migration, the file \'/etc/config/ipsec\' is moved to \'/etc/config/ipsec_bak\'.') + ' ' +
_('The migration may require manual intervention.') + ' ' +
_('Therefore, the VPN is not restarted.')),
E('p', _('Warning: The migration will drop all IPsec VPN connections.'))
]);
if (errorMessage)
content.appendChild(E('p', errorMessage));
content.appendChild(E('div', { 'class': 'right' }, [migrateButton]));
dialog.replaceChildren(content);
}
function showMigrationOverlay(viewNode) {
if (migrationOverlay)
return;
const dialog = E('div', { 'class': 'modal', 'style': 'margin:0;' });
migrationOverlay = E('div', {
'class': 'migration-overlay',
'style': 'position:absolute;top:0;right:0;bottom:0;left:0;z-index:900;display:flex;align-items:center;justify-content:center;padding:1em;pointer-events:all;'
}, [dialog]);
viewNode.style.position = 'relative';
viewNode.appendChild(migrationOverlay);
const maincontent = document.getElementById('maincontent');
if (maincontent)
maincontent.style.pointerEvents = 'none';
renderMigrationContent(null);
}
function handleMigrate() {
const dialog = migrationOverlay.firstElementChild;
dialog.replaceChildren(E([], [
E('h4', _('Migrate IPsec configuration')),
E('p', _('Migrating IPsec configuration…'))
]));
fs.exec('/etc/init.d/swanctl', ['migrate', L.env.sessionid]).then(function (result) {
if (result.code != 0)
renderMigrationContent(_('Migration failed: %s').format(result.stderr || result.stdout || _('unknown error')));
else
window.location.reload();
}).catch(function (e) {
renderMigrationContent(_('Migration failed: %s').format(e.message));
});
}
return view.extend({
load: function () {
return Promise.all([
callListAlgorithms(),
L.resolveDefault(uci.load('network'), null),
L.resolveDefault(uci.load('ipsec'), null),
]).then(function (data) {
let hasNoGlobals = uci.sections('ipsec', 'globals').length === 0;
return {
algorithms: data[0],
legacyConfig: hasNoGlobals,
};
});
},
render: function (result) {
let m, s, o;
const legacyConfig = result.legacyConfig;
const algorithms = result.algorithms.data ?? {};
const error = result.algorithms.error;
@@ -576,6 +647,10 @@ return view.extend({
o.depends({'is_esp': '0', 'use_custom_proposal': '0'});
addAlgorithms(o, algorithms.prf);
return m.render();
return m.render().then(function (node) {
if (legacyConfig)
showMigrationOverlay(node);
return node;
});
}
});
@@ -3,6 +3,7 @@
"description": "Grant access to luci-app-strongswan-swanctl",
"read": {
"file": {
"/etc/config/ipsec": [ "list" ],
"/usr/sbin/swanmon version": [ "exec" ],
"/usr/sbin/swanmon stats": [ "exec" ]
},
@@ -13,6 +14,7 @@
},
"write": {
"file": {
"/etc/init.d/swanctl migrate *": [ "exec" ],
"/usr/sbin/swanctl --initiate --ike *": [ "exec" ],
"/usr/sbin/swanctl --initiate --child *": [ "exec" ],
"/usr/sbin/swanctl --terminate --ike *": [ "exec" ],