使用 Nginx Stream 模块来实现 sniproxy

发布于 2024-04-09
之前写过一篇使用 sniproxy 这个软件来实现免证书代理 https 网站用来做到对某些资源加速访问的作用,不过现在这个项目已经不更新了,不过好在 nginx 通过 stream 模块也能支持同样的功能,这里来介绍下使用方式。 nginx 启用相应模块

之前写过一篇使用 sniproxy 这个软件来实现免证书代理 https 网站用来做到对某些资源加速访问的作用,不过现在这个项目已经不更新了,不过好在 nginx 通过 stream 模块也能支持同样的功能,这里来介绍下使用方式。

nginx 启用相应模块

我这里的 nginx 使用 docker 方式来部署,其他方式部署的也类似,这里对 nginx 有些要求,需要编译时包含 –with-stream –with-stream_ssl_preread_module –with-stream_ssl_module,可以使用 nginx -V 命令来查看编译时是否包含,我这里使用 nginx:alpine 镜像,已经默认包含了这些模块。

部署 nginx (docker)

docker

docker run -d --name nginx-sniproxy -p 443:443 --restart always -v $(pwd)/nginx.conf:/etc/nginx/nginx.conf nginx:alpine

docker-compose

version: '3.7'

services:
  nginx-sniproxy:
    image: nginx:alpine
    container_name: nginx-sniproxy
    ports:
      - "443:443"
    restart: always
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf

还需要在当前目录放一个 nginx.conf,这个配置下文会介绍。

配置文件

代理所有域名

worker_processes 1;

events {
    worker_connections 1024;
}

stream {
    server {
        listen 443;
        resolver 1.1.1.1;
        ssl_preread on;
        proxy_pass $ssl_preread_server_name:443;
    }
}

这个配置表示允许代理所有域名,也允许任何 IP 访问,这是不安全的,可能被别人白嫖你的服务。

代理指定域名

worker_processes 1;

events {
    worker_connections 1024;
}

stream {
    map $ssl_preread_server_name $filtered_sni_name {
        ~^(.*fast\.com)$ $ssl_preread_server_name;
        default "127.255.255.255";  
    }

    server {
        resolver 1.1.1.1;
        listen 443;
        proxy_pass $filtered_sni_name:443;  # 由于这里上面没有指定端口,这里要指定为 443
        ssl_preread on;
    }
}

这里增加了一组 map,根据规则过滤 $ssl_preread_server_name(也就是请求的 SNI),上面的配置表示允许 fast.com 这个域名使用,然后指向 $ssl_preread_server_name,如果没有匹配到任何规则,则默认为一个本地回环地址,用于访问了未被允许的域名时,返回空。

如果希望和 https 服务共存,可以让 https 使用一个其他端口,如 1443,然后 defualt 改为 127.0.0.1:1443。

限制只允许指定 ip 使用

还可以限制指定 ip 使用该服务,在 server 代码块中增加:

server {
    resolver 1.1.1.1;
    listen 443;
    proxy_pass $filtered_sni_name:443;  # 由于这里上面没有指定端口,这里要指定为 443
    ssl_preread on;

    deny   192.168.1.2;
    allow  192.168.1.1/24;
    deny   all;
}

规则从上到下执行,如果有 deny 匹配,则后面所有的 allow 都将无效,上面这个配置的意思是只允许 192.168.1.1/24 这个网段访问,但不包含 192.168.1.2 这个 ip。

扩展-负载均衡

worker_processes 1;

events {
    worker_connections 1024;
}

stream {
    map $ssl_preread_server_name $filtered_sni_name {
        fast.com backend_example;
    }

    upstream backend_example {
        server fast1.com:443;
        server fast2.com:443;
    }

    server {
        resolver 1.1.1.1;
        listen 443;
        proxy_pass $filtered_sni_name;
        ssl_preread on;
    }
}

map 的 value 中不止可以写根据 $ssl_preread_server_name 获取到的 SNI,还可以指定一个 upstream,里面可以写多个地址,进行负载均衡。

还可以指定负载均衡策略:

  1. 最少连接负载均衡
upstream myapp1 {
    least_conn;
    server srv1.example.com;
    server srv2.example.com;
    server srv3.example.com;
}
  1. 会话持久化(hash)
upstream myapp1 {
    ip_hash;
    server srv1.example.com;
    server srv2.example.com;
    server srv3.example.com;
}
  1. 指定权重
upstream myapp1 {
    server srv1.example.com weight=3;
    server srv2.example.com;
    server srv3.example.com;
}