使用 NGINX STEAM(TCP 代理) 反代 Emby 服务器

使用 NGINX STEAM(TCP 代理) 反代 Emby 服务器

由于云服务器「便宜、CN 路由好、配置高」的不可能三角,我一直都有给自建的 EMBY 服务器前面加一个「线路机」作为反向代理,以便让不会 7x24 在个人设备上开着某个七层代理的正常人能够流畅地访问我的 EMBY。

为什么是四层 TCP 代理/直通

常见的作用于七层的反向代理一般是通过类似 Nginx 或者 Caddy 之类的 Web 服务器实现,但是对于我上面的需求来说,这种方式需要两次的 TLS 的加解密,会带来不小的 overhead,特别是对于低配置的「线路机」来说。而「直通」,一句话解释就是在 L4 只做 TCP 转发,不碰 HTTPS 内容,效率高、消耗低,在后面 Nginx 的配置里能看到,我们只做了最小程度的介入即 ssl_preread

因为这种简单的反代场景并不需要对 HTTP 进行改写等等,也不需要考虑在运行代理的服务器上维护 TLS 证书的问题,反而让维护变得简单。唯一要注意的是,用 TCP 直通(TCP Passthrough)进行反代更适合自建的后端,因为Web 服务器、域名解析等可控,比如,可以配合 GeoDNS 分地区解析,仅在某些地区将域名解析为线路机 IP,其他解析为源站 IP,以此自建 poor man's CDN。

NGINX 的配置

当然也可以用 HAProxy,看起来很强大但我不会用。

检查 stream 模块是否可用:

Nginx 对 TCP 代理的支持由 Stream 模块提供,这个模块有可能没有被默认包括在你安装的 Nginx 版本中

nginx -V 2>&1 | grep – '--with-stream'

如果输出里有 --with-stream(或者显示 dynamic module: ngx_stream_module.so),说明已编译进来;否则请安装 nginx-full 或者从官方源安装带 stream 支持的包。

在主配置中加载 stream 配置

在 Debian 默认的 /etc/nginx/nginx.conf 中,stream {} 区块通常不被包含。你可以在 nginx.conf 的顶层(最外层)加上一行

# 在 http { … } 块之前或之后,都可以
include /etc/nginx/streams-enabled/*.conf;

创建 L4 代理配置

创建 /etc/nginx/streams-enabled/emby-stream.conf

stream {
    # 定义专用日志格式
    log_format proxy_stream  '$remote_addr [$time_local] '
                             'SNI=$ssl_preread_server_name '
                             'bytes_sent=$bytes_sent '
                             'bytes_received=$bytes_received '
                             'session_time=$session_time '
                             'upstream=$upstream_addr';

    # 打开日志文件缓存
    open_log_file_cache max=1000 inactive=20s valid=1m min_uses=2;

    # 可选:根据 SNI 决定上游后端
    map $ssl_preread_server_name $backend {
        yourdomain.com     emby_tls;
        #other.example.com other_tls;
        #default           upstream_default;
    }

    # 定义上游 Emby TLS 实例
    upstream emby_tls {
        server <你的EMBY服务器的IP>:443;
    }

    # TCP+TLS 直通的 server 块
    server {
        listen        443 reuseport;         # 接收客户端 TLS
        proxy_pass    emby_tls;              # 原样转发给后端

        ssl_preread   on;                    # 提取 SNI 但不终结 TLS

        access_log    /var/log/nginx/stream_emby.access.log  proxy_stream;
        error_log     /var/log/nginx/stream_emby.error.log   notice;
    }
}

重启 Nginx:

sudo nginx -t
sudo systemctl reload nginx

…et voilà