nginx 的 allow/deny 指令在 stream 上下文需配合 ngx_stream_access_module 使用,必须置于 stream > server 块内,支持 cidr 和 ipv6,不支持 http 模块的用法;未写 deny all 时默认放行,推荐优先使用原生 allow/deny 而非 geo+if。

Nginx 的 allow/deny 指令不能用于 TCP 代理层(stream 上下文),因为它们属于 ngx_http_access_module,仅在 HTTP 模块的 http、server、location 块中有效。而 TCP/UDP 代理由 ngx_stream_core_module 处理,运行在更底层的 stream 上下文,不加载 HTTP 访问控制模块。
所以,直接在 stream { } 块里写 allow 192.168.1.10; deny all; 会报错:
unknown directive "allow"或directive is not allowed here
✅ 正确实现 TCP 层 IP 黑白名单的方法
使用 stream 模块内置的 access 指令(推荐)
Nginx 1.19.0+ 原生支持 stream 上下文的访问控制,通过 stream_access_module(默认编译进主干版本),使用 allow 和 deny —— 但注意:这是另一个同名不同模块的指令,语法相同,作用域不同。
你需要在 stream { } 块中配置,且规则必须放在 server { } 内部(不是 stream 顶层):
stream {
# 全局日志等可选配置
server {
listen 3306;
proxy_pass backend_db;
# ✅ TCP 层黑白名单(生效!)
allow 10.0.0.0/8;
allow 172.16.0.0/12;
allow 192.168.0.0/16;
deny all;
}
upstream backend_db {
server 192.168.5.10:3306;
}
}
⚠️ 关键点:
-
allow/deny必须写在server { }块内,不能写在stream { }顶层或upstream中; - 规则按顺序匹配,第一条匹配即生效,后续不再检查;
- 支持 IPv4 CIDR(如
192.168.1.0/24)和 IPv6(如2001:db8::/32); - 若未显式写
deny all,默认放行所有未被deny拦截的连接(即隐含allow all);
配合 geo 模块实现更灵活的 TCP 层控制(高级用法)
当黑白名单条目较多或需动态管理时,可用 geo 定义变量,再在 server 中用 if 判断(注意:stream 中 if 仅支持有限条件,且 return 不可用,只能配合 proxy_pass 路由或 close):
stream {
# 定义可信客户端范围
geo $trusted {
default 0;
192.168.1.0/24 1;
2001:db8::/32 1;
123.45.67.89 1;
}
server {
listen 6379;
# 仅放行 $trusted == 1 的连接
if ($trusted = 0) {
# 注意:stream 中 if 不支持 return,但可指向空 upstream 或直接 close
# 推荐做法:用 proxy_pass 指向一个不存在的 server 触发拒绝(不优雅)
# 更稳妥方式:依赖前面的 allow/deny,geo 仅作辅助标记
}
proxy_pass redis_backend;
}
upstream redis_backend {
server 192.168.5.20:6379;
}
}
? 实际生产中,优先使用原生 allow/deny(stream access 模块),geo + if 在 stream 中能力受限,易出错,不推荐作为主控手段。
⚠️ 常见误区与注意事项
-
不要在
http { }块里配 TCP 黑白名单:http块只管 HTTP 流量,对stream无效; -
不要混淆 HTTP 和 stream 的
allow/deny:两者同名,但所属模块、生效位置、依赖条件完全不同; -
真实客户端 IP 可能被代理掩盖:TCP 层无
X-Forwarded-For,若前端有 L4 负载均衡(如 AWS NLB、HAProxy TCP mode),需确保它透传原始源 IP(启用 PROXY protocol); - 启用 PROXY protocol 示例:
server { listen 8000 so_keepalive=on; proxy_protocol on; # 启用接收 PROXY 协议头 allow 10.0.0.0/8; deny all; proxy_pass backend; }对应上游也需支持 PROXY protocol 解析,否则
$remote_addr仍是代理 IP。
✅ 验证与调试步骤
-
检查 Nginx 是否启用 stream 模块:
nginx -V 2>&1 | grep -o with-stream
-
修改
stream { }配置后,先测试语法:nginx -t -c /etc/nginx/nginx.conf
-
重载配置:
nginx -s reload
从允许 IP 和禁止 IP 分别尝试连接(如
telnet ip port或nc),观察是否被拒绝(连接立即关闭或超时);-
查看错误日志定位问题:
tail -f /var/log/nginx/error.log
Nginx TCP 层访问控制不复杂,但容易因模块混淆或作用域错放导致失效。抓住「必须在 stream > server 内写 allow/deny」这一核心,就能稳定落地。










