问题
I am connecting to a Unix server using SSH.NET with the following code
ssh = new SshClient("myserver.univ.edu", Username, Password);
ssh.Connect();
The connection goes through and appears to generate no exception. The server is set up to require two factor authentication, but I get no prompt on my phone (using that as physical authenticator/OTP device). But the connection seems to be ok.
I then issue a command with this code:
SshCommand NewLookup = ssh.CreateCommand("newlookup " + IpNameOrAddress.Text))
LogText.Text += NewLookup.Execute().Replace("\n", Environment.NewLine);
And then I get the push to my phone (2nd factor verification request). Once I accept the verification request through the phone, then the command executes just fine. This would be all ok, except that...
Problem
I get a push to my phone for every subsequent command, so if I want to use that connection to run multiple commands, I have to sit on my phone clicking "Accept" for every command. So, how do I avoid getting a push on every command?
回答1:
In order to send multiple commands in a single session with SSH.NET, you probably need to use a ShellStream
. This should reduce your 2FA approval to just the opening of the session to the host. This can also be useful for devices (such as HPE Switches) that don't support a command channel, but do support SSH remote terminal (e.g. you can putty to them), and for situations where commands change the (shell) environment and so you need to keep the session open for the duration of the job. Otherwise, the SSH command channel is the intended (and better) way to handle this.
You can find further SSH.NET documentation at NuDoc - SSH.NET and the GitHub Releases for the SSH.Net project include a Windows Help file.
Here's some code I wrote to wrap the ShellStream
in another object that keeps a StreamReader
and StreamWriter
and handles reading input from a(n HP) switch and filtering out the escape sequences, as well as reading up to the next prompt:
public static class SshClientExt {
public static ExtShellStream CreateExtShellStream(this SshClient sc, string termName, uint rows, uint cols, uint width, uint height, int bufSize) =>
new ExtShellStream(sc.CreateShellStream(termName, rows, cols, width, height, bufSize));
}
public class ExtShellStream : IDisposable {
static Regex reEscVT100 = new Regex("\x1B\\[[^A-Z]+[A-Z]", RegexOptions.Compiled);
static TimeSpan ReadTimeout = new TimeSpan(0, 0, 10);
ShellStream ssh;
StreamReader sr;
StreamWriter sw;
public ExtShellStream(ShellStream anSSH) {
ssh = anSSH;
sr = new StreamReader(ssh);
sw = new StreamWriter(ssh);
}
public List<string> ReadLines() {
// try to read all in
long prev;
do {
prev = ssh.Length;
Thread.Sleep(250);
} while (ssh.Length != prev);
"-".Repeat(40).Dump();
var ans = new List<string>();
while (true) {
var line = sr.ReadLine();
if (line != null) {
line = line.Remove(reEscVT100).TrimEnd();
$@"""{line}""".Dump();
if (line.EndsWith("#")) // stop when prompt appears
break;
else
ans.Add(line);
}
else
Thread.Sleep(500);
}
return ans;
}
public void DumpLines() => ReadLines();
public List<string> DoCommand(string cmd) {
sw.Write(cmd);
sw.Write("\r");
sw.Flush();
while (!ssh.DataAvailable)
Thread.Sleep(500);
return ReadLines().SkipWhile(l => l == cmd).ToList();
}
#region IDisposable Support
private bool disposedValue = false; // To detect redundant calls
protected virtual void Dispose(bool disposing) {
if (!disposedValue) {
if (disposing) {
// prevent double dispose
// don't dispose of sr or sw: only disposable resource is ssh
ssh.Dispose();
}
disposedValue = true;
}
}
// This code added to correctly implement the disposable pattern.
public void Dispose() {
// Do not change this code. Put cleanup code in Dispose(bool disposing) above.
Dispose(true);
}
#endregion
}
And here's a sample function that uses that code with SSH.Net to retrieve ons-screen copies of configuration information from the switch:
public static void RetrieveConfigFiles(IDFStack idf) {
using (var sshClient = new SshClient(idf.IPAddress, username, password)) {
sshClient.Connect();
using (var ssh = sshClient.CreateExtShellStream("dumb", 120, 80, 0, 0, 200000)) {
ssh.DumpLines();
ssh.DoCommand("no page");
File.WriteAllLines(idf.ConfigPath, ssh.DoCommand("show running-config structured"));
File.WriteAllLines(idf.StatusPath, ssh.DoCommand("show interfaces brief"));
File.WriteAllLines(idf.LLDPPath, ssh.DoCommand("show lldp info remote-device detail"));
}
sshClient.Disconnect();
}
}
来源:https://stackoverflow.com/questions/51449718/2nd-factor-verification-on-every-command-through-ssh-net