NAS 入坑系列五、公网访问配置
如果我们家里 Nas 部署的服务希望能让自己在外面也能访问主要有两种方式,一种是通过公网将服务暴露出去,一种是搭建 VPN,自己想访问时开启 VPN。这里主要介绍前面的方式。
访问形式
家宽公网 IP
需要将服务通过公网暴露出去那就一定需要一个公网 IP,如果自己家的宽带就有公网 IP 那是最好的了。这种情况需要自己配置端口映射。整体拓扑如下(一般情况下是这样三层结构)
这样当光猫的 10000 端口收到访问请求,最后就被一步步转发到了 Nas 的 10000 端口。
内网穿透
内网传透有很多工具可以实现,这里拿 frp 举例,整体都差不多。如果自己没有 VPS,可以试下 CloudFlare Tunnel,ZeroTier 方案。
内网穿透的拓扑结构如下。
Nginx 配置
前面介绍了公网到 Nas 环节的访问配置方式,流量到了 Nas 内部还需要做一次分发,根据域名的不同分发到这个具体的服务,也即反向代理。
目前常用的反向代理有这样几个 Nginx,Caddy,Traefik。
Caddy 配置比 Nginx 简单点,然后支持自己续期 HTTPS 证书,这三个都不会的可以学习下用这个。
Traefik 是一个比较新的反向代理工具,支持自己续期 HTTPS 证书。然后他还支持自动注册代理,意思是启动新的服务和域名后,Nginx 和 Caddy 都需要手动配置下新域名,但 Traefik 不一样,我们可以在 docker-compose 的配置文件里描述好域名信息,Traefik 就能自动发现并为之代理。还有没有其它优势我也不了解了。有兴趣研究下新工具的可以试试 Traefik。
我一开始就用的 Nginx,最近也还没准备尝试 Traefik(后面研究完了再在这篇文章里补上),所以这里还是介绍 Nginx 的配置。
部署
nginx 的 docker-compose.yaml(我一般放在 /opt/app/nginx 下)
services:
nginx:
image: nginx:alpine
container_name: nginx
network_mode: host
volumes:
- ./conf.d:/etc/nginx/conf.d
- ./sslcert:/etc/sslcert
restart: always
在同目录创建 conf.d 目录和 sslcert 目录,后面使用。
域名配置
我在 Nas 上部的服务很多,所以是配置的是泛域名,比如你有一个域名 foo.com,可以直接配置 *.foo.com
,如果下多加一级用泛域名可以用类似 *.nas.foo.com
,不过我以前有后者,发现有的域名太长了(家宽的公网 IP 访问需要加上端口就更长了),现在就用了 *.foo.com
这种形式。
泛域名的好处是不用每次加新服务就要申请一次证书配置一次。
在 conf.d 目录里用以你选择的域名命名创建一个新文件,比如 foo.com.conf ,在里面贴上这样的内容。
server {
listen 10000 ssl;
server_name *.foo.com; # 匹配所有子域
ssl_certificate /etc/sslcert/*.foo.com/fullchain.cer;
ssl_certificate_key /etc/sslcert/*.foo.com/*.foo.com.key;
location / {
# 使用映射的端口构建上游服务器地址
proxy_pass http://127.0.0.1:$map_upstream;
# 普通代理设置
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;
# WebSocket 支持
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_read_timeout 300s;
proxy_connect_timeout 75s;
}
}
map $host $map_upstream{
emby.foo.com 8057;
nas.foo.com 5000;
}
前面的 server_name 和 ssl_certificate 相关的内容要根据自己的域名修改。最后面 map 里则是填你需要配置的域名和本地的端口。
每次部署了新服务,只需要在最后的 map 里加上一个映射,然后 reload nginx(docker compose exec nginx /usr/sbin/nginx -s reload) 就好了。
完整的配置文件可以看这里
单域名配置
单域名的配置整体上和泛域名差不多,主要是 server_name 和 ssl_certificate 相关的位置要写好域名,然后 proxy_pass 要写端口。
server {
listen 10000 ssl;
server_name aaa.foo.com; # 匹配所有子域
ssl_certificate /etc/sslcert/aaa.foo.com/fullchain.cer;
ssl_certificate_key /etc/sslcert/aaa.foo.com/aaa.foo.com.key;
location / {
# 使用映射的端口构建上游服务器地址
proxy_pass http://127.0.0.1:<port>;
# 普通代理设置
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;
# WebSocket 支持
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_read_timeout 300s;
proxy_connect_timeout 75s;
}
}
证书申请
经过上面的配置 Nginx 侧都做完了,接下来是申请证书。我自己还是比较习惯 acme.sh 这个工具。
首先安装 acme
curl https://get.acme.sh | sh -s email=<your email>
然后根据文档去获取你域名所有 dns 提供商的 token, https://github.com/acmesh-official/acme.sh/wiki/dnsapi。
然后修改下面这份文件里关于 dns 的配置(DNS 提供商的 token 信息和 DNS_PROVIDER),创建一个文件比如 gencert.sh
#!/bin/bash
# gen_cert.sh sub.a.com
# set your dns token and dns provider
# https://github.com/acmesh-official/acme.sh/wiki/dnsapi
#
# for exapmle
# export CF_Token=''
# export CF_Account_ID=''
# DNS_PROVIDER=dns_cf
domain=$1
CMD=$HOME/.acme.sh/acme.sh
$CMD --set-default-ca --server letsencrypt
$CMD --issue --dns $DNS_PROVIDER -d $domain
path="/opt/app/nginx/sslcert/$domain" # 提前创建好这个目录,并且把权限给给到当前用户,不是给到 root。
sudo mkdir -p /opt/app/nginx/sslcert
sudo chown $USER /opt/app/nginx/sslcert
mkdir -p $path
$CMD --install-cert -d $domain \
--key-file $path/$domain.key \
--fullchain-file $path/fullchain.cer \
--reloadcmd "docker exec nginx nginx -s reload"
然后 bash ./gencert.sh *.foo.com
就可以创建好 foo.com 的泛域名证书了,非泛域名就是 bash ./gencert.sh bar.foo.com
。之后创建新证书也是这样执行命令就好了。
可以去 /opt/app/nginx/ 下看看是否已经正常申请到证书。
接下来就可以启动 nginx 了,docker compose up -d
,理论上域名就可以正常访问了。