什么是ssh?
ssh(secure shell)是一种对数据进行加密安全传输的协议。 利用ssh工具(比如iTerm2)可以非常方便的登录远程提供有ssh服务的主机,也可以很方便的进行文件传输。
什么是ssh隧道,为什么需要打隧道?
ssh隧道就是ssh tunnel,利用 ssh tunnel 可以进行端口转发(port forwarding), 它在ssh连接上建立一个加密的通道。创建了ssh tunnel之后,可以突破一些网络的限制访问不能直接访问的资源。 ssh tunnel分为三种:本地(L),远程(R)和动态(D)。
使用场景
客户端clientC在公司内部使用的是内网IP,而服务器serverA和serverB在IDC机房或者在云服务器商那边。服务器serverA有公网IP能与客户端clientC正常通信,而服务器serverB没有公网IP(又或者没有暴露80端口到公网),不能与客户端clientC直接通信,但是服务器serverA和服务器serverB是可以通过内网进行通信的。现在我们需要通过clientC访问serverB的相关端口。
通过posman无法直接调用到server B上的服务
解决方案
1. 登录目标服务器:通过跳板机serverA ssh登录到目标服务器serverB之后,用curl命令调用localhost接口。
- 缺点:
- 输入两次密码,必须每次登录到跳板机
- curl调用参数较多,难以保存和修改
2. 使用ssh tunnel
常用命令详解
-L port:host:hostport 将本地机(客户机)的某个端口转发到远端指定机器的指定端口. 工作原理是这样的, 本地机器上分配了一个 socket 侦听 port 端口, 一旦这个端口上有了连接, 该连接就经过安全通道转发出去, 同时远程主机和 host 的 hostport 端口建立连接. 可以在配置文件中指定端口的转发. 只有 root 才能转发特权端口. IPv6 地址用另一种格式说明: port/host/hostport
-R port:host:hostport 将远程主机(服务器)的某个端口转发到本地端指定机器的指定端口. 工作原理是这样的, 远程主机上分配了一个 socket 侦听 port 端口, 一旦这个端口上有了连接, 该连接就经过安全通道转向出去, 同时本地主机和 host 的 hostport 端口建立连接. 可以在配置文件中指定端口的转发. 只有用 root 登录远程主机才能转发特权端口. IPv6 地址用另一种格式说明: port/host/hostport
-D port 指定一个本地机器 “动态的’’ 应用程序端口转发. 工作原理是这样的, 本地机器上分配了一个 socket 侦听 port 端口, 一旦这个端口上有了连接, 该连接就经过安全通道转发出去, 根据应用程序的协议可以判断出远程主机将和哪里连接. 目前支持 SOCKS4 协议, 将充当 SOCKS4 服务器. 只有 root 才能转发特权端口. 可以在配置文件中指定动态端口的转发.
我的实现
使用ssh -L命令,实现本地端口转发。注意:创建隧道前先确保已将跳板机(serverA)的ssh公钥上传至目标服务器(serverB)。
ssh -N -L 9999:remoteIP:remotePort root@jumperIP
jumper就是跳板机serverA, remote就是提供服务的serverB
- 缺点:
- 每次都要密码
- 一直要保证iTerm2窗口打开,有些服务器设置了idle超时,就无法持续访问。
- 没办法实现自动化接口测试
再(更)进(懒)一(一)步(点)
问:这样每次我必须手动输入ssh命令,并且输入跳板机密码,能不能用代码实现? 答:可以!用JSch!
什么是JSch?
官网:www.jcraft.com/jsch/ 简介:JSch是一个SSH2的纯Java实现,可以让你使用连接到sshd服务器,并且使用端口转发,X11转发,文件传输等功能。 你还可以集成这些功能到你的Java程序中,并且是BSD开源软件。(BSD开源协议是一个给于使用者很大自由的协议。基本上使用者可以”为所欲为”,可以自由的使用,修改源代码,也可以将修改后的代码作为开源或者专有软件再发布。)
实战使用
- 添加Maven依赖
<dependency>
<groupId>com.jcraft</groupId>
<artifactId>jsch</artifactId>
<version>0.1.54</version>
</dependency>
- 编写测试类Tunnel
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.Session;
import com.jcraft.jsch.UserInfo;
/**
* Created by taojiaju on 2017/5/26.
*/
public class Tunnel {
public static void main(String[] args) {
Tunnel t = new Tunnel();
try {
t.go();
} catch (Exception ex) {
ex.printStackTrace();
}
}
public void go() throws Exception {
//跳板机(图中的serverA),需要输入用户名、密码
String host = "XXX.XXX.XXX.XXX";
String user = "username";
String password = "password";
int port = 22; //ssh默认端口22
// 目标服务器(图中的serverB)
int tunnelLocalPort = 9999; //本地服务器转发的端口
String tunnelRemoteHost = "YYY.YYY.YYY.YYY";
int tunnelRemotePort = 80; //目标服务器服务访问的端口
JSch jsch = new JSch();
Session session = jsch.getSession(user, host, port);
session.setPassword(password);
localUserInfo lui = new localUserInfo();
session.setUserInfo(lui);
session.connect();
session.setPortForwardingL(tunnelLocalPort, tunnelRemoteHost, tunnelRemotePort);
System.out.println("Connected");
}
class localUserInfo implements UserInfo {
String passwd;
public String getPassword() {
return passwd;
}
public boolean promptYesNo(String str) {
return true;
}
public String getPassphrase() {
return null;
}
public boolean promptPassphrase(String message) {
return true;
}
public boolean promptPassword(String message) {
return true;
}
public void showMessage(String message) {
}
}
}
通过ssh tunnel的远程http接口调用
通过JSch连接上之后,我就可以通过HttpClient调用目标机上的接口了,妈妈再也不用担心啦。
CloseableHttpClient httpClient = HttpClients.createDefault();
HttpPost httppost = new HttpPost("http://localhost:9999/your/interface/position");
httppost.setHeader("Content-type", "application/json");
httppost.setHeader("Accept", "application/json;charset=UTF-8");
String result = "";
String requestStr = "{\"taskName\":\"test\", \"taskParameter\":{\"extend\":{\"operateDate\": \"2017-05-25 14:10:32\", \"userIds\": \"123456\" }}}";
try {
StringEntity entity = new StringEntity(requestStr, "UTF-8");
entity.setContentEncoding("UTF-8");
httppost.setEntity(entity);
HttpResponse response = httpClient.execute(httppost);
if (response.getStatusLine().getStatusCode() == 200) {
//读返回数据
result = EntityUtils.toString(response.getEntity());
System.out.println(result);
}
} catch (ClientProtocolException e) {
e.printStackTrace();
}
// 关闭session
session.disconnect();
blog.creke.net/722.html www.linuxdiyf.com/linux/17234… codelife.me/blog/2012/1… www.jianshu.com/p/90c10a242… www.beanizer.org/site/index.…