Nginx Tcp 转发保留客户端真实 IP (PROXY Protocol)
本文最后更新于:1 年前
由于在家里搭建了博客,Git 等服务, 经过了阿里云用 Nginx 做了 TCP Proxy, 导致了一个问题,家里看到的服务访问 IP 都是转发服务器的 IP,无法看到真实IP。联想到平常 LB 上的是支持 PROXY Protocol 转发真实IP, 于是搜了一下,Nginx 还真支持!
PROXY Protocol
什么是 PROXY Protocol ? 其实很简单,就是为了解决多层NET或TCP转发时 无法获取客户端真实IP的问题,在 TCP 第一行加入了一些信息标识协议、客户端地址、转发地址以及端口等。目前有 v1 和 v2 两个版本。
v1 格式如下:
1 |
|
示例
1 |
|
v2 格式如下:
12 字节固定签名
1
\x0D\x0A\x0D\x0A\x00\x0D\x0A\x51\x55\x49\x54\x0A
13字节包含协议版本和命令
1
2
3
4
5# 版本
\x2
# 命令
\x0 # LOCAL ,客户段直连
\x1 # PROXY ,进过转发第 14 个字节包含传输协议和地址族
1
2
3
4
5
6
7
8
9# 传输协议
\x0 # AF_UNSPEC
\x1 # AF_INET (IPv4)
\x2 # AF_INET6 (IPv6)
\x3 # AF_UNIX (UNIX)
# 地址族
\x0 # UNSPEC
\x1 # STREAM (如:TCP 或 UNIX_STREAM)
\x2 # DGRAM (如:UDP 或 UNIX_DGRAM)第15、16字节为长度标记(剩下的报头长度)
1
2
3
4
5
6
7# 前面的结构
struct proxy_hdr_v2 {
uint8_t sig[12]; /* 十六进制 0D 0A 0D 0A 00 0D 0A 51 55 49 54 0A */
uint8_t ver_cmd; /* 协议版本和命令 */
uint8_t fam; /* 协议族和地址 */
uint16_t len; /* 头的后续字节数 */
};接下来就是
- 源第三层地址
- 目标第三层地址
- 源第四层地址(如果有)
- 目标第四层地址(如果有)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18union proxy_addr {
struct { /* for TCP/ UDP over IPv4, len = 12 */
uint32_t src_addr;
uint32_t dst_addr;
uint16_t src_port;
uint16_t dst_port;
} ipv4_addr;
struct { /* for TCP/UDP over IPv6, len = 36 */
uint8_t src_addr[16];
uint8_t dst_addr[16];
uint16_t src_port;
uint16_t dst_port;
} ipv6_addr;
struct { /* 对于 AF_UNIX 套接字,len = 216 */
uint8_t src_addr[108];
uint8_t dst_addr[108];
} unix_addr;
};
但,要开启 PROXY Protocol , 必须在转发器和服务端同时支持该协议, 很庆幸 Nginx 早就支持了。
- 开源版本 v1.5.12 以及之后支持v1; v1.13.11以及之后支持v2。
配置
首先转发服务上 stream
和 stream_realip_module
模块肯定要编译到 Nginx 中去,服务端http_realip_module
也要编辑进去。
然后,简单配置如下:
转发服务
1 |
|
服务端
服务端通过proxy_protocol_addr
就能拿到真实IP了,于是我们可以通过Header转发到对应后面的服务。
1 |
|
这样就大功告成,成功通过Nginx TCP Proxy 获取真实客户端IP~
参考
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!