真实客户端 IP

CF-Connecting-IP 头本来就送到了 nginx(它就是真实客户端 IP)。要把它变成 $remote_addr, 靠的是 nginx 自带的 ngx_http_realip_module(Debian 官方 nginx 与本模块的构建都编了它):

nginx
http {
    cfd_tunnel_token                eyJhIjoi...;
    cfd_tunnel_inject_source_addr   192.0.2.1;      # 0.13.0

    server {
        listen 127.0.0.1:8080;
        set_real_ip_from 192.0.2.1;
        real_ip_header   CF-Connecting-IP;
        # 业务照旧
    }
}

为什么值得配这一条 ​

ngx_http_realip_module 判断「信不信这条连接带来的头」的唯一依据是 ngx_cidr_match(c->sockaddr, set_real_ip_from 的集合) —— 它只看连接的对端地址,不管端口, 也不管这个地址在本机存不存在。而虚拟连接的对端地址正是本模块填的:

cfd_tunnel_inject_source_addr注入连接的 $remote_addr(realip 之前)set_real_ip_from 必须写谁能伪造 CF-Connecting-IP
不写(默认)127.0.0.1127.0.0.1(= 整个回环)云端访客不能;本机任意能连到注入端口的进程都能
192.0.2.1192.0.2.1192.0.2.1(只这一个地址)云端访客不能(RFC 5737 地址公网路由不到);本机非特权进程也不能(bind() 不属于本机的源地址会 EADDRNOTAVAIL)

不配 realip 时,注入请求的 $remote_addr 就是那个假地址本身 —— 日志里所有隧道请求都来自 同一个地址,这是能一眼看出来的地方。

要点与坑 ​

  • 默认不配 = 与 cloudflared 对齐。 真 cloudflared 直面本机源站时,源站看到的就是 127.0.0.1,所以默认值刻意保持它(老配置的 set_real_ip_from 127.0.0.1; 照旧管用)。
  • 配了只影响对端地址。 c->local_sockaddr 没动 —— $server_addr / $server_port / ngx_connection_local_sockaddr() 仍是真实监听地址,listen 与 server_name 匹配、 静态文件、proxy_pass 全都不受影响。
  • 先检查按 127.0.0.1 写的规则。 allow / deny、limit_req_zone / limit_conn_zone 用 $binary_remote_addr 做键的,配了这条之后都不再命中隧道流量。要么按新地址再写一份, 要么别配这条。
  • $remote_port 恒为空串。 虚拟连接没有真实端口(端口恒为 0),realip 采纳头部之后也一样空 —— 头部里没有端口信息。
  • 只收单个地址。 不写网段(网段是 set_real_ip_from 那边的事)、不写端口;IPv4 / IPv6 都行 (IPv6 例如 RFC 3849 的 2001:db8::1)。写错(网段 / 带端口 / 0.0.0.0 / :: / 拼错 / 同一处写两遍)都是 nginx -t 直接报错,不会静默退回默认值。
  • 只挡得住非特权进程。 本机 root / 带 CAP_NET_ADMIN 的进程仍可用 IP_FREEBIND / IP_TRANSPARENT 从任意源地址连进来伪造。这条指令收窄的是「本机任何用户都能冒充实访客 IP」, 不是「本机已被攻破也安全」。
  • 块内可以一条隧道一个地址(多条隧道指向不同源站、各自 set_real_ip_from 时用得上):
nginx
cfd_tunnel { name a; token_file /etc/nginx/cloudflared/a.token; inject_listen 127.0.0.1:8080; inject_source_addr 192.0.2.1; }
cfd_tunnel { name b; token_file /etc/nginx/cloudflared/b.token; inject_listen 127.0.0.1:8081; inject_source_addr 198.51.100.7; }

配了就会在 error_log 里留一条启动 NOTICE(地址 + 配套的 set_real_ip_from 写法)。

模块文档 · 对齐依据是 cloudflared 的源码与实测行为