SSH local forward

This commit is contained in:
Xinyu Sui
2022-06-22 13:41:25 -07:00
parent b8364875ff
commit d252813b70
6 changed files with 365 additions and 32 deletions
+224 -8
View File
@@ -3358,7 +3358,7 @@
},
{
"type": "string",
"pattern": "^\\d+$"
"pattern": "^\\d+$|^\\${.+}$"
}
],
"description": "%c_cpp.debuggers.host.port.description%",
@@ -3391,7 +3391,7 @@
},
{
"type": "string",
"pattern": "^\\d+$"
"pattern": "^\\d+$|^\\${.+}$"
}
],
"description": "%c_cpp.debuggers.host.port.description%",
@@ -3399,6 +3399,60 @@
}
}
}
},
"localForwards": {
"type": "array",
"description": "%c_cpp.debuggers.host.localForward.description%",
"items": {
"type": "object",
"default": {},
"properties": {
"bindAddress": {
"type": "string",
"description": "%c_cpp.debuggers.host.localForward.bindAddress.description%",
"default": ""
},
"port": {
"anyOf": [
{
"type": "number"
},
{
"type": "string",
"pattern": "^\\d+$|^\\${.+}$"
}
],
"description": "%c_cpp.debuggers.host.localForward.port.description%"
},
"host": {
"type": "string",
"description": "%c_cpp.debuggers.host.localForward.host.description%",
"default": ""
},
"hostPort": {
"anyOf": [
{
"type": "number"
},
{
"type": "string",
"pattern": "^\\d+$|^\\${.+}$"
}
],
"description": "%c_cpp.debuggers.host.localForward.hostPort.description%"
},
"localSocket": {
"type": "string",
"description": "%c_cpp.debuggers.host.localForward.localSocket.description%",
"default": ""
},
"remoteSocket": {
"type": "string",
"description": "%c_cpp.debuggers.host.localForward.remoteSocket.description%",
"default": ""
}
}
}
}
}
},
@@ -3461,7 +3515,7 @@
},
{
"type": "string",
"pattern": "^\\d+$"
"pattern": "^\\d+$|^\\${.+}$"
}
],
"description": "%c_cpp.debuggers.host.port.description%",
@@ -3494,7 +3548,7 @@
},
{
"type": "string",
"pattern": "^\\d+$"
"pattern": "^\\d+$|^\\${.+}$"
}
],
"description": "%c_cpp.debuggers.host.port.description%",
@@ -3502,6 +3556,60 @@
}
}
}
},
"localForwards": {
"type": "array",
"description": "%c_cpp.debuggers.host.localForward.description%",
"items": {
"type": "object",
"default": {},
"properties": {
"bindAddress": {
"type": "string",
"description": "%c_cpp.debuggers.host.localForward.bindAddress.description%",
"default": ""
},
"port": {
"anyOf": [
{
"type": "number"
},
{
"type": "string",
"pattern": "^\\d+$|^\\${.+}$"
}
],
"description": "%c_cpp.debuggers.host.localForward.port.description%"
},
"host": {
"type": "string",
"description": "%c_cpp.debuggers.host.localForward.host.description%",
"default": ""
},
"hostPort": {
"anyOf": [
{
"type": "number"
},
{
"type": "string",
"pattern": "^\\d+$|^\\${.+}$"
}
],
"description": "%c_cpp.debuggers.host.localForward.hostPort.description%"
},
"localSocket": {
"type": "string",
"description": "%c_cpp.debuggers.host.localForward.localSocket.description%",
"default": ""
},
"remoteSocket": {
"type": "string",
"description": "%c_cpp.debuggers.host.localForward.remoteSocket.description%",
"default": ""
}
}
}
}
}
},
@@ -3925,7 +4033,7 @@
},
{
"type": "string",
"pattern": "^\\d+$"
"pattern": "^\\d+$|^\\${.+}$"
}
],
"description": "%c_cpp.debuggers.host.port.description%",
@@ -3958,7 +4066,7 @@
},
{
"type": "string",
"pattern": "^\\d+$"
"pattern": "^\\d+$|^\\${.+}$"
}
],
"description": "%c_cpp.debuggers.host.port.description%",
@@ -3966,6 +4074,60 @@
}
}
}
},
"localForwards": {
"type": "array",
"description": "%c_cpp.debuggers.host.localForward.description%",
"items": {
"type": "object",
"default": {},
"properties": {
"bindAddress": {
"type": "string",
"description": "%c_cpp.debuggers.host.localForward.bindAddress.description%",
"default": ""
},
"port": {
"anyOf": [
{
"type": "number"
},
{
"type": "string",
"pattern": "^\\d+$|^\\${.+}$"
}
],
"description": "%c_cpp.debuggers.host.localForward.port.description%"
},
"host": {
"type": "string",
"description": "%c_cpp.debuggers.host.localForward.host.description%",
"default": ""
},
"hostPort": {
"anyOf": [
{
"type": "number"
},
{
"type": "string",
"pattern": "^\\d+$|^\\${.+}$"
}
],
"description": "%c_cpp.debuggers.host.localForward.hostPort.description%"
},
"localSocket": {
"type": "string",
"description": "%c_cpp.debuggers.host.localForward.localSocket.description%",
"default": ""
},
"remoteSocket": {
"type": "string",
"description": "%c_cpp.debuggers.host.localForward.remoteSocket.description%",
"default": ""
}
}
}
}
}
},
@@ -4028,7 +4190,7 @@
},
{
"type": "string",
"pattern": "^\\d+$"
"pattern": "^\\d+$|^\\${.+}$"
}
],
"description": "%c_cpp.debuggers.host.port.description%",
@@ -4061,7 +4223,7 @@
},
{
"type": "string",
"pattern": "^\\d+$"
"pattern": "^\\d+$|^\\${.+}$"
}
],
"description": "%c_cpp.debuggers.host.port.description%",
@@ -4069,6 +4231,60 @@
}
}
}
},
"localForwards": {
"type": "array",
"description": "%c_cpp.debuggers.host.localForward.description%",
"items": {
"type": "object",
"default": {},
"properties": {
"bindAddress": {
"type": "string",
"description": "%c_cpp.debuggers.host.localForward.bindAddress.description%",
"default": ""
},
"port": {
"anyOf": [
{
"type": "number"
},
{
"type": "string",
"pattern": "^\\d+$|^\\${.+}$"
}
],
"description": "%c_cpp.debuggers.host.localForward.port.description%"
},
"host": {
"type": "string",
"description": "%c_cpp.debuggers.host.localForward.host.description%",
"default": ""
},
"hostPort": {
"anyOf": [
{
"type": "number"
},
{
"type": "string",
"pattern": "^\\d+$|^\\${.+}$"
}
],
"description": "%c_cpp.debuggers.host.localForward.hostPort.description%"
},
"localSocket": {
"type": "string",
"description": "%c_cpp.debuggers.host.localForward.localSocket.description%",
"default": ""
},
"remoteSocket": {
"type": "string",
"description": "%c_cpp.debuggers.host.localForward.remoteSocket.description%",
"default": ""
}
}
}
}
}
},
+7
View File
@@ -305,6 +305,13 @@
"c_cpp.debuggers.host.hostName.description": "Host name.",
"c_cpp.debuggers.host.port.description": "SSH port on the host. Default is 22.",
"c_cpp.debuggers.host.jumpHost.description": "Connect to the target host by first making a connection to the jump hosts.",
"c_cpp.debuggers.host.localForward.description": "Forward connections to the given TCP port or Unix socket on the local (client) host to the given host and port, or Unix socket, on the remote side",
"c_cpp.debuggers.host.localForward.bindAddress.description": "Local address",
"c_cpp.debuggers.host.localForward.port.description": "Local port",
"c_cpp.debuggers.host.localForward.host.description": "Host name",
"c_cpp.debuggers.host.localForward.hostPort.description": "Host port",
"c_cpp.debuggers.host.localForward.localSocket.description": "Local socket",
"c_cpp.debuggers.host.localForward.remoteSocket.description": "Remote socket",
"c_cpp.debuggers.deploySteps.description": "Steps needed to deploy the application. Order matters.",
"c_cpp.debuggers.deploySteps.scp.description": "Copy files using SCP.",
"c_cpp.debuggers.deploySteps.scp.files.description": "Files to be copied via SCP. Supports path pattern.",
@@ -981,7 +981,9 @@ export class DebugConfigurationProvider implements vscode.DebugConfigurationProv
}
const host: util.ISshHostInfo = { hostName: step.host.hostName, user: step.host.user, port: step.host.port };
const jumpHosts: util.ISshHostInfo[] = step.host.jumpHosts;
const sshResult: util.ProcessReturnType = await ssh(host, step.command, config.sshPath, jumpHosts, step.continueOn, cancellationToken);
const localForwards: util.ISshLocalForwardInfo[] = step.host.localForwards;
const continueOn: string = step.continueOn;
const sshResult: util.ProcessReturnType = await ssh(host, step.command, config.sshPath, jumpHosts, localForwards, continueOn, cancellationToken);
if (!sshResult.succeeded || cancellationToken?.isCancellationRequested) {
return false;
}
+47 -2
View File
@@ -4,10 +4,14 @@
* ------------------------------------------------------------------------------------------ */
import * as vscode from 'vscode';
import { ISshHostInfo, ProcessReturnType } from '../common';
import * as nls from 'vscode-nls';
import { ISshHostInfo, ISshLocalForwardInfo, ProcessReturnType } from '../common';
import { defaultSystemInteractor } from './commandInteractors';
import { runSshTerminalCommandWithLogin } from './sshCommandRunner';
nls.config({ messageFormat: nls.MessageFormat.bundle, bundleFormat: nls.BundleFormat.standalone })();
const localize: nls.LocalizeFunc = nls.loadMessageBundle();
export async function scp(files: vscode.Uri[], host: ISshHostInfo, targetDir: string, scpPath?: string, jumpHosts?: ISshHostInfo[], cancellationToken?: vscode.CancellationToken): Promise<ProcessReturnType> {
const args: string[] = [];
if (jumpHosts && jumpHosts.length > 0) {
@@ -22,7 +26,7 @@ export async function scp(files: vscode.Uri[], host: ISshHostInfo, targetDir: st
return runSshTerminalCommandWithLogin(host, { systemInteractor: defaultSystemInteractor, nickname: 'scp', command: `"${scpPath || 'scp'}" ${args.join(' ')}`, token: cancellationToken });
}
export function ssh(host: ISshHostInfo, command: string, sshPath?: string, jumpHosts?: ISshHostInfo[], continueOn?: string, cancellationToken?: vscode.CancellationToken): Promise<ProcessReturnType> {
export function ssh(host: ISshHostInfo, command: string, sshPath?: string, jumpHosts?: ISshHostInfo[], localForwards?: ISshLocalForwardInfo[], continueOn?: string, cancellationToken?: vscode.CancellationToken): Promise<ProcessReturnType> {
const args: string[] = [];
if (jumpHosts && jumpHosts.length > 0) {
args.push('-J', jumpHosts.map(getFullHostAddress).join(','));
@@ -31,6 +35,9 @@ export function ssh(host: ISshHostInfo, command: string, sshPath?: string, jumpH
// lower case p
args.push('-p', `${host.port}`);
}
if (localForwards) {
localForwards.forEach(info => args.push(...localForwardToArgs(info)));
}
args.push(getFullHostAddressNoPort(host), `"${command}"`);
return runSshTerminalCommandWithLogin(host, {
@@ -42,6 +49,44 @@ export function ssh(host: ISshHostInfo, command: string, sshPath?: string, jumpH
});
}
/**
* Takes one local forward info and convert it to '-L' args in ssh.
*/
function localForwardToArgs(localForward: ISshLocalForwardInfo): string[] {
// Do not combine error checking and arg conversion for clarity.
if (localForward.localSocket && (localForward.bindAddress || localForward.port)) {
throw Error(localize('local.forward.local.conflict', '"localSocket" cannot be specifed at the same time with "bindAddress" or "port" in localForwards'));
}
if (!localForward.localSocket && !localForward.port) {
throw Error(localize('local.forward.local.missing', '"port" or "localSocket" required in localForwards'));
}
if (localForward.remoteSocket && (localForward.host || localForward.hostPort)) {
throw Error(localize('local.forward.remote.conflict', '"remoteSocket" cannot be specifed at the same time with "host" or "hostPort" in localForwards'));
}
if (!localForward.remoteSocket && (!localForward.host || !localForward.hostPort)) {
throw Error(localize('local.forward.remote.missing', '"host" and "hostPort", or "remoteSocket" required in localForwards'));
}
let arg: string = '';
if (localForward.localSocket) {
arg += `${localForward.localSocket}:`;
}
if (localForward.bindAddress) {
arg += `${localForward.bindAddress}:`;
}
if (localForward.port) {
arg += `${localForward.port}:`;
}
if (localForward.remoteSocket) {
arg += `${localForward.remoteSocket}`;
}
if (localForward.host && localForward.hostPort) {
arg += `${localForward.host}:${localForward.hostPort}`;
}
return ['-L', arg];
}
/** user@host */
function getFullHostAddressNoPort(host: ISshHostInfo): string {
return host.user ? `${host.user}@${host.hostName}` : `${host.hostName}`;
+9
View File
@@ -1550,3 +1550,12 @@ export interface ISshHostInfo {
user?: string;
port?: number | string;
}
export interface ISshLocalForwardInfo {
bindAddress?: string;
port?: number | string;
host?: string;
hostPort?: number | string;
localSocket?: string;
remoteSocket?: string;
}
+75 -21
View File
@@ -256,15 +256,15 @@
"default": ""
},
"port": {
"anyOf":[
{
"type": "number"
},
{
"type": "string",
"pattern": "^\\d+$"
}
],
"anyOf": [
{
"type": "number"
},
{
"type": "string",
"pattern": "^\\d+$|^\\${.+}$"
}
],
"description": "%c_cpp.debuggers.host.port.description%",
"default": 22
},
@@ -289,20 +289,74 @@
"default": ""
},
"port": {
"anyOf":[
{
"type": "number"
},
{
"type": "string",
"pattern": "^\\d+$"
}
"anyOf": [
{
"type": "number"
},
{
"type": "string",
"pattern": "^\\d+$|^\\${.+}$"
}
],
"description": "%c_cpp.debuggers.host.port.description%",
"default": 22
}
}
}
},
"localForwards": {
"type": "array",
"description": "%c_cpp.debuggers.host.localForward.description%",
"items": {
"type": "object",
"default": {},
"properties": {
"bindAddress": {
"type": "string",
"description": "%c_cpp.debuggers.host.localForward.bindAddress.description%",
"default": ""
},
"port": {
"anyOf": [
{
"type": "number"
},
{
"type": "string",
"pattern": "^\\d+$|^\\${.+}$"
}
],
"description": "%c_cpp.debuggers.host.localForward.port.description%"
},
"host": {
"type": "string",
"description": "%c_cpp.debuggers.host.localForward.host.description%",
"default": ""
},
"hostPort": {
"anyOf": [
{
"type": "number"
},
{
"type": "string",
"pattern": "^\\d+$|^\\${.+}$"
}
],
"description": "%c_cpp.debuggers.host.localForward.hostPort.description%"
},
"localSocket": {
"type": "string",
"description": "%c_cpp.debuggers.host.localForward.localSocket.description%",
"default": ""
},
"remoteSocket": {
"type": "string",
"description": "%c_cpp.debuggers.host.localForward.remoteSocket.description%",
"default": ""
}
}
}
}
}
},
@@ -654,10 +708,10 @@
"default": {}
},
"variables": {
"$ref": "#/definitions/Variables"
"$ref": "#/definitions/Variables"
},
"deploySteps": {
"$ref": "#/definitions/DeploySteps"
"$ref": "#/definitions/DeploySteps"
}
}
},
@@ -774,10 +828,10 @@
"description": "%c_cpp.debuggers.symbolLoadInfo.description%"
},
"variables": {
"$ref": "#/definitions/Variables"
"$ref": "#/definitions/Variables"
},
"deploySteps": {
"$ref": "#/definitions/DeploySteps"
"$ref": "#/definitions/DeploySteps"
}
}
},