使用PROXY protocol获取客户IP
获取客户IP是常见的需求,对于大流量的项目都会使用反向代理、负载均衡等,甚至多重代理,导致架构和网络都比较复杂,在这种情况下获取IP就不那么容易了。七层代理可以通过添加头信息来实现,如http协议的X-Forword-For,还比较方便;四层代理基本无法简单的获取到客户端IP地址,像LVS的FULLNAT模式,前端LVS把真实IP写在TCP option里面,后端服务器用内核toa模块获取客户IP;haproxy配合TPROXY也是类似的方式实现,两者都需编译内核非常麻烦,这种情况下就可以考虑使用代理协议
一、代理协议简介
代理协议即 PROXY protocol,是haproxy的作者Willy Tarreau于2010年开发和设计的一个Internet协议,通过为tcp添加一个很小的头信息,来方便的传递客户端信息(协议栈、源IP、目的IP、源端口、目的端口等),在网络情况复杂又需要获取客户IP时非常有用。如:
- 多层NAT网络
- TCP代理(四层)或多层tcp代理
- https反向代理http(某些情况下由于Keep-alive导致不是每次请求都传递x-forword-for)
代理协议分为v1和v2两个版本,v1人类易读,v2是二进制格式,方便程序处理。Proxy protocol是比较新的协议,但目前已经有很多软件支持,如haproxy、nginx、apache、squid、mysql等等,要使用proxy protocol需要两个角色sender和receiver,sender在与receiver之间建立连接后,会先发送一个带有客户信息的tcp header,因为更改了tcp协议,需receiver也支持proxy protocol,否则不能识别tcp包头,导致无法成功建立连接。
二、nginx配置示例
NGINX版本建议不要低于1.9.10,否则对proxy protocol支持不好,很多bug,nginx前端sender直接配置tcp 转发443,不需要配置证书:
stream {
upstream nixops_https {
server nixops.me.backend.ip:8443;
}
server {
listen 443 ;
proxy_connect_timeout 1s;
proxy_timeout 3s;
proxy_pass nixops_https;
proxy_protocol on;
}
}
后端receiver nginx配置所有https证书:
server {
listen 8443 ssl proxy_protocol default;
server_name
$host
;
ssl on;
ssl_certificate "/usr/local/nginx/conf/keys/nixops.me.crt";
ssl_certificate_key "/usr/local/nginx/conf/keys/nixops.me.key";
location / {
proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_pass http://127.0.0.1;
}
}
配置后用http是访问前端直接就能看到绿锁了,也能直接拿到客户IP,很方便
三 、haproxy 配置示例
haproxy是proxy protocol的亲爹,支持和使用都非常,就send-proxy和accept-proxy两个参数即可,haproxy编译:
make TARGET=linux2628 USE_PCRE=1 USE_OPENSSL=1 USE_ZLIB=1
配置文件
global
maxconn 100000
defaults
mode http
timeout connect 10s
timeout client 10s
timeout server 10s
frontend nixopsfront
mode tcp
bind :443
#bind *:443 accept-proxy 作为receiver
default_backend nixopsbackend
backend nixopsbackend
mode tcp
balance roundrobin
server s1 8.8.8.8:443 send-proxy check
#server s2 8.8.8.8:443 send-proxy-v2 check 作为sender
启动 :
./haproxy -d -f ssl.cfg
参考文章:
https://www.nginx.com/resources/admin-guide/proxy-protocol/
http://blog.haproxy.com/haproxy/proxy-protocol/