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 <stokito@gmail.com>
This commit is contained in:
Sergey Ponomarev 2023-12-17 14:34:32 +02:00 committed by Paul Donald
parent 3933633dd0
commit 949e7cbcb3
2 changed files with 65 additions and 30 deletions

View file

@ -11,14 +11,16 @@ return view.extend({
load: function () { load: function () {
return L.resolveDefault(fs.list('/root/.ssh/'), []).then(function (entries) { return L.resolveDefault(fs.list('/root/.ssh/'), []).then(function (entries) {
var tasks = [ var tasks = [
// detect if OpenSSH ssh-keygen is installed
L.resolveDefault(fs.stat('/usr/bin/ssh-keygen'), {}), L.resolveDefault(fs.stat('/usr/bin/ssh-keygen'), {}),
]; ];
var sshKeyNames = _findAllPossibleIdKeys(entries);
// read pub keys // read pub keys
for (var i = 0; i < entries.length; i++) { for (var sshKeyName of sshKeyNames) {
if (entries[i].type === 'file' && entries[i].name.match(/\.pub$/)) { var sshPubKeyName = sshKeyName + '.pub';
tasks.push(Promise.resolve(entries[i].name)); tasks.push(Promise.resolve(sshKeyName));
tasks.push(fs.lines('/root/.ssh/' + entries[i].name)); tasks.push(fs.lines('/root/.ssh/' + sshPubKeyName));
}
} }
return Promise.all(tasks); 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) { function _splitSshKeys(sshFiles) {
var sshKeys = {}; var sshKeys = {};
for (var i = 0; i < sshFiles.length; i++) { for (var i = 0; i < sshFiles.length; i++) {
var sshPubKeyName = sshFiles[i]; var sshKeyName = sshFiles[i];
var sshKeyName = sshPubKeyName.substring(0, sshPubKeyName.length - 4); i++; // next is a .pub content
i++;
var sshPub = sshFiles[i]; var sshPub = sshFiles[i];
sshKeys[sshKeyName] = '<small><code>' + sshPub + '</code></small>'; sshKeys[sshKeyName] = '<small><code>' + sshPub + '</code></small>';
} }

View file

@ -5,24 +5,17 @@
'require ui'; 'require ui';
'require view'; 'require view';
var allSshKeys = {};
return view.extend({ return view.extend({
load: function () { load: function () {
return L.resolveDefault(fs.list('/root/.ssh/'), []).then(function (entries) { return L.resolveDefault(fs.list('/root/.ssh/'), []).then(function (entries) {
var tasks = []; var sshKeyNames = _findAllPossibleIdKeys(entries);
for (var i = 0; i < entries.length; i++) { return Promise.resolve(sshKeyNames);
if (entries[i].type === 'file' && entries[i].name.match(/\.pub$/)) {
tasks.push(Promise.resolve(entries[i].name));
}
}
return Promise.all(tasks);
}); });
}, },
render: function (data) { render: function (data) {
var sshKeys = _splitSshKeys(data); var sshKeyNames = data;
if (sshKeys.length === 0) { if (sshKeyNames.length === 0) {
ui.addNotification(null, E('p', _('No SSH keys found, <a %s>generate a new one</a>').format('href="./ssh_keys"')), 'warning'); ui.addNotification(null, E('p', _('No SSH keys found, <a %s>generate a new one</a>').format('href="./ssh_keys"')), 'warning');
} }
@ -58,9 +51,9 @@ return view.extend({
'See <em>ssh_config IdentityFile</em>') 'See <em>ssh_config IdentityFile</em>')
); );
o.value(''); o.value('');
Object.keys(sshKeys).forEach(function (keyName) { for (var sshKeyName of sshKeyNames) {
o.value('/root/.ssh/' + keyName, keyName); o.value('/root/.ssh/' + sshKeyName, sshKeyName);
}); }
o.optional = true; o.optional = true;
@ -135,13 +128,28 @@ return view.extend({
}, },
}); });
function _splitSshKeys(sshFiles) { function _findAllPossibleIdKeys(entries) {
var sshKeys = {}; var sshKeyNames = [];
for (var i = 0; i < sshFiles.length; i++) { for (var item of entries) {
var sshPubKeyName = sshFiles[i]; 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); var sshKeyName = sshPubKeyName.substring(0, sshPubKeyName.length - 4);
sshKeys[sshKeyName] = ''; if (!sshKeyNames.includes(sshKeyName)) {
sshKeyNames.push(sshKeyName)
} }
allSshKeys = sshKeys; } else {
return sshKeys; // 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;
} }