【问题标题】:2nd factor verification on every command through ssh.net通过 ssh.net 对每个命令进行第二因素验证
【发布时间】:2018-12-29 04:32:50
【问题描述】:

我正在使用 SSH.NET 使用以下代码连接到 Unix 服务器

ssh = new SshClient("myserver.univ.edu", Username, Password);
ssh.Connect();

连接通过并且似乎没有产生异常。服务器设置为需要两个因素身份验证,但我的手机上没有提示(将其用作物理身份验证器/OTP 设备)。但是连接似乎没问题。

然后我用这个代码发出一个命令:

SshCommand NewLookup = ssh.CreateCommand("newlookup " + IpNameOrAddress.Text))
LogText.Text += NewLookup.Execute().Replace("\n", Environment.NewLine);

然后然后我收到推送到我的手机(第二因素验证请求)。一旦我通过电话接受了验证请求,然后该命令就可以正常执行了。一切都会好的,除了...

问题

我会为每个后续命令推送到我的手机,所以如果我想使用该连接来运行多个命令,我必须坐在手机上为每个命令单击“接受”。那么,如何避免每条命令都被推送?

【问题讨论】:

  • 空闲时间是多少?命令间隔多长时间?
  • @Jeremiah - 几秒钟,不到 5 秒,我已将连接超时增加到 60 秒。不过想法不错。
  • 我只是将连接超时时间增加到 15 分钟,但没有任何效果。
  • 问题是您使用 ssh 中的命令通道发送命令,这与 shell 会话不同。虽然通常不推荐,但我认为您需要考虑使用 ShellStream 发送命令。
  • @NetMage - 是的,我就是这么想的,但是由于不推荐使用有关 ShellStream 的所有警告,我不愿意去那里。请把它作为答案,如果我很快没有得到任何其他东西,我会接受它。

标签: c# two-factor-authentication ssh.net


【解决方案1】:

为了在 SSH.NET 的单个会话中发送多个命令,您可能需要使用ShellStream。这应该会将您的 2FA 批准减少到仅向主持人开放会话。这对于不支持命令通道但支持 SSH 远程终端的设备(例如 HPE 交换机)也很有用(例如,您可以对它们进行腻子处理),以及命令更改(shell)环境等的情况您需要在工作期间保持会话打开。否则,SSH 命令通道是处理此问题的预期(和更好)方式。

您可以在 NuDoc - SSH.NET 找到更多 SSH.NET 文档,SSH.Net 项目的 GitHub 版本包括 Windows Help file。

这是我编写的一些代码,用于将 ShellStream 包装在另一个对象中,该对象保留 StreamReader 和 StreamWriter 并处理从 (n HP) 开关读取输入并过滤掉转义序列,以及向上读取到下一个提示:

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

}

下面是一个示例函数,它使用该代码和 SSH.Net 从交换机检索配置信息的屏幕副本:

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();
    }
}

【讨论】:

  • 您可能只是说“使用 shellstream”,但您已经超越了给出一个非常完整的代码示例。考虑到 ssh.net 的文档和示例很少,这是黄金。不能感谢你。我已经设置了赏金,只要 SE 允许我,我就会奖励给你。
  • @AgapwIesu 仅供参考,SSH.NET 的文档很难找到,但NuDoq - SSH.NET 有一些不错的 API 文档。
  • 我应该找到的!但我没有...叹息(对自己感到沮丧)....再次感谢您加倍努力。
  • @MartinPrikryl 添加了一些使用它的警告/解释。
  • 在该主机上的 2FA 实现是最普通的。这里的问题是“按命令身份验证”,它是 SSH.NET 如何实现其 SshClient.Connect(仅比 IP 连接)和 CreateCommand(身份验证加上执行命令)的一个属性。如果 CreateCommand 进行身份验证,那么无论您的 2FA 是如何实现的,用户每次发出 CreateCommand 时都会经过它。
猜你喜欢
  • 1970-01-01
  • 2014-10-30
  • 2015-08-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-06-18
  • 2015-10-05
  • 1970-01-01
相关资源
最近更新 更多