Execute ssh command inside a "for of"
I have an array where I have "hosts/nas" as main and "logins" as secondary, what I'm trying to do is go through the array connect to "host/nas" go through the "logins" array and for each "login" execute the command, but I have the error:
Client :: Ready
node:events:496
throw er; // Unhandled 'error' event
^
Error: (SSH) Channel open failure:
at onChannelOpenFailure (/Users/raphael/Documents/Projeto Tiger/TigerServer/node_modules/ssh2/lib/utils.js:16:11)
at CHANNEL_OPEN_FAILURE (/Users/raphael/Documents/Projeto Tiger/TigerServer/node_modules/ssh2/lib/client.js:572:11)
at 92 (/Users/raphael/Documents/Projeto Tiger/TigerServer/node_modules/ssh2/lib/protocol/handlers.misc.js:881:16)
at Protocol.onPayload (/Users/raphael/Documents/Projeto Tiger/TigerServer/node_modules/ssh2/lib/protocol/Protocol.js:2059:10)
at GenericDecipherBinding.decrypt (/Users/raphael/Documents/Projeto Tiger/TigerServer/node_modules/ssh2/lib/protocol/crypto.js:1418:26)
at Protocol.parsePacket [as _parse] (/Users/raphael/Documents/Projeto Tiger/TigerServer/node_modules/ssh2/lib/protocol/Protocol.js:2028:25)
at Protocol.parse (/Users/raphael/Documents/Projeto Tiger/TigerServer/node_modules/ssh2/lib/protocol/Protocol.js:313:16)
at Socket.<anonymous> (/Users/raphael/Documents/Projeto Tiger/TigerServer/node_modules/ssh2/lib/client.js:775:21)
at Socket.emit (node:events:518:28)
at addChunk (node:internal/streams/readable:559:12)
Emitted 'error' event on Client instance at:
at Socket.<anonymous> (/Users/raphael/Documents/Projeto Tiger/TigerServer/node_modules/ssh2/lib/client.js:777:20)
at Socket.emit (node:events:518:28)
at addChunk (node:internal/streams/readable:559:12)
at readableAddChunkPushByteMode (node:internal/streams/readable:510:3)
at Readable.push (node:internal/streams/readable:390:5)
at TCP.onStreamRead (node:internal/stream_base_commons:190:23) {
reason: 4
}
Node.js v20.11.0
My function code:
const disconnectMultiUserSSH = async (value) => {
try {
for (const nas of value) {
const dataNas = await prisma.nas.findUnique({
where: { id: nas.NaId },
select: {
host: true,
port_ssh: true,
username: true,
password: true,
type: true,
},
})
const bytes_password = CryptoJS.AES.decrypt(dataNas.password, process.env.SECRET_API)
const decrypted_password = bytes_password.toString(CryptoJS.enc.Utf8)
const conn = new Client()
conn
.on("ready", () => {
console.log("Client :: Ready")
for (const login of nas.logins) {
conn.exec(`show subscriber session username ${login}`, (err, stream) => {
if (err) throw err
stream
.on("close", (code, signal) => {
console.log("Stream :: close :: code: " + code + ", signal: " + signal)
conn.end()
})
.on("data", (data) => {
console.log("STDOUT: " + data)
})
.stderr.on("data", (data) => {
console.log("STDERR: " + data)
})
})
}
})
.connect({
host: dataNas.host,
port: dataNas.port_ssh,
username: dataNas.username,
password: decrypted_password,
algorithms: {
kex: ["diffie-hellman-group1-sha1"],
},
})
}
} catch (error) {
console.error(error)
}
}
Be aware that almost all SSH servers have limits on the number of concurrent "channels" you can have open per connection. Channels include things like .shell(), .exec(), local or remote connection forwards, X11 sessions, etc. This could be what you're running into. IIRC the default channel limit for OpenSSH is 10.
I need to access the equipment and disconnect users, how can I solve this?
Perhaps only perform one .exec() at a time using whichever means you wish.
If I repeat the command manually, for example 3 times, it does not show the error.
but inside the "for" it may be 1 time that it presents the error.
I wanted to solve the "for" problem so that I could later solve the ssh limit.
mscdex @.***> writes:
I would also add that in addition to limits on the number of concurrent connections, many systems also have a form of rate limiting in the connection process i.e. key exchange and cipher negotiation - the stuff done prior to the final 'real' connection. For example, from memory, openssh allows up to 10 'connection attempts' at once before it starts a form of rate limiting where it will reject a percentage of connection attempts up until a maximum of 20 at which time it will stop accepting connections until the number drops back down.
Note that this is different from concurrent connections. This is about clients attempting to establish a connection. The point to note is that the backoff algorithm is initially random wrt deciding which connections to reject/drop. This can make debugging challenging as behaviour is different each run. Also, because this is a DDoS mitigation strategy, the response from the remote server can vary from informative to attempts simply being dropped with no indication why.
Key point to note is you need to be very careful when making 10+ connections within a very short period with most ssh servers.
I'm thinking about dividing it into blocks of 5 and configuring the Cisco router parameters to work like this.
However, my main problem is that when adding the connection command ".on("ready", () => {" inside the first "for" and the execution command "conn.exec" inside the second "for" already displays the error.
I believe it is not an error with the limitations of ssh, so I need help at the moment.
command structure:
- first "for" -- data/connection --- second "for" ---- data/command
@rfsilvagyn Without seeing the new code that you're attempting to use, it's difficult to make a suggestion.
If you want to keep your for loop, you're better off writing a helper function that returns a Promise that gets resolved when the command finishes. That way you can just await the helper function calls inside your loop. This will also ensure there is only one command executing at a time.