在企業實際運用中,真實業務服務器每每都不是直接對外提供服務的,前面大多都會添加反代服務器以及防火牆,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。