前言

一般家宽不给开443端口(ipv4和ipv6检测到就封),所以我用8443替代https的默认443端口来反代。 但是方案要考虑同时支持ipv4和ipv6。 本文主要利用OpenResty将lxc容器 8443端口的ipv6流量 转发给443端口来实现ipv6同端口访问。

环境介绍

系统:pve 路由:pve-ikuai虚拟机 网关:pve-lxc容器安装的1panel 域名证书:所有要发布到公网的web服务都是用的同一个通配符证书(比如:*.a.com),由1panel申请、续期。 dns管理:cloudflare

前置条件,已经配置了ikuai的ipv6,内网设备能拿到公网ipv6地址(这一步网上有大把教程)

1. 双栈域名设置

  1. 使用ikuai的ddns(动态域名),将wan口的公网ip绑定到域名,比如a.com(A记录)
  2. 使用ikuai的ddns(动态域名),将lxc的容器ipv6绑定到域名(通过终端DUID配置),比如a.com(AAAA记录)

注意,ipv4和ipv6用的同一个域名。此时nslookup a.com能同时获取到ipv4和ipv6

2. ipv4设置

  1. ikuai做端口转发,将8443转发到lxc容器的443端口(同时转发8444作为备用)。这个配置只对ipv4有效

实现ipv4外网访问,只需此步骤即可。

3. ipv6设置

  1. 【重点】将lxc容器 8443端口的ipv6流量 转发给443端口 利用 1Panel 预置的 OpenResty(网站服务组件,是docker 容器)作为本地透明代理,将 8443端口的IPv6加密流量解密后转发给443端口(后续就和普通网页分发流程一样了):
  • 找到OpenResty容器内的证书位置,因为都是用的同一个通配符证书,所以随便记录一个如/www/sites/test1.a.com/ssl/fullchain.pem/www/sites/test1.a.com/ssl/privkey.pem

  • OpenResty容器内添加转发配置文件,/usr/local/openresty/nginx/conf/nginx.conf/v6_bridge.conf:

      server {
          # 监听 IPv6 的 8443,不需要写死域名,支持所有域名
          listen [::]:8443 ssl;
          server_name _; 
     
          # 这里建议使用一个通用的证书,或者依然保留你现有的证书
          ssl_certificate      /www/sites/pve.966668.xyz/ssl/fullchain.pem;
          ssl_certificate_key  /www/sites/pve.966668.xyz/ssl/privkey.pem;
     
          ssl_protocols TLSv1.2 TLSv1.3;
     
          location / {
              # 1. 动态传递主机名,这样后端 443 就能识别是哪个网站
              proxy_set_header Host $http_host;
     
              # 2. 核心修正:透传 SNI 域名,解决 SSL internal error
              proxy_ssl_server_name on;
              proxy_ssl_name $http_host;
     
              # 3. 忽略后端证书校验(防止因为 127.0.0.1 不匹配证书名而报错)
              proxy_ssl_verify off;
     
              # 4. 转发给本地 443
              proxy_pass https://127.0.0.1:443;
     
              # 保持 WebSocket 和其他 Header 支持
              proxy_http_version 1.1;
              proxy_set_header Upgrade $http_upgrade;
              proxy_set_header Connection "upgrade";
              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 Host $http_host;:你浏览器里输入的是什么域名,它就原样传给后端 443。

proxy_ssl_name $http_host;:这是最关键的一行。它解决了你刚才 curl 看到的 SSL routines::tlsv1 alert internal error。它告诉后端 443 模块:“我要找的是这个域名的 SSL 证书”。

proxy_ssl_verify off;:因为你是转发到 127.0.0.1,证书里的域名通常是你的公网域名,这会导致校验失败,关掉校验可以确保 100% 转发成功。

  • OpenResty容器内执行openresty -t && openresty -s reload

拓扑图

 互联网 (Internet)8443端口进 】
                          /          \
                  (IPv4) /            \ (IPv6)
                        /              \
        +-----------------------+      +-----------------------+
 
        |   iKuai / PVE 路由层   |      |   IPv6 直通 (Pass-thru) |
        |  (NAT 映射: 8443->443) |      |   (目标: LXC 容器 IPv6)  |
        +-----------+-----------+      +-----------+-----------+
 
                    |                              |
                    | (IPv4:443)                   | (IPv6:8443)
                    |                              |
    ________________|______________________________|_________________
   |                v                              v                 |
   |      +--------------------+        +-----------------------+    |
   |      |   1Panel 面板程序   | <----- |   OpenResty (Nginx)   |    |
   |      | (监听 0.0.0.0:443)  |  proxy  |    (监听 [::]:8443)    |    |
   |      +---------+----------+         +-----------+-----------+    |
   |                |                                                |
   |                |                                                |
   |       [ 最终处理与响应 ]                                         |
   |                                                                 |
   | LXC 容器内部环境                         |
   |_________________________________________________________________|
 
 

4. 测试

  1. 把ikuai转发的8443端口关闭(开启8444作为后备,防止在外失联)
  2. 使用蜂窝网络手机访问test1.a.com:8443,能打开页面即成功(通过ipv6直接指向了lxc的8443端口)
  3. 有ipv6的电脑测试:curl -6 -v https://test1.a.com:8443

其他特殊nginx配置(已过时,仅参考)

transmission:端口区分

transmission的问题是静态资源加载不出来。

解决办法:除了默认的配置,还需要添加custom location:

location ^~ /transmission {
    proxy_pass http://192.168.100.5:9091;
}

按上述改了之后,就可以用https://群晖域名:8443/transmission访问transmission套件了。 参考

至于为啥非得加一个custom location,而且内容和界面上配置的是一样的,那我就不知道了。

当然,你如果不想重用群晖的域名,那直接用一个新域名配置反代即可:

这样不会出现静态js、css加载不出的问题

SurveillanceStation:路径区分

群晖的摄像机管理套件SurveillanceStation,是以路径挂载在群晖域名下区分管理的。

SurveillanceStation内置服务在接收到界面请求的时候会有一个重定向的动作,而这个重定向链接端口为访问来源的端口。

这时候你要问了,这不是很正常吗?对,这是正确的流程。

但是npm并不知道外部真正的访问端口是8443(路由器端口转发的),所以SurveillanceStation也不知道真正的访问端口。npm告诉SurveillanceStation它的默认端口是443,所以SurveillanceStation重定向的地址也是443端口:

xxx:8443 -> npm -> xxx:443 -> SurveillanceStation -> redirectxxx:443/webman

所以解决这个问题,我们除了添加套件服务的路径,还得手动改写请求头里的host端口:

location ^~ /webman {
  proxy_pass http://192.168.100.5:5000; // 这里保持群晖的ip
  proxy_set_header Host $host:8443; #这里是重点,这样配置才不会丢失端口
}

按上述改了之后,就可以用https://群晖域名:8443/webman访问SurveillanceStation套件了。

ttyd:websocket协议升级

直接反代ttyd会报错,因为ws协议没有升级,location也不能直接写ttyd,这里给出解决方法

location /ttyd {
    proxy_http_version 1.1;
    proxy_set_header Host $host;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
    proxy_read_timeout 1d; # dont kill connection after 60s of inactivity
    proxy_pass http://127.0.0.1:7681/;
}

按上述改了之后,就可以用https://群晖域名:8443/ttyd访问ttyd套件了。

alist:修改服务配置

群晖部署的alist套件,默认是5244端口,站点默认路径是根目录。请求的静态资源路径如:https://a.b.c:8443/alist/assets/index.f101cd32.js。所以一般是单独使用一个域名去代理。

如果想要代理到群晖的路径下,那么需要改alist的配置:如何对子目录进行反向代理,链接里的配置仅供参考,npm里不会生效,要采用transmission一样的方式

  • 改根路径为:alist,进入群晖控制台,以root身份修改alist里的config.json

/volume1/@appdata/alist3# vim config.json:

 
{
  "force": false,
  "site_url": "/alist",
  ...
}
  • 在群晖套件中心重启alist
  • 配置npm

location ^~ /alist {
    proxy_pass http://192.168.100.5:5244;    #需要代理的服务器内网访问方式
}

按上述改了之后,就可以用https://群晖域名:8443/alist访问alist套件了。