如何在 Meteor 中使用带有 socket.io 的 ssh2 确保单个私有 ssh 连接
How to ensure a single, private ssh connection using ssh2 with socket.io in Meteor
我正在使用 ssh2 and socket.io to enable a real-time ssh connection to a remote server for users of my Meteor 1.8.1 应用程序。该应用程序在 Nginx 和 Phusion Passenger 下的 Ubuntu 上运行。这是应用程序需要执行的操作:
- 每个授权用户已经在远程服务器上拥有一个帐户。
- 用户将通过输入其凭据并单击应用程序中的 "connect" 按钮来启动会话。
- 用户可以在远程服务器上的主目录中浏览目录列表。
- 任何用户都不应访问其他用户的 ssh 会话。
- 当用户单击 "disconnect" 按钮时,他们的 ssh 会话应该从服务器中删除。
我的 ssh 连接正常,但我不知道如何在用户会话结束时破坏 ssh 连接。每次他们按下“断开连接”然后 "connect",另一个 ssh 会话就会启动,旧的 ssh 会话仍在运行,因此发送的每个 ssh 命令都会执行多次,并且会向浏览器发送多个响应。
我还担心连接不安全;在开发中,我正在使用 require('http').createServer(); 创建服务器。在生产中,在我配置了 SSL 的 Ubuntu 服务器上,使用 require('https').createServer(); 是否足够?或者是否需要其他配置,例如Nginx 的? Socket.io 当 websocket 不可用时回退到旧技术;这是如何保障的?
主要问题:为什么每次用户断开连接然后连接时我都会看到重复的 SSH 会话?
第二个问题:我在哪里可以找到关于如何保护 socket.io 的最新说明?或者我应该放弃 socket.io 并使用 WebSocket?
我已经阅读了很多文章和堆栈溢出帖子,但我发现这非常令人困惑,而且大多数 material 都已过时。例如socketio-auth is not maintained. I can find almost nothing in the Socket.io documentation on authentication or authorization - there is a handshake条目,但我不清楚这是我需要的功能还是如何使用它。
这是我的代码。
服务器
io.on('connection', (socket) => {
console.log('socket id', socket.id); // this shows a new id after disconnect / reconnect
const conn = new SSHClient();
socket.on('disconnect', () => {
console.log('disconnect on server');
conn.end();
});
conn.on('ready', () => {
socket.emit('message', '*** SSH CONNECTION ESTABLISHED ***');
socket.emit('ready', 'ready');
conn.shell((err, stream) => {
stream.write('stty -echo \n'); // don't echo our own command back, or the user's password...
if (err) {
return socket.emit('message', `*** SSH SHELL ERROR: ' ${err.message} ***`);
}
socket.on('path', (path) => {
// path is a request for a directory listing
if (typeof path === 'string') {
const bashCommand = `ls -l ${path} --time-style=full-iso`;
console.log('*** WRITE'); // if you disconnect and reconnect this runs twice. Disconnect and reconnect again, it runs 3 times.
console.log('socket id again', socket.id); // this shows the same new socket id each time
stream.write(`${bashCommand} \n`);
}
});
stream.on('data', (d) => {
socket.emit('data', response); // tell the browser!
}).on('close', () => {
conn.end();
});
});
}).on('close', () => {
socket.emit('message', '*** SSH CONNECTION CLOSED ***');
}).on('error', (err) => {
socket.emit('message', `*** SSH CONNECTION ERROR: ${err.message} ***`);
}).connect({
'host': hosturl,
'username': ausername,
'agent': anagent, // just for dev I'm using public / private key from my local machine but this will be replaced with the user's entered credentials
});
}).on('disconnect', () => {
console.log('user disconnected');
});
server.listen(8080);
客户:
const io = require('socket.io-client');
const EventEmitter = require('events');
class MyEmitter extends EventEmitter {};
const myEmitter = new MyEmitter();
const PORT = 8080;
let socket;
myEmitter.on('connectClicked', () => {
if (socket) {
this.connected.set(socket.connected);
}
if (this.connected.get() === false) {
socket = io(`http://localhost:${PORT}`);
socket.on('connect', () => {
this.connected.set(true);
socket.on('ready', () => {
console.log('ready');
});
// Backend -> Browser
socket.on('message', (data) => {
console.log('socket on message', data);
});
// Backend -> Browser
socket.on('data', (data) => {
console.log('got data', data);
this.parseResponse(data); // client function to handle data, not shown here
});
// Browser -> Backend
myEmitter.on('selectDirectory', () => {
console.log('*** SELECT DIRECTORY');
socket.emit('path', pathArray.join('/')); // path array is set in client code, it is a simple array of directory names
});
socket.on('disconnect', () => {
console.log('\r\n*** Disconnected from backend***\r\n');
this.connected.set(false);
});
});
}
myEmitter.on('disconnectClicked', () => {
socket.disconnect();
});
});
保持 ssh 连接独立的答案是维护当前 ssh 连接的列表并修改代码,以便接收到的 ssh 数据仅发送到与传入消息对应的浏览器。
我也放弃了 socket.io,因为我对安全性没有信心。我现在通过 the Meteor Direct Stream Access package 使用 Meteor 的内置 DDP 消息系统。我认为这可以避免打开任何新的网络服务器访问点。
我正在使用 ssh2 and socket.io to enable a real-time ssh connection to a remote server for users of my Meteor 1.8.1 应用程序。该应用程序在 Nginx 和 Phusion Passenger 下的 Ubuntu 上运行。这是应用程序需要执行的操作:
- 每个授权用户已经在远程服务器上拥有一个帐户。
- 用户将通过输入其凭据并单击应用程序中的 "connect" 按钮来启动会话。
- 用户可以在远程服务器上的主目录中浏览目录列表。
- 任何用户都不应访问其他用户的 ssh 会话。
- 当用户单击 "disconnect" 按钮时,他们的 ssh 会话应该从服务器中删除。
我的 ssh 连接正常,但我不知道如何在用户会话结束时破坏 ssh 连接。每次他们按下“断开连接”然后 "connect",另一个 ssh 会话就会启动,旧的 ssh 会话仍在运行,因此发送的每个 ssh 命令都会执行多次,并且会向浏览器发送多个响应。
我还担心连接不安全;在开发中,我正在使用 require('http').createServer(); 创建服务器。在生产中,在我配置了 SSL 的 Ubuntu 服务器上,使用 require('https').createServer(); 是否足够?或者是否需要其他配置,例如Nginx 的? Socket.io 当 websocket 不可用时回退到旧技术;这是如何保障的?
主要问题:为什么每次用户断开连接然后连接时我都会看到重复的 SSH 会话?
第二个问题:我在哪里可以找到关于如何保护 socket.io 的最新说明?或者我应该放弃 socket.io 并使用 WebSocket?
我已经阅读了很多文章和堆栈溢出帖子,但我发现这非常令人困惑,而且大多数 material 都已过时。例如socketio-auth is not maintained. I can find almost nothing in the Socket.io documentation on authentication or authorization - there is a handshake条目,但我不清楚这是我需要的功能还是如何使用它。
这是我的代码。
服务器
io.on('connection', (socket) => {
console.log('socket id', socket.id); // this shows a new id after disconnect / reconnect
const conn = new SSHClient();
socket.on('disconnect', () => {
console.log('disconnect on server');
conn.end();
});
conn.on('ready', () => {
socket.emit('message', '*** SSH CONNECTION ESTABLISHED ***');
socket.emit('ready', 'ready');
conn.shell((err, stream) => {
stream.write('stty -echo \n'); // don't echo our own command back, or the user's password...
if (err) {
return socket.emit('message', `*** SSH SHELL ERROR: ' ${err.message} ***`);
}
socket.on('path', (path) => {
// path is a request for a directory listing
if (typeof path === 'string') {
const bashCommand = `ls -l ${path} --time-style=full-iso`;
console.log('*** WRITE'); // if you disconnect and reconnect this runs twice. Disconnect and reconnect again, it runs 3 times.
console.log('socket id again', socket.id); // this shows the same new socket id each time
stream.write(`${bashCommand} \n`);
}
});
stream.on('data', (d) => {
socket.emit('data', response); // tell the browser!
}).on('close', () => {
conn.end();
});
});
}).on('close', () => {
socket.emit('message', '*** SSH CONNECTION CLOSED ***');
}).on('error', (err) => {
socket.emit('message', `*** SSH CONNECTION ERROR: ${err.message} ***`);
}).connect({
'host': hosturl,
'username': ausername,
'agent': anagent, // just for dev I'm using public / private key from my local machine but this will be replaced with the user's entered credentials
});
}).on('disconnect', () => {
console.log('user disconnected');
});
server.listen(8080);
客户:
const io = require('socket.io-client');
const EventEmitter = require('events');
class MyEmitter extends EventEmitter {};
const myEmitter = new MyEmitter();
const PORT = 8080;
let socket;
myEmitter.on('connectClicked', () => {
if (socket) {
this.connected.set(socket.connected);
}
if (this.connected.get() === false) {
socket = io(`http://localhost:${PORT}`);
socket.on('connect', () => {
this.connected.set(true);
socket.on('ready', () => {
console.log('ready');
});
// Backend -> Browser
socket.on('message', (data) => {
console.log('socket on message', data);
});
// Backend -> Browser
socket.on('data', (data) => {
console.log('got data', data);
this.parseResponse(data); // client function to handle data, not shown here
});
// Browser -> Backend
myEmitter.on('selectDirectory', () => {
console.log('*** SELECT DIRECTORY');
socket.emit('path', pathArray.join('/')); // path array is set in client code, it is a simple array of directory names
});
socket.on('disconnect', () => {
console.log('\r\n*** Disconnected from backend***\r\n');
this.connected.set(false);
});
});
}
myEmitter.on('disconnectClicked', () => {
socket.disconnect();
});
});
保持 ssh 连接独立的答案是维护当前 ssh 连接的列表并修改代码,以便接收到的 ssh 数据仅发送到与传入消息对应的浏览器。
我也放弃了 socket.io,因为我对安全性没有信心。我现在通过 the Meteor Direct Stream Access package 使用 Meteor 的内置 DDP 消息系统。我认为这可以避免打开任何新的网络服务器访问点。