nginx折腾

相同nginx配置在不同服务器主机上,访问结果却不相同。

问题出现

使用nginx反代理wiki.js应用的示例如下:

#参考示例
server {
    listen 80;
    listen [::]:80;
    listen 443 ssl http2;
    listen [::]:443 ssl http2;

    ssl on;
    ssl_certificate /etc/ssl/certs/cert.pem;
    ssl_certificate_key /etc/ssl/private/key.pem;

    server_name your-domain.com;
    location / {
        proxy_pass http://localhost:6830;
        proxy_set_header Host $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;
    }
}

在自己服务器上配置wiki.js的反代理如下,可以正常访问(在腾讯云中申请的免费SSL证书)

#配置1
server {
    listen  80;
    server_name  x.x.x;
    rewrite ^(.*) https://$host$1 permanent;
}

server {
    listen 443 ssl;
    server_name x.x.x;
    index index.php index.html index.htm; 
    client_max_body_size 8192M;
    client_body_buffer_size 128k;
    ssl_certificate /etc/nginx/conf.d/ssl/x.x.x_bundle.crt;
    ssl_certificate_key /etc/nginx/conf.d/ssl/x.x.x.key;
    ssl_session_timeout 10m;
    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
    ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:HIGH:!aNULL:!MD5:!RC4:!DHE;
    ssl_prefer_server_ciphers on;

    location / {
        proxy_pass  http://192.168.x.x:xxx;
        proxy_redirect     off;
        proxy_set_header   Host $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_set_header   X-Forwarded-Host $server_name;
        proxy_set_header   X-Forwarded-Proto https;
        proxy_set_header   Upgrade $http_upgrade;
        proxy_set_header   Connection "upgrade";
        proxy_read_timeout 86400;
    }
}

而在另一台服务器上按如上配置,却无法访问,SSL是通过mkcert生成的证书,该证书供局域网测试使用,均有修改为对应的域名、证书名称、服务器IP和端口号。

访问异常的表现:使用x.x.x访问时,打开的页面却是部署的另一个web应用页面,页面URL的“scheme://host:port/”部分正确,但是后续的“path?query#fragment”却错误,不是目标页面的内容。多次检测nginx配置、刷新网页、重启nginx均是这样的异常。(系统、nginx版本、代理的应用及版本均相同)

然后nginx反代wiki.js的配置修改为如下,则访问正常。

#配置2
server {
    listen 80;
    server_name y.y.y;
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl;
    server_name y.y.y;
    index index.php index.html index.htm;
    client_max_body_size 8192M;
    client_body_buffer_size 128k;
    ssl_certificate /etc/nginx/conf.d/ssl/y.y.y.pem;
    ssl_certificate_key /etc/nginx/conf.d/ssl/y.y.y.pem;
    ssl_session_timeout 5m;
    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
    ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:HIGH:!aNULL:!MD5:!RC4:!DHE;
    ssl_prefer_server_ciphers on;

    location / {
       proxy_pass  http://192.168.x.x:xxx; # 转发规则
       proxy_read_timeout 300;
       proxy_set_header Upgrade $http_upgrade;
       proxy_set_header Connection "upgrade";
       proxy_set_header Host $proxy_host;
       proxy_set_header X-Real-IP $remote_addr;
       proxy_set_header X-Real-PORT $proxy_port;
    }
}

(目前还未细致研究nginx的配置文件)

问题再现

后续又使用docker部署了一个应用(参考上述“#配置2“的配置修改),同样使用mksert生成证书,然后出现了和上面一样的情况。

即访问https://A.com时引用了A.com的SSL证书。

通过查找网络资料,找到一篇类似的贴子,但问题没有得到处理,帖子链接:[点击访问]

  1. 是否使用mkcert自颁发的SSL证书才会存在这种情况?(另一台服务器使用在云平台申请的免费证书,从未出现过这种异常)
  2. 查阅帖子[点击访问],提及到的“TLS SNI support enabled”(一个IP地址运行多个HTTPS服务器),检查发现该配置是支持的。
  3. 试着在新应用上使用上面的”#配置1“,重新测试,结果浏览器不再提示证书的问题,而是提醒”重定向的次数过多ERR_TOO_MANY_REDIRECTS“ 。
  4. 感觉还是nginx反代理配置存在问题。

得到解决

背景:应该部署另一个应用服务对我管理事项很有必要,所以重新对ssl、nginx处理下,现在所有应用都能正常访问了。

解决:具体是如何解决的,目前也没有一个统一结论,只能将操作和自己的一些思路贴在下面,以便参考。

nginx err

检查了nging的log,有个警告提示“proxy_headers_hash_max_size”。按照 链接 提供的方法处理该警告。

由于使用docker容器部署nginx服务,所以需要将“/etc/nginx/nginx.conf”文件拷贝到主机上修改后重新拷贝回容器(目前这里一配置没有做持久化操作)。在http区添加如下代码

proxy_headers_hash_max_size 51200;
   proxy_headers_hash_bucket_size 6400;

重新启动容器。

这个警告应该不是造成访问异常得原因,后续可以重新创建nginx容器测试验证。

mkcert ssl

由于部署得应用服务只在局域网环境下使用,所有使用mkcert工具生成ssl证书。前期创建的详细命令已经没有记录了,但都是为每个子域名单独创建了一个域名证书(创建时还添加服务器IP、localhost、127.0.0.1、::1等等)

这次折腾将之前创建的证书全删了,使用泛域名证书替换所有证书。创建命令如下:

mkcert-v1.4.4-windows-amd64.exe *.xxx.xxx xxx.xxx

使用泛域名,同时支持主域名,使用方便。之前就一直怀疑证书问题。(虽然更新后,重测访问依旧异常)

清理

为了方便检查测试是否存在异常,经常手动清理docker nginx的log文件。同时也考虑了nginx的缓存,由于缓存在容器内部,就没有进入清理(每次进入容器清理数据有点麻烦)

浏览器缓存清理,为了方便,测试使用chrome浏览器,随用随清,而平时则使用edge。(浏览器缓存清理很重要!!!)

重启ubuntu系统,有时侯确实能发现重启前后访问结果不一样的情况。

nginx的配置

1、default.config按照默认配置

2、域名跳转

server {
    listen 80;
    server_name xxx.xxx;
    if ($host = 'xxx.xxx'){
        return 301 https://www.xxx.xxx$request_uri;
    }
}

server {
    listen 80;
    server_name www.xxx.xxx;
    if ($host = 'www.xxx.xxx'){
        return 301 https://www.xxx.xxx$request_uri;
    }
}

server {
    listen 443 ssl;
    server_name xxx.xxx;
    ssl_certificate /etc/nginx/conf.d/ssl/xxx.xxx.xxx.pem;
    ssl_certificate_key /etc/nginx/conf.d/ssl/xxx.xxx.xxx_key.pem;
    if ($host = 'xxx.xxx'){
        return 301 https://www.xxx.xxx$request_uri;
    }
}

server {
    listen 443 ssl;
    server_name www.xxx.xxx;
    ssl_certificate /etc/nginx/conf.d/ssl/xxx.xxx.xxx.pem;
    ssl_certificate_key /etc/nginx/conf.d/ssl/xxx.xxx.xxx_key.pem;

    location / {
        root   /usr/share/nginx/html;
        index  index.html index.htm;
    }
}

3、反代理gitea配置

server {
    listen 80;
    server_name git.xxx.xxx;
    if ($host = 'git.xxx.xxx'){
        return 301 https://git.xxx.xxx$request_uri;
    }
}

server {
    listen 443 ssl;
    server_name git.xxx.xxx;

    index  index.html index.htm;

    client_max_body_size 8192M;
    client_body_buffer_size 128k;

    ssl_certificate /etc/nginx/conf.d/ssl/xxx.xxx.xxx.pem;
    ssl_certificate_key /etc/nginx/conf.d/ssl/xxx.xxx.xxx_key.pem;
    ssl_session_timeout 5m;
    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
    ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:HIGH:!aNULL:!MD5:!RC4:!DHE;
    ssl_prefer_server_ciphers on;

    location / {
        proxy_pass  http://xxx.xxx.xxx.xxx:xxx;
        proxy_set_header Host $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;
    }
}

4、反代理wiki.js

(这次又重新按照“问题出现”中的配置1写回去了)

server {
    listen  80;
    server_name  wiki.xxx.xxx;
    rewrite ^(.*) https://$host$1 permanent;
}

server {
    listen 443 ssl;
    server_name wiki.xxx.xxx;

    index index.php index.html index.htm;

    client_max_body_size 8192M;
    client_body_buffer_size 128k;

    ssl_certificate /etc/nginx/conf.d/ssl/xxx.xxx.xxx.pem;
    ssl_certificate_key /etc/nginx/conf.d/ssl/xxx.xxx.xxx_key.pem;
    ssl_session_timeout 10m;
    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
    ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:HIGH:!aNULL:!MD5:!RC4:!DHE;
    ssl_prefer_server_ciphers on;

    location / {
        proxy_pass  http://xxx.xxx.xxx.xxx:xxx;
        proxy_redirect     off;
        proxy_set_header   Host $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_set_header   X-Forwarded-Host $server_name;
        proxy_set_header   X-Forwarded-Proto https;
        proxy_set_header   Upgrade $http_upgrade;
        proxy_set_header   Connection "upgrade";
        proxy_read_timeout 86400;
    }
}

在上述一系列操作了(每次更改配置测试前都需要重启nginx容器,同时检查logs),所有域名都能正常访问了,不会出现访问A时,跳到B的异常情况。

keeweb代理

keeweb是一个密码管理工具,提供80和443访问端口,开始使用docker部署时按照其他应用部署的方式,将主机未使用的端口映射到容器的80端口,然后通过反代理访问服务。(一直无法访问,SSL安全异常)

(进入keeweb容器,可以发现里面还有一个nginx。)

考虑到其已经提供了443端口,那么我就可以直接通过容器内部的443端口访问,而不是去访问内部的80端口。

重新创建容器命令:

$ docker run \
-p 192.xxx.xxx.xxx:8006:443 \
-p 192.xxx.xxx.xxx:8007:80 \
-v /.../Shanghai:/etc/localtime \
-v /xxx/keeweb/pwd:/etc/nginx/external \
--name keeweb \
--restart=always \
-d antelle/keeweb

开始创建的时候由于习惯用80端口,所以并没有加上 “-p 192.xxx.xxx.xxx:8006:443 ”这一行。

同理创建容器后,nginx反代理,将http更改为https即可,配置如下:

server {
    listen 80;
    server_name xxx.xxx.xxx;
    if ($host = 'xxx.xxx.xxx'){
        return 301 https://xxx.xxx.xxx$request_uri;
    }
}

server {
    listen 443 ssl;
    server_name xxx.xxx.xxx;

    index index.php index.html index.htm;

    client_max_body_size 8192M;
    client_body_buffer_size 128k;

    ssl_certificate /etc/nginx/conf.d/ssl/xxx.xxx.xxx.pem;
    ssl_certificate_key /etc/nginx/conf.d/ssl/xxx.xxx.xxx_key.pem;
    ssl_session_timeout 5m;
    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
    ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:HIGH:!aNULL:!MD5:!RC4:!DHE;
    ssl_prefer_server_ciphers on;

    location / {
       proxy_pass  https://192.xxx.xxx.xxx:8006; # 转发规则
       proxy_set_header Host $host;
       proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
}

测试,访问已经正常。

相关学习

nginx的学习链接:链接 (可以对nginx有一个基本的了解)


评论

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注