From d08fc85459013549e60d6e1ad6258b5d33c493c1 Mon Sep 17 00:00:00 2001 From: Jan Prochazka Date: Sun, 14 Feb 2021 09:11:40 +0100 Subject: [PATCH] SSL support --- packages/api/src/utility/connectUtility.js | 65 ++++++++++++---------- packages/web/src/modals/ConnectionModal.js | 20 +++++++ packages/web/src/utility/forms.js | 2 +- 3 files changed, 58 insertions(+), 29 deletions(-) diff --git a/packages/api/src/utility/connectUtility.js b/packages/api/src/utility/connectUtility.js index 88a3942a..c11d4d99 100644 --- a/packages/api/src/utility/connectUtility.js +++ b/packages/api/src/utility/connectUtility.js @@ -1,45 +1,54 @@ const { SSHConnection } = require('node-ssh-forward'); const portfinder = require('portfinder'); +const fs = require('fs-extra'); const { decryptConnection } = require('./crypting'); const { getSshTunnel } = require('./sshTunnel'); const { getSshTunnelProxy } = require('./sshTunnelProxy'); async function connectUtility(driver, storedConnection) { - let connection = decryptConnection(storedConnection); + const connection = { + ...decryptConnection(storedConnection), + }; if (connection.useSshTunnel) { const tunnel = await getSshTunnelProxy(connection); if (tunnel.state == 'error') { throw new Error(tunnel.message); } - // const sshConfig = { - // endHost: connection.sshHost || '', - // endPort: connection.sshPort || 22, - // bastionHost: '', - // agentForward: false, - // passphrase: undefined, - // username: connection.sshLogin, - // password: connection.sshPassword, - // skipAutoPrivateKey: true, - // noReadline: true, - // }; - // const sshConn = new SSHConnection(sshConfig); - // const localPort = await portfinder.getPortPromise({ port: 10000, stopPort: 60000 }); - // // workaround for `getPortPromise` not releasing the port quickly enough - // await new Promise(resolve => setTimeout(resolve, 500)); - // const tunnelConfig = { - // fromPort: localPort, - // toPort: connection.port, - // toHost: connection.server, - // }; - // const tunnel = await sshConn.forward(tunnelConfig); - // console.log(`Created SSH tunnel to ${connection.sshHost}-${connection.server}:${connection.port}, using local port ${localPort}`) + connection.server = '127.0.0.1'; + connection.port = tunnel.localPort; + } - connection = { - ...connection, - server: '127.0.0.1', - port: tunnel.localPort, - }; + if (!connection.port && driver.defaultPort) connection.port = driver.defaultPort.toString(); + + // SSL functionality - copied from https://github.com/beekeeper-studio/beekeeper-studio + if (connection.useSsl) { + connection.ssl = {}; + + if (connection.sslCaFile) { + connection.ssl.ca = await fs.readFile(connection.sslCaFile); + } + + if (connection.sslCertFile) { + connection.ssl.cert = await fs.readFile(connection.sslCertFile); + } + + if (connection.sslKeyFile) { + connection.ssl.key = await fs.readFile(connection.sslKeyFile); + } + + if (!connection.ssl.key && !connection.ssl.ca && !connection.ssl.cert) { + // TODO: provide this as an option in settings + // or per-connection as 'reject self-signed certs' + // How it works: + // if false, cert can be self-signed + // if true, has to be from a public CA + // Heroku certs are self-signed. + // if you provide ca/cert/key files, it overrides this + connection.ssl.rejectUnauthorized = false; + } else { + connection.ssl.rejectUnauthorized = connection.sslRejectUnauthorized; + } } const conn = await driver.connect(connection); diff --git a/packages/web/src/modals/ConnectionModal.js b/packages/web/src/modals/ConnectionModal.js index f42b4917..2ed3e74d 100644 --- a/packages/web/src/modals/ConnectionModal.js +++ b/packages/web/src/modals/ConnectionModal.js @@ -98,6 +98,7 @@ function DriverFields({ extensions }) { name="port" disabled={disabledFields.includes('port')} templateProps={{ noMargin: true }} + placeholder={driver && driver.defaultPort} /> @@ -235,6 +236,22 @@ function SshTunnelFields() { ); } +function SslFields() { + const { values } = useForm(); + const { useSsl } = values; + const electron = getElectron(); + + return ( + <> + + + + + + + ); +} + export default function ConnectionModal({ modalState, connection = undefined }) { const [sqlConnectResult, setSqlConnectResult] = React.useState(null); const extensions = useExtensions(); @@ -288,6 +305,9 @@ export default function ConnectionModal({ modalState, connection = undefined }) + + + diff --git a/packages/web/src/utility/forms.js b/packages/web/src/utility/forms.js index 7d20a742..2c315cdf 100644 --- a/packages/web/src/utility/forms.js +++ b/packages/web/src/utility/forms.js @@ -119,7 +119,7 @@ export function FormCheckboxField({ label, templateProps = undefined, ...other } setFieldValue(other.name, !values[other.name])} + onLabelClick={other.disabled ? undefined : () => setFieldValue(other.name, !values[other.name])} {...templateProps} >