群晖 NAS 之 Docker Nginx 反向代理

近期给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.mustacheDSM.mustacheWWWService.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服务器。

至此,我们已经成功释放了 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  

注意Docker容器的默认时区和主机不一致,还需要映射下时区文件 docker/etc/localtime --> /etc/localtime,需要先执行命令:cp /etc/localtime /volume1/docker/etc/

5.还有一件很重要的事

这只是为了安全意识。如果你定期运行安全顾问程序并让它检查已修改的文件,将看到关于我们修改的文件的警告信息。别担心,因为是我们修改的,所以这些文件并没有什么错。

你可以在应用程序属性中禁用此检查,但我强烈建议你不要。我的做法是:不禁用,忽略了这个警告。

温馨提示:修改系统文件属于危险操作,如果你没有足够把握,请不要随意修改。

6.附本机 Docker 内 nginx 配置

upstream photostation {  
    server xxx:8080;
}
upstream photostation_https {  
    server xxx:4430;
}

upstream dsm {  
    server xxx:5000;
}
upstream dsm_https {  
    server xxx:5001;
}

server {  
    listen       80;
    server_name  "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  "xxx.com";

    ## send https request back to DSM
    ssl_certificate        /etc/nginx/cert/xxx.pem;
    ssl_certificate_key    /etc/nginx/cert/xxx.key;

    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;
    }
}

7.域名访问群晖导致虚拟机novnc不正常的解决方法

解决方法来自https://github.com/orobardet/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可以访问,等待后续再看吧。。)

参考

Freeing up port 80 on Synology DSM | Tony Lawrence
【NAS-群辉玩机之旅-nginx】使用反向代理实现多域名访问不同应用,规避IP+端口访问