近期给NAS配备了一个公网IP(使用公网ip盒子,此处不细说了)
诉求:利用 Docker 容器中的 Nginx 实现多域名访问不同应用、或者单域名不同文根访问不同应用,规避IP+端口访问。
1.准备
需要先在 Docker 中下载 Nginx 镜像(国内Docker hub网站支持不好,可再注册表配置中配置阿里云注册表镜像服务,服务地址可从阿里云容器镜像服务控制台获取)。
2.修改掉群晖自带 Nginx 默认占用的 80 端口
我们知道 Synology DSM 默认使用端口5000 (http)和5001 (https),但它的 Nginx 进行重定向 占用了端口 80 。例如,Photo Station 就使用了 80 端口(https对应的为:443)。
群晖不提供可修改端口的UI界面(因为他根本就不希望我们使用80端口),我们需要进入SSH,更改一些系统文件。
群晖每次运行自带nginx时,会根据模板文件,重新生成配置文件,位置在
/etc/nginx/app.d -> /var/tmp/nginx/app.d
(软链接指向临时目录),所以我们修改这个文件意义是不大的,需要直接修改模板文件(倒是可以参考其配置设置我们的 https 转发)。
进入cd /usr/syno/share/nginx
目录,修改 server.mustache
,DSM.mustache
,WWWService.mustache
这三个配置文件(使用 root 账号,sudo -i
)。
Mustache是一种简单的模板语言,及时我们对他不够了解,也并不影响我们的修改工作。
升级DSM后可能会被覆盖==(暂不确定,要注意下)==。
使用编辑器打开,会发现服务器默认配置如下所示:
server {
listen 80 default_server{{#reuseport}} reuseport{{/reuseport}};
listen [::]:80 default_server{{#reuseport}} reuseport{{/reuseport}};
listen 443 default_server ssl{{#reuseport}} reuseport{{/reuseport}};
listen [::]:443 default_server ssl{{#reuseport}} reuseport{{/reuseport}};
server_name _;
{{> /usr/syno/share/nginx/X-Accel}}
我们使用另外的端口替换80,我使用的是 8080,使用 4430 替换 443。
server {
listen 8080 default_server{{#reuseport}} reuseport{{/reuseport}};
listen [::]:8080 default_server{{#reuseport}} reuseport{{/reuseport}};
listen 4430 ssl{{#https.http2}} http2{{/https.http2}}{{#reuseport}} reuseport{{/reuseport}};
listen [::]:4430 ssl{{#https.http2}} http2{{/https.http2}}{{#reuseport}} reuseport{{/reuseport}};
在上述的3个文件中都进行此种更改,配置完这一步,我们只是改变了用于生成 Nginx 配置文件的模板,而不是配置文件本身。所以我们现在还需要强制重建一下 Nginx 配置文件。应该是有一个终端命令来执行此操作,但还没有找到。我们可以在控制面板>>网络
中更改任何设置并保存,这样会触发重建配置文件和重启动Web服务器。
或者使用命令(摘自网络大神,未经测试):
/usr/syno/etc/rc.sysv/nginx.sh reload
stop pkg-apache22
start pkg-apache22
reload pkg-apache22
至此,我们已经成功释放了 80 和 443 端口。
需要注意的是,修改完这里的 80 端口后,我们访问一些NAS服务就要使用上面修改后的 8080 (https: 4430)端口了(例如:Photo Station 等)。
3.转发 80/443 端口到 Docker 容器
我们可以在 Synology应用程序门户>>反向代理服务器规则
中配置将所有通过 80 端口进入NAS的流量都转发到端口 8081 上,然后将 8081 端口再映射到 Docker 容器中的 80 端口(类似的,https 的 443 端口先转发到端口 4431上,再映射到 Docker 容器中的 443)。
你可能会觉得奇怪,从 80 端口到 8080 然后再回到 80,这不是多此一举吗。。其实原因很简单,群晖的Docker UI 中不允许我使用端口 80/443。
正确的修改如下:
反向代理服务器规则 配置如下(下图是我配置好之后的效果,后面介绍方法):
然而,前文已经提到,群晖千方百计的不让我们使用 80/443 端口,在直接配置 反向代理服务器规则 使用 80/443 端口时会有以下错误提示:
所以,这里我们要使用一点小小的手段,在 反向代理服务器规则UI 中我们可以先配置 12345 (https: 12346)端口,然后手动修改配置文件覆盖。
此处的配置文件是/usr/syno/etc/www/ReverseProxy.json
。
这个文件只是一个json文件,遗憾的是并没有格式化,因此不易阅读。这是我的(手动格式化了)配置文件,我们可以将其中的端口 12345 手动修改为 80 (https:12346 手动改成 443) 并保存文件。
{
"592b270d-8fd2-4700-8fd0-e74969498929": {
"backend": {
"fqdn": "localhost",
"port": 8081,
"protocol": 0
},
"customize_headers": [],
"description": "All",
"frontend": {
"acl": null,
"fqdn": null,
"https": null,
"port": 12345,
"protocol": 0
},
"proxy_connect_timeout": 60,
"proxy_http_version": 1,
"proxy_intercept_errors": false,
"proxy_read_timeout": 60,
"proxy_send_timeout": 60
},
"84b1e627-2820-4ad5-85a7-94fa59979037": {
"backend": {
"fqdn": "localhost",
"port": 4431,
"protocol": 1
},
"customize_headers": [],
"description": "All_https",
"frontend": {
"acl": null,
"fqdn": null,
"https": {
"hsts": false,
"http2": false
},
"port": 12346,
"protocol": 1
},
"proxy_connect_timeout": 60,
"proxy_http_version": 1,
"proxy_intercept_errors": false,
"proxy_read_timeout": 60,
"proxy_send_timeout": 60
},
"version": 2
}
如果你的反向代理规则比较多,该文件可能会比较大,小心修改即可。
下一次重新生成 Nginx 配置文件时(对Web设置进行任何更改),新端口号 80 将显示在规则中而不是 12345(https:443 将显示而不是 12346)。
至此,全部配置已经完成。
4.Docker 内 Nginx 设置反向代理
Docker>>容器>>Nginx>>详情>>终端机>>通过命令启动
使用命令bash
或者,在NAS ssh中使用
docker run -it nginx:latest /bin/bash
连接 nginx bash。
4.1 查找 Nginx 配置文件位置
使用nginx -t
命令,返回:
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
/etc/nginx/nginx.conf
即为配置文件路径。
可以使用 vim nginx.conf
编辑配置文件。
可能需要先安装vim,
apt-get update
apt-get install vim
国内源速度奇慢无比,试了几次都失败了。可以使用Docker的卷映射功能,映射NAS中的配置文件给docker nginx。
例如:
# 文件
docker/conf/nginx/nginx.conf --> /etc/nginx/nginx.conf
# 文件夹
docker/conf/nginx/conf.d --> /etc/nginx/conf.d
# 证书
document/cert/syno-acme/acme.sh/xxx.com --> /etc/nginx/cert
注意Docker容器的默认时区和主机不一致,还需要映射下时区文件
docker/etc/localtime --> /etc/localtime
docker/etc/TZ --> /etc/TZ
docker/etc/timezone --> /etc/timezone
需要先执行命令:cp /etc/localtime /volume1/docker/etc/
5.还有一件很重要的事
这只是为了安全意识。如果你定期运行安全顾问程序并让它检查已修改的文件,将看到关于我们修改的文件的警告信息。别担心,因为是我们修改的,所以这些文件并没有什么错。
你可以在应用程序属性中禁用此检查,但我强烈建议你不要。我的做法是:不禁用,忽略了这个警告。
温馨提示:修改系统文件属于危险操作,如果你没有足够把握,请不要随意修改。
6.附本机 Docker 内 nginx 配置(摘录如下)
# SSL 10 MB共享SSL会话缓存配置
ssl_session_tickets off;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 3600s;
# SSL cert, server {} can inherit this
ssl_certificate /etc/nginx/cert/fullchain.cer;
ssl_certificate_key /etc/nginx/cert/xxx.com.key;
#TLSv1.3(需要openSSL支持TLS1.3)
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers TLS13-AES-256-GCM-SHA384:TLS13-CHACHA20-POLY1305-SHA256:TLS13-AES-128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:!aNULL:!eNULL:!LOW:!ADH:!RC4:!3DES:!MD5:!EXP:!PSK:!SRP:!DSS;
ssl_prefer_server_ciphers on;
# 开启HSTS
add_header Strict-Transport-Security "max-age=15768000; includeSubdomains; preload" always;
upstream photostation {
server 192.168.12.5:8080;
}
upstream photostation_https {
server 192.168.12.5:4430;
}
upstream dsm {
server 192.168.12.5:5000;
}
upstream dsm_https {
server 192.168.12.5:5001;
}
server {
listen 80;
server_name "192.168.12.5" "xxx.com";
location / {
root html;
index index.html index.htm;
proxy_pass http://dsm;
client_max_body_size 3000m;
access_log off;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Host $http_host;
# : X-Frame-Options 防止点击劫持
add_header X-Frame-Options SAMEORIGIN;
}
location /photo {
root html;
index index.html index.htm;
proxy_pass http://photostation;
client_max_body_size 3000m;
access_log off;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Host $http_host;
add_header X-Frame-Options SAMEORIGIN;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
}
server {
listen 443 ssl;
server_name "192.168.12.5" "xxx.com";
## send https request back to DSM
location / {
root html;
index index.html index.htm;
proxy_pass https://dsm_https;
client_max_body_size 3000m;
#proxy_set_header Host $host;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header REMOTE-HOST $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
add_header X-Frame-Options SAMEORIGIN;
access_log off;
}
location /photo {
root html;
index index.html index.htm;
proxy_pass https://photostation_https;
client_max_body_size 3000m;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header REMOTE-HOST $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
add_header X-Frame-Options SAMEORIGIN;
access_log off;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
}
https 可以参考:群晖 NAS Let's Encrypt 泛域名证书自动更新
7.域名访问群晖导致虚拟机novnc
不正常的解决方法
解决方法来自dsm-reverse-proxy-websocket
首先自行备份/usr/syno/share/nginx/Portal.mustache
文件,然后编辑该文件并在location
字段内添加如下内容:
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
#proxy_http_version 1.1;
proxy_read_timeout 86400;
然后重启网络。
sudo synoservicecfg --restart nginx
(目前测试无效,用ip可以访问,等待后续再看吧。。)
这个问题理论上和Docker下查看终端机失败是一个问题。
7.1 DSM 6.2.1 及以上
从 DSM 6.2.1 开始,不再需要修改 Portal.mustache 文件,如果按照上面的方法,反而可能导致错误 。
操作步骤:
控制面板 --> 应用程序门户 --> 反向代理服务器
修改 https 的 启用 HTTP/2 + 增加自定义标题 新增下拉 WebSocket。
或者还可以使用三方Docker图形化管理工具Portainer的Exec Console功能。
参考
Freeing up port 80 on Synology DSM | Tony Lawrence
【NAS-群辉玩机之旅-nginx】使用反向代理实现多域名访问不同应用,规避IP+端口访问
评论区