不开端口、不碰防火墙:用 Natapp 隧道把内网 Web 服务安全开放给外部 内网服务要给外面用端口一开就收不住企业内部这种服务器很常见报表系统、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 端口一个都不在这条隧道里。最后一条是底线隧道只指向 nginxnginx 后面的东西它一概看不见。第一步买一条 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-fsSLhttps://natapp.cn/get.sh?authtoken你的authtoken|sh脚本会在当前目录下载客户端再生成一个启动脚本它不写系统目录、不用 sudo想卸载直接删目录。启动有两种方式都支持# 方式一命令行直接传新版主推./natapp-authtoken你的authtoken-logstdout# 方式二读同级的 config.ini./natapp-configconfig.ini版本差异提醒一句新版客户端 3.x 主推命令行传 authtokenconfig.ini 那套虽然还兼容但网上大量老教程写的都是配置文件法照着旧教程配了 config.ini 却起不来先换成命令行试试。启动成功的日志长这样Tunnel established at https://你的域名 statusonline protohttps forwardinghttps://你的域名 local127.0.0.1:8081**看到 statusonline而且 local 后面跟着的端口和 nginx 监听的那个一致隧道就通了。**这次两边都是 8081以后换了端口这里也要跟着对上。开机自启这块客户端自带 -service 系列命令install / start / status / uninstallmacOS 上要注意它默认往 /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 foundTunnel report.example.com not found这个页面是 natapp 的节点返回的不是本地 nginx意思是请求到了 natapp但找不到对应的隧道。原因基本只有一个客户端没在跑隧道靠客户端主动连上去才存在客户端一停natapp 那边这条隧道就没了回去看客户端进程。页面 502502 Bad Gateway看签名就能判断是谁返回的页脚写着 nginx 版本号比如 nginx/1.30.4那就是 nginx 给的说明请求已经完整穿过隧道交到它手上。502 意味着 nginx 连上游失败去 nginx 错误日志里找这一行connect() failed (61: Connection refused) while connecting to upstreamConnection refused 说明那个端口上根本没有程序在监听跟超时是两回事回到本地 curl 一下前端和后端端口把服务起回来就行。三个错误码的分工记一下排错能少走弯路现象谁返回的说明Tunnel not foundnatapp客户端没跑隧道不存在502nginx上游服务没启动或端口写错504nginx上游连上了但不回话服务卡住配了访问令牌自己访问却不弹验证明明配好了令牌自己打开却直接进去了。设计如此跟配置有没有保存没关系官方文档写得很清楚natapp客户端的ip会自动加入白名单,无需输入令牌跑客户端的就是本机浏览器出去的 IP 和客户端是同一个natapp 一看是自己人放行本地怎么刷新、换浏览器都没用判断依据是 IP不是 cookie。所以自己测试永远是一切正常的假象实际的安全效果只有外部访问才看得到一定用手机流量或者别处的网络测一遍。防火墙一个字都没改整套流程就是上面这几步买一条 Web 隧道和一年的时长绑好备案域名并做解析后台勾一下 HTTPS配一个访问令牌本地 nginx 挑一个端口把请求分给前端和后端最后在服务器上把客户端跑起来。按这个顺序走一遍配置是可以完整复现的中间不用改网络设备也不用去申请公网 IP。里面的端口、时长、证书类型都是按这次的需求定的换成别的一样成立。再回到开头那两个问题。服务多、端口开得多这个现状不用动这次防火墙上一条规则都没加公网这一侧唯一的入口就是隧道指向的那个 nginx数据库、Redis、SSH 这些端口在外面等于不存在哪天不对外开放了把客户端停掉就行隧道立刻消失入口也跟着没了不会留下一个忘了关的端口。不允许内网直接对公网开放的环境这套同样走得通它对网络的要求只有一条内网这台机器能正常上外网连接是从里往外建的不需要公网 IP也不需要有人在公网上留个位置。这次多出来的东西就两样那台机器上一个客户端进程以及业务系统前面多出来的一道验证。本来只是想开个口子结果门口还多了一道防护。