V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
kingpo
V2EX  ›  分享发现

一个骚操作, nginx 只监听 1 个非标端口就实现 http 重定向 https

  •  
  •   kingpo · 2023-05-24 20:16:34 +08:00 · 2802 次点击
    这是一个创建于 546 天前的主题,其中的信息可能已经有所发展或是发生改变。

    家宽封了 80,443 ,服务只能带端口访问
    设置 nginx 监听一个高位端口如 2222 指向 443 ,另一个 3333 指向 80
    因为非标准端口,没法同时监听 80 和 443
    访问带指向 443 的端口 2222 时,如果开了强制 ssl ,只能 https 访问,http 访问时就会报 400 错,因为 http 是 3333 端口监听的,这样在访问时输入就很麻烦了。

    怎么才能实现在一个端口访问,http 也能重定向呢,找了几天发现方法
    有点骚,但能实现 nginx 只监听 1 个非标端口 http 重定向 https
    本质是修改利用 497 错误码重定向指向 https 的地址

    客户端请求未使用 HTTPS 协议,但如果您已启用强制 SSL ,并且客户端尝试使用 HTTP 协议访问您的网站,则会收到 HTTP 497 错误。我们这时候就修改 497 的页面指向我们 https 即可

    server {
      listen      2222 ssl;
      server_name your.site.tld;
      ssl         on;
      ...
      error_page  497 https://$host:2222$request_uri;
      ...
    }
    
    22 条回复    2023-05-26 12:28:39 +08:00
    rekulas
        1
    rekulas  
       2023-05-24 22:45:51 +08:00
    我都没理解到你的回路,http 访问报错判断下端口 301 不就行了?是不是想复杂了
    eudemonwind
        2
    eudemonwind  
       2023-05-24 22:54:03 +08:00 via Android
    扫到你开了 http 直接封宽带
    kingpo
        3
    kingpo  
    OP
       2023-05-24 23:03:23 +08:00
    @rekulas #1 非标准端口你 xxx.com:2222 只能监听 443 ,这时候不能同时监听 80 ,你这时候 http://xxx.com:2222 访问是报 400 ,301 也还是报 400 ;你 80 是通过 3333 ,http://xxx.com:3333 这个才可以 301 过去 https//:xxx.com:2222 ,我默认要访问的是 xxx.com:2222 ,只记一个端口就行
    kingpo
        4
    kingpo  
    OP
       2023-05-24 23:04:29 +08:00
    @eudemonwind #2 没那么严重吧,翻了之前的帖子好多老哥都开端口访问的
    kingpo
        5
    kingpo  
    OP
       2023-05-24 23:07:54 +08:00
    这里实现的是 1 个端口直接访问 http 和 https 的效果,正常是要两个端口
    kingpo
        6
    kingpo  
    OP
       2023-05-24 23:08:10 +08:00
    @rekulas #1 这里实现的是 1 个端口直接访问 http 和 https 的效果,正常是要两个端口
    kaedeair
        7
    kaedeair  
       2023-05-24 23:26:48 +08:00
    这个用 traefik 写两条路由,一条 http ,一条 https ,用相同的 entrypoint ,然后在 http 的路由加个 RedirectSchema 的中间件很容易实现,不必死死抱着 nginx
    kingpo
        8
    kingpo  
    OP
       2023-05-24 23:36:09 +08:00
    @kaedeair #7 主要是为了方便申请 let's encrypt 证书
    kaedeair
        9
    kaedeair  
       2023-05-24 23:38:12 +08:00
    @kingpo #8 traefik 同样支持 acme 自动签发
    rekulas
        10
    rekulas  
       2023-05-24 23:45:34 +08:00
    @kingpo 明白你意思了 这种模式我之前也用过
    rekulas
        11
    rekulas  
       2023-05-24 23:47:14 +08:00   ❤️ 1
    @kingpo 其实可以把你的描述精简下,一句话:我是如何实现 http 协议访问 https 端口的时候自动重定向的 🤣
    olaloong
        12
    olaloong  
       2023-05-24 23:57:18 +08:00 via Android
    这样也行啊
    我是套了层 haproxy 来端口复用,rdp ssh http https 全在一个端口上
    ochatokori
        13
    ochatokori  
       2023-05-25 00:27:41 +08:00 via Android
    @rekulas #11 看完你这条我终于看懂楼主想说什么了
    Tink
        14
    Tink  
       2023-05-25 08:44:26 +08:00
    我理解,就算是没封 80 和 443 ,你 nginx 也得开两个端口呀,只监听 443 不是也不能跳吗
    chronos
        15
    chronos  
       2023-05-25 09:12:11 +08:00
    @Tink 他这个方案就是只开放一个 https 的端口,当 http 协议访问 https 的端口进来时返回 497 状态码告诉浏览器要使用 https 协议来访问。
    netnr
        16
    netnr  
       2023-05-25 09:13:11 +08:00 via Android
    我来当大聪明,以下都能正常访问
    http://your.site.tld:2222
    https://your.site.tld:2222

    https 的 2222 端口是正常配置的,http 也能访问是因为触发了 https 的 497 错误码,利用这个错误码来访问

    以上有个前提是 80 443 都被封了
    Imr
        17
    Imr  
       2023-05-25 09:20:36 +08:00   ❤️ 1
    nginx 自己就有更优雅的解决办法
    用 stream 里的 ssl_preread ,类似下面配置(没有验证)

    stream {
    upstream http {
    server 127.0.0.1:80;
    }
    upstream https {
    server 127.0.0.1:443;
    }

    map $ssl_preread_protocol $upstream {
    default https;
    "" http;
    }

    server {
    listen 2222;
    listen [::]:2222;
    proxy_pass $upstream;
    ssl_preread on;
    }
    }
    kingpo
        18
    kingpo  
    OP
       2023-05-25 09:21:58 +08:00
    @netnr #16 对的
    yunyuyuan
        19
    yunyuyuan  
       2023-05-25 10:16:26 +08:00
    贴一下我的,直连 /zerotier 通用配置,好处是打洞的服务也可以用域名+https 访问:
    server {
    listen [::]:5678 ssl;
    listen 443 ssl;
    listen 80;
    server_name foo.example.com foo-zto.example.com;
    error_page 497 https://$host:5678$request_uri;

    set $whole_url "$scheme://$host";
    if ($whole_url ~ ^http://[^.]*-zto) {
    return https://$host$request_uri;
    }
    }

    * 直连用 http://foo.example.com:5678 ,重定向到 https://foo.example.com:5678

    * zerotier 用 http://foo-zto.example.com ,可以重定向到 https://foo-zto.example.comfoo-zto.example.com 解析地址是 vpn 组网的地址
    caiqichang
        20
    caiqichang  
       2023-05-25 11:04:49 +08:00
    说的好,我选择用 stream_ssl_preread 模块,同 #17
    kkocdko
        21
    kkocdko  
       2023-05-25 23:23:42 +08:00
    我理解你的意思。HTTP 到 HTTPS 重定向这个问题我研究过,并没有标准答案。

    最常见的方法是 80 端口和 443 端口同时监听,重定向用 302 或者 497 都有,对于监听单个非默认端口,Nginx 有使用 stream_ssl_preread 的,楼主的方法我也看到别人用过,并不是首创。

    顺便讲一些更深入一点的东西。要区分 HTTP 和 HTTPS ,观察 TCP 连接进来的第一个字节即可。如果第一个字节是 0x16 那就说明对方希望进行 TLS 握手,是 HTTPS ,正常服务。如果不是,那就要当作 HTTP 来解析,并进行重定向。

    参考实现如下。这里我还使用了一个偷懒技巧,不解析 HTTP 直接给客户端灌一段 JS 来实现重定向。

    https://github.com/kkocdko/ksite/blob/76f8f15b02412fc1bf765517518dd10f8c44fbba/src/tls.rs#L109
    kingpo
        22
    kingpo  
    OP
       2023-05-26 12:28:39 +08:00
    @kkocdko #21 学到了,不过我这个也不是我首创,也是网上找到的一种方案,当然他是不够优雅的,但是对于个人的低频使用场景来说它足够简单和能满足需求已经足够
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2801 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 29ms · UTC 15:29 · PVG 23:29 · LAX 07:29 · JFK 10:29
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.