@microsoft/dev-tunnels-ssh
Version:
SSH library for Dev Tunnels
268 lines • 13.5 kB
JavaScript
"use strict";
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
Object.defineProperty(exports, "__esModule", { value: true });
exports.SshSessionConfiguration = exports.SshProtocolExtensionNames = void 0;
const vscode_jsonrpc_1 = require("vscode-jsonrpc");
const sshAlgorithms_1 = require("./algorithms/sshAlgorithms");
const keyExchangeService_1 = require("./services/keyExchangeService");
const connectionService_1 = require("./services/connectionService");
const authenticationService_1 = require("./services/authenticationService");
const sshMessage_1 = require("./messages/sshMessage");
var SshProtocolExtensionNames;
(function (SshProtocolExtensionNames) {
/**
* Lists host key signature algorithms enabled by the sender.
*
* This is a "standard" protocol extension supported by most SSH implementations.
*/
SshProtocolExtensionNames["serverSignatureAlgorithms"] = "server-sig-algs";
/**
* An optimization that enables sending an initial channel request without
* waiting for a channel open confirmation message.
*/
SshProtocolExtensionNames["openChannelRequest"] = "open-channel-request@microsoft.com";
/**
* Enables reconnecting to a session that was recently disconnected.
*/
SshProtocolExtensionNames["sessionReconnect"] = "session-reconnect@microsoft.com";
/**
* Enables continual latency measurements between client and server.
*
* This extension requires that the reconnect extension is also enabled, because
* it leverages some of the session history info for reconnect to compute latency.
*/
SshProtocolExtensionNames["sessionLatency"] = "session-latency@microsoft.com";
})(SshProtocolExtensionNames = exports.SshProtocolExtensionNames || (exports.SshProtocolExtensionNames = {}));
/**
* Specifies the sets of algorithms and other configuration for an SSH session.
*
* Each collection of algorithms is in order of preference. Server and client
* negotiate the most-preferred algorithm that is supported by both.
*/
class SshSessionConfiguration {
constructor(useSecurity = true) {
/**
* Gets the protocol extensions that are enabled for the session.
*/
this.protocolExtensions = [];
/**
* Gets a dictionary that maps from service types to service configuration objects.
*
* Service types must extend the `SshService` abstract class.
*
* The service configuration object is passed to the service constructor upon activation.
*/
this.services = new Map();
/**
* Gets the list of enabled authentication methods.
*
* Add or remove `AuthenticationMethod` constants to restrict which client authentication
* methods the client will try or the server will allow. In any case, the client or server must
* handle the `SshSession.onAuthenticating` event to perform authentication.
*/
this.authenticationMethods = [];
/**
* Gets a dictionary that maps from known message numbers to message types.
*
* Message types must extend the `SshMessage` abstract class. Message subclasses that do
* not have a distinct message type from their base class must not be included in this map.
*/
this.messages = new Map();
/**
* Gets a dictionary that maps from message context to message type mappings for each context.
*
* Services like `AuthenticationService` may set the current message context to
* disambiguate when the same message number may be re-used in different contexts.
*/
this.contextualMessages = new Map();
/**
* Gets the collection of algorithms that are enabled for key exchange.
*
* Client and server sides negotiate which of these algorithms will be used.
*
* If this collection includes `null`, and if negotiation selects it, then the session is
* allowed to skip key exchange and connect with no security of any kind: No key exchange,
* no authentication, no encryption, no HMAC, and no compression.
*/
this.keyExchangeAlgorithms = [];
/**
* Gets the collection of algorithms that are enabled for server (host) and client
* public-key authentication.
*
* Client and server sides negotiate which of these algorithms will be used.
*/
this.publicKeyAlgorithms = [];
/*
* Gets the collection of algorithms that are enabled for encryption.
*
* Client and server sides negotiate which of these algorithms will be used.
*/
this.encryptionAlgorithms = [];
/**
* Gets the collection of algorithms that are enabled for message integrity (HMAC).
*
* Client and server sides negotiate which of these algorithms will be used.
*/
this.hmacAlgorithms = [];
/**
* Gets the collection of algorithms that are enabled for message compression.
*
* Client and server sides negotiate which of these algorithms will be used.
*/
this.compressionAlgorithms = [];
this.traceChannelDataValue = false;
this.configurationChangedEmitter = new vscode_jsonrpc_1.Emitter();
/* @internal */
this.onConfigurationChanged = this.configurationChangedEmitter.event;
/**
* Gets or sets the number of times the server will allow a client to attempt to
* authenticate.
*
* The default value is 5.
*
* This setting applies only to server sessions. If the client has failed to authenticate
* after the maximum number of atttempts, the server will close the session.
*
* The SSH protocol allows a client to make multiple attempts to authenticate with
* the server, e.g. to find which public key algorithm a server will support, or to
* retry a mis-typed password. This maximum prevents unlimited retries, which would
* make it easier to "guess" a password.
*
* In certain applications the server may only support a single authentication method
* (which is not a typed password). Then it could be appropriate to set this value to 1.
*/
this.maxClientAuthenticationAttempts = 5;
/**
* Gets or sets whether the client sends a key-exchange "guess" message before receiving
* the server's key-exchange algorithm preferences, slightly reducing the time to connect.
*
* This setting only applies to client sessions. (The server always supports the option when
* used by a client.)
*
* The "guess" mechanism is somewhat ambiguously defined in the SSH protocol spec, and as
* a result is not implemented or incorrectly implemented by some server implementations,
* including older versions of this library. Therefore it is disabled in the default
* configuration, and should only be enabled when connecting to a known-good server.
*/
this.enableKeyExchangeGuess = false;
/**
* Gets or sets the keep-alive timeout in seconds.
*
* When set to a value greater than 0, the session will send keep-alive messages
* at the specified interval to detect connection failures. If no response is received
* within the timeout period, the keep-alive failed event will be raised.
*
* Set to 0 to disable keep-alive messages.
*/
this.keepAliveTimeoutInSecondsValue = 0;
/* @internal */
this.keyRotationThreshold = 512 * 1024 * 1024; // 0.5 GiB;
this.protocolExtensions.push(SshProtocolExtensionNames.serverSignatureAlgorithms);
this.protocolExtensions.push(SshProtocolExtensionNames.openChannelRequest);
this.services.set(keyExchangeService_1.KeyExchangeService, null);
this.services.set(connectionService_1.ConnectionService, null);
this.services.set(authenticationService_1.AuthenticationService, null);
this.authenticationMethods.push("none" /* AuthenticationMethod.none */, "password" /* AuthenticationMethod.password */, "publickey" /* AuthenticationMethod.publicKey */, "keyboard-interactive" /* AuthenticationMethod.keyboardInteractive */);
for (const [messageKey, messageType] of sshMessage_1.SshMessage.index) {
if (typeof messageKey === 'number') {
this.messages.set(messageKey, messageType);
}
else {
const [messageNumber, messageContext] = messageKey;
let contextMessageTypes = this.contextualMessages.get(messageContext);
if (!contextMessageTypes) {
contextMessageTypes = new Map();
this.contextualMessages.set(messageContext, contextMessageTypes);
}
contextMessageTypes.set(messageNumber, messageType);
}
}
if (useSecurity) {
this.keyExchangeAlgorithms.push(sshAlgorithms_1.SshAlgorithms.keyExchange.ecdhNistp384Sha384);
this.keyExchangeAlgorithms.push(sshAlgorithms_1.SshAlgorithms.keyExchange.ecdhNistp256Sha256);
this.keyExchangeAlgorithms.push(sshAlgorithms_1.SshAlgorithms.keyExchange.dhGroup16Sha512);
this.keyExchangeAlgorithms.push(sshAlgorithms_1.SshAlgorithms.keyExchange.dhGroup14Sha256);
this.publicKeyAlgorithms.push(sshAlgorithms_1.SshAlgorithms.publicKey.rsaWithSha512);
this.publicKeyAlgorithms.push(sshAlgorithms_1.SshAlgorithms.publicKey.rsaWithSha256);
this.publicKeyAlgorithms.push(sshAlgorithms_1.SshAlgorithms.publicKey.ecdsaSha2Nistp384);
this.publicKeyAlgorithms.push(sshAlgorithms_1.SshAlgorithms.publicKey.ecdsaSha2Nistp256);
////this.encryptionAlgorithms.push(SshAlgorithms.encryption.aes256Cbc);
this.encryptionAlgorithms.push(sshAlgorithms_1.SshAlgorithms.encryption.aes256Gcm);
this.encryptionAlgorithms.push(sshAlgorithms_1.SshAlgorithms.encryption.aes256Ctr);
this.hmacAlgorithms.push(sshAlgorithms_1.SshAlgorithms.hmac.hmacSha512Etm);
this.hmacAlgorithms.push(sshAlgorithms_1.SshAlgorithms.hmac.hmacSha256Etm);
this.hmacAlgorithms.push(sshAlgorithms_1.SshAlgorithms.hmac.hmacSha512);
this.hmacAlgorithms.push(sshAlgorithms_1.SshAlgorithms.hmac.hmacSha256);
}
else {
this.keyExchangeAlgorithms.push(sshAlgorithms_1.SshAlgorithms.keyExchange.none);
this.publicKeyAlgorithms.push(sshAlgorithms_1.SshAlgorithms.publicKey.none);
this.encryptionAlgorithms.push(sshAlgorithms_1.SshAlgorithms.encryption.none);
this.hmacAlgorithms.push(sshAlgorithms_1.SshAlgorithms.hmac.none);
}
this.compressionAlgorithms.push(sshAlgorithms_1.SshAlgorithms.compression.none);
}
/**
* Adds a service to the configuration.
*/
addService(serviceType, serviceConfig) {
if (this.services.has(serviceType)) {
throw new Error('Duplicate service entry.');
}
this.services.set(serviceType, serviceConfig);
}
getKeyExchangeAlgorithm(name) {
return this.getAlgorithm(name, this.keyExchangeAlgorithms);
}
getPublicKeyAlgorithm(name) {
return this.getAlgorithm(name, this.publicKeyAlgorithms);
}
getEncryptionAlgorithm(name) {
return this.getAlgorithm(name, this.encryptionAlgorithms);
}
getHmacAlgorithm(name) {
return this.getAlgorithm(name, this.hmacAlgorithms);
}
getCompressionAlgorithm(name) {
return this.getAlgorithm(name, this.compressionAlgorithms);
}
getAlgorithm(name, collection) {
const algorithm = collection.find((a) => (a ? a.name === name : false));
if (!algorithm) {
if (name === 'none') {
return null;
}
throw new Error('Algorithm not found: ' + name);
}
return algorithm;
}
get traceChannelData() {
return this.traceChannelDataValue;
}
/**
* Enables tracing of all channel data messages.
*
* Unlike other configuration, this option may be adjusted any time while the session
* is active. Channel data tracing produces a large volume of trace events, so it is
* primarily meant only for debugging.
*/
set traceChannelData(value) {
if (value !== this.traceChannelDataValue) {
this.traceChannelDataValue = value;
this.configurationChangedEmitter.fire();
}
}
get keepAliveTimeoutInSeconds() {
return this.keepAliveTimeoutInSecondsValue;
}
set keepAliveTimeoutInSeconds(value) {
if (this.keepAliveTimeoutInSecondsValue !== value) {
this.keepAliveTimeoutInSecondsValue = value;
this.configurationChangedEmitter.fire();
}
}
}
exports.SshSessionConfiguration = SshSessionConfiguration;
//# sourceMappingURL=sshSessionConfiguration.js.map