Added tools (run multi cmds), fixed UI scrolling, added SSH algo's and key types.
This commit is contained in:
@@ -58,6 +58,8 @@ CREATE TABLE IF NOT EXISTS ssh_data (
|
||||
password TEXT,
|
||||
auth_method TEXT,
|
||||
key TEXT,
|
||||
key_password TEXT,
|
||||
key_type TEXT,
|
||||
save_auth_method INTEGER,
|
||||
is_pinned INTEGER,
|
||||
FOREIGN KEY(user_id) REFERENCES users(id)
|
||||
|
||||
@@ -18,7 +18,9 @@ export const sshData = sqliteTable('ssh_data', {
|
||||
username: text('username'),
|
||||
password: text('password'),
|
||||
authMethod: text('auth_method'),
|
||||
key: text('key', { length: 2048 }),
|
||||
key: text('key', { length: 8192 }), // Increased for larger keys
|
||||
keyPassword: text('key_password'), // Password for protected keys
|
||||
keyType: text('key_type'), // Type of SSH key (RSA, ED25519, etc.)
|
||||
saveAuthMethod: integer('save_auth_method', { mode: 'boolean' }),
|
||||
isPinned: integer('is_pinned', { mode: 'boolean' }),
|
||||
});
|
||||
|
||||
@@ -69,7 +69,7 @@ function authenticateJWT(req: Request, res: Response, next: NextFunction) {
|
||||
// Route: Create SSH data (requires JWT)
|
||||
// POST /ssh/host
|
||||
router.post('/host', authenticateJWT, async (req: Request, res: Response) => {
|
||||
const { name, folder, tags, ip, port, username, password, authMethod, key, saveAuthMethod, isPinned } = req.body;
|
||||
const { name, folder, tags, ip, port, username, password, authMethod, key, keyPassword, keyType, saveAuthMethod, isPinned } = req.body;
|
||||
const userId = (req as any).userId;
|
||||
if (!isNonEmptyString(userId) || !isNonEmptyString(ip) || !isValidPort(port)) {
|
||||
logger.warn('Invalid SSH data input');
|
||||
@@ -93,13 +93,19 @@ router.post('/host', authenticateJWT, async (req: Request, res: Response) => {
|
||||
if (authMethod === 'password') {
|
||||
sshDataObj.password = password;
|
||||
sshDataObj.key = null;
|
||||
sshDataObj.keyPassword = null;
|
||||
sshDataObj.keyType = null;
|
||||
} else if (authMethod === 'key') {
|
||||
sshDataObj.key = key;
|
||||
sshDataObj.keyPassword = keyPassword;
|
||||
sshDataObj.keyType = keyType;
|
||||
sshDataObj.password = null;
|
||||
}
|
||||
} else {
|
||||
sshDataObj.password = null;
|
||||
sshDataObj.key = null;
|
||||
sshDataObj.keyPassword = null;
|
||||
sshDataObj.keyType = null;
|
||||
}
|
||||
|
||||
try {
|
||||
@@ -114,7 +120,7 @@ router.post('/host', authenticateJWT, async (req: Request, res: Response) => {
|
||||
// Route: Update SSH data (requires JWT)
|
||||
// PUT /ssh/host/:id
|
||||
router.put('/host/:id', authenticateJWT, async (req: Request, res: Response) => {
|
||||
const { name, folder, tags, ip, port, username, password, authMethod, key, saveAuthMethod, isPinned } = req.body;
|
||||
const { name, folder, tags, ip, port, username, password, authMethod, key, keyPassword, keyType, saveAuthMethod, isPinned } = req.body;
|
||||
const { id } = req.params;
|
||||
const userId = (req as any).userId;
|
||||
|
||||
@@ -139,13 +145,19 @@ router.put('/host/:id', authenticateJWT, async (req: Request, res: Response) =>
|
||||
if (authMethod === 'password') {
|
||||
sshDataObj.password = password;
|
||||
sshDataObj.key = null;
|
||||
sshDataObj.keyPassword = null;
|
||||
sshDataObj.keyType = null;
|
||||
} else if (authMethod === 'key') {
|
||||
sshDataObj.key = key;
|
||||
sshDataObj.keyPassword = keyPassword;
|
||||
sshDataObj.keyType = keyType;
|
||||
sshDataObj.password = null;
|
||||
}
|
||||
} else {
|
||||
sshDataObj.password = null;
|
||||
sshDataObj.key = null;
|
||||
sshDataObj.keyPassword = null;
|
||||
sshDataObj.keyType = null;
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
@@ -81,11 +81,13 @@ wss.on('connection', (ws: WebSocket) => {
|
||||
username: string;
|
||||
password?: string;
|
||||
key?: string;
|
||||
keyPassword?: string;
|
||||
keyType?: string;
|
||||
authMethod?: string;
|
||||
};
|
||||
}) {
|
||||
const { cols, rows, hostConfig } = data;
|
||||
const { ip, port, username, password, key, authMethod } = hostConfig;
|
||||
const { ip, port, username, password, key, keyPassword, keyType, authMethod } = hostConfig;
|
||||
|
||||
if (!username || typeof username !== 'string' || username.trim() === '') {
|
||||
logger.error('Invalid username provided');
|
||||
@@ -147,7 +149,23 @@ wss.on('connection', (ws: WebSocket) => {
|
||||
|
||||
sshConn.on('error', (err: Error) => {
|
||||
logger.error('SSH connection error: ' + err.message);
|
||||
ws.send(JSON.stringify({ type: 'error', message: 'SSH error: ' + err.message }));
|
||||
|
||||
let errorMessage = 'SSH error: ' + err.message;
|
||||
if (err.message.includes('No matching key exchange algorithm')) {
|
||||
errorMessage = 'SSH error: No compatible key exchange algorithm found. This may be due to an older SSH server or network device.';
|
||||
} else if (err.message.includes('No matching cipher')) {
|
||||
errorMessage = 'SSH error: No compatible cipher found. This may be due to an older SSH server or network device.';
|
||||
} else if (err.message.includes('No matching MAC')) {
|
||||
errorMessage = 'SSH error: No compatible MAC algorithm found. This may be due to an older SSH server or network device.';
|
||||
} else if (err.message.includes('ENOTFOUND') || err.message.includes('ENOENT')) {
|
||||
errorMessage = 'SSH error: Could not resolve hostname or connect to server.';
|
||||
} else if (err.message.includes('ECONNREFUSED')) {
|
||||
errorMessage = 'SSH error: Connection refused. The server may not be running or the port may be incorrect.';
|
||||
} else if (err.message.includes('ETIMEDOUT')) {
|
||||
errorMessage = 'SSH error: Connection timed out. Check your network connection and server availability.';
|
||||
}
|
||||
|
||||
ws.send(JSON.stringify({ type: 'error', message: errorMessage }));
|
||||
cleanupSSH();
|
||||
});
|
||||
|
||||
@@ -162,12 +180,51 @@ wss.on('connection', (ws: WebSocket) => {
|
||||
keepaliveInterval: 5000,
|
||||
keepaliveCountMax: 10,
|
||||
readyTimeout: 10000,
|
||||
|
||||
algorithms: {
|
||||
kex: [
|
||||
'diffie-hellman-group14-sha256',
|
||||
'diffie-hellman-group14-sha1',
|
||||
'diffie-hellman-group1-sha1',
|
||||
'diffie-hellman-group-exchange-sha256',
|
||||
'diffie-hellman-group-exchange-sha1',
|
||||
'ecdh-sha2-nistp256',
|
||||
'ecdh-sha2-nistp384',
|
||||
'ecdh-sha2-nistp521'
|
||||
],
|
||||
cipher: [
|
||||
'aes128-ctr',
|
||||
'aes192-ctr',
|
||||
'aes256-ctr',
|
||||
'aes128-gcm@openssh.com',
|
||||
'aes256-gcm@openssh.com',
|
||||
'aes128-cbc',
|
||||
'aes192-cbc',
|
||||
'aes256-cbc',
|
||||
'3des-cbc'
|
||||
],
|
||||
hmac: [
|
||||
'hmac-sha2-256',
|
||||
'hmac-sha2-512',
|
||||
'hmac-sha1',
|
||||
'hmac-md5'
|
||||
],
|
||||
compress: [
|
||||
'none',
|
||||
'zlib@openssh.com',
|
||||
'zlib'
|
||||
]
|
||||
}
|
||||
};
|
||||
if (authMethod === 'key' && key) {
|
||||
connectConfig.privateKey = key;
|
||||
if (keyPassword) {
|
||||
connectConfig.passphrase = keyPassword;
|
||||
}
|
||||
} else {
|
||||
connectConfig.password = password;
|
||||
}
|
||||
|
||||
sshConn.connect(connectConfig);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user