kopia-ui: UI/API server security (TLS & base auth)

This automatically launches Kopia CLI with new flags:

`--tls-generate-cert` which generates in-memory TLS certs for the server
and prints its fingerprint to stderr.

`--random-password` which sets up random base auth password

KopiaUI will then listen to the stderr messages and parse the cert
fingerprint and password and automatically provide them in the browser
it opens to show the UI.

This ensures that any local process (or script in a browser, etc.)
other than KopiaUI itself won't have access to the server API nor
UI because they won't know the ephemeral random password and won't
be able to intercept it due to TLS protection.
This commit is contained in:
Jarek Kowalski committed 2020-02-05 17:08:47 -08:00
1 parent 0a42ceab27
commit 758dbccd01
2 files changed
+76 -3

No files matched your search

+28 -2
View File
@@ -5,7 +5,7 @@ const config = require('electron-json-config');
const { resourcesPath, selectByOS } = require('./utils');
const { toggleLaunchAtStartup, willLaunchAtStartup, refreshWillLaunchAtStartup } = require('./auto-launch');
const { stopServer, actuateServer } = require('./server');
const { stopServer, actuateServer, getServerAddress, getServerCertSHA256, getServerPassword } = require('./server');
ipcMain.on('fetch-config', (event, arg) => {
@@ -62,7 +62,7 @@ function showMainWindow() {
autoHideMenuBar: true,
})
mainWindow.loadURL('http://localhost:51515/?ts=' + new Date().valueOf());
mainWindow.loadURL(getServerAddress() + '/?ts=' + new Date().valueOf());
mainWindow.on('closed', function () {
// forget the reference.
@@ -86,6 +86,32 @@ app.on('will-quit', function () {
stopServer();
});
app.on('login', (event, webContents, request, authInfo, callback) => {
// intercept password prompts and automatically enter password that the server has printed for us.
const p = getServerPassword();
if (p) {
event.preventDefault();
console.log('automatically logging in...');
callback('kopia', p);
}
});
app.on('certificate-error', (event, webContents, url, error, certificate, callback) => {
// intercept certificate errors and automatically trust the certificate the server has printed for us.
const expected = 'sha256/' + Buffer.from(getServerCertSHA256(), 'hex').toString('base64');
if (certificate.fingerprint === expected) {
console.log('accepting server certificate.');
// On certificate error we disable default behaviour (stop loading the page)
// and we then say "it is all fine - true" to the callback
event.preventDefault();
callback(true);
return;
}
console.log('certificate error:', certificate.fingerprint, expected);
});
// Ignore
app.on('window-all-closed', function () {})
+48 -1
View File
@@ -6,6 +6,37 @@ const { defaultServerBinary } = require('./utils');
const { spawn } = require('child_process');
let serverProcess = null;
let serverCertSHA256 = "";
let serverPassword = "";
let serverAddress = "";
function detectServerParam(data) {
let lines = (data + '').split('\n');
for (let i = 0; i < lines.length; i++) {
const p = lines[i].indexOf(": ");
if (p < 0) {
continue
}
const key = lines[i].substring(0, p);
const value = lines[i].substring(p + 2);
switch (key) {
case "SERVER PASSWORD":
serverPassword = value;
break;
case "SERVER CERT SHA256":
serverCertSHA256 = value;
break;
case "SERVER ADDRESS":
serverAddress = value;
break;
}
}
appendToLog(data);
}
function startServer() {
let kopiaPath = config.get('kopiaPath');
@@ -22,12 +53,16 @@ function startServer() {
args.push('server', '--ui');
args.push('--random-password')
args.push('--tls-generate-cert')
// args.push('--auto-shutdown=600s')
console.log(`spawning ${kopiaPath} ${args.join(' ')}`);
serverProcess = spawn(kopiaPath, args);
ipcMain.emit('server-status-updated');
serverProcess.stdout.on('data', appendToLog);
serverProcess.stderr.on('data', appendToLog);
serverProcess.stderr.on('data', detectServerParam);
serverProcess.on('close', (code, signal) => {
appendToLog(`child process exited with code ${code} and signal ${signal}`);
@@ -81,5 +116,17 @@ module.exports = {
}
},
getServerAddress() {
return serverAddress;
},
getServerCertSHA256() {
return serverCertSHA256;
},
getServerPassword() {
return serverPassword;
},
stopServer: stopServer,
}