From 949e7cbcb33cc457bfcdd27aaf4da487bab89f30 Mon Sep 17 00:00:00 2001 From: Sergey Ponomarev Date: Sun, 17 Dec 2023 14:34:32 +0200 Subject: [PATCH] luci-app-sshtunnel: ssh_keys: list keys without a corresponding .pub The private keys doesn't have any extension so the only way to clearly say that this file is a key is a presence of the corresponding .pub file. Most of time key files have a prefix id_ e.g. id_rsa etc. The dropbearkey generates a key without a corresponding .pub file e.g. id_dropbearkey. So we need to detect a key files by both .pub file or id_ prefix. Key files without the id_ prefix won't be listed, sorry. Signed-off-by: Sergey Ponomarev --- .../resources/view/sshtunnel/ssh_keys.js | 43 ++++++++++++--- .../resources/view/sshtunnel/ssh_servers.js | 52 +++++++++++-------- 2 files changed, 65 insertions(+), 30 deletions(-) diff --git a/applications/luci-app-sshtunnel/htdocs/luci-static/resources/view/sshtunnel/ssh_keys.js b/applications/luci-app-sshtunnel/htdocs/luci-static/resources/view/sshtunnel/ssh_keys.js index 4001404aad..2c3ee7a53f 100644 --- a/applications/luci-app-sshtunnel/htdocs/luci-static/resources/view/sshtunnel/ssh_keys.js +++ b/applications/luci-app-sshtunnel/htdocs/luci-static/resources/view/sshtunnel/ssh_keys.js @@ -11,14 +11,16 @@ return view.extend({ load: function () { return L.resolveDefault(fs.list('/root/.ssh/'), []).then(function (entries) { var tasks = [ + // detect if OpenSSH ssh-keygen is installed L.resolveDefault(fs.stat('/usr/bin/ssh-keygen'), {}), ]; + var sshKeyNames = _findAllPossibleIdKeys(entries); + // read pub keys - for (var i = 0; i < entries.length; i++) { - if (entries[i].type === 'file' && entries[i].name.match(/\.pub$/)) { - tasks.push(Promise.resolve(entries[i].name)); - tasks.push(fs.lines('/root/.ssh/' + entries[i].name)); - } + for (var sshKeyName of sshKeyNames) { + var sshPubKeyName = sshKeyName + '.pub'; + tasks.push(Promise.resolve(sshKeyName)); + tasks.push(fs.lines('/root/.ssh/' + sshPubKeyName)); } return Promise.all(tasks); }); @@ -42,12 +44,37 @@ return view.extend({ }, }); +function _findAllPossibleIdKeys(entries) { + var sshKeyNames = []; + for (var item of entries) { + if (item.type !== 'file') { + continue + } + // a key file should have a corresponding .pub file + if (item.name.endsWith('.pub')) { + var sshPubKeyName = item.name; + var sshKeyName = sshPubKeyName.substring(0, sshPubKeyName.length - 4); + if (!sshKeyNames.includes(sshKeyName)) { + sshKeyNames.push(sshKeyName) + } + } else { + // or at least it should start with id_ e.g. id_dropbear + if (item.name.startsWith('id_')) { + var sshKeyName = item.name; + if (!sshKeyNames.includes(sshKeyName)) { + sshKeyNames.push(sshKeyName) + } + } + } + } + return sshKeyNames; +} + function _splitSshKeys(sshFiles) { var sshKeys = {}; for (var i = 0; i < sshFiles.length; i++) { - var sshPubKeyName = sshFiles[i]; - var sshKeyName = sshPubKeyName.substring(0, sshPubKeyName.length - 4); - i++; + var sshKeyName = sshFiles[i]; + i++; // next is a .pub content var sshPub = sshFiles[i]; sshKeys[sshKeyName] = '' + sshPub + ''; } diff --git a/applications/luci-app-sshtunnel/htdocs/luci-static/resources/view/sshtunnel/ssh_servers.js b/applications/luci-app-sshtunnel/htdocs/luci-static/resources/view/sshtunnel/ssh_servers.js index 5aa33e6df8..e932806631 100644 --- a/applications/luci-app-sshtunnel/htdocs/luci-static/resources/view/sshtunnel/ssh_servers.js +++ b/applications/luci-app-sshtunnel/htdocs/luci-static/resources/view/sshtunnel/ssh_servers.js @@ -5,24 +5,17 @@ 'require ui'; 'require view'; -var allSshKeys = {}; - return view.extend({ load: function () { return L.resolveDefault(fs.list('/root/.ssh/'), []).then(function (entries) { - var tasks = []; - for (var i = 0; i < entries.length; i++) { - if (entries[i].type === 'file' && entries[i].name.match(/\.pub$/)) { - tasks.push(Promise.resolve(entries[i].name)); - } - } - return Promise.all(tasks); + var sshKeyNames = _findAllPossibleIdKeys(entries); + return Promise.resolve(sshKeyNames); }); }, render: function (data) { - var sshKeys = _splitSshKeys(data); - if (sshKeys.length === 0) { + var sshKeyNames = data; + if (sshKeyNames.length === 0) { ui.addNotification(null, E('p', _('No SSH keys found, generate a new one').format('href="./ssh_keys"')), 'warning'); } @@ -58,9 +51,9 @@ return view.extend({ 'See ssh_config IdentityFile') ); o.value(''); - Object.keys(sshKeys).forEach(function (keyName) { - o.value('/root/.ssh/' + keyName, keyName); - }); + for (var sshKeyName of sshKeyNames) { + o.value('/root/.ssh/' + sshKeyName, sshKeyName); + } o.optional = true; @@ -135,13 +128,28 @@ return view.extend({ }, }); -function _splitSshKeys(sshFiles) { - var sshKeys = {}; - for (var i = 0; i < sshFiles.length; i++) { - var sshPubKeyName = sshFiles[i]; - var sshKeyName = sshPubKeyName.substring(0, sshPubKeyName.length - 4); - sshKeys[sshKeyName] = ''; +function _findAllPossibleIdKeys(entries) { + var sshKeyNames = []; + for (var item of entries) { + if (item.type !== 'file') { + continue + } + // a key file should have a corresponding .pub file + if (item.name.endsWith('.pub')) { + var sshPubKeyName = item.name; + var sshKeyName = sshPubKeyName.substring(0, sshPubKeyName.length - 4); + if (!sshKeyNames.includes(sshKeyName)) { + sshKeyNames.push(sshKeyName) + } + } else { + // or at least it should start with id_ e.g. id_dropbear + if (item.name.startsWith('id_')) { + var sshKeyName = item.name; + if (!sshKeyNames.includes(sshKeyName)) { + sshKeyNames.push(sshKeyName) + } + } + } } - allSshKeys = sshKeys; - return sshKeys; + return sshKeyNames; } -- 2.30.2