# 2010-03-13  利用外部VPS主机通过ssh隧道穿透防火墙连接内网

前几天一时冲动在国外买了个VPS主机，最大的好处是拥有全部的权限，操作系统自己想怎么搞就怎么搞。于是动了给公司内网通过这个VPS作为中转开个ssh隧道的念头，这样就不用很麻烦的每次都要去连vpn了。

公司内网访问外网目前不需要设置代理服务器，但是因为防火墙的原因，要访问公司内网地址还是需要通过vpn才能访问。通过ssh建立隧道的文章已经很多了，[这篇文章](https://www.ibm.com/developerworks/cn/linux/l-cn-sshforward/) 讲的很详细，不熟悉的同学可以去学习下。

我面临的情况，无非是由于防火墙的原因，内网可以出来，但是外部连不进去。因此需要通过ssh端口转发，从内网建立一个到VPS主机的ssh隧道。由于只能内部往外连接，因此使用的是ssh的远程端口映射（概念不懂的去看前面推荐的文章）：`ssh -qnfNT -g -R 3322:localhost:22 myname@vpshost`这样就在vpshost上开了一个端口3322，和内网的22端口建立起了一个ssh隧道。在vpshost上，通过 ssh -p 3322 localhost 就可以直接连到公司内网了。

按理来说，我从我本地的笔记本ssh到vpshost的3322端口应该是可以直接连接到公司内网的--这也是"-g"这个参数的作用，没有这个参数则只允许从vpshost本地发起ssh连接。但是很奇怪试了很多次总是不行，vpshost的3322端口不接受外部（非本地）的ssh连接，也许是我哪里理解有误，知道的同学请告诉我。

现在问题其实也很明显，我没法从外部直接通过vpshost的3322端口连接上去，只能从vpshost本地发起连接。那么如果想从外部发起连接，暂且想到两个办法：

1. 在vps上用ssh新开一个本地端口转发，转发到本机的3322端口，这个新开端口也要用ssh的-g参数。按理来所应该能够起作用，但是尝试之后几次仍然不行，于是便放弃继续受挫了。
2. 通过本地端口映射，开一个能够接受外部连接的端口，然后将这个端口接收到的数据转发到3322上。

方法2其实有很多工具可以实现，比如iptables、netcat之类的。我嫌iptables太麻烦，而nc又不支持多连接。于是经过一番google，发现了socat这个极其变态强大的工具--果然一不小心就知道的太多了。

socat本质上就是一个数据管道：从一个数据源接收数据流，然后转发到另外的数据目的地。支持socket,tcp,udp,unix domain,pipe,文件等等几乎各类你能想到和想不到的数据源。socat有海量的参数，我就不一一解释了，想了解的同学可以去看[文档](http://www.dest-unreach.org/socat/doc/socat.html)，有不少例子应该能够照猫画虎的满足需求。我从文档中的例子里抄了一个TCP方式的端口转发

```
vps# socat TCP4-LISTEN:2222,reuseaddr,fork TCP4:localhost:3322
```

第一个地址表示使用tcp监听本地的2222端口，reuseaddr表示复用本地地址，fork表示如果接受了一个连接之后马上开启一个新的进程准备接受下一个连接。 第二个地址表示，将前者2222这个端口的数据，使用tcp方式转发到本地（localhost）的3322端口去。

这下我从本机上ssh 到vps的2222端口，果然顺利连接到公司内网。
