前言——关于内网穿透

什么是内网穿透?

百度百科

内网穿透,即NAT穿透,网络连接时术语,计算机是局域网内时,外网与内网的计算机节点需要连接通信,有时就会出现不支持内网穿透。就是说映射端口,能让外网的电脑找到处于内网的电脑,提高下载速度。不管是内网穿透还是其他类型的网络穿透,都是网络穿透的统一方法来研究和解决。

人话:就是将内网(LAN)IP转换成可以在公网(WAN)使用的公网IP。让互联网上的用户可以通过此公网IP地址访问特定的内网主机所提供的网站或者服务器。而内网主机可以是你的PC,也可以是云服务器,甚至可以是你的树莓派。

内网穿透可以干什么?

说白了就是内网穿透可以通过外网直接访问到你家的电脑。

具体一点的:

  • 发布应用/网站,外网可以访问。
  • 可以实现远程控制,随时随地都能用家里的电脑。
  • 自建游戏服务。
  • 搭设个人网络存储服务器。

解决方案——NPS

名称 API 客户端单独KEY 子域名支持 收费模式 语言
ngrok 支持 未知 支持(但收费) 按客户端数量收费 GO
frp 不支持 统一 支持(但收费) 按客户端数量收费 GO
lanproxyp 不支持 单独 未知 免费 java
goproxy 支持 未知 未知 API版收费 Go
nps 支持 单独 支持 免费 Go

上面列举了几个比较主流的内网穿透工具,此外国内还有比较“知名”的花生壳(百家号警告.jpg)。

NPS

nps原名easyProxy,是一款轻量级、高性能、功能强大的内网穿透代理服务器。目前支持tcp、udp流量转发,可支持任何tcp、udp上层协议(访问内网网站、本地支付接口调试、ssh访问、远程桌面,内网dns解析等等……),此外还支持内网http代理、内网socks5代理、p2p等,并带有功能强大的web管理端。

特点:

  • 协议支持全面,兼容几乎所有常用协议,例如tcp、udp、http(s)、socks5、p2p、http代理…
  • 全平台兼容(linux、windows、macos、群辉等),支持一键安装为系统服务
  • 控制全面,同时支持服务端和客户端控制
  • https集成,支持将后端代理和web服务转成https,同时支持多证书
  • 操作简单,只需简单的配置即可在web ui上完成其余操作
  • 展示信息全面,流量、系统信息、即时带宽、客户端版本等
  • 扩展功能强大,该有的都有了(缓存、压缩、加密、流量限制、带宽限制、端口复用等等)
  • 域名解析具备自定义header、404页面配置、host修改、站点保护、URL路由、泛解析等功能
  • 服务端支持多用户和用户注册功能

详情请看开发者文档:中文文档

具体安装方法文档也有说,我只讲我的安装流程。

安装流程

一般内网穿透工具都有服务端和客户端,安装要求如下:

服务端:需要安装在一个有公网IP的服务器上,系统为Linux/Windows/Mac均可。
客户端:一般安装在一个内网的VPS服务器或Windows/Mac电脑上使用。

服务端安装需要我们有一台拥有静态公网IP的服务器,国内各大云主机服务商均有销售。

(阿里云、腾讯云、华为云、天翼云、百度云、新浪云、京东云、美团云、七牛云…….)

服务器选购方面:

操作系统就两个方案,linux和windows sever

windows sever我就不多说了,有点追求的都用linux 推荐centos6/7

用作内网穿透的话对CPU性能、内存、以及硬盘容量都没有要求,选择最便宜的即可。

网络带宽最重要,一般服务器就贵在带宽。

网络带宽参考:

应用方面 协议 推荐服务器带宽 网络延迟(本地ping服务器)
远程桌面 tcp/udp隧道 >5mbps <50ms
游戏开服 tcp/udp隧道 无要求 <80ms
个人云端 p2p连接 >10mbps <30ms
网站调试 http/https 无要求 无要求

注:网络带宽速度取决于使用场景

服务器买完后,请从供应商处获取相应的登录信息。

Windows Sever建议直接远程桌面。
Linux有puTTY、SecureCRT、XShell等多种选择。

服务端配置

ssh链接成功后,开始配置服务端

首先请确保防火墙开放了所有端口

iptables -L -n

出现以下结果,代表端口全部开放。

Chain INPUT (policy ACCEPT)
target prot opt source destination

Chain FORWARD (policy ACCEPT)
target prot opt source destination

Chain OUTPUT (policy ACCEPT)
target prot opt source destination

否则可以使用以下命令关闭防火墙

CentOS6::

systemctl stop iptables
systemctl disable iptables

CentOS7:

systemctl stop firewalld
systemctl disable firewalld

如果你使用的是其他Linux发行版,以上命令无效,请自行搜索Linux如何关闭防火墙。

注:文章中出现的所有端口都要放行.
(阿里云要在服务器控制台的安全组中设置.)

完成以上步骤后去Github下载作者编译好的压缩包。
下载地址:https://github.com/ehang-io/nps/releases
请选择和服务器对应的版本,比如我的服务器用的CentOS7 64位,选择的是linux_amd64_server.tar.gz

右键要下载的版本,点复制下载链接,然后在linux里输入以下指令( wget+[下载链接] )

wget https://github.com/ehang-io/nps/releases/download/v0.26.6/linux_amd64_server.tar.gz

下载完后,输入ls查看目录下是否有 linux_amd64_server.tar.gz压缩包

注:linux下的压缩包显示为红色、可执行文件为绿色文件、目录为蓝色。

最近github抽风,下载速度几kb是正常的。

确认无误后,输入以下指令解压压缩包。(PS:这里我忘记解压到文件夹里了,将就吧。)

tar -zxf linux_amd64_server.tar.gz

解压完成后输入ls确认多出来的文件。

具体文件大致如下:

conf  linux_amd64_server.tar.gz nps  web

注:想删掉压缩包就输入rm linux_amd64_server.tar.gz

然后输入以下指令进入conf文件夹

cd conf

并用vim编辑配置文件件:

vi nps.conf

i键开始编辑,编辑完后按esc键,然后输入:wq保存退出。

以下是配置文件是精简后的,按需修改。( []内输入你要的内容。 )

appname = nps
#Boot mode(dev|pro)
runmode = dev

##bridge
# 底层通信协议,默认tcp,可选用kcp
bridge_type=tcp
# 底层通信端口,默认8024,如已被占用请指定其他端口
bridge_port=8024
bridge_ip=0.0.0.0

# 当客户端以配置文件模式启动时会用到的验证密钥,可自行设置。不设置他会自动生成
public_vkey=[你的通信密钥]

#web
web_host=[这里输入你的服务器IP或域名]
web_username=[设置用户名]
web_password=[设置密码]
web_port = [网页面板的端口]
web_ip=0.0.0.0

服务端配置文件含义:

名称 含义
web_port web管理端口
web_password web界面管理密码
web_username web界面管理账号
web_base_url web管理主路径,用于将web管理置于代理子路径后面
bridge_port 服务端客户端通信端口
https_proxy_port 域名代理https代理监听端口
http_proxy_port 域名代理http代理监听端口
auth_key web api密钥
bridge_type 客户端与服务端连接方式kcp或tcp
public_vkey 客户端以配置文件模式启动时的密钥,设置为空表示关闭客户端配置文件连接模式
ip_limit 是否限制ip访问,true或false或忽略
flow_store_interval 服务端流量数据持久化间隔,单位分钟,忽略表示不持久化
log_level 日志输出级别
auth_crypt_key 获取服务端authKey时的aes加密密钥,16位
p2p_ip 服务端Ip,使用p2p模式必填
p2p_port p2p模式开启的udp端口
pprof_ip debug pprof 服务端ip
pprof_port debug pprof 端口

详细的配置参数,请参考开发者的网站

调完配置参数后,请使用以下命令启动nps服务

./nps start  /* 后台启动服务 */
./nps stop /* 结束后台进程 */
./nps reload /* 重新加载配置 */
./nps /* 前台启动服务 */

注:如果输入./nps start无效,请输入./nps启动。

启动后,可以去浏览器访问网页管理面板(在浏览器中输入”[你服务器的IP或域名]:[网页面板端口]”)

如果能打开网页并成功登陆,则说明服务端配置完成。

登陆界面
输入设置好的账号密码登陆页面。
管理面板

如果打不开页面,并报错以下内容。

[E] [http.go:67] listen tcp 0.0.0.0:80: bind: address already in use

说明你的端口被占用了,解决办法:

输入netstat -nap查看占用端口的程序PID,输入kill PID,干掉那个在运行的程序。(你也可以选择改端口)

然后再启动nps

客户端配置

内网设备中下载对应的nps客户端下载地址
比如,我用的是windows 64位,所以选择的是windows_amd64_client.tar.gz(32位系统请下载386版本)

下载完成后,放在合适的目录并解压缩。

与其他老牌内网穿透工具不同,nps能在网页完成对客户端的管理和配置,极其方便。(如果你想,也可以在客户端完成所有配置而不去理会网页管理面板)


进入网页管理面板:

第一步 新增客户端

备注随便填 唯一验证密匙可以不用管 允许客户端通过配置文件链接 开压缩和加密

允许客户端通过配置文件链接可能会造成配置文件冲突 后面附带解决方案。

添加完成后,可以在列表中看到客户端信息,且客户端处于离线状态。

如果添加不了,并且浏览器出现undefined的提示框说明你用的浏览器太垃圾了,让你换谷歌浏览器。

第二步 复制客户端指令

点击左侧+号按钮查看详细信息,我们可以看到系统生成(或手动指定)的验证密钥,以及客户端连接服务端的客户端命令

我们需要复制那个红色代码块。

./npc -server=公网IP地址:端口 -vkey=你的通信密钥 -type=tcp

第三步 启动客户端&RDP协议

这里我忘记说了,windows电脑需要 专业版,并且开启远程桌面。

步骤1:设置——系统——远程左面
步骤2:控制面板——系统和安全——系统——远程设置>选允许远程设置——高级>允许被控制

完成以上步骤确定保存。

非专业版windows要打补丁。

下载RDP Wrapper Library

推荐下载 RDPWrap-v1.6.2.zip

解压后以管理员的身份运行install.bat

然后运行RDPConf.exe dignostics全绿表示正常

如果出现红字,请更新rdpwrap.ini文件
Github大佬做的自动更新文件:目前最新版本

复制全部内容编辑添加到rdpwrap.ini里,再次运行RDPConf.exe便会出现全绿。
rdpwrap.ini,在C:\PROGRAM FILES\RDP WAPPER文件夹里。

注意:RDP需要重启电脑才能生效

复制红色代码块后,启动windows的CMD

移动到到存放nps客户端的目录

注:Windows下使用的命令与管理面板中复制的有所不同,请把”./npc”部分替换为”npc”。

npc.exe -server=公网IP地址:端口 -vkey=你的通信密钥 -type=tcp

如果你用的git bash,可以之间去nps客户端目录右键git bash且客户端代码不需要改动。

如果没有报错,可以访问网页管理面板查看客户端是否变为在线状态。

如果是在线状态,则代表内网穿透成功。

注:命令窗口不能关闭,要一直开着,关闭会变成离线。


客户端通过配置文件链接_报错解决方案 & 配置nps客户端npc.conf文件

配置npc.conf文件可以解决每次启动都需要客户端代码的问题。

进入客户端目录,打开conf文件夹,编辑npc.conf文件,具体如图。

删掉多余的配置参数,保留需要的参数,比如:common

否则启动npc.exe的时候会报错。

配置文件说明

全局配置
含义
server_addr 服务端ip:port
conn_type 与服务端通信模式(tcp或kcp)
vkey 服务端配置文件中的密钥(非web)
username socks5或http(s)密码保护用户名(可忽略)
password socks5或http(s)密码保护密码(可忽略)
compress 是否压缩传输(true或false或忽略)
crypt 是否加密传输(true或false或忽略)
rate_limit 速度限制,可忽略
flow_limit 流量限制,可忽略
remark 客户端备注,可忽略
max_conn 最大连接数,可忽略
pprof_addr debug pprof ip:port
域名代理
含义
web1 备注
host 域名(http
target_addr 内网目标,负载均衡时多个目标,逗号隔开
host_change 请求host修改
header_xxx 请求header修改或添加,header_proxy表示添加header proxy:nps
tcp隧道模式
含义
mode tcp
server_port 在服务端的代理端口
tartget_addr 内网目标
http代理模式
含义
mode httpProxy
server_port 在服务端的代理端口
socks5代理模式
含义
mode socks5
server_port 在服务端的代理端口
multi_account socks5多账号配置文件(可选),配置后使用basic_username和basic_password无法通过认证
私密代理模式
含义
mode secret
password 唯一密钥
target_addr 内网目标
p2p代理模式
含义
mode p2p
password 唯一密钥
target_addr 内网目标
文件访问模式

利用nps提供一个公网可访问的本地文件服务,此模式仅客户端使用配置文件模式方可启动

含义
mode file
server_port 服务端开启的端口
local_path 本地文件目录
strip_pre 前缀

对于strip_pre,访问公网ip:9100/web/相当于访问/tmp/目录

NPS玩法

TCP/UDP隧道

使用场景:Windows远程桌面,ssh连接,vnc连接,游戏开服,内网架设dns服务器等

配置方法

以Windows远程桌面为例,其默认使用3389端口。

假设局域网内有一台IP为192.168.1.100的Windows设备,那么,在同一局域网下,只要访问192.168.1.100:3389即可连接。

但如果想要从外网连接,则必须添加一条TCP转发规则,把内网设备的3389端口,映射到服务端的某个端口,这里假设使用服务器的10000端口。

假如服务器IP为12.34.56.78,那么,映射完成后,外网访问12.34.56.78:10000的请求会全部被转发到192.168.1.100:3389。

也就是说,访问12.34.56.78:10000即可连接内网的windows设备。具体配置如下。

服务端端口 范围是从0 到65535的整数数字,这个范围内随便选一个没有被占用的端口。

推荐是10000 ~ 65000这个范围内选一个。

目标 (IP:端口) 填 127.0.0.1:3389

127.0.0.1是内网的地址,端口3389是windows远程桌面的默认端口。

点击新增完成配置。

完成后,可以在手机上安装微软的手机端远程桌面控制—— RD Client

尝试通过 你的公网服务器IP:你选的服务端端口 连接内网windows设备,如可以成功连接,则内网穿透成功。

P2P连接

使用场景:大文件传输,如在内网架设NAS,流量不经过服务器转发。

目标内网设备与访问端都需要运行npc,且二者NAT类型不能同时为对称型网络

配置方法与TCP/UDP的配置方法大同小异,只需要修改配置文件即可,不做演示。

疑难解答

为什么 断开Linux远程终端关闭后,NPS后台的进程也结束了?

Windows远程桌面连接后,运行某个程序,可以退出连接并且不会随着中止连接而结束。

Linux在终端登陆远程主机并运行程序后,如果此时直接中止连接退出,那么这个连接所开的会话(session)下运行的所有进程都会随着中止连接而结束。

这是windows与Linux的不同。

原理:

1)进程的相关概念:

在Linux中,每个进程都属于一个进程组(group),每个进程组有一个组长。

多个进程组构成一个会话,会话是由其中的进程建立的,该进程叫做会话的领导进程(session leader)。

会话领导进程的PID成为识别会话的SID(session ID)。

会话中的每个进程组称为一个工作(job)。

会话可以有一个进程组成为会话的前台工作(foreground job),而其他的进程组是后台工作(background job)。

并不是进程组中的每个进程都是job中的内容,job是由session进程直接的“儿子”组成的,但是当job中的进程又产生子进程的时候,子进程便不是job中的内容。

每个会话可以连接一个控制终端(control terminal)。当控制终端有输入输出时,都传递给该会话的前台进程组。当前台进程组或者说job中的最后一个进程结束后,后台的session控制进程自动切换至前端,由终端产生的信号,比如CTRL+Z, CTRL+\,会传递到前台进程组。

会话主要是针对一个终端建立,当我们打开多个终端窗口时,实际上就创建了多个终端会话。每个会话都会有自己的前台工作和后台工作。这样,我们就为进程增加了管理和运行的层次。

2)终端的相关概念

在UNIX系统中,用户通过终端登录系统后得到一个Shell进程,这个终端成为Shell进程的控制终端(Controlling Terminal),控制终端是保存在PCB中的信息,而我们知道fork会复制PCB中的信息,因此由Shell进程启动的其它进程的控制终端也是这个终端。

默认情况下(没有重定向),每个进程的标准输入、标准输出和标准错误输出都指向控制终端,进程从标准输入读也就是读用户的键盘输入,进程往标准输出或标准错误输出写也就是输出到显示器上。在控制终端输入一些特殊的控制键可以给前台进程发信号,例如Ctrl-C表示SIGINT,Ctrl-表示SIGQUIT。

每个进程都可以通过一个特殊的设备文件/dev/tty访问它的控制终端。事实上每个终端设备都对应一个不同的设备文件,/dev/tty提供了一个通用的接口,一个进程要访问它的控制终端既可以通过/dev/tty也可以通过该终端设备所对应的设备文件来访问。ttyname函数可以由文件描述符查出对应的文件名,该文件描述符必须指向一个终端设备而不能是任意文件。在linux上的命令tty 也可以查看到当前的终端。

比如我们在图形界面下打开一个终端可能是/dev/pts/0, 第二个可能是/dev/pts/1 .. (网络终端),而切换到字符界面下可能是/dev/tty1 …(虚拟终端)

3)进程和终端:

用户通过终端登录系统后得到一个Shell进程,Shell分前后台来控制的不是进程而是作业(Job)或者进程组(Process Group)。一个前台作业可以由多个进程组成,一个后台作业也可以由多个进程组成,Shell可以同时运行一个前台作业和任意多个后台作业,这称为作业控制(JobControl)。
例如用以下命令启动5个进程(这个例子出自APUE):
$ proc1 | proc2 &

$ proc3 | proc4 | proc5

  其中proc1和proc2属于同一个后台进程组,proc3、proc4、proc5属于同一个前台进程组,Shell进程本身属于一个单独的进程组。这些进程组的控制终端相同,它们属于同一个Session,一个Session与一个控制终端相关。当用户在控制终端输入特殊的控制键(例如Ctrl-C)时,内核会发送相应的信号(例如SIGINT)给前台进程组的所有进程。各进程、进程组、Session的关系如下图所示。

在上面的例子中,proc3、proc4、proc5被Shell放到同一个前台进程组,其中有一个进程是该进程组的Leader,Shell调用wait等待它们运行结束。一旦它们全部运行结束,Shell就调用tcsetpgrp函数将自己提到前台继续接受命令。但是注意,如果proc3、proc4、proc5中的某个进程又fork出子进程,子进程也属于同一进程组,但是Shell并不知道子进程的存在,也不会调用wait等待它结束。换句话说,proc3 | proc4 | proc5是Shell的作业,而这个子进程不是,这是作业和进程组在概念上的区别。一旦作业运行结束,Shell就把自己提到前台,如果原来的前台进程组还存在(如果这个子进程还没终止),则它自动变成后台进程,被init进程接管。

Linux的普通进程(守护进程除外)是终端的子进程,进程的存在要依赖终端为其提供空间包括标准输入、标准输出、标准出错。当打开终端的最初的进程(也就是SID进程)退出后,其子进程也会结束。

4)终端登录和执行命令的过程:

现在我们从Session和进程组的角度重新来看登录和执行命令的过程。

getty或telnetd进程在打开终端设备之前调用setsid函 数创建一个新的Session,该进程称为SessionLeader,该进程的id也可以看作Session的id,然后该进程打开终端设备作为这个Session中所有进程的控制终端。在创建新Session的同时也创建了一个新的进程组,该进程是这个进程组的Process Group Leader,该进程的id也是进程组的id。

在登录过程中,getty或telnetd进程变成login,然后变成Shell,但仍然是同一个进程,仍然是Session Leader。

由 Shell进程fork出的子进程(比如说上例中的p3,p4,p5)本来具有和Shell相同的Session、进程组和控制 终端,但是Shell调用setpgid函数将作业中的某个子进程指定为一个新进程组的 Leader(比如说p3),然后调用setpgid将该作业中的其它子进程也转移到这个进程组中。如果这个进程组需要在前台运行,就调用tcsetpgrp函数将它设置为前台进程组,由于一个 Session只能有一个前台进程组,所以Shell所在的进程组就自动变成后台进程组。

原理的参考资料:Click Here

解决方案:

nohup命令

linux下输入以下指令

nohup ./nps &

注:Unix/Linux下一般比如想让某个程序在后台运行,很多都是使用 & 在程序结尾来让程序自动运行。可能我们的程序只是普通程序而已(并不是守护进程),一般这种程序使用 & 结尾,但是如果终端关闭,那么程序也会被关闭。

使用nohup命令可以在后台执行程序并且终端退出仍然运行.

当屏幕屏幕提示:

  [~]$ appending output to nohup.out
  证明运行成功,同时把程序运行的输出信息放到当前目录的 nohup.out 文件中去。

附:nohup命令参考
  nohup 命令
  用途:不挂断地运行命令。
  语法:nohup Command [ Arg … ] [ & ]
  描述:nohup 命令运行由 Command 参数和任何相关的 Arg 参数指定的命令,忽略所有挂断(SIGHUP)信号。在注销后使用 nohup 命令运行后台中的程序。要运行后台中的 nohup 命令,添加 & (表示“and”的符号)到命令的尾部。
无论是否将 nohup 命令的输出重定向到终端,输出都将附加到当前目录的 nohup.out 文件中。如果当前目录的 nohup.out 文件不可写,输出重定向到 $HOME/nohup.out 文件中。如果没有文件能创建或打开以用于追加,那么 Command 参数指定的命令不可调用。如果标准错误是一个终端,那么把指定的命令写给标准错误的所有输出作为标准输出重定向到相同的文件描述符。

screen命令

简单来说,Screen是一个可以在多个进程之间多路复用一个物理终端的窗口管理器。Screen中有会话的概念,用户可以在一个screen会话中创建多个screen窗口,在每一个screen窗口中就像操作一个真实的telnet/SSH连接窗口那样。在screen中创建一个新的窗口有这样几种方式:
1.直接在命令行键入screen命令

Screen将创建一个执行shell的全屏窗口。你可以执行任意shell程序,就像在ssh窗口中那样。在该窗口中键入exit退出该窗口,如果这是该screen会话的唯一窗口,该screen会话退出,否则screen自动切换到前一个窗口。

2.Screen命令后跟你要执行的程序。

Screen创建一个执行命令的单窗口会话,退出同时也将退出该窗口/会话。

3.以上两种方式都创建新的screen会话。我们还可以在一个已有screen会话中创建新的窗口。在当前screen窗口中键入C-a c,即Ctrl键+a键,之后再按下c键,screen 在该会话内生成一个新的窗口并切换到该窗口。

screen还有更高级的功能。你可以不中断screen窗口中程序的运行而暂时断开(detach)screen会话,并在随后时间重新连接(attach)该会话,重新控制各窗口中运行的程序。

参考资料:Click Here