公司網站架構為:
前面2台HA負載均衡,后面3台Nginx負載均衡反向代理,然后后面有N台WEB服務器
由於要統計IP,需要在WEB服務器日志里體現客戶端真實IP
那么問題來了,通過HA代理的HTTP協議是沒有問題的,后端的WEB服務器可以正常獲取到客戶端真實IP
但是通過HA代理的HTTPS協議就不行了,為什么呢,因為我們HA設置的是代理模式就是TCP模式,TCP代理SSL協議跳轉到后面的NG上
由於4層協議是不能轉發heder的,那后端https自然獲取不到客戶端真實IP了,怎么辦呢,網上找了些資料,做了些測試有2種方案
第一:HA的https不再使用HA代理,直接使用nginx來做負載均衡,這樣問題就解決了,方法和配置參照我上篇隨筆。
第二:是最近2天測試出來的新方案,就是用proxy protocol 協議,也就是代理協議。
什么是代理協議?解釋如下,不是我自己寫的,網上COPY的
1.代理協議即 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包頭,導致無法成功建立連接。
以上就是解釋,haproxy和Nginx都支持這個協議,nginx需要1.5版本以上,有了這個協議就好辦了,haproxy依然使用tcp代理ssl(https),只需要加一點點配置就行,示例配置如下:
global maxconn 64000 chroot /usr/share/haproxy uid 99 gid 99 daemon nbproc 5 tune.ssl.default-dh-param 2048 stats bind-process 1 stats socket /var/run/haproxy.stats level admin defaults log global log 127.0.0.1 local0 mode http option dontlognull retries 3 option redispatch option httpclose balance roundrobin #balance leastconn #option forwardfor #option forwardfor if-none maxconn 64000 timeout http-request 5s timeout connect 5000 timeout client 10000 timeout server 30000 listen monitor_stat :8088 stats uri /ihaproxy-stats stats realm Haproxy\ Statistics stats auth ha_house:ZW5dmKRTObmOuA1nnS5U stats hide-version bind-process 1 frontend yidonghttps-in mode tcp bind *:443 default_backend yidongclient_server_https frontend http-in bind *:80 log global option httplog option forwardfor backend yidongclient_server_http server yidonghttp_41 172.17.2.110:80 weight 1 check inter 5000 rise 2 fall 5 backend yidongclient_server_https mode tcp server yidonghttps_37 172.17.2.110:443 send-proxy
上面配置其實沒添加什么,只是在最后的backend yidongclient_server_https的server里的最后面添加上了 send-proxy 參數,這樣HA就把proxy protocol協議發送到后端Nginx上了。
配置完ha后,后面的nginx也需要配置,由於80端口自然能獲取客戶端IP地址,我們主要來配置443端口
配置文件如下
upstream ihouse443{ server 172.17.3.108:443 max_fails=3 fail_timeout=60 weight=1; } server { listen 443 proxy_protocol; server_name ihouse.ifeng.com; access_log /data/logs/nginx/ihouse_access.log access; error_log /data/logs/nginx/ihouse_error.log ; ssl on; ssl_certificate /data/ifengsite/htdocs/ihouse.ifeng.com.crt; ssl_certificate_key /data/ifengsite/htdocs/ihouse.ifeng.com.key; ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDH:AES:HIGH:!aNULL:!MD5:!ADH:!DH; ssl_protocols TLSv1 TLSv1.1 TLSv1.2; location / { proxy_pass https://ihouse443; proxy_redirect off; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Real-IP $proxy_protocol_addr; #proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-For $http_x_forwarded_for; proxy_set_header X-Forwarded-For $proxy_protocol_addr; proxy_next_upstream error timeout invalid_header http_500 http_502 http_503 http_504; proxy_max_temp_file_size 0; proxy_connect_timeout 90; proxy_send_timeout 90; proxy_read_timeout 90; proxy_buffer_size 4k; proxy_buffers 4 32k; proxy_busy_buffers_size 64k; proxy_temp_file_write_size 64k; } }
主要添加以上紅色粗體內容,server段里添加 proxy_protocol 使之支持代理協議
location 里添加紅色粗體內容,定義新的headr信息,並且發送到后端WEB上
最后需要定義下日志文件,重新定義下,這個日志文件指的是負載均衡的nginx的日志文件,至於后端的WEB則不需要
#user www www; worker_processes 1; #error_log /data/logs/nginx/error.log; #pid /var/run/nginx.pid; #Specifies the value for maximum file descriptors that can be opened by this process. worker_rlimit_nofile 1024; events { use epoll; worker_connections 51200; } http { include mime.types; #include proxy.conf; default_type application/octet-stream; server_names_hash_bucket_size 128; client_header_buffer_size 32k; large_client_header_buffers 4 32k; client_max_body_size 300m; #client_max_body_size 32m; #limit_req_zone $baidu_spider zone=baidu_spider:10m rate=15r/m; sendfile on; tcp_nopush on; keepalive_timeout 30; tcp_nodelay on; #ssi on; #ssi_silent_errors on; fastcgi_connect_timeout 180; fastcgi_send_timeout 180; fastcgi_read_timeout 180; fastcgi_buffer_size 128k; fastcgi_buffers 8 128k; fastcgi_busy_buffers_size 128k; fastcgi_temp_file_write_size 128k; proxy_headers_hash_max_size 51200; proxy_headers_hash_bucket_size 6400; gzip on; gzip_min_length 2k; gzip_buffers 4 16k; gzip_http_version 1.0; gzip_comp_level 6; gzip_types text/plain application/x-javascript text/css application/xml; gzip_vary on; #log_format access '$proxy_protocol_addr-,$remote_addr--,$proxy_add_x_forwarded_for---,$http_x_forwarded_for----,$remote_user,$time_local,$host,$request,$status,$http_referer,$HTTP_X_UP_CALLING_LINE_ID,$request_time,$http_user_agent $upstream_addr $upstream_response_time $upstream_cache_status'; log_format access '-$proxy_protocol_addr-,--$remote_addr--,---$http_x_forwarded_for---,----$proxy_add_x_forwarded_for----,$remote_user,$time_local,$host,$request,$status,$http_referer,$HTTP_X_UP_CALLING_LINE_ID,$request_time,$http_user_agent $upstream_addr $upstream_response_time $upstream_cache_status'; #log_format main '$remote_addr - $remote_user [$time_local] "$request" ' # '$status $body_bytes_sent "$http_referer" ' # '"$http_x_forwarded_for" "$http_user_agent"'; #upstream backend { # server 127.0.0.1:9000 weight=5 max_fails=3 fail_timeout=30s; #} include conf.d/*; }
主要添加上以上紅色粗體日志參數,這樣在負載均衡的nginx上就可以看到https過來的客戶端真實IP了,后端的WEB的https也可以獲取真實IP了
另外如果是HA后面不再通過NG做反向代理轉發的話,那么NG的主機文件配置需要如下配置
server { listen 443 ssl proxy_protocol; server_name ihouse.ifeng.com; ssl_certificate /data/ifengsite/htdocs/ihouse.ifeng.com.crt; ssl_certificate_key /data/ifengsite/htdocs/ihouse.ifeng.com.key; ssl_session_cache shared:SSL:1m; ssl_session_timeout 5m; ssl_ciphers HIGH:!aNULL:!MD5; ssl_prefer_server_ciphers on; set_real_ip_from 172.17.3.0/24; real_ip_header proxy_protocol; location / { root html; index index.html index.htm; } }
依然需要設置 proxy_protocol
設置real_ip的前端代理主機IP或者IP段
設置real_ip 的header
參考文獻
https://www.52os.net/articles/PROXY_protocol_pass_client_ip.html
https://docs.nginx.com/nginx/admin-guide/load-balancer/using-proxy-protocol/