2nd factor verification on every command through ssh.net

心已入冬 提交于 2019-12-22 10:34:46

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!