如果你用过 frp,应该知道可以把家里服务器的 80 和 443 端口映射到公网服务器的 80 和 443 端口,再配合 Caddy / Nginx 服务,就可以用域名访问家里服务器上的网站了。
这个方案的优点有:
- 家里服务器不需要有公网 IP,也不需要暴露 80 和 443 端口(frpc 每次会使用随机端口连接到服务器),因此也没有被运营商封端口的风险
- 后续 HTTP/HTTPS 服务器的所有配置都在家里服务器配置
- 公网服务器只需要做 frp 转发,性能要求很低(阿里云的抢占式 t6,0.2c0.5g 足够)
也有缺点:
- 公网服务器的 80 和 443 端口被这个服务占用了
- 每次流量需要做转发。
# frpc.toml
serverAddr = "xxx"
serverPort = 7000
[[proxies]]
name = "http"
type = "tcp"
localIp = "127.0.0.1"
localPort = 80
remotePort = 80
useEncryption = true
[[proxies]]
name = "https"
type = "tcp"
localIp = "127.0.0.1"
localPort = 443
remotePort = 443
useEncryption = true
# 多个服务器共享一个 HTTPS 端口
让多个 HTTPS 服务共享一个服务器的端口,理论是可行的。HTTPS 协议里有 SNI(Server Name Indication)扩展,可以让客户端在握手时告诉服务器它想访问的域名。服务器根据这个域名来进行分流和 TCP 转发。
因此,我们可以在公网服务器上用 Nginx 的 stream 模块来根据 SNI 分流到不同的服务端口。配置如下:
# 使用 stream 模块需要在 Ubuntu 上安装 `nginx-full` 包
stream {
# 读取TLS SNI信息
ssl_preread on;
# 根据SNI匹配域名分流
map $ssl_preread_server_name $backend {
docker-proxy.lyh543.cn 127.0.0.1:4999;
default 127.0.0.1:10443;
}
server {
listen 443;
proxy_pass $backend;
proxy_timeout 300s;
}
}
这里 Nginx 仅仅是做了 TCP 转发,没有进行 TLS 解密,因此不需要配置证书。证书配置和 TLS 解密仍然是需要在后面的服务器做。
家里服务器的 frpc 配置也需要小改一下 remotePort
,其余不变。
# frpc.toml
serverAddr = "xxx"
serverPort = 7000
[[proxies]]
name = "http"
type = "tcp"
localIp = "127.0.0.1"
localPort = 80
remotePort = 80
useEncryption = true
[[proxies]]
name = "https"
type = "tcp"
localIp = "127.0.0.1"
localPort = 443
remotePort = 10443 # 服务端再用 nginx 做转发
useEncryption = true
这样就可以让两个 frp 客户端共享同一个服务器的 443 端口,对外提供 HTTPS 服务了。显然,也支持多个 frp 客户端共享同一个 443 端口,只要在 Nginx 的 map 里添加更多的域名和端口映射即可。
- 上面的 Nginx 只转发了 443,没有转发 80。因为 80 端口经由 frp 直接访问家里的 Nginx/Caddy,会自动 HTTP 重定向到 HTTPS URL。客户端使用 HTTPS 再次访问时,就会被 Nginx 正确分流。至于证书的申请,我使用的是 DNS 验证(caddy-dns/dnspod (opens new window))。当然也可以在 Nginx 层做 80 端口识别 HTTP 域名进行分流。