180 lines
4.8 KiB
JavaScript
180 lines
4.8 KiB
JavaScript
/**
|
|
* Module SSH Terminal - Gestion des connexions SSH via WebSocket
|
|
* Utilise la bibliothèque ssh2 pour établir des connexions SSH
|
|
*/
|
|
const { Client } = require('ssh2');
|
|
const WebSocket = require('ws');
|
|
const jwt = require('jsonwebtoken');
|
|
const config = require('./config');
|
|
|
|
/**
|
|
* Initialise le serveur WebSocket SSH sur /ws-ssh
|
|
* @param {http.Server} server - Le serveur HTTP Express
|
|
*/
|
|
function initSSHWebSocket(server) {
|
|
const wssSsh = new WebSocket.Server({ server, path: '/ws-ssh' });
|
|
|
|
wssSsh.on('connection', (ws, req) => {
|
|
// Vérifier l'authentification via le token dans l'URL
|
|
const url = new URL(req.url, 'http://localhost');
|
|
const token = url.searchParams.get('token');
|
|
|
|
if (!token) {
|
|
ws.send(JSON.stringify({ type: 'error', message: 'Token manquant' }));
|
|
ws.close();
|
|
return;
|
|
}
|
|
|
|
try {
|
|
jwt.verify(token, config.jwtSecret);
|
|
} catch (err) {
|
|
ws.send(JSON.stringify({ type: 'error', message: 'Token invalide' }));
|
|
ws.close();
|
|
return;
|
|
}
|
|
|
|
console.log('Nouvelle connexion WebSocket SSH');
|
|
|
|
let sshClient = null;
|
|
let sshStream = null;
|
|
let connected = false;
|
|
|
|
ws.on('message', (data) => {
|
|
try {
|
|
const msg = JSON.parse(data.toString());
|
|
|
|
switch (msg.type) {
|
|
case 'connect':
|
|
// Établir la connexion SSH
|
|
handleConnect(ws, msg, (client, stream) => {
|
|
sshClient = client;
|
|
sshStream = stream;
|
|
connected = true;
|
|
});
|
|
break;
|
|
|
|
case 'input':
|
|
// Envoyer des données au terminal SSH
|
|
if (sshStream && connected) {
|
|
sshStream.write(msg.data);
|
|
}
|
|
break;
|
|
|
|
case 'resize':
|
|
// Redimensionner le terminal
|
|
if (sshStream && connected) {
|
|
sshStream.setWindow(msg.rows, msg.cols, 0, 0);
|
|
}
|
|
break;
|
|
|
|
case 'disconnect':
|
|
// Fermer la connexion SSH
|
|
if (sshClient) {
|
|
sshClient.end();
|
|
}
|
|
break;
|
|
|
|
default:
|
|
console.warn('Type de message SSH inconnu:', msg.type);
|
|
}
|
|
} catch (err) {
|
|
console.error('Erreur parsing message SSH:', err.message);
|
|
}
|
|
});
|
|
|
|
ws.on('close', () => {
|
|
console.log('Connexion WebSocket SSH fermée');
|
|
if (sshClient) {
|
|
sshClient.end();
|
|
}
|
|
});
|
|
|
|
ws.on('error', (err) => {
|
|
console.error('Erreur WebSocket SSH:', err.message);
|
|
if (sshClient) {
|
|
sshClient.end();
|
|
}
|
|
});
|
|
});
|
|
|
|
console.log('Serveur WebSocket SSH initialisé sur /ws-ssh');
|
|
return wssSsh;
|
|
}
|
|
|
|
/**
|
|
* Gère la connexion SSH
|
|
*/
|
|
function handleConnect(ws, msg, onConnected) {
|
|
const { host, port, username, password, privateKey } = msg;
|
|
|
|
if (!host || !username) {
|
|
ws.send(JSON.stringify({ type: 'error', message: 'Hôte et utilisateur requis' }));
|
|
return;
|
|
}
|
|
|
|
const sshClient = new Client();
|
|
|
|
const connConfig = {
|
|
host: host,
|
|
port: port || 22,
|
|
username: username,
|
|
readyTimeout: 15000,
|
|
keepaliveInterval: 10000,
|
|
};
|
|
|
|
if (privateKey) {
|
|
connConfig.privateKey = privateKey;
|
|
} else if (password) {
|
|
connConfig.password = password;
|
|
} else {
|
|
ws.send(JSON.stringify({ type: 'error', message: 'Mot de passe ou clé privée requis' }));
|
|
return;
|
|
}
|
|
|
|
ws.send(JSON.stringify({ type: 'status', message: `Connexion à ${username}@${host}:${port || 22}...` }));
|
|
|
|
sshClient.on('ready', () => {
|
|
ws.send(JSON.stringify({ type: 'connected', message: `Connecté à ${host}` }));
|
|
|
|
sshClient.shell({ term: 'xterm-256color', rows: 24, cols: 80 }, (err, stream) => {
|
|
if (err) {
|
|
ws.send(JSON.stringify({ type: 'error', message: `Erreur shell: ${err.message}` }));
|
|
sshClient.end();
|
|
return;
|
|
}
|
|
|
|
onConnected(sshClient, stream);
|
|
|
|
// Transmettre les données du terminal SSH vers le WebSocket
|
|
stream.on('data', (data) => {
|
|
if (ws.readyState === WebSocket.OPEN) {
|
|
ws.send(JSON.stringify({ type: 'output', data: data.toString('base64') }));
|
|
}
|
|
});
|
|
|
|
stream.stderr.on('data', (data) => {
|
|
if (ws.readyState === WebSocket.OPEN) {
|
|
ws.send(JSON.stringify({ type: 'output', data: data.toString('base64') }));
|
|
}
|
|
});
|
|
|
|
stream.on('close', () => {
|
|
ws.send(JSON.stringify({ type: 'disconnected', message: 'Session SSH terminée' }));
|
|
sshClient.end();
|
|
});
|
|
});
|
|
});
|
|
|
|
sshClient.on('error', (err) => {
|
|
ws.send(JSON.stringify({ type: 'error', message: `Erreur SSH: ${err.message}` }));
|
|
});
|
|
|
|
sshClient.on('end', () => {
|
|
ws.send(JSON.stringify({ type: 'disconnected', message: 'Connexion SSH fermée' }));
|
|
});
|
|
|
|
sshClient.connect(connConfig);
|
|
}
|
|
|
|
module.exports = { initSSHWebSocket };
|