'use strict'; 'require form'; 'require fs'; 'require ui'; 'require view'; var allSshKeys = {}; var hasSshKeygen = false; return view.extend({ handleSaveApply: null, handleSave: null, handleReset: null, 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 sshKeyName of sshKeyNames) { var sshPubKeyName = sshKeyName + '.pub'; tasks.push(Promise.resolve(sshKeyName)); tasks.push(_loadPublicKey(sshPubKeyName)); } return Promise.all(tasks); }); }, render: function (data) { hasSshKeygen = data[0].type === 'file'; var sshKeys = _splitSshKeys(data.splice(1)); var m, s, o; m = new form.Map('sshtunnel', _('SSH Tunnels'), _('This configures SSH Tunnels.') .format('href="https://openwrt.org/docs/guide-user/services/ssh/sshtunnel"') ); s = m.section(form.GridSection, '_keys'); s.render = L.bind(_renderSshKeys, this, sshKeys); return m.render(); }, }); function _findAllPossibleIdKeys(entries) { var sshKeyNames = new Set(); var fileNames = entries.filter(item => item.type === 'file').map(item => item.name); for (var fileName of fileNames) { // a key file should have a corresponding .pub file if (fileName.endsWith('.pub')) { var sshKeyName = fileName.slice(0, -4); // if such a key exists then add it if (fileNames.includes(sshKeyName)) { sshKeyNames.add(sshKeyName); } } else { // or at least it should start with id_ e.g. id_dropbear if (fileName.startsWith('id_')) { var sshKeyName = fileName; sshKeyNames.add(sshKeyName); } } } return Array.from(sshKeyNames); } function _splitSshKeys(sshFiles) { var sshKeys = {}; for (var i = 0; i < sshFiles.length; i++) { var sshKeyName = sshFiles[i]; i++; // next is a .pub content var sshPub = sshFiles[i]; sshKeys[sshKeyName] = '' + sshPub + ''; } allSshKeys = sshKeys; return sshKeys; } function _renderSshKeys(sshKeys) { var table = E('table', {'class': 'table cbi-section-table', 'id': 'keys_table'}, [ E('tr', {'class': 'tr table-titles'}, [ E('th', {'class': 'th'}, _('Name')), E('th', {'class': 'th'}, _('Public Key')), ]) ]); var rows = Object.entries(sshKeys); cbi_update_table(table, rows, null); var keyGenBtn = E('div', {}, [ E('h4', _('Generate a new key')), E('form', { 'submit': _handleKeyGenSubmit, }, [ E('label', {}, _('Name') + ': '), E('span', {'class': 'control-group'}, [ E('input', { 'type': 'text', 'name': 'keyName', 'value': 'id_ed25519', 'pattern': '^[a-zA-Z][a-zA-Z0-9_.@\\-+]+', 'required': 'required', 'maxsize': '35', 'autocomplete': 'off', }), E('button', { 'id': 'btnGenerateKey', 'type': 'submit', 'class': 'btn cbi-button cbi-button-action', }, [_('Generate')]) ]) ]) ]); return E('div', {'class': 'cbi-section cbi-tblsection'}, [ E('h3', _('SSH Keys')), E('div', {'class': 'cbi-section-descr'}, _('Add the pub key to %s or %s.') .format('/root/.ssh/authorized_keys', '/etc/dropbear/authorized_keys') + ' ' + _('In LuCI you can do that with System / Administration / SSH-Keys') .format('href="/cgi-bin/luci/admin/system/admin/sshkeys"') ), table, keyGenBtn, ]); } function _handleKeyGenSubmit(event) { event.preventDefault(); var keyName = document.querySelector('input[name="keyName"]').value; keyName = keyName.startsWith('id_') ? keyName : 'id_' + keyName; if (allSshKeys[keyName]) { document.body.scrollTop = document.documentElement.scrollTop = 0; ui.addNotification(null, E('p', _('A key with that name already exists.'), 'error')); return false; } let command = '/usr/bin/ssh-keygen'; let commandArgs = ['-t', 'ed25519', '-q', '-N', '', '-f', '/root/.ssh/' + keyName]; if (!hasSshKeygen) { command = '/usr/bin/dropbearkey'; commandArgs = ['-t', 'ed25519', '-f', '/root/.ssh/' + keyName]; } fs.exec(command, commandArgs).then(function (res) { if (res.code === 0) { // refresh the page to see the new key location.reload(); } else { throw new Error(res.stdout + ' ' + res.stderr); } }).catch(function (e) { document.body.scrollTop = document.documentElement.scrollTop = 0; ui.addNotification(null, E('p', _('Unable to generate a key: %s').format(e.message)), 'error'); }); return false; } function _extractPubKeyFromOutput(res) { var lines = res.stdout.split('\n'); for (var line of lines) { if (line.startsWith('ssh-')) { return line; } } return ''; } function _loadPublicKey(sshPubKeyName) { return fs.read('/root/.ssh/' + sshPubKeyName) .catch(() => { // If there is no the .pub file then this is probably a Dropbear key e.g. id_dropbear. // We can extract it's public key by executing: dropbearkey -y -f /root/.ssh/id_dropbear var sshKeyName = sshPubKeyName.substring(0, sshPubKeyName.length - 4); return fs.exec('/usr/bin/dropbearkey', ['-y', '-f', '/root/.ssh/' + sshKeyName]).then((res) => { if (res.code === 0) { return _extractPubKeyFromOutput(res); } else { console.log('dropbearkey: ' + res.stdout + ' ' + res.stderr); return ''; } }).catch(()=> ''); }); }