Comments (14)
Ok, check out the previous example again, I had also forgotten about the need to manually emit 'connect' on the stream.
from ssh2.
@jpillora and anyone else who may be interested:
Here's an example HTTP/HTTPS Agent implementation that tunnels requests over SSH. It could be improved a bit (e.g. actual/proper ref()
/unref()
handling) and it might be possible to simplify it a bit because for example, I think node may retry requests that never received a socket, so we may not need to manually handle the 'ready'
event handlers.
var HttpAgent = require('http').Agent;
var HttpsAgent = require('https').Agent;
var inherits = require('util').inherits;
var Client = require('ssh2').Client;
[HttpAgent, HttpsAgent].forEach(function(ctor) {
function SSHAgent(connectCfg, agentOptions) {
if (!(this instanceof SSHAgent))
return new SSHAgent(connectCfg, agentOptions);
ctor.call(this, agentOptions);
createSSH(this, connectCfg);
this._defaultSrcIP = (agentOptions && agentOptions.srcIP) || 'localhost';
}
inherits(SSHAgent, ctor);
SSHAgent.prototype.createConnection = createConnection;
exports[ctor === HttpAgent ? 'SSHTTPAgent' : 'SSHTTPSAgent'] = SSHAgent;
});
function createConnection(options, cb) {
if (!this._ready) {
const onReady = () => { this.createConnection(options, cb); };
onReady.httpCb = cb;
this._client.once('ready', onReady);
return;
}
var srcIP = (options && options.localAddress) || this._defaultSrcIP;
var srcPort = (options && options.localPort) || 0;
var dstIP = options.host;
var dstPort = options.port;
this._client.forwardOut(srcIP, srcPort, dstIP, dstPort, (err, stream) => {
if (err)
return cb(err);
cb(null, decorateStream(stream));
});
}
function createSSH(agent, config) {
agent._ready = false;
agent._client = new Client();
agent._client.on('ready', function() {
agent._ready = true;
}).on('error', function(err) {
// Treat all errors has fatal, canceling pending http
// requests
const readies = this.listeners('ready');
for (var i = 0; i < readies.length; ++i) {
const fn = readies[i];
if (typeof fn.httpCb === 'function')
fn.httpCb(err);
}
this.removeAllListeners('ready');
}).on('close', function() {
createSSH(agent, config);
// Re-add callbacks for http requests that were waiting
// for an SSH connection
const readies = this.listeners('ready');
for (var i = 0; i < readies.length; ++i) {
const fn = readies[i];
if (typeof fn.httpCb === 'function')
agent._client.once('ready', fn);
}
}).connect(config);
}
function decorateStream(stream) {
stream.setKeepAlive = noop;
stream.setNoDelay = noop;
stream.setTimeout = noop;
stream.ref = noop;
stream.unref = noop;
stream.destroySoon = stream.destroy;
return stream;
}
function noop() {}
Then use like:
// Or use `SSHTTPSAgent` for HTTPS
const httpAgent = new SSHTTPAgent({
host: '192.168.100.1',
username: 'foo',
agent: process.env.SSH_AUTH_SOCK
});
require('http').get({
host: '192.168.200.1',
agent: httpAgent
}, (res) => {
console.log(res.statusCode);
console.dir(res.headers);
res.resume();
});
from ssh2.
Published ssh-http-agent:
const sshAgent = require("ssh-http-agent");
const http = require("http");
https.get(
{
host: "echo.jpillora.com",
path: "/foo/bar",
agent: sshAgent.http({
host: "1.2.3.4",
port: 22,
username: "root",
password: "supersecret"
})
},
res => {
console.log(res.headers);
}
);
I thought Node's http
would call ref
and unref
though it doesn't seem to...
from ssh2.
forwardOut() doesn't actually listen on the local port (2221). If you want to do that and pipe to stream
and back on incoming connections, that's up to you.
However for connection hopping like this, you can specify a sock
connection setting that can be used instead of a hostname and port. Example:
var fs = require('fs'),
Connection = require('ssh2');
var c1 = new Connection();
c1.connect({
host: '[F/WALL IP]',
port: 222,
username: '[F/WALL UNAME]',
privateKey: require('fs').readFileSync('./keys/sample.key')
});
c1.on('ready', function(){
c1.exec('nc [SERVER IP] 22', function(err, stream){
if(err) throw err;
console.log('ForwardOut Ready!');
var c2 = new Connection();
c2.connect({
sock: stream,
username: '[SERVER UNAME]',
password: '[SERVER PASSWORD]'
});
c2.on('ready', function(){
console.log('Connected!');
});
c2.on('error', function(err) {
console.log('Connection2 :: error :: ' + err);
});
stream.emit('connect'); // needed to simulate a TCP socket
})
});
c1.on('error', function(err) {
console.log('Connection1 :: error :: ' + err);
});
from ssh2.
That looks perfect.. However, when I run that code and expect to see:
ForwardOut Ready!
Connected!
But I just get:
ForwardOut Ready!
and it just sits there, so I'm assuming something still isn't right if "ready" isn't being triggered?
from ssh2.
@matouka I've updated the previous example. I know using netcat works for tunneling.
from ssh2.
Thanks for such quick help! It's very much appreciated.
I tried that and it seems to just be stopping at the same spot.
To see if I could see what was being sent I opened up a listening port on the server nc -l 2222
and then tried to see if anything came through, and surprisingly it didn't.
The interesting thing though, was that when I manually did it from the firewall nc [serverIP] 2222
`it worked fine, everything I typed was transferred.
NodeJS seems to open a connection to the server though, because when the command is run in Node I then can't connect to that port on the server from another computer. It seems that it the c2.connect
function though is not sending the data though.
#Edit
Actually, I just stuck the server in verbose mode and got a little more.
Listening on [0.0.0.0] (family 0, port 2222)
Connection from [192.168.0.1] port 2222 [tcp/*] accepted (family 2, sport 46775)
Then is just stops.
from ssh2.
Mate, you are a legend!!!
Thanks so much for all your help!
from ssh2.
@mscdex Hey Brian, is this possible without nc
installed on the ssh host?
from ssh2.
@jpillora Yes, you can use forwardOut()
to get a similarly suitable stream for setting sock
.
from ssh2.
Thanks @mscdex ! Got it working:
const http = require("http");
const tls = require("tls");
const Client = require("ssh2").Client;
//borrowed from
//https://github.com/mafintosh/http-ssh-agent/blob/master/tcpish.js
// (note: i need https, which http-ssh-agent does not support)
const tcpish = require("./tcpish");
const config = {
//tunnel via this host
ssh: {
host: "1.1.1.1",
port: 22,
username: "root",
password: "supersecret"
},
//and then make an http request against this host
http: {
host: "2.2.2.2",
port: 443
}
};
function httpReady(httpSock) {
const req = http.request(
{
method: "GET",
host: config.http.host,
path: "/",
createConnection(opts) {
var t = tcpish();
t.connect(httpSock);
return t;
}
},
res => {
console.log(`response ${res.statusCode}`);
res.on("data", d => console.log(d.toString()));
}
);
req.on("error", e => {
console.error("request error", e);
});
req.end();
}
function handleForward(err, tcpSock) {
if (err) {
console.log("forward err", err);
return;
}
console.log("forward open");
tcpSock.on("close", function() {
console.log("forward closed");
conn.end();
});
tcpSock.on("data", function(data) {
console.log("tcp data:", data.length);
});
//in order to speak https, we
//manually upgrade tcp to tls
let tlsSock = tls.connect({
socket: tcpSock,
rejectUnauthorized: false, //localhost testing
servername: config.http.host //support SNI
});
tlsSock.on("secureConnect", function() {
console.log("tls connect");
tlsSock.setEncoding("utf8");
httpReady(tlsSock);
});
tlsSock.on("data", function(data) {
console.log("tls data:", data.length);
});
tlsSock.on("tlsClientError", function(err) {
console.log("tls client error", err);
});
tlsSock.on("error", function(err) {
console.log("tls error", err);
});
tlsSock.on("end", function() {
console.log("tls end");
});
}
var conn = new Client();
conn.on("ready", function() {
console.log("ssh ready");
conn.forwardOut(
"127.0.0.1",
0,
config.http.host,
config.http.port,
handleForward
);
});
console.log("ssh connect");
conn.connect(config.ssh);
Warning for anyone using this: This has many debug console logs and does not do proper error checking.
Suggestion: Maybe forwardOut
could optionally omit local interface/port (default to 127.0.0.1
/0
)?
from ssh2.
Ah cool, thanks
from ssh2.
server.start(options, async () => {
conn.on('ready', async function (err) {
if (err) throw err;
console.log('Client :: ready');
conn.forwardOut('localhost', 22, 'remote address', 8003, async function (err, stream) {
if (err) throw err;
//ORACLE CONNECTION
console.log(>> HERMES API is up and running in port ${options.port}
);
});
}).connect({
host: 'host-name',
port: 22,
username: 'user',
privateKey: require('fs').readFileSync('file.pem'),
readyTimeout: 40000
});
});
Hello I'm trying to use tunneling ssh using the code above with nodejs for connecting to an Oracle db but it's not working, any idea of what is going on?
from ssh2.
Does forwardOut method can only local forward 127.0.0.1:0?
from ssh2.
Related Issues (20)
- How to pass parameters to ssh2? HOT 1
- ssh key
- Terrapin advice HOT 2
- Vulnerability CVE-2023-48795 Reported: Prefix Truncation Attack, Fix Requested HOT 2
- Pure JS fallback when WASM is not available
- Can't connect "ignoring handled error" HOT 9
- any method to execute command after ssh connected. HOT 10
- Get remote port forwarding -R local port HOT 2
- Question: HTTPSAgent reuse HOT 3
- Execute ssh command inside a "for of" HOT 7
- Test code should be excluded from the published package HOT 1
- Question: Missing documentation in the code
- forwardIn binding to 127.0.0.1 instead HOT 2
- App stops on error timeout even if handled HOT 3
- Not building in Cloudflare Workers HOT 1
- Issue Connecting to MongoDB via SSH Tunnel in Node.js HOT 1
- Issue/Suggestion :- Adding of maxEventListner on all the event HOT 4
- allow server to connect to forwarded agent from client HOT 1
- ecdsa-sha2-nistp256 private key support
- "Key auth required before password auth" error when connecting HOT 2
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from ssh2.