内网服务的自动分流,让内网走内网,公网走公网

因为轻量应用服务器开始承受不了immich的需求,上传图片的时候开始频繁卡顿。正好又是许久之前就有了组一台Nas的想法。在部署immich服务的内网和公网访问的时候遇到了一些问题。考虑到杂碎且后续可能频繁遇到,故在此记录。

现状与目标

现状

这是当前服务的访问情况,用户在公网发起请求,流量由nginx接管后按照域名规则,转发到本地的指定端口。在nginx上部署ssl和强制https。

原来的链路

展望

  • 域名访问
  • 全程HTTPS
  • 内网环境自动走内网地址

因为部署immich的nas所处的网络环境没有公网ip,所以对于公网访问的情况,我们还需要部署frp来做流量转发。

这意味这内网访问和公网访问所走的链路是完全不同的。nginx、frp需要在内外网都部署。

画个图吧

新链路

从图看,实现分流的重要一步就是DNS解析。我们只需要在家中的路由器中设置别名或者直接劫持就行了。

接下来展一下其中可能会遇到的各种问题~

公网链路

Frp服务

我们先看公网链路,DNS之前就解析着这个服务器,不用修改,nginx的反向代理也不用修改,我们只需要撤下之前的immich服务,上线一个同样监听2283端口的frp服务即可。

最新的frp默认用了toml作为配置文件,不太常见。

frps

从github下载tar.gz,解压后编辑frps.toml文件

1
2
3
4
5
bindPort = 7827

[auth]
method = "token"
token = "123123"

修改token后面的值为随机字符串!记得加上双引号。

服务端的配置文件确实没啥好配置的,声明用于frp传输数据的端口和认证方式即可,注意在云服务器后台放行我们声明的7827端口。

然后我们可以启动frp服务,记得用tmux或者screen让它可以后台运行

1
./frps -c frps.toml

frpc

我们重点编辑frp客户端的配置文件

1
2
3
4
5
6
7
8
9
10
11
12
13
serverAddr = "114.114.114.114"
serverPort = 7000

[auth]
method = "token"
token = "123123"

[[proxies]]
name = "immich"
type = "tcp"
localIP = "127.0.0.1"
localPort = 2283
remotePort = 2283

修改token后面的值为frps.toml中设定的值!记得加上双引号。

修改serverAddr字段为部署了服务器端的服务器的ip或者域名。

如果需要配置多个代理,只需要重复[[proxies]]部分即可,比如我们再加一个Minecraft的转发,那么完整的toml配置为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
serverAddr = "114.114.114.114"
serverPort = 7000

[auth]
method = "token"
token = "123123"

[[proxies]]
name = "immich"
type = "tcp"
localIP = "127.0.0.1"
localPort = 2283
remotePort = 2283

[[proxies]]
name = "Minecraft Server"
type = "tcp"
localIP = "127.0.0.1"
localPort = 25565
remotePort = 25565
注册为systemd服务

考虑到未来需要的修改配置,重启frpc需求。我们还需要将其注册为systemd服务。

1
sudo vim /etc/systemd/system/frpc.service

可以编辑为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
[Unit]
Description=Frp Client Service
After=network.target

[Service]
Type=simple
User=root
Restart=on-failure
RestartSec=5s
ExecStart=/home/yang/frp/frpc -c /home/yang/frp/frpc.toml
ExecReload=/home/yang/frp/frpc reload -c /home/yang/frp/frpc.toml

[Install]
WantedBy=multi-user.target

修改ExecStart字段和ExecReload字段的值为具体的文件路径。

记得给frpc文件赋予执行权限

1
chmod +x /home/yang/frp/frpc

重新加载systemd的配置文件

1
systemctl daemon-reload

OK!让我们使用systemctl启动frpc服务。

1
sudo systemctl start frpc.service

至此,公网访问链路所有流程已完成部署。尝试直接在浏览中输入https://immich.coooolfan.com访问即可。

如果访问出现5**错误,或许是frp服务启动没有成功。

查看frpc服务状态

1
sudo systemctl status frpc.service

查看frpc服务实时日志

1
sudo journalctl -u frpc.service -f

内网链路

内网DNS劫持

实现内网DNS劫持的方法有很多,这里采用的是主机名映射,直接在OpenWrt中配置对应的域名和ip即可。

公网环境下的DNS解析无须修改,因为我们本来就还要用这个服务器做流量转发。

DNS劫持

获取SSL证书

因为我们需要全程HTTPS,所以内网访问还需要部署SSL证书。因为之前用的lnmp脚本,所以这还是第一次用acme相关的东西。

安装ACME

1
wget -O -  https://get.acme.sh | sh

一键脚本安装就行了,如果有提示需要提前安装cron,那就先装上cron再重新安装acme。

刷新一下环境变量,不然用不了acme.sh命令

1
source ~/.bashrc

设置默认CA为Let’s Encrypt

1
acme.sh --set-default-ca --server letsencrypt

从DNS服务商申请API并配置ACME

因为我们是在内网环境下申请SSL证书,所以只能使用DNS验证的方式验证域名的所有权。acme支持调用DNS服务商的API来自动化执行所有步骤。

acme支持超级多的DNS服务商,不同服务商需要配置的字段和其标志都不同,详情见https://github.com/acmesh-official/acme.sh/wiki/dnsapi

我们的DNS服务商是DNSPod,需要从其后台申请https://console.dnspod.cn/account/token/token

注意:DNSPod分cn和com;DNSPod的API和腾讯云API不一样,不要申请错了。

DNS API的申请

拿到ID和Token后,我们编辑acme的account.conf文件

1
vim .acme.sh/account.conf

对于DNSPod,对应的字段为

1
2
export DP_Id="<id>"
export DP_Key="<token>"

然后尝试向证书服务商申请一个证书

1
acme.sh --issue --dns dns_dp -d immich.coooolfan.com

如果证书申请成功,你会看到acme打印的各种东西。我们只会用到证书链fullchain.cer和密钥immich.coooolfan.com.key

如果申请失败,查看日志,不要盲目重试,证书服务商一般会显示失败次数,过多的失败次数会出发rateLimited。

生成ssl_dhparam(可选)

这是一种用于密钥交换的加密算法。

我们直接生成一个

1
openssl dhparam -out ~/.acme.sh/dhparam.pem 2048

这个过程超级无敌慢!其实不生成也没事,后面的nginx删掉对应的一行就行了。

配置Nginx服务

新服务器,啥都没装,装一下

1
sudo apt install nginx

创建一个用于配置这个站点的配置文件

1
sudo vim /etc/nginx/sites-available/immich.coooolfan.com

这东西好像也没啥要改的,公式化。

需要修改

  • 两个server块的server_name字段为要监听的域名
  • ssl_certificatessl_certificate_key为之前acme输出的路径
  • ssl_dhparam为之前生成的dhparam文件的文字,如果没生成那就删掉这行
  • location块中的proxy_pass为反向代理的目标地址
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
server {
listen 80;
server_name immich.coooolfan.com;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl http2;
client_max_body_size 0;
server_name immich.coooolfan.com;

ssl_certificate /home/yang/.acme.sh/immich.coooolfan.com_ecc/fullchain.cer;
ssl_certificate_key /home/yang/.acme.sh/immich.coooolfan.com_ecc/immich.coooolfan.com.key;
ssl_session_timeout 5m;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers on;
ssl_ciphers "TLS13-AES-256-GCM-SHA384:TLS13-CHACHA20-POLY1305-SHA256:TLS13-AES-128-GCM-SHA256:TLS13-AES-128-CCM-8-SHA256:TLS13-AES-128-CCM-SHA256:EECDH+CHACHA20:EECDH+CHACHA20-draft:EECDH+AES128:RSA+AES128:EECDH+AES256:RSA+AES256:EECDH+3DES:RSA+3DES:!MD5";
ssl_dhparam /home/yang/.acme.sh/dhparam.pem;


location / {
proxy_pass http://127.0.0.1:2283;
proxy_set_header Host $http_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;

proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_redirect off;
}
}

nginx默认并不会读取文件夹sites-available下的配置文件,我们需要对其创建一个软链接,让文件夹sites-enabled下也有一个一样的文件。

1
sudo ln -s /etc/nginx/sites-available/immich.coooolfan.com /etc/nginx/sites-enabled/immich.coooolfan.com

OK!让我们检查一下nginx的配置文件有没有出错

1
sudo nginx -t

如果没有出错,重启nginx即可

1
sudo nginx -s reload

至此,内网访问链路所有流程已完成部署。尝试直接在浏览中输入https://immich.coooolfan.com访问即可。

碎碎念

同一个域名解析得到不同的证书或许不是最佳实践,集中式的证书管理获取是更好的选择,有机会再说吧。


内网服务的自动分流,让内网走内网,公网走公网
http://coooolfan.com/2024/08/07/Intranet-access-for-intranet-services/
作者
Coolfan
发布于
2024年8月7日
许可协议