内网服务要给外面用,端口一开就收不住
企业内部这种服务器很常见:报表系统、OA、接口服务、后台管理,几套东西一起挂在 nginx 后面,平时只在内网用,用着没什么问题,直到外面开始有人要访问。异地的合作方要看几张报表,分公司的同事要登一下后台,出差的人在酒店查个数据。
常规做法是在防火墙或者路由器上做端口映射,一个服务配一个端口,对外的东西一多,这个口子就跟着越开越大。
端口开得越多,暴露面越大。 每个映射到公网的端口都是一扇朝全网开着的门,端口扫描全天候在跑,快的话几分钟就有人来敲门,哪个服务带着默认口令、哪个后台忘了改密码,被撞开只是时间问题。这些口子还常常没人记得关,项目结束大半年,端口还挂在公网上。
有些环境则压根不允许这么干。一些行业的安全规范写得很死,内网边界上不许开任何入站端口,服务器也不许直接对公网开放,端口映射这条路一上来就走不通。
真正要满足的需求,往往小得可怜,让有限的几个人,在一段时间内,访问指定的那个服务。既要让内网的服务给外面用,又不能在防火墙上开一堆端口,更不能把整个内网摆到公网上,尺度全卡在这里。
下面这套环境是个典型例子,报表系统跑在内网,Vue 前端、Spring Boot 后端,中间 nginx 做反向代理,现在要让外部合作方看到其中几张报表。
换个方向:让内网这边主动连出去
Natapp 的做法和端口映射正好相反,客户端装在内网那台机器上,由它主动向 Natapp 的节点建一条长连接,外部访问先到 Natapp 的节点,再顺着这条已经存在的连接送回内网。
差别就在这一步,防火墙上不需要为这条隧道放行任何入站端口。 不用公网 IP,不用改端口映射,路由器一行都不用动,内网除了被指定的那一个服务,其余端口对外完全不存在。
说不上是把内网暴露出去,更像内网自己伸手出去接了一根线,方向从内到外,发起权始终在内网这台机器手里。整套方案对网络的要求只有一条,这台机器能正常上外网。
它不只是给内网开了条通道,还在业务系统前面多支了一道岗。通道建好之后,natapp 那边还能再挂一层验证,访问令牌、IP 白名单都行,外面的人得先过这一关,才轮到业务系统自己的登录。一套本来连公网 IP 都没有的内网系统,门没多开一扇,门口倒多了道岗。
这也是这次选它的主要理由,堆硬件、上一整套企业级安全网关也能达到类似效果,但那是另一个量级的成本和周期,而这次的需求是给一个 Web 服务开个口子。
其余好处都是附带的,一条 VIP2 型 Web 隧道年付 102 元;客户端 Windows、Linux、macOS 全有;国内 VIP 隧道官方标了 SLA 99.9%,比免费隧道那种不定时强制更换域名和端口的做法稳当得多;后台还能自助开数电发票,企业走报销不用去烦客服。另外它自带访问记录,这一项不在最初的预期里,后面单独说。
动手之前,先确认这几件事
以下条件有一项不满足,就先停在这里:
- 内网这台机器能正常上网,测试期间不休眠、不关机;
- nginx 在本机已经跑起来,前端和后端各有自己的监听端口。这套环境是 nginx 8081、前端 9200、后端 9100,这三个数字只是这次挑的,换成别的空闲端口一样跑得通;
- 有一个已完成备案的域名。国内节点绑自主域名要求备案,香港节点不受限制;
- 清楚这次要对外的只有一个服务。数据库端口、Redis 端口、SSH 端口,一个都不在这条隧道里。
最后一条是底线,隧道只指向 nginx,nginx 后面的东西它一概看不见。
第一步:买一条 Web 隧道
Web 隧道是给 HTTP/HTTPS 服务用的,网站、接口、内部系统都算。别去选 TCP 隧道,那是给远程桌面、SSH、数据库这类纯 TCP 服务准备的。协议这一项买完就不能改了,选之前先想清楚服务类型。
配置页要填的不多:
| 配置项 | 填什么 | 说明 |
|---|---|---|
| 隧道协议 | Web | 报表系统走的是 HTTP |
| 二级域名 | 不需要 | 这次走自主域名,想省掉备案也可以用 natapp 的二级域名 |
| 本地端口 | 8081 | 指到 nginx 监听的那个端口。这里挑了 8081,换成别的空闲端口也行,和 nginx 那边对齐就好 |
| 购买时长 | 一年 | 见下面的说明 |
购买时长选年付,年付贵不了多少,差别在功能上:安全设置里的访问令牌、IP 白名单、IP 黑名单,三项在后台都标着 "仅年付隧道可开启" ,先买了月付,等真要开访问保护时才发现开不了,再想升级就麻烦了。这次选的是 VIP2 型加一年,102 元,规格是 100M 带宽、流量计费 1.1 元/G,后面会用到这个数字。
付费的 Web 隧道必须绑域名才能用,嫌备案麻烦可以用 natapp 提供的二级域名,9 到 20 元一年。这次是要长期给合作方访问的,直接上了自主域名。
第二步:备案、绑域名、做解析
隧道买好,接下来接域名,顺序别乱。先把域名加进备案域名库,这是国内节点绑自主域名的前提。
再做 CNAME 解析:到域名商控制台,把要用的子域名 CNAME 到隧道详情页给出的那个 natapp.cc 地址上,解析值后台能一键复制,别手动拼。
最后回后台绑定,隧道配置页有"绑定域名"一栏,选"自主域名",把完整域名填进去。
顺序反了,后台会提示"未绑定任何域名,无法 Web 访问"。碰上这个提示,多半是解析还没传播完,等几分钟,或者 dig 一下确认生效再回来看,不用改配置。
这里有两个词容易混,隧道详情里的 authtoken 是启动凭证,跟后面要配的"访问令牌"完全是两回事,authtoken 用来让客户端连上隧道,访问令牌用来挡住访问者,别混用,也别用同一个值。
第三步:把 HTTPS 开起来
等域名指向正确、HTTP 能正常访问了,再去开 HTTPS,不然排错时分不清是哪一层的问题。
证书用云厂商发的免费 DV 证书就行,比如 TrustAsia 签的,有效期三个月,手上有商业证书同样能传。在 natapp 隧道配置里上传证书内容和私钥,点验证就生效,证书和私钥都是 PEM 格式,从证书下载页选 Apache 那一项拿到的就是。
代价得提前知道,到期要手动换。 三个月一签,忘了续,网站当天就打不开。不想操这个心,后台还有一个"证书自动续期服务",60 元一年,natapp 负责申请和续期,本地什么都不用配。
HTTPS 怎么接一共三种方式,区别只在一处,TLS 在哪一端结束。
方式 1 是 natapp 托管证书,最省事,本地 nginx 一个字不用改,适合大部分人;方式 2 是自己上传证书,就是上面那个做法,手上有证书时用,续期自己负责;方式 3 叫 HTTPS 直通,端到端加密,natapp 只搬密文,听着最安全,但要慎重。
方式 3 的问题在于,natapp 看不到明文,就记不了访问日志,也拿不到访客的真实 IP。这次选 natapp,其中一个原因就是它的日志能力,选方式 3 等于主动把这份能力丢掉,除非确实有中间环节不得解密的合规要求,否则不值。
另外,开了域名 HTTPS 就别再去开"本地 HTTPS",那是两码事,后者指在本地服务器上配 443 证书,一起开容易互相打架,本地老老实实跑 HTTP。
第四步:配访问令牌
通道通了、加密有了,但这时候任何知道域名的人都能访问,显然不行。进隧道的"安全设置"选项卡配令牌。
配完之后,外部访问者第一次打开会看到验证页,输对了才放行。令牌别用能猜的值,带品牌词、带年份的短串最容易被撞,官方文档里那句"实际应用得复杂点"不是客套话,直接上一段长随机串。
这道验证发生在 natapp 那一侧
这句话看着普通,后果却相当实际。验证页是 natapp 服务端吐出来的,请求在那一层就被拦下,压根不会往隧道里转。换一个没过验证的来源访问一次,再去看本地 nginx 的访问日志,一行都不会多,服务器根本不知道外面有人来敲过门。
顺着往下还有一个好处,被挡在验证页外面的请求,不占隧道流量。 在按流量计费的套餐上这是真金白银,VIP2 型 100M 带宽、流量计费 1.1 元/G,每个字节都从账上走。公网上每天有多少扫描和试探在乱撞,要是这些请求都被转发进来、都算进流量,成本会涨得莫名其妙,什么都没干,钱却在往外走。现在它们全被拦在 natapp 那一层,一次都没进隧道,没进服务器,也没进账单。
令牌、白名单、黑名单的判定顺序
安全设置里不止令牌,还有 IP 白名单和 IP 黑名单,它们不叠加生效,而是按顺序判定,任何一层命中就立即出结果:
黑名单优先级最高,命中就直接拒绝,连白名单和令牌都不看;白名单则是免令牌的,IP 一旦命中就直接放行,根本走不到令牌那一步,所以别把 IP 白名单当唯一防线,出口 IP 一变,或者被复用,保护就等于没有。官方推荐只开访问令牌,理由是最好用、效果也够,白名单和黑名单留空就行。
需要注意的是,http basic 认证和访问令牌不要同时开。两个一起开,访问者会先碰到一个浏览器原生的用户名密码框,输完又被跳到令牌页,判断不出自己在过哪一关,排错也麻烦,选一个就好。
改完安全设置,重启一下客户端,确保配置下发到位。
第五步:本地 nginx 怎么配
前面都是 natapp 后台的事,本地这边只有一个 nginx 配置要写。前提是前后端服务已经在跑,这套环境前端 9200、后端 9100,接口都在 /api/ 前缀下,nginx 要做的事就一件,把它俩拼成一个入口。监听哪个端口自己定,这里挑了 8081,和隧道里填的本地端口对上就行,以后想换,两边一起改。
server {
listen 8081; # 端口自选,和隧道里的本地端口保持一致即可
server_name _;
client_max_body_size 100m; # 按实际上传需求调
# 后端接口,前缀按项目改
location /api/ {
proxy_pass http://127.0.0.1:9100;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
# 前端
location / {
proxy_pass http://127.0.0.1:9200;
proxy_http_version 1.1;
# 下面这行是这次踩坑的重点,见正文说明,端口按前端的实际端口写
proxy_set_header Host 127.0.0.1:9200;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# 开发服务器的热更新走 WebSocket
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}
配置本身不长,容易出问题的是 Host 这一行。本地怎么测都是好的,从公网一访问,页面直接返回:
403 Blocked request. This host ("report.example.com") is not allowed.
这个 403 跟 natapp 没关系,是 Vite 自带的 Host 校验,较新版本的 Vite 默认只接受本地 Host,从公网域名访问,它认为来路不明,直接拒掉。
两种解法,一是改前端配置加 allowedHosts,二是让 nginx 转发时把 Host 重写成 Vite 认的值。建议第二种,业务项目的代码一行都不用动,配置只留在运维这一层,原始域名通过 X-Forwarded-Host 保下来,排查时还看得到。前端如果用的是打包后的静态文件而不是开发服务器,就不会遇到这个问题,只要跑的是 dev server,这个坑大概率躲不掉。
第六步:把客户端跑起来
nginx 跑起来、本地能访问之后,再启动 natapp 客户端。客户端从官方下载页取,一键脚本和手动下载都有,Linux 和 macOS 直接跑脚本:
curl -fsSL "https://natapp.cn/get.sh?authtoken=你的authtoken" | sh
脚本会在当前目录下载客户端,再生成一个启动脚本,它不写系统目录、不用 sudo,想卸载直接删目录。启动有两种方式,都支持:
# 方式一:命令行直接传(新版主推)
./natapp -authtoken=你的authtoken -log=stdout
# 方式二:读同级的 config.ini
./natapp -config=config.ini
版本差异提醒一句,新版客户端 3.x 主推命令行传 authtoken,config.ini 那套虽然还兼容,但网上大量老教程写的都是配置文件法,照着旧教程配了 config.ini 却起不来,先换成命令行试试。
启动成功的日志长这样:
Tunnel established at https://你的域名
status=online proto=https forwarding=https://你的域名 local=127.0.0.1:8081
看到 status=online,而且 local= 后面跟着的端口和 nginx 监听的那个一致,隧道就通了。 这次两边都是 8081,以后换了端口,这里也要跟着对上。
开机自启这块,客户端自带 -service 系列命令(install / start / status / uninstall),macOS 上要注意,它默认往 /Library/LaunchDaemons 写文件,必须用管理员权限,没这个权限就在终端前台跑着,或者自己写一个用户级启动项,别用 -service。
第七步:换一条网络,从头验一遍
关键一条,不要用本机浏览器验证。正确做法是让设备离开内网,手机关掉 WiFi,用 4G 或 5G 流量访问这个域名,正常会看到 natapp 的验证页:
输入令牌,页面跳到真实系统,整条链路就通了。
顺带多出来的一套运维后台
链路验证完本来准备收工,回后台随手点进访问分析,发现它记了一份相当完整的访问账本,请求总数、独立 IP 数、状态码分布、来源 IP 归属地、热门访问路径,全在一页上。
图上三组数字摆在一起挺说明问题:总请求 701 次,独立 IP 只有 1 个,错误请求 13 个。那唯一一个独立 IP 是客户端所在的那台机器,也就是自己人,到看数据那一刻,外面一个陌生访客都没来过。
明明用手机流量从外网走了一遍,独立 IP 怎么还是 1 个?因为那次被拦在验证页了,请求压根没进隧道,自然也进不了这份统计。前面说拦下的请求不占流量,这里再补一句,它连记录都不留,两个现象同一个原因,能对上,说明判定逻辑确实在按预期跑。
当初要是图省事做了端口映射,同一个地址会收到什么?扫描器不会客气,独立 IP 那一栏大概率就是一串陌生地址,不会是现在的 1 个。那 13 个错误请求点开看全是 502,时间也对得上,是本地服务停掉那段时间留下的,以后有人问"刚才怎么打不开",这儿直接有答案。
来源 IP Top 10 也值得看一眼,里面只有预期内的地址,说明入口是干净的;冒出一串不认识的,回去调安全设置。
本地那份更细的,在 4040
云端那份是汇总,本地还藏着一份明细。本地流量监控控制台(WebInterface)默认挂在 http://127.0.0.1:4040,在隧道配置里勾选开启、重启客户端,然后在客户端那台机器上打开这个地址,就能看到每个请求的 HTTP 详情,请求头、原始数据都在,还支持请求重放(Replay),调接口时很省事。
几个限制记一下:
- 只有 Web 隧道支持;
- 默认端口 4040,被占用了可以改;
- 最多只留 50 条记录,超出会截断,别当长期存档用;
- 会分析并存储所有请求,占性能也占内存,不用的时候关掉。
云端一份汇总,本地一份明细,够得上"可审计"三个字。官网那句"具备企业应用审计与合规日志能力"摆在页面上不太显眼,真用起来才知道分量。对这次的需求还有个小便利,以后合作方说打不开,或者被问"我那几次到底成没成",不用猜,后台一查就有。
几个会卡住人的地方
后台提示 Tunnel not found
Tunnel report.example.com not found
这个页面是 natapp 的节点返回的,不是本地 nginx,意思是请求到了 natapp,但找不到对应的隧道。原因基本只有一个,客户端没在跑,隧道靠客户端主动连上去才存在,客户端一停,natapp 那边这条隧道就没了,回去看客户端进程。
页面 502
502 Bad Gateway
看签名就能判断是谁返回的,页脚写着 nginx 版本号,比如 nginx/1.30.4,那就是 nginx 给的,说明请求已经完整穿过隧道交到它手上。502 意味着 nginx 连上游失败,去 nginx 错误日志里找这一行:
connect() failed (61: Connection refused) while connecting to upstream
Connection refused 说明那个端口上根本没有程序在监听,跟超时是两回事,回到本地 curl 一下前端和后端端口,把服务起回来就行。
三个错误码的分工记一下,排错能少走弯路:
| 现象 | 谁返回的 | 说明 |
|---|---|---|
Tunnel not found | natapp | 客户端没跑,隧道不存在 |
502 | nginx | 上游服务没启动或端口写错 |
504 | nginx | 上游连上了但不回话,服务卡住 |
配了访问令牌,自己访问却不弹验证
明明配好了令牌,自己打开却直接进去了。设计如此,跟配置有没有保存没关系,官方文档写得很清楚:
natapp客户端的ip会自动加入白名单,无需输入令牌
跑客户端的就是本机,浏览器出去的 IP 和客户端是同一个,natapp 一看是自己人,放行,本地怎么刷新、换浏览器都没用,判断依据是 IP,不是 cookie。所以自己测试永远是一切正常的假象,实际的安全效果只有外部访问才看得到,一定用手机流量或者别处的网络测一遍。
防火墙一个字都没改
整套流程就是上面这几步,买一条 Web 隧道和一年的时长,绑好备案域名并做解析,后台勾一下 HTTPS,配一个访问令牌,本地 nginx 挑一个端口把请求分给前端和后端,最后在服务器上把客户端跑起来。按这个顺序走一遍,配置是可以完整复现的,中间不用改网络设备,也不用去申请公网 IP。里面的端口、时长、证书类型都是按这次的需求定的,换成别的一样成立。
再回到开头那两个问题。服务多、端口开得多,这个现状不用动,这次防火墙上一条规则都没加,公网这一侧唯一的入口就是隧道指向的那个 nginx,数据库、Redis、SSH 这些端口在外面等于不存在,哪天不对外开放了,把客户端停掉就行,隧道立刻消失,入口也跟着没了,不会留下一个忘了关的端口。不允许内网直接对公网开放的环境,这套同样走得通,它对网络的要求只有一条,内网这台机器能正常上外网,连接是从里往外建的,不需要公网 IP,也不需要有人在公网上留个位置。
这次多出来的东西就两样,那台机器上一个客户端进程,以及业务系统前面多出来的一道验证。本来只是想开个口子,结果门口还多了一道防护。