【问题标题】:How to SSH to a server behind another SSH server using JSch?如何使用 JSch SSH 到另一个 SSH 服务器后面的服务器?
【发布时间】:2011-10-09 20:18:25
【问题描述】:

我需要能够从 Java 程序 ssh 到远程服务器,并从那里 SSH 到另一台服务器。我的客户端上的两台服务器都有凭据。

命令将在应用程序内作为常规字符串自动传递(无需用户输入)。我需要能够在第二台服务器上运行这些自定义命令,并能够根据输出和一些简单的逻辑来决定在运行时发出哪些命令。

我可以使用 JSch 来做到这一点吗?如果可以,我应该从哪里开始研究? (示例,信息)

================================================ ===============

添加:

线程“主”com.jcraft.jsch.JSchException 中的异常: 未知主机密钥:host.net。 RSA 密钥指纹是 'blahblahblah'

到目前为止,我正在通过修改 known_hosts 文件并在其中手动添加主机来解决此问题。 我可以通过在某处设置一个选项来绕过这个小问题,告诉 JSch 在询问这个 YES-NO 问题时自动按 YES 吗?

【问题讨论】:

    标签: java ssh jsch


    【解决方案1】:

    要连接到防火墙后面的第二台服务器,原则上有两种选择。

    天真的方法是在第一台服务器上调用ssh(来自 exec 通道),指示正确的服务器。这需要使用 JSch 进行代理转发,并且不提供 JSch API 来访问第二个服务器,仅提供 ssh 命令行。

    更好的方法是使用与第一台服务器的连接来建立 TCP 隧道,并使用此隧道连接到第二台服务器。 JSch Wiki 包含一个ProxySSH class(连同一些示例代码),它允许将 JSch 会话用作第二个 JSch 会话的隧道。 (免责声明:这门课主要是我自己写的,也得到了JSch作者的一些支持。)

    当您连接到第二台服务器时,使用shell 频道或一系列exec 频道来执行您的命令。 (有关概述,请参阅 JSch Wiki 中的 Shell, Exec or Subsystem Channel,有关详细信息,请参阅 Javadocs。)


    对于您的 unknown-host-key 问题:

    secure 版本将收集之前的所有主机密钥(以安全方式)并将它们放入 known_hosts 文件中。 (如果您只是信任提供给您的密钥,那么您很容易受到中间人攻击。如果这些在您的网络中无关紧要,因为它是物理安全的,对您有好处。)

    方便版本将configuration option StrictHostKeyChecking 设置为no - 这会将未知的主机密钥添加到主机密钥文件中:

    JSch.setConfig("StrictHostKeyChecking", "no");
    

    (如果您只想为代理会话而不是隧道会话设置它,您也可以在会话上单独设置它。或者使用yesask为隧道会话覆盖它 - 那里MITM 的危险可能更大。)

    中间方式是启用实际询问用户(然后应该将指纹与某个列表进行比较)-为此,实现UserInfo 接口并将对象提供给会话。 (JSch Wiki 包含一个 example implementation using Swing JOptionPanes,如果您的客户端程序在具有 GUI 的系统上运行,您可以简单地使用它。)

    为了保存已接受的主机密钥,您必须使用带有文件名参数的JSch.setKnownHosts 方法,而不是带有 InputStream 参数的方法 - 否则每次重新启动客户端时都必须重复接受。

    【讨论】:

    • 效果很好!我可以使用同一个网关访问该网关后面的两个或多个不同的 SSH 服务器吗?
    • @Martin:是的,我用过这种方式(用于访问隐藏在单个网关后面的大约 23 台服务器)。
    • 这实际上是创建这个 ProxySSH 类的原因 :-)
    • @Martin:添加到答案中。
    • 添加:我看到你提到了 JSch ssh 转发代理。我该如何使用它?我没有看到任何例子。虽然 ProxySSH 效果很好,但我实际上并不需要它,因为我需要在第二台主机上运行自定义命令,我可以从代理主机运行它,但我需要能够将 ssh 凭据转发到它。
    【解决方案2】:

    使用SSH tunnel,又名local port forwarding,通过 A 打开到 B 的 SSH/SFTP 连接。

    Session sessionA = jsch.getSession("usernameA", "hostA");
    // ...
    sessionA.connect();
    
    int forwardedPort = 2222; // any port number which is not in use on the local machine
    sessionA.setPortForwardingL(forwardedPort, "hostB", 22);
    
    Session sessionB = jsch.getSession("usernameB", "localhost", forwardedPort);
    // ...
    sessionB.connect();
    
    // Use sessionB here for shell/exec/sftp
    

    您可能需要deal with UnknownHostKey exception

    【讨论】:

      【解决方案3】:

      这可以帮助任何人。工作正常:

       public static void sesionA(){
           try {
              Session sessionA = jSch.getSession(username, hostA);  
              Properties config = new Properties(); 
              config.put("StrictHostKeyChecking", "no");
              sessionA.setConfig(config);
              sessionA.setPassword(passwordA);
              sessionA.connect();
      
      
              if(sessionA.isConnected()) {
                  System.out.println("Connected host A!");
                  forwardedPort = 2222;
                  sessionA.setPortForwardingL(forwardedPort, hostB, 22);      
              }
      
          } catch (JSchException e) {
              e.printStackTrace();
          }
       }
      
       public static void sesionB(){
      
      
              try {
                  Session sessionB = jSch.getSession(username, "localhost", forwardedPort);
      
                  Properties config = new Properties(); 
                  config.put("StrictHostKeyChecking", "no");
                  sessionB.setConfig(config);
                  sessionB.setPassword(passwordB);
                  sessionB.connect();
      
                if(sessionB.isConnected()) {
                   System.out.println("Connected host B!");
                }
           }
       }
      

      【讨论】:

      • 我的答案只是一个臃肿的代码。 + 永远不要在不解释安全后果的情况下建议任何人使用StrictHostKeyChecking。 - 你正在失去对MITM attacks 的保护。