Skip to content
This repository was archived by the owner on Aug 26, 2025. It is now read-only.

Commit f899f34

Browse files
committed
Include compiled files in git
1 parent 4700926 commit f899f34

File tree

3 files changed

+240
-1
lines changed

3 files changed

+240
-1
lines changed

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
.nyc_output
22
node_modules/
3-
dist/
3+
# dist/
44
.DS_Store

dist/Connection.d.ts

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
/// <reference types="node" />
2+
interface Options {
3+
username?: string;
4+
password?: string;
5+
privateKey?: string | Buffer;
6+
agentForward?: boolean;
7+
bastionHost?: string;
8+
passphrase?: string;
9+
endPort?: number;
10+
endHost: string;
11+
agentSocket?: string;
12+
skipAutoPrivateKey?: boolean;
13+
noReadline?: boolean;
14+
readyTimeout?: number;
15+
}
16+
interface ForwardingOptions {
17+
fromPort: number;
18+
toPort: number;
19+
toHost?: string;
20+
}
21+
declare class SSHConnection {
22+
private options;
23+
private debug;
24+
private server;
25+
private connections;
26+
private isWindows;
27+
constructor(options: Options);
28+
shutdown(): Promise<void>;
29+
tty(): Promise<void>;
30+
executeCommand(command: any): Promise<void>;
31+
private shell;
32+
private establish;
33+
private connectViaBastion;
34+
private connect;
35+
private getPassphrase;
36+
forward(options: ForwardingOptions): Promise<{}>;
37+
}
38+
export { SSHConnection, Options };

dist/Connection.js

Lines changed: 201 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,201 @@
1+
"use strict";
2+
/*
3+
* Copyright 2018 Stocard GmbH.
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
Object.defineProperty(exports, "__esModule", { value: true });
18+
const ssh2_1 = require("ssh2");
19+
const net = require("net");
20+
const fs = require("fs");
21+
const os = require("os");
22+
const path = require("path");
23+
const readline = require("readline");
24+
const debug = require("debug");
25+
class SSHConnection {
26+
constructor(options) {
27+
this.options = options;
28+
this.connections = [];
29+
this.isWindows = process.platform === 'win32';
30+
this.debug = debug('ssh');
31+
if (!options.username) {
32+
this.options.username = process.env['SSH_USERNAME'] || process.env['USER'];
33+
}
34+
if (!options.endPort) {
35+
this.options.endPort = 22;
36+
}
37+
if (!options.privateKey && !options.agentForward && !options.skipAutoPrivateKey) {
38+
const defaultFilePath = path.join(os.homedir(), '.ssh', 'id_rsa');
39+
if (fs.existsSync(defaultFilePath)) {
40+
this.options.privateKey = fs.readFileSync(defaultFilePath);
41+
}
42+
}
43+
}
44+
async shutdown() {
45+
this.debug('Shutdown connections');
46+
for (const connection of this.connections) {
47+
connection.removeAllListeners();
48+
connection.end();
49+
}
50+
return new Promise((resolve) => {
51+
if (this.server) {
52+
this.server.close(resolve);
53+
}
54+
return resolve();
55+
});
56+
}
57+
async tty() {
58+
const connection = await this.establish();
59+
this.debug('Opening tty');
60+
await this.shell(connection);
61+
}
62+
async executeCommand(command) {
63+
const connection = await this.establish();
64+
this.debug('Executing command "%s"', command);
65+
await this.shell(connection, command);
66+
}
67+
async shell(connection, command) {
68+
return new Promise((resolve, reject) => {
69+
connection.shell((err, stream) => {
70+
if (err) {
71+
return reject(err);
72+
}
73+
stream.on('close', async () => {
74+
stream.end();
75+
process.stdin.unpipe(stream);
76+
process.stdin.destroy();
77+
connection.end();
78+
await this.shutdown();
79+
return resolve();
80+
}).stderr.on('data', (data) => {
81+
return reject(data);
82+
});
83+
stream.pipe(process.stdout);
84+
if (command) {
85+
stream.end(`${command}\nexit\n`);
86+
}
87+
else {
88+
process.stdin.pipe(stream);
89+
}
90+
});
91+
});
92+
}
93+
async establish() {
94+
let connection;
95+
if (this.options.bastionHost) {
96+
connection = await this.connectViaBastion(this.options.bastionHost);
97+
}
98+
else {
99+
connection = await this.connect(this.options.endHost);
100+
}
101+
return connection;
102+
}
103+
async connectViaBastion(bastionHost) {
104+
this.debug('Connecting to bastion host "%s"', bastionHost);
105+
const connectionToBastion = await this.connect(bastionHost);
106+
return new Promise((resolve, reject) => {
107+
connectionToBastion.forwardOut('127.0.0.1', 22, this.options.endHost, this.options.endPort || 22, async (err, stream) => {
108+
if (err) {
109+
return reject(err);
110+
}
111+
const connection = await this.connect(this.options.endHost, stream);
112+
return resolve(connection);
113+
});
114+
});
115+
}
116+
async connect(host, stream) {
117+
this.debug('Connecting to "%s"', host);
118+
const connection = new ssh2_1.Client();
119+
return new Promise(async (resolve, reject) => {
120+
const options = {
121+
host,
122+
port: this.options.endPort,
123+
username: this.options.username,
124+
password: this.options.password,
125+
privateKey: this.options.privateKey
126+
};
127+
if (this.options.readyTimeout) {
128+
options['readyTimeout'] = this.options.readyTimeout;
129+
}
130+
if (this.options.agentForward) {
131+
options['agentForward'] = true;
132+
// see https://github.com/mscdex/ssh2#client for agents on Windows
133+
// guaranteed to give the ssh agent sock if the agent is running (posix)
134+
let agentDefault = process.env['SSH_AUTH_SOCK'];
135+
if (this.isWindows) {
136+
// null or undefined
137+
if (agentDefault == null) {
138+
agentDefault = 'pageant';
139+
}
140+
}
141+
const agentSock = this.options.agentSocket ? this.options.agentSocket : agentDefault;
142+
if (agentSock == null) {
143+
throw new Error('SSH Agent Socket is not provided, or is not set in the SSH_AUTH_SOCK env variable');
144+
}
145+
options['agent'] = agentSock;
146+
}
147+
if (stream) {
148+
options['sock'] = stream;
149+
}
150+
// PPK private keys can be encrypted, but won't contain the word 'encrypted'
151+
// in fact they always contain a `encryption` header, so we can't do a simple check
152+
options['passphrase'] = this.options.passphrase;
153+
const looksEncrypted = this.options.privateKey ? this.options.privateKey.toString().toLowerCase().includes('encrypted') : false;
154+
if (looksEncrypted && !options['passphrase'] && !this.options.noReadline) {
155+
options['passphrase'] = await this.getPassphrase();
156+
}
157+
connection.on('ready', () => {
158+
this.connections.push(connection);
159+
return resolve(connection);
160+
});
161+
connection.on('error', (error) => {
162+
reject(error);
163+
});
164+
try {
165+
connection.connect(options);
166+
}
167+
catch (error) {
168+
reject(error);
169+
}
170+
});
171+
}
172+
async getPassphrase() {
173+
return new Promise((resolve) => {
174+
const rl = readline.createInterface({
175+
input: process.stdin,
176+
output: process.stdout
177+
});
178+
rl.question('Please type in the passphrase for your private key: ', (answer) => {
179+
return resolve(answer);
180+
});
181+
});
182+
}
183+
async forward(options) {
184+
const connection = await this.establish();
185+
return new Promise((resolve, reject) => {
186+
this.server = net.createServer((socket) => {
187+
this.debug('Forwarding connection from "localhost:%d" to "%s:%d"', options.fromPort, options.toHost, options.toPort);
188+
connection.forwardOut('localhost', options.fromPort, options.toHost || 'localhost', options.toPort, (error, stream) => {
189+
if (error) {
190+
return reject(error);
191+
}
192+
socket.pipe(stream);
193+
stream.pipe(socket);
194+
});
195+
}).listen(options.fromPort, 'localhost', () => {
196+
return resolve();
197+
});
198+
});
199+
}
200+
}
201+
exports.SSHConnection = SSHConnection;

0 commit comments

Comments
 (0)