Palemoons' Archive
Palemoons' Archive
自宅用内网穿透方案
发布于 2024310|更新于 20241019|遵循 CC BY-NC-SA 4.0 许可

年终奖发了,春节期间兴致冲冲地在家组了一台自己的家庭服务器,主要用来全自动追番、备份主力笔记本数据以及存储RAW格式照片,主打一个all-in-boom。

运行中的Home Server

机器起初放在家里的阳台上,方便用网线连上墙背后的路由器。不过成都的夏天实在太过炎热,阳台正好又面朝西晒,于是八月份几乎每天都能收到温度报警。现在重新做了无线Mesh组网,总算可以把机器搬进客厅里的路由器分身边上放着了。

话说回来,由于一年里有大半的时间在外工作,必须得有一套可靠的内网穿透方案,把诸如jellyfin之类的服务暴露在外网上,方便从外地连回家。起初我把用来搭梯子的服务器当作跳板机,不过随便访问个服务就得绕地球一圈,延迟实在有些感人。于是在阿里云上重新租了台99元/年的VPS,带宽是小了点(3Mb/s),不过只是在手机上看看番的话还算够用。

FRP

内网穿透使用的是frp,应该算一个比较常用的内网穿透方案了。阿里云装的是Ubuntu,只需要找包管理器下载frps然后用systemd管理就行;家里的服务器装的是nas用的Unraid,没有更方便的途径,只能先下载编译好的frpc然后自行打包一个docker镜像:

Dockerfile
复制代码
1# Dockerfile for frpc 2FROM alpine:latest 3WORKDIR /app 4COPY . /app 5EXPOSE 1234 6CMD ["./frpc", "-c", "./frpc.toml"]

frp本身的配置不麻烦,有公网IP的服务器(后续我们假设域名为example.com)作为服务端暴露高位端口,家里的服务器作为客户端与服务端保持连接。同时为了安全,添加一个复杂的token鉴权。

一个简单的示例如下:

toml
复制代码
1# frps.toml 2bindPort = 1234 3auth.token = "a-complicated-token"
toml
复制代码
1# frpc.toml 2serverAddr = "example.com" 3serverPort = 1234 4auth.token = "a-complicated-token" 5 6[[proxies]] 7name = "service-1" 8type = "tcp" 9localIP = "192.168.1.1" 10localPort = 4321 11remotePort = 2134

这样配置后,我们就创建了一个名为service-1的服务,该服务在家庭服务器的192.168.1.1:4321上运行,并被映射到公网上,只需前往example.com:2134就能访问服务。

NPM

经过上面的配置之后其实就能够用IP+端口的方式访问内网服务了,不过看起来有些不太优雅。为了能够通过三级域名直接访问对应的服务,考虑用nginx配置反向代理实现。为方便管理,我自己部署了一个Nginx Proxy Manager面板进行操作。

npm有官方docker镜像,直接通过docker-compose.yml配置就好:

yaml
复制代码
1version: '3.8' 2services: 3 app: 4 image: 'jc21/nginx-proxy-manager:latest' 5 restart: unless-stopped 6 ports: 7 - '80:80' 8 - '81:81' 9 - '443:443' 10 volumes: 11 - ./data:/data 12 - ./letsencrypt:/etc/letsencrypt 13 - ./npm-patch/force-ssl.conf:/etc/nginx/conf.d/include/force-ssl.conf

接着运行docker-compose up -d,服务默认部署在81端口上。用默认账号密码登录后界面如下:

首页

反向代理

为了实现上述功能,我们需要在Proxy Hosts列表添加新配置。例如添加上述服务,使得用户能够在service.example.com处访问:

设置示例

这里由于使用了docker部署npm,对应的IP可以直接填写docker网关,即172.17.0.1,端口则对应配置服务暴露在公网的端口。

这样配置后我们就能以http协议访问服务。为了提高安全性,需要另外配置证书。点开SSL选项卡,可以选择直接申请通配符证书,或者每个三级域名分别申请证书。

第二种方式疑似有bug,在持续运行一段时间后,会有概率出现证书申请/续期失败的可能,查看GitHub issue也没有更好的解决方案(毕竟不可能为此直接删除npm相关的全部数据重新部署服务)。因此不如一开始直接申请通配符证书。

这里以申请通配符证书为例,进入SSL Certificates页面,添加证书,进行如下配置:

通配符证书申请需要对应的密钥

添加完成,后续所有服务都可以使用该通配符证书。

重定向

完成上述「反向代理」的配置后,如果访问service.example.com,我们能正确进入对应的网页应用。而对于一些没有部署服务的域名,例如abc.example.com,网站将直接不响应,这仍然不够优雅。因此,需要自己再配置一下来实现将未命中的请求重定向回主页的功能。

npm在Setting页面中自带一个Default Site设置,可以设置多种响应,并且自带Custom Page的设置。不过我们想要的并不是直接响应一个页面,而是进行重定向,因此为了在面板上直接实现功能,需要一些邪道方式进行配置。

  1. 添加一个Redirection Hosts设置,source设置为*.example.com,并用之前申请的通配符证书添加SSL。Destination设置为example.com

    配置示例
  2. example.com添加一个404 Hosts设置,其中在Advanced中配置如下nginx设置:

    nginx
    复制代码
    1location / { 2 proxy_set_header Host $host; 3 proxy_set_header X-Real-IP $remote_addr; 4 proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 5 proxy_set_header X-Forwarded-Proto $scheme; 6 root /data/nginx/default_www; 7 index index.html; 8 rewrite ^/* /index.html break; 9}
    配置示例
  3. 登录服务器,在对应的/data/nginx/default_www位置编写自己需要的html文件。

注意,这样配置之后,不再需要对Default Site添加额外的设置。

其它

如果读者像我一样使用的是国内服务器,那么就需要进行网页备案。建议在部署上述服务前为二级域名做好备案,避免出现部署服务过程中发现无法访问、查问题才发现是备案背锅的情况……

备案本身并不麻烦,云服务器厂商通常会给出非常详尽的文档,手把手教用户申请。不过需注意个人备案并没有支持太多的类型。因此最简单、直白且易过审的方案的就是「静态网页展示」,例如我的二级域名首页

另外,由于把家庭服务暴露在公网上,不可避免地会有网络安全风险。因此关闭服务器密码访问+网页服务复杂密码应该都是基操,就不再多提。

Reference

Comments