防火牆+多層反向代理的環境中核心web服務器獲取用戶真實訪問IP

在企業實際運用中,真實業務服務器每每都不是直接對外提供服務的,前面大多都會添加反代服務器以及防火牆,php

但這樣以來核心的業務服務器如何獲取到用戶的真實IP每每是運維考慮的一個核心問題,今天我給你們分享一下html

企業真實企業真實環境下核心web服務器如何獲取用戶的真實IP。因爲線上大多使用硬件防火牆,咱們這裏前端

使用CentOS自帶的iptables來進行模擬,也方便你們閱讀本文進行實踐。nginx


環境展現:web

虛擬化平臺: Vmware Workstationapache

操做系統: CentOS 7.5後端

防火牆: CentOS系統的iptables作DNAT+SNAT瀏覽器

反代服務器: nginx的七層反代模塊服務器

web服務器: nginx或者httpd網絡


這次的模擬過程咱們用四臺虛擬機來實現:

webserver  一臺單獨的虛機  192.168.10.254

proxyserver  一臺單獨的虛機  192.168.10.100

proxyserver2  一臺單獨的虛機  192.168.10.50

firewall  一臺單獨的虛機  內網IP:192.168.10.8 外網IP:10.0.0.10


四臺服務器同時禁用和中止SELinux和默認的firewalld服務,實際企業環境中,防火牆之內的服務器也不多會啓用此兩項。


webserver安裝nginx和php-fpm

proxyserver安裝nginx

firewall默認就可使用iptables,若是要想使用更完備的iptables功能,能夠安裝iptables-services


webserver的nginx主配置文件

grep -v '[[:space:]]*#' /etc/nginx/nginx.conf

user  nginx;

worker_processes  auto;


error_log  /data/log/nginx/error.log warn;

pid        /var/run/nginx.pid;


events {

    worker_connections  1024;

}


http {

    include       /etc/nginx/mime.types;

    default_type  application/octet-stream;


    log_format  main  '$remote_addr "$http_x_forwarded_for" "$proxy_add_x_forwarded_for" '

                      '$remote_user [$time_local] "$request" $status $body_bytes_sent '

                      '$upstream_addr $request_time $upstream_response_time '

                      '"$http_referer" "$http_user_agent" ';


    access_log  /data/log/nginx/access.log  main;


    sendfile        on;

    keepalive_timeout  65;


    include /etc/nginx/conf.d/*.conf;

}


webserver的nginx虛擬主機配置文件

grep -v '[[:space:]]*#' /etc/nginx/conf.d/default.conf

server {

    listen       80;

    server_name  www.test.com;


    root   /data/website;

    index  index.php index.html index.htm;

   

    access_log  /data/log/nginx/test.access.log  main;

    error_log  /data/log/nginx/test.error.log  warn;


    location ~ \.php$ {

        fastcgi_pass   127.0.0.1:9000;

        fastcgi_index  index.php;

        fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;

        include        fastcgi_params;

    }

}


測試頁面php代碼以下:

/data/website/index.php 

<?php


  foreach ($_SERVER as $key=>$value) {

    echo $key.'='.$value.'<br />';

  }


?>


使用宿主機的瀏覽器訪問核心業務服務器192.168.10.254,頁面輸出以下:

USER=apache

HOME=/usr/share/httpd

FCGI_ROLE=RESPONDER

SCRIPT_FILENAME=/data/website/index.php

QUERY_STRING=

REQUEST_METHOD=GET

CONTENT_TYPE=

CONTENT_LENGTH=

SCRIPT_NAME=/index.php

REQUEST_URI=/

DOCUMENT_URI=/index.php

DOCUMENT_ROOT=/data/website

SERVER_PROTOCOL=HTTP/1.1

REQUEST_SCHEME=http

GATEWAY_INTERFACE=CGI/1.1

SERVER_SOFTWARE=nginx/1.14.0

REMOTE_ADDR=192.168.10.1

REMOTE_PORT=57829

SERVER_ADDR=192.168.10.254

SERVER_PORT=80

SERVER_NAME=www.test.com

REDIRECT_STATUS=200

HTTP_HOST=192.168.10.254

HTTP_CONNECTION=keep-alive

HTTP_CACHE_CONTROL=max-age=0

HTTP_UPGRADE_INSECURE_REQUESTS=1

HTTP_USER_AGENT=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.92 Safari/537.36

HTTP_ACCEPT=text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8

HTTP_ACCEPT_ENCODING=gzip, deflate

HTTP_ACCEPT_LANGUAGE=zh-CN,zh;q=0.9

PHP_SELF=/index.php

REQUEST_TIME_FLOAT=1539053341.08

REQUEST_TIME=1539053341


webserver的nginx日誌內容以下

cat /data/log/nginx/test.access.log

192.168.10.1 "-" "192.168.10.1" - [09/Oct/2018:10:54:34 +0800] "GET / HTTP/1.1" 200 1146 127.0.0.1:9000 0.001 0.001 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.92 Safari/537.36"


因爲宿主機的ip都是本網段的第一個ip,因此咱們ip是192.168.10.1是真實的客戶端ip。


爲了方便查看HTTP_X_FORWARDED_FOR的變化,咱們直接使用fastcgi_param參數嚮應用服務器傳遞一個HTTP_X_FORWARDED_FOR變量

    location ~ \.php$ {

        #root   /data/website;

        fastcgi_pass   127.0.0.1:9000;

        fastcgi_index  index.php;

        fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;

        fastcgi_param  HTTP_X_FORWARDED_FOR $proxy_add_x_forwarded_for;  #添加了此行

        include        fastcgi_params;

    }


此時再次刷新訪問頁面192.168.10.254,輸出內容以下:

USER=apache

HOME=/usr/share/httpd

FCGI_ROLE=RESPONDER

SCRIPT_FILENAME=/data/website/index.php

HTTP_X_FORWARDED_FOR=192.168.10.1

QUERY_STRING=

REQUEST_METHOD=GET

CONTENT_TYPE=

CONTENT_LENGTH=

SCRIPT_NAME=/index.php

REQUEST_URI=/

DOCUMENT_URI=/index.php

DOCUMENT_ROOT=/data/website

SERVER_PROTOCOL=HTTP/1.1

REQUEST_SCHEME=http

GATEWAY_INTERFACE=CGI/1.1

SERVER_SOFTWARE=nginx/1.14.0

REMOTE_ADDR=192.168.10.1

REMOTE_PORT=59095

SERVER_ADDR=192.168.10.254

SERVER_PORT=80

SERVER_NAME=www.test.com

REDIRECT_STATUS=200

HTTP_HOST=192.168.10.254

HTTP_CONNECTION=keep-alive

HTTP_CACHE_CONTROL=max-age=0

HTTP_UPGRADE_INSECURE_REQUESTS=1

HTTP_USER_AGENT=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.92 Safari/537.36

HTTP_ACCEPT=text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8

HTTP_ACCEPT_ENCODING=gzip, deflate

HTTP_ACCEPT_LANGUAGE=zh-CN,zh;q=0.9

PHP_SELF=/index.php

REQUEST_TIME_FLOAT=1539054347.9258

REQUEST_TIME=1539054347


HTTP_X_FORWARDED_FOR=192.168.10.1,能夠明確的看到fastcgi向後端應用服務器傳遞的HTTP_X_FORWARDED_FOR的值


webserver的nginx日誌內容以下

192.168.10.1 "-" "192.168.10.1" - [09/Oct/2018:11:05:47 +0800] "GET / HTTP/1.1" 200 1185 127.0.0.1:9000 0.001 0.000 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.92 Safari/537.36"


接下來咱們給webserver前端添加第一層反向代理


proxyserver的nginx主配以下:

user  nginx;

worker_processes  auto;


error_log  /data/log/nginx/error.log warn;

pid        /var/run/nginx.pid;


events {

    worker_connections  1024;

}


http {

    include       /etc/nginx/mime.types;

    default_type  application/octet-stream;


    log_format  main  '$remote_addr "$http_x_forwarded_for" "$proxy_add_x_forwarded_for" '

                      '$remote_user [$time_local] "$request" $status $body_bytes_sent '

                      '$upstream_addr $request_time $upstream_response_time '

                      '"$http_referer" "$http_user_agent" ';


    access_log  /data/log/nginx/access.log  main;


    sendfile        on;


    keepalive_timeout  65;


    include /etc/nginx/conf.d/*.conf;

}


proxyserver的nginx小配以下:

upstream backend_server {

    server 192.168.10.254;

}


server {

    listen       80;

    server_name  proxy.test.com;


    access_log  /data/log/nginx/proxy.access.log  main;

    error_log  /data/log/nginx/proxy.error.log  warn;


    location / {

        proxy_pass http://backend_server;

    }

}


此時使用宿主機的瀏覽器訪問proxyserver的ip 192.168.10.100,輸出內容以下:


USER=apache

HOME=/usr/share/httpd

FCGI_ROLE=RESPONDER

SCRIPT_FILENAME=/data/website/index.php

HTTP_X_FORWARDED_FOR=192.168.10.100

QUERY_STRING=

REQUEST_METHOD=GET

CONTENT_TYPE=

CONTENT_LENGTH=

SCRIPT_NAME=/index.php

REQUEST_URI=/

DOCUMENT_URI=/index.php

DOCUMENT_ROOT=/data/website

SERVER_PROTOCOL=HTTP/1.0

REQUEST_SCHEME=http

GATEWAY_INTERFACE=CGI/1.1

SERVER_SOFTWARE=nginx/1.14.0

REMOTE_ADDR=192.168.10.100

REMOTE_PORT=44866

SERVER_ADDR=192.168.10.254

SERVER_PORT=80

SERVER_NAME=www.test.com

REDIRECT_STATUS=200

HTTP_HOST=backend_server

HTTP_CONNECTION=close

HTTP_UPGRADE_INSECURE_REQUESTS=1

HTTP_USER_AGENT=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.92 Safari/537.36

HTTP_ACCEPT=text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8

HTTP_ACCEPT_ENCODING=gzip, deflate

HTTP_ACCEPT_LANGUAGE=zh-CN,zh;q=0.9

PHP_SELF=/index.php

REQUEST_TIME_FLOAT=1539055499.4538

REQUEST_TIME=1539055499


此時能夠看到proxyserver響應的頁面中HTTP_X_FORWARDED_FOR=192.168.10.100,由原來記錄的用戶的真實IP變成了反代proxyserver本身的IP。REMOTE_ADDR=192.168.10.100由原來須要訪問核心業務服務器變成了訪問proxyserver的IP,從而起到了隱藏核心業務服務器的做用。


proxyserver的nginx日誌輸出以下:

192.168.10.1 "-" "192.168.10.1" - [09/Oct/2018:11:25:03 +0800] "GET / HTTP/1.1" 200 1150 192.168.10.254:80 0.002 0.001 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.92 Safari/537.36"


webserver的nginx日誌輸出以下:

192.168.10.100 "-" "192.168.10.100" - [09/Oct/2018:11:24:59 +0800] "GET / HTTP/1.0" 200 1138 127.0.0.1:9000 0.001 0.000 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.92 Safari/537.36"


在這裏可以很清晰的看到前端proxyserver可以拿到用戶的真實訪問IP,但核心業務服務器webserver看到全部的訪問是來自前端反代服務器的,

這樣以來,用戶的真實IP就獲取不到了。這是沒法企業的需求的,那麼接下就要給你們分享在前端有反向代理服務器時如何獲取用戶的真實IP。


修改proxyserver的nginx小配文件

    location / {

        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_pass http://backend_server;

    }


reload nginx後從新訪問192.168.10.100,頁面輸出內容以下:

USER=apache

HOME=/usr/share/httpd

FCGI_ROLE=RESPONDER

SCRIPT_FILENAME=/data/website/index.php

HTTP_X_FORWARDED_FOR=192.168.10.1, 192.168.10.100

QUERY_STRING=

REQUEST_METHOD=GET

CONTENT_TYPE=

CONTENT_LENGTH=

SCRIPT_NAME=/index.php

REQUEST_URI=/

DOCUMENT_URI=/index.php

DOCUMENT_ROOT=/data/website

SERVER_PROTOCOL=HTTP/1.0

REQUEST_SCHEME=http

GATEWAY_INTERFACE=CGI/1.1

SERVER_SOFTWARE=nginx/1.14.0

REMOTE_ADDR=192.168.10.100

REMOTE_PORT=44870

SERVER_ADDR=192.168.10.254

SERVER_PORT=80

SERVER_NAME=www.test.com

REDIRECT_STATUS=200

HTTP_HOST=192.168.10.100

HTTP_X_REAL_IP=192.168.10.1

HTTP_CONNECTION=close

HTTP_CACHE_CONTROL=max-age=0

HTTP_UPGRADE_INSECURE_REQUESTS=1

HTTP_USER_AGENT=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.92 Safari/537.36

HTTP_ACCEPT=text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8

HTTP_ACCEPT_ENCODING=gzip, deflate

HTTP_ACCEPT_LANGUAGE=zh-CN,zh;q=0.9

PHP_SELF=/index.php

REQUEST_TIME_FLOAT=1539057291.9947

REQUEST_TIME=1539057291


HTTP_X_FORWARDED_FOR=192.168.10.1, 192.168.10.100,能夠看到HTTP_X_FORWARDED_FOR變量中保存了全部通過的服務器,這裏咱們只通過了一層反向服務器,若是有多層的話,會依次追加至真實客戶端IP的後面。


此時咱們再來查看一下proxyserver的nginx訪問日誌

192.168.10.1 "-" "192.168.10.1" - [09/Oct/2018:11:54:55 +0800] "GET / HTTP/1.1" 200 1231 192.168.10.254:80 0.001 0.003 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.92 Safari/537.36"


能夠看出和以前同樣


webserver的nginx訪問日誌

192.168.10.100 "192.168.10.1" "192.168.10.1, 192.168.10.100" - [09/Oct/2018:11:54:51 +0800] "GET / HTTP/1.0" 200 1219 127.0.0.1:9000 0.000 0.000 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.92 Safari/537.36"


但核心業務服務器日誌的HTTP_X_FORWARDED_FOR字段的值就是用戶端的真實IP "192.168.10.1"。但這只是針對單層反向代碼的狀況,若是核心業務服務器前端有多層反向代理時,HTTP_X_FORWARDED_FOR字段可能不上一個IP了,是會同時攜帶最接近用戶真實IP層面的幾層IP信息。


爲了可以讓你們看到效果,咱們繼續給proxyserver 192.168.10.100的前端再添加一層反代服務器,使用192.168.10.50這臺服務器也安裝nginx,並配置成爲proxyserver2,作爲192.168.10.100的前端反向代理。


proxyserver2的nginx主配置文件

user  nginx;

worker_processes  auto;


error_log  /data/log/nginx/error.log warn;

pid        /var/run/nginx.pid;


events {

    worker_connections  1024;

}


http {

    include       /etc/nginx/mime.types;

    default_type  application/octet-stream;


    log_format  main  '$remote_addr "$http_x_forwarded_for" "$proxy_add_x_forwarded_for" '

                      '$remote_user [$time_local] "$request" $status $body_bytes_sent '

                      '$upstream_addr $request_time $upstream_response_time '

                      '"$http_referer" "$http_user_agent" ';


    access_log  /data/log/nginx/access.log  main;


    sendfile        on;


    keepalive_timeout  65;


    include /etc/nginx/conf.d/*.conf;

}


proxyserver2的nginx小配文件

upstream backend_server {

    server 192.168.10.100;

}


server {

    listen       80;

    server_name  proxy.test.com;


    access_log  /data/log/nginx/proxy.access.log  main;

    error_log  /data/log/nginx/proxy.error.log  warn;


    location / {

        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_pass http://backend_server;

    }

}


此時訪問最前端反代服務器proxyserver2的ip,192.168.10.50,輸出內容以下:

USER=apache

HOME=/usr/share/httpd

FCGI_ROLE=RESPONDER

SCRIPT_FILENAME=/data/website/index.php

HTTP_X_FORWARDED_FOR=192.168.10.1, 192.168.10.50, 192.168.10.100

QUERY_STRING=

REQUEST_METHOD=GET

CONTENT_TYPE=

CONTENT_LENGTH=

SCRIPT_NAME=/index.php

REQUEST_URI=/

DOCUMENT_URI=/index.php

DOCUMENT_ROOT=/data/website

SERVER_PROTOCOL=HTTP/1.0

REQUEST_SCHEME=http

GATEWAY_INTERFACE=CGI/1.1

SERVER_SOFTWARE=nginx/1.14.0

REMOTE_ADDR=192.168.10.100

REMOTE_PORT=44880

SERVER_ADDR=192.168.10.254

SERVER_PORT=80

SERVER_NAME=www.test.com

REDIRECT_STATUS=200

HTTP_HOST=192.168.10.50

HTTP_X_REAL_IP=192.168.10.50

HTTP_CONNECTION=close

HTTP_CACHE_CONTROL=max-age=0

HTTP_UPGRADE_INSECURE_REQUESTS=1

HTTP_USER_AGENT=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.92 Safari/537.36

HTTP_ACCEPT=text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8

HTTP_ACCEPT_ENCODING=gzip, deflate

HTTP_ACCEPT_LANGUAGE=zh-CN,zh;q=0.9

PHP_SELF=/index.php

REQUEST_TIME_FLOAT=1539059555.4869

REQUEST_TIME=1539059555


proxyserver2服務器nginx的訪問日誌

192.168.10.1 "-" "192.168.10.1" - [09/Oct/2018:12:32:39 +0800] "GET / HTTP/1.1" 200 1246 192.168.10.100:80 0.003 0.004 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.92 Safari/537.36"


因爲這一層在最前端,獲取到是用戶的真實IP。


proxyserver的nginx訪問日誌

192.168.10.50 "192.168.10.1" "192.168.10.1, 192.168.10.50" - [09/Oct/2018:12:32:39 +0800] "GET / HTTP/1.0" 200 1234 192.168.10.254:80 0.001 0.001 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.92 Safari/537.36" 


能可以看到用戶的真實ip是被傳遞給了HTTP_X_FORWARDED_FOR變量的,但也是源於proxyserver2的反向代理層面添加了以下三行配置:

        proxy_set_header   Host             $host;

        proxy_set_header   X-Real-IP        $remote_addr;

        proxy_set_header   X-Forwarded-For  $proxy_add_x_forwarded_for;

不然proxyserver層是獲取不到用戶端的真實IP的


再看一下核心層webserver服務器nginx訪問日誌


192.168.10.100 "192.168.10.1, 192.168.10.50" "192.168.10.1, 192.168.10.50, 192.168.10.100" - [09/Oct/2018:12:32:35 +0800] "GET / HTTP/1.0" 200 1234 127.0.0.1:9000 0.001 0.000 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.92 Safari/537.36"


此時能夠看到核心業務服務器HTTP_X_FORWARDED_FOR變量的值就不止一個IP了,包含了用戶的真實IP和離用戶最近一層的反向代理服務器的IP。

在這種多層反向代理的環境,若是要想從HTTP_X_FORWARDED_FOR變量中取到用戶真實IP,就得寫代碼從HTTP_X_FORWARDED_FOR變量中取第一個,號前的內容了。若是想讓REMOTE_ADDR字段直接保存用戶的真實IP,還須要進一步的配置。


在這裏經過筆者的實踐得出了兩種配置思路:

第一種,只在核心業務器webserver上配置(前端反代不用關心)

須要在server段中添加以下配置

    set_real_ip_from 192.168.10.50;

    set_real_ip_from 192.168.10.100;

    real_ip_header  X-Forwarded-For;

    real_ip_recursive  on;

也即把全部外層反代服務器的ip地址所有使用set_real_ip_from羅列出來,並指定用戶真實IP從X-Forwarded-For字段中取值,

而且啓用真實IP遞歸解析(也即從X-Forwarded-For字段的最右側開始,排除方式取出第一個,號以前IP)


第二種,每層反代理都配置set_real_ip_from 上層反代IP;(上文中的其配置均可省略)

此種思路配置較爲複雜,但每層反代均可以使用REMOTE_ADDR字段保存用戶的真實IP,

若是反代服務器不是本身的資源時具備較多的不可操做性。


首先,咱們看一下第一種方案配置後的日誌記錄


訪問最外層反代proxyserver2的ip,頁面輸出以下

USER=apache

HOME=/usr/share/httpd

FCGI_ROLE=RESPONDER

SCRIPT_FILENAME=/data/website/index.php

HTTP_X_FORWARDED_FOR=192.168.10.1, 192.168.10.50, 192.168.10.1

QUERY_STRING=

REQUEST_METHOD=GET

CONTENT_TYPE=

CONTENT_LENGTH=

SCRIPT_NAME=/index.php

REQUEST_URI=/

DOCUMENT_URI=/index.php

DOCUMENT_ROOT=/data/website

SERVER_PROTOCOL=HTTP/1.0

REQUEST_SCHEME=http

GATEWAY_INTERFACE=CGI/1.1

SERVER_SOFTWARE=nginx/1.14.0

REMOTE_ADDR=192.168.10.1

REMOTE_PORT=

SERVER_ADDR=192.168.10.254

SERVER_PORT=80

SERVER_NAME=www.test.com

REDIRECT_STATUS=200

HTTP_HOST=192.168.10.50

HTTP_X_REAL_IP=192.168.10.50

HTTP_CONNECTION=close

HTTP_CACHE_CONTROL=max-age=0

HTTP_UPGRADE_INSECURE_REQUESTS=1

HTTP_USER_AGENT=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.92 Safari/537.36

HTTP_ACCEPT=text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8

HTTP_ACCEPT_ENCODING=gzip, deflate

HTTP_ACCEPT_LANGUAGE=zh-CN,zh;q=0.9

PHP_SELF=/index.php

REQUEST_TIME_FLOAT=1539090819.6079

REQUEST_TIME=1539090819


proxyserver2的nginx日誌以下:

192.168.10.1 "-" "192.168.10.1" - [10/Oct/2018:19:36:53 +0800] "GET / HTTP/1.1" 200 1237 192.168.10.100:80 0.307 0.308 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.92 Safari/537.36" 


最外層記錄了用戶的真實訪問IP,並向PROXY_ADD_X_FORWARDED_FOR變量中插入用戶的真實IP


proxyserver的nginx日誌以下:

192.168.10.50 "192.168.10.1" "192.168.10.1, 192.168.10.50" - [09/Oct/2018:21:13:07 +0800] "GET / HTTP/1.0" 200 1225 192.168.10.254:80 0.307 0.307 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.92 Safari/537.36"


中間層沒有使用set_real_ip_from指令,因此REMOTE_ADDR字段仍是存放的上層反代服務器的訪問IP,但HTTP_X_FORWARDED_FOR變量的第一段已經有了用戶的真實訪問IP,若是這裏想獲取用戶的真實訪問IP就得經過代碼來實現。


webserver的nginx日誌以下:

192.168.10.1 "192.168.10.1, 192.168.10.50" "192.168.10.1, 192.168.10.50, 192.168.10.1" - [09/Oct/2018:21:13:39 +0800] "GET / HTTP/1.0" 200 1225 127.0.0.1:9000 0.306 0.306 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.92 Safari/537.36"


核心層的nginx配置中添加過了REAL_IP的相關配置,因此REMOTE_ADDR字段直接保存了用戶的真實訪問IP,X-Forwarded-For字段保存了用戶的真實IP和離用戶最近的上層代理,proxy_add_x_forwarded_for變量中的最後一段也是用戶的真實IP,這個有待於研究。


至此,咱們核心業務服務器在多層反向代理的環境下,已經能夠獲取用戶的真實訪問IP了。若是想模擬在最外層有防火牆的狀況,請繼續往下。


此時咱們啓用firewall這臺虛機,須要另外添加一張外網網卡,內網使用eth0,配置IP:192.168.10.8,外網使用eth1,配置IP:10.0.0.10,

若是要想作爲網絡防火牆使用,必須打開內核轉發參數,也即net.ipv4.ip_forward = 1


添加相應外網到內網的映射規則,也即DNAT規則

iptables -t nat -I PREROUTING -p tcp -d 10.0.0.10 --dport 80 -j DNAT --to-destination 192.168.10.50:80

此條規則是將全部訪問防火牆的外網接口10.0.0.10的80端口的數據修改目標地址及端口後轉發到內網服務器192.168.10.50的80端口,

而咱們內網服務器192.168.10.50也不是真實web服務器,而是多層反向代理的最外層反代服務器。因此這整個鏈路的層次仍是比較多的。


僅有DNAT規則,只能將外網用戶的請求轉發到內網的反代服務器,因此還須要SNAT規則將反代服務器的響應數據修改源地及端口後轉發給用戶

iptables -t nat -I POSTROUTING -p tcp -s 192.168.10.50 --sport 80 -j SNAT --to-source 10.0.0.10:80


跟着作的朋友可能會發現如今DNAT和SNAT都作好了,但爲何仍是訪問10.0.0.10仍是不正常了,緣由在於內網的反代服務器響應數據時找不到通向10.0網絡的路由,因此須要在內網服務器上配置防火牆192.168.10.8爲本身的網關。


須要修改proxyserver2的網絡配置

GATEWAY=192.168.10.8


此時使用宿主機的瀏覽器訪問10.0.0.10,頁面輸出內容以下:

USER=apache

HOME=/usr/share/httpd

FCGI_ROLE=RESPONDER

SCRIPT_FILENAME=/data/website/index.php

HTTP_X_FORWARDED_FOR=10.0.0.1, 192.168.10.50, 10.0.0.1

QUERY_STRING=

REQUEST_METHOD=GET

CONTENT_TYPE=

CONTENT_LENGTH=

SCRIPT_NAME=/index.php

REQUEST_URI=/

DOCUMENT_URI=/index.php

DOCUMENT_ROOT=/data/website

SERVER_PROTOCOL=HTTP/1.0

REQUEST_SCHEME=http

GATEWAY_INTERFACE=CGI/1.1

SERVER_SOFTWARE=nginx/1.14.0

REMOTE_ADDR=10.0.0.1

REMOTE_PORT=

SERVER_ADDR=192.168.10.254

SERVER_PORT=80

SERVER_NAME=www.test.com

REDIRECT_STATUS=200

HTTP_HOST=10.0.0.10

HTTP_X_REAL_IP=192.168.10.50

HTTP_CONNECTION=close

HTTP_CACHE_CONTROL=max-age=0

HTTP_UPGRADE_INSECURE_REQUESTS=1

HTTP_USER_AGENT=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.92 Safari/537.36

HTTP_ACCEPT=text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8

HTTP_ACCEPT_ENCODING=gzip, deflate

HTTP_ACCEPT_LANGUAGE=zh-CN,zh;q=0.9

PHP_SELF=/index.php

REQUEST_TIME_FLOAT=1539111297.9179

REQUEST_TIME=1539111297


proxyserver2的nginx日誌以下:

10.0.0.1 "-" "10.0.0.1" - [11/Oct/2018:01:18:11 +0800] "GET / HTTP/1.1" 200 1221 192.168.10.100:80 0.004 0.005 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.92 Safari/537.36


proxyserver的nginx日誌以下:

192.168.10.50 "10.0.0.1" "10.0.0.1, 192.168.10.50" - [10/Oct/2018:02:54:26 +0800] "GET / HTTP/1.0" 200 1209 192.168.10.254:80 0.002 0.003 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.92 Safari/537.36"


webserver的nginx日誌以下:

10.0.0.1 "10.0.0.1, 192.168.10.50" "10.0.0.1, 192.168.10.50, 10.0.0.1" - [10/Oct/2018:02:54:57 +0800] "GET / HTTP/1.0" 200 1209 127.0.0.1:9000 0.001 0.001 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.92 Safari/537.36"


接下來咱們再爲你們展現一下層層代理都配置後獲得的日誌效果


proxyserver2是最外層的反向代理,因此不須要使用set_real_ip指定上級反代的真實IP

proxyserver2的nginx日誌以下:

10.0.0.1 "-" "10.0.0.1" - [11/Oct/2018:02:05:00 +0800] "GET / HTTP/1.1" 200 1211 192.168.10.100:80 0.004 0.005 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.92 Safari/537.36"


proxyserver是中間反代服務器,須要使用set_real_ip指定上級反代的真實IP

須要在nginx小配文件中增長以下配置

set_real_ip_from 192.168.10.50;


proxyserver的nginx日誌以下:

10.0.0.1 "10.0.0.1" "10.0.0.1, 10.0.0.1" - [10/Oct/2018:03:41:14 +0800] "GET / HTTP/1.0" 200 1199 192.168.10.254:80 0.002 0.001 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.92 Safari/537.36"


webserver的nginx也須要使用set_real_ip指定上級反代的真實IP

set_real_ip_from 192.168.10.100;


webserver的nginx日誌以下:

10.0.0.1 "10.0.0.1, 10.0.0.1" "10.0.0.1, 10.0.0.1, 10.0.0.1" - [10/Oct/2018:03:41:46 +0800] "GET / HTTP/1.0" 200 1199 127.0.0.1:9000 0.001 0.000 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.92 Safari/537.36"


從核心業務服務器日誌的proxy_add_x_forwarded_for的變量值來看,每通過一次處理,都會把用戶的真實IP解析出來並向

proxy_add_x_forwarded_for值。


至此,咱們的環境是最前端防火牆,防火牆事後穿越兩層反向代理,用戶請求才到達核心業務服務器,即使是如此複雜的網絡環境,依然可以保證咱們的核心業務服務器可以獲取到用戶的真實IP。

相關文章
相關標籤/搜索