Running Nim 2.3.1 on WSL [Linux: amd64]
I'm trying to keep an ssh session alive for an indefinite period of time and periodically run a command. I'm able to get a response from a non-interactive session using the OS ssh like this:
import std/[os, osproc, streams, strformat]
const ... # host IP, username, password
echo execCmd(&"sshpass -p{BMC_PWD} ssh -J bastion1 root@{BMC_IP} uname -a")
However when I try to keep it running I can only echo out the initial text from the login, but then it hangs. I'm not sure how to manipulate the instream, outstream, hascontent, buffers, etc.,
I've tried variations of the following:
import std/[os, osproc, streams, strformat]
const
opts = {poStdErrToStdOut} # I've tried a bunch of the other options too
# host IP, username, password
type ProcArgs =
tuple
command: string
args: openArray[string]
let procargs = [(command: "/usr/bin/sshpass",
args: @["-p", BMC_PWD, "ssh", "-t", "-o", "UserKnownHostsFile=/dev/null", "-o", "StrictHostKeyChecking=no", "-J", "bastion1", &"root@{BMC_IP}"]), ...]
let
p1 = startProcess(command=procargs[0].command, args=procargs[0].args, options=opts)
in1 = p1.inputStream
out1 = p1.outputStream
echo "Host SSH Output:"
while p1.hasData:
echo outs.readLine() # <-- this outputs the initial stuff you see when you log in
# Never escapes above while.
# I think hasData() is hanging when host is done outputting initial login messages
echo "done with that"
while some condition bla bla:
in1.write("uname -a") # should be same as typing the command
echo outs.readLine() # return value to nim to do something
sleep(1000)
echo "done sampling"
p.kill, etc.
In case anyone suggests using an ssh lib, the target machine is behind a bastion which is protected by a bunch of Teleport stuff, so the local ~/.ssh/config file is full of fanciness and calls out to tsh, magic encryption keys, etc. Sorry, all that stuff is beyond my knowledge, but in any case I'm able to ssh where I need to from the command line; the only way to get to the target machine is to leverage the system's ssh, rather than an ssh lib.
I investigated using libssh2.nim but I've been unable to find whether it supports the equivalent of the -J option of the cmd line ssh. Another possibility is finding something that supports the SOCKS5 protocol and point this login attempt at a local SSH proxy pointing to the target machine behind the bastion. libssh2 also doesn't appear to do this. In python I'm able to use Paramiko with the SOCKS5 thing to talk to a local proxy, but I really want to do this in nim. FWIW I can get the libssh2.nim example to log into another SSH target (not behind bastion) without the -J option.
Maybe try ssh keepalive: https://stackoverflow.com/a/25087194
Also that in1.write might need a newline at the end?
If it is the second option then I don't think there's another solution besides reading the stream character by character. That being said you might want to consider using the package asynctools with its async proc handling. This way you can set up an input reader that just echos anything that is written and use timers to fire messages on the output stream.