mirror of
https://github.com/balena-io/balena-cli.git
synced 2025-01-18 02:39:49 +00:00
tls: Use TLS for tunnel connection
Switch to using the exposed tunnelUrl and TLS for making tunnels to the device, to improve security. Change-type: patch Signed-off-by: Rich Bayliss <rich@balena.io>
This commit is contained in:
parent
d60ec13d5c
commit
7ae83d9ce5
@ -15,10 +15,14 @@ limitations under the License.
|
||||
*/
|
||||
import type { BalenaSDK } from 'balena-sdk';
|
||||
import { Socket } from 'net';
|
||||
import * as tls from 'tls';
|
||||
import { TypedError } from 'typed-error';
|
||||
import { ExpectedError } from '../errors';
|
||||
|
||||
const PROXY_CONNECT_TIMEOUT_MS = 10000;
|
||||
|
||||
class TunnelServerNotTrustedError extends ExpectedError {}
|
||||
|
||||
class UnableToConnectError extends TypedError {
|
||||
public status: string;
|
||||
public statusCode: string;
|
||||
@ -42,17 +46,17 @@ export const tunnelConnectionToDevice = (
|
||||
sdk: BalenaSDK,
|
||||
) => {
|
||||
return Promise.all([
|
||||
sdk.settings.get('vpnUrl'),
|
||||
sdk.settings.get('tunnelUrl'),
|
||||
sdk.auth.whoami(),
|
||||
sdk.auth.getToken(),
|
||||
]).then(([vpnUrl, whoami, token]) => {
|
||||
]).then(([tunnelUrl, whoami, token]) => {
|
||||
const auth = {
|
||||
user: whoami || 'root',
|
||||
password: token,
|
||||
};
|
||||
|
||||
return (client: Socket): Promise<void> =>
|
||||
openPortThroughProxy(vpnUrl, 3128, auth, uuid, port)
|
||||
openPortThroughProxy(tunnelUrl, 443, auth, uuid, port)
|
||||
.then((remote) => {
|
||||
client.pipe(remote);
|
||||
remote.pipe(client);
|
||||
@ -96,30 +100,41 @@ const openPortThroughProxy = (
|
||||
}
|
||||
|
||||
return new Promise<Socket>((resolve, reject) => {
|
||||
const proxyTunnel = new Socket();
|
||||
proxyTunnel.on('error', reject);
|
||||
proxyTunnel.connect(proxyPort, proxyServer, () => {
|
||||
const proxyConnectionHandler = (data: Buffer) => {
|
||||
proxyTunnel.removeListener('data', proxyConnectionHandler);
|
||||
const [httpStatus] = data.toString('utf8').split('\r\n');
|
||||
const [, httpStatusCode, ...httpMessage] = httpStatus.split(' ');
|
||||
|
||||
if (parseInt(httpStatusCode, 10) === 200) {
|
||||
proxyTunnel.setTimeout(0);
|
||||
resolve(proxyTunnel);
|
||||
} else {
|
||||
const proxyTunnel = tls.connect(
|
||||
proxyPort,
|
||||
proxyServer,
|
||||
{ servername: proxyServer }, // send the hostname in the SNI field
|
||||
() => {
|
||||
if (!proxyTunnel.authorized) {
|
||||
console.error('Unable to authorize the tunnel server');
|
||||
reject(
|
||||
new UnableToConnectError(httpStatusCode, httpMessage.join(' ')),
|
||||
new TunnelServerNotTrustedError(proxyTunnel.authorizationError),
|
||||
);
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
proxyTunnel.on('timeout', () => {
|
||||
reject(new RemoteSocketNotListening(devicePort));
|
||||
});
|
||||
proxyTunnel.on('data', proxyConnectionHandler);
|
||||
proxyTunnel.setTimeout(PROXY_CONNECT_TIMEOUT_MS);
|
||||
proxyTunnel.write(httpHeaders.join('\r\n').concat('\r\n\r\n'));
|
||||
});
|
||||
proxyTunnel.once('data', (data: Buffer) => {
|
||||
const [httpStatus] = data.toString('utf8').split('\r\n');
|
||||
const [, httpStatusCode, ...httpMessage] = httpStatus.split(' ');
|
||||
|
||||
if (parseInt(httpStatusCode, 10) === 200) {
|
||||
proxyTunnel.setTimeout(0);
|
||||
resolve(proxyTunnel);
|
||||
} else {
|
||||
reject(
|
||||
new UnableToConnectError(httpStatusCode, httpMessage.join(' ')),
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
proxyTunnel.on('timeout', () => {
|
||||
reject(new RemoteSocketNotListening(devicePort));
|
||||
});
|
||||
|
||||
proxyTunnel.setTimeout(PROXY_CONNECT_TIMEOUT_MS);
|
||||
proxyTunnel.write(httpHeaders.join('\r\n').concat('\r\n\r\n'));
|
||||
},
|
||||
);
|
||||
proxyTunnel.on('error', reject);
|
||||
});
|
||||
};
|
||||
|
6
npm-shrinkwrap.json
generated
6
npm-shrinkwrap.json
generated
@ -2760,9 +2760,9 @@
|
||||
}
|
||||
},
|
||||
"balena-settings-client": {
|
||||
"version": "4.0.5",
|
||||
"resolved": "https://registry.npmjs.org/balena-settings-client/-/balena-settings-client-4.0.5.tgz",
|
||||
"integrity": "sha512-w1SWIQYViMP51PYnPvbwgGavipkBv8wbRj1ISjPYZ5M45oEVRcktDfix8c3xOlWl+vWqW8aA4L8BjhqnxhAvRQ==",
|
||||
"version": "4.0.6",
|
||||
"resolved": "https://registry.npmjs.org/balena-settings-client/-/balena-settings-client-4.0.6.tgz",
|
||||
"integrity": "sha512-bB14Zvg1N6t7XXPJqZs48SajgTuk2WTMm2AnxcOfoIQ2d/Lh0RsEGxD9toF2v+WhF2Ip4u7ko5tKlCr2kFddXA==",
|
||||
"requires": {
|
||||
"@resin.io/types-hidepath": "1.0.1",
|
||||
"@resin.io/types-home-or-tmp": "3.0.0",
|
||||
|
@ -203,7 +203,7 @@
|
||||
"balena-release": "^3.0.0",
|
||||
"balena-sdk": "^15.20.0",
|
||||
"balena-semver": "^2.3.0",
|
||||
"balena-settings-client": "^4.0.5",
|
||||
"balena-settings-client": "^4.0.6",
|
||||
"balena-settings-storage": "^7.0.0",
|
||||
"balena-sync": "^11.0.2",
|
||||
"bluebird": "^3.7.2",
|
||||
|
Loading…
Reference in New Issue
Block a user