近期在其云否維護客戶的網站時,客戶要求屏蔽國外IP的訪問,因為從日誌來看攻擊的IP大部分都是來自國外,並且自己的目標用戶為國內,所以只允許國內的IP訪問網站可阻止絕大多數的CC和DDoS攻擊。實際測試後,發現效果還是不錯,攻擊想再次攻擊成本增加了不少。

不過,隨後發現了一個問題,就是使用了Cloudflare CDN後,網站取得到的IP位址都是Cloudflare的CDN節點的,不能得到真實使用者的IP位址,防禦效果大大折扣。還好Cloudflare已經為我們想到這一點了,將訪客的 IP 位址包含在  X-Forwarded-For 標頭和 CF-Connecting-IP 標頭。

有了 X-Forwarded-For 標頭,如果是Nginx可以使用ngx_http_realip_module模組,如果是Apache,則可以使用mod_remoteip模組來取得使用者的真實IP。本篇文章就來分享如何編譯和啟用ngx_http_realip_module模組和mod_remoteip模組來取得使用者的真實IP位址。

一般來說CDN廠商都採用了X-Forwarded-For和X-Real_IP等標準協議,所以本文介紹了獲取用戶真實IP的訪問基本上適用於其它的CDN廠商。更多的關於CDN加速和伺服器優化加速的方法,這裡有:

  1. Cloudflare Partner接取管理Cloudflare CDN-啟用Railgun動態加速
  2. 又拍雲CDN加速申請使用教學-一鍵鏡像,靜態動態CDN和免費SSL
  3. WordPress開啟Nginx fastcgi_cache快取加速方法-Nginx設定實例

一、Nginx編譯ngx_http_realip_module

1.1  Oneinstack編譯

如果用的是Oneinstack一鍵包,可以用以下指令來編譯ngx_http_realip_module:

#下编译安装nginx的时候,都编译安装的哪些模块
[root@wzfoume ~]# nginx -V 
nginx version: nginx/1.14.2
built by gcc 4.4.7 20120313 (Red Hat 4.4.7-23) (GCC) 
built with OpenSSL 1.1.1a  20 Nov 2018
TLS SNI support enabled
configure arguments: --prefix=/usr/local/nginx --user=www --group=www --with-http_stub_status_module --with-http_v2_module --with-http_ssl_module --with-http_gzip_static_module --with-http_realip_module --with-http_flv_module --with-http_mp4_module --with-openssl=../openssl-1.1.1a --with-pcre=../pcre-8.42 --with-pcre-jit --with-ld-opt=-ljemalloc

#进入到oneinstack的nginx安装目录下,如果没有请先解压
[root@wzfoume src]# cd /root/oneinstack/src
[root@wzfoume src]# tar xzf nginx-1.14.2.tar.gz
[root@wzfoume src]# cd /root/oneinstack/src/nginx-1.14.2
[root@wzfoume nginx-1.14.2]# ./configure --prefix=/usr/local/nginx --user=www --group=www --with-http_stub_status_module --with-http_v2_module --with-http_ssl_module --with-http_gzip_static_module --with-http_realip_module --with-http_flv_module --with-http_mp4_module --with-openssl=../openssl-1.1.1a --with-pcre=../pcre-8.42 --with-pcre-jit --with-ld-opt=-ljemalloc --with-http_realip_module

make

#如果出现错误,应该是依赖路径不对,请cd ..到上一个目录解压相应的软件
tar xzf pcre-8.42.tar.gz
tar xzf openssl-1.0.2q.tar.gz
tar xzf openssl-1.1.1a.tar.gz

#编译完成,备份原先配置,然后替换nginx二进制文件
mv /usr/local/nginx/sbin/nginx{,_`date +%F`}  #备份nginx
cp objs/nginx /usr/local/nginx/sbin

#查看是否已经把http_realip_module模块加入进去
nginx -V

1.2  LNMP編譯

如果你用的是LNMP一鍵包,在lnmp安裝目錄下找到lnmp.conf編輯它,在Nginx_Modules_Options裡加上realip,保存後執行./upgrade.sh nginx來升級下Nginx就可以了。命令如下:

Nginx_Modules_Options='--with-http_realip_module'

1.3  BT寶塔面板

如果你用的是BT寶塔面板,可以使用以下指令來編譯ngx_http_realip_module:

#宝塔面板安装模块

#先查看一下本机的Nginx配置情况
[root@cs ~]# nginx -V
nginx version: nginx/1.14.2
built by gcc 4.8.5 20150623 (Red Hat 4.8.5-36) (GCC) 
built with OpenSSL 1.0.2l  25 May 2017
TLS SNI support enabled
configure arguments: --user=www --group=www --prefix=/www/server/nginx --with-openssl=/www/server/nginx/src/openssl --add-module=/www/server/nginx/src/ngx_devel_kit --add-module=/www/server/nginx/src/lua_nginx_module --add-module=/www/server/nginx/src/ngx_cache_purge --add-module=/www/server/nginx/src/nginx-sticky-module --add-module=/www/server/nginx/src/nginx-http-concat --with-http_stub_status_module --with-http_ssl_module --with-http_v2_module --with-http_image_filter_module --with-http_gzip_static_module --with-http_gunzip_module --with-stream --with-stream_ssl_module --with-ipv6 --with-http_sub_module --with-http_flv_module --with-http_addition_module --with-http_realip_module --with-http_mp4_module --with-ld-opt=-Wl,-E --with-pcre=pcre-8.40 --with-ld-opt=-ljemalloc
#开始下载Nginx,这里用的是1.15.1,你也可以下载其它的版本
wget http://nginx.org/download/nginx-1.15.1.tar.gz
tar -xzvf nginx-1.15.1.tar.gz
cd nginx-1.15.1
#下面的命令只是在上面的Nginx -v得到的配置详情后加上了--with-http_realip_module,目的是为了保持原来的配置不变同时又增加新的模块
./configure --user=www --group=www --prefix=/www/server/nginx --with-openssl=/www/server/nginx/src/openssl --add-module=/www/server/nginx/src/ngx_devel_kit --add-module=/www/server/nginx/src/lua_nginx_module --add-module=/www/server/nginx/src/ngx_cache_purge --add-module=/www/server/nginx/src/nginx-sticky-module --add-module=/www/server/nginx/src/nginx-http-concat --with-http_stub_status_module --with-http_ssl_module --with-http_v2_module --with-http_image_filter_module --with-http_gzip_static_module --with-http_gunzip_module --with-stream --with-stream_ssl_module --with-ipv6 --with-http_sub_module --with-http_flv_module --with-http_addition_module --with-http_realip_module --with-http_mp4_module --with-ld-opt=-Wl,-E --with-pcre=pcre-8.40 --with-ld-opt=-ljemalloc --with-http_realip_module
#只编译不安装
make

#先停用Nginx,然后替换新的Nginx并查看模块是否已经加载。命令如下:
mv /www/server/nginx/sbin/nginx /www/server/nginx/sbin/nginx-wzfou.backup
cp objs/nginx /www/server/nginx/sbin/nginx
nginx -V

#重启Nginx

二、Nginx設定set_real_ip_from

編譯好了ngx_http_realip_module,現在我們只需要在Nginx設定檔中加入set_real_ip_from程式碼,範例如下:

set_real_ip_from 222.222.222.222;  #这里是需要填写具体的CDN服务器IP地址,可添加多个
set_real_ip_from 222.222.111.111; 
real_ip_header  X-Forwarded-For;
real_ip_recursive on;

如果你用的是CloudFlare免費CDN,請將以下程式碼加入你的Nginx設定檔當中。

location / {
 set_real_ip_from 103.21.244.0/22;
 set_real_ip_from 103.22.200.0/22;
 set_real_ip_from 103.31.4.0/22;
 set_real_ip_from 104.16.0.0/12;
 set_real_ip_from 108.162.192.0/18;
 set_real_ip_from 131.0.72.0/22;
 set_real_ip_from 141.101.64.0/18;
 set_real_ip_from 162.158.0.0/15;
 set_real_ip_from 172.64.0.0/13;
 set_real_ip_from 173.245.48.0/20;
 set_real_ip_from 188.114.96.0/20;
 set_real_ip_from 190.93.240.0/20;
 set_real_ip_from 197.234.240.0/22;
 set_real_ip_from 198.41.128.0/17;
 set_real_ip_from 199.27.128.0/21;
 set_real_ip_from 2400:cb00::/32;
 set_real_ip_from 2606:4700::/32;
 set_real_ip_from 2803:f800::/32;
 set_real_ip_from 2405:b500::/32;
 set_real_ip_from 2405:8100::/32;
 set_real_ip_from 2c0f:f248::/32;
 set_real_ip_from 2a06:98c0::/29;
 # use any of the following two
 real_ip_header CF-Connecting-IP;
 #real_ip_header X-Forwarded-For;
 }

#不要忘记重启nginx
service nginx restart

一般來說CloudFlare的IP位址是不會改變的,你可以在這裡找到:https://www.cloudflare.com/ips/,但為了以防萬一,wzfou.com建議設定自動更新CloudFlare的IP的定時任務,自動將最新的IP加入Nginx的設定檔當中。程式碼如下:

#在nginx配置目录创建cloudflare_ip.conf文件
touch /usr/local/nginx/conf/cloudflare_ip.conf

#修改原有的vhost配置,将原来第五步配置的信息改为
include cloudflare_ip.conf;

#创建自更新脚本update_cloudflare_ip.sh(假定该文件放在 /root 目录下),内容如下:

#!/bin/bash
echo "#Cloudflare" > /usr/local/nginx/conf/cloudflare_ip.conf;
for i in `curl https://www.cloudflare.com/ips-v4`; do
        echo "set_real_ip_from $i;" >> /usr/local/nginx/conf/cloudflare_ip.conf;
done
for i in `curl https://www.cloudflare.com/ips-v6`; do
        echo "set_real_ip_from $i;" >> /usr/local/nginx/conf/cloudflare_ip.conf;
done
echo "" >> /usr/local/nginx/conf/cloudflare_ip.conf;
echo "# use any of the following two" >> /usr/local/nginx/conf/cloudflare_ip.conf;
echo "real_ip_header CF-Connecting-IP;" >> /usr/local/nginx/conf/cloudflare_ip.conf;
echo "#real_ip_header X-Forwarded-For;" >> /usr/local/nginx/conf/cloudflare_ip.conf;


#配置crontab 每周一的上午5点更新
0 5 * * 1 /bin/bash /root/update_cloudflare_ip.sh

三、Apache配置mod_remoteip模組

3.1  apache 2.4

apache 2.4自備mod_remoteip模組不需要安裝,請依照下文操作:

#启用模块

vim /usr/local/apache/conf/httpd.conf

Include conf/extra/httpd-remoteip.conf

#添加如下内容
vim /usr/local/apache/conf/extra/httpd-remoteip.conf

LoadModule remoteip_module modules/mod_remoteip.so
RemoteIPHeader X-Forwarded-For
RemoteIPInternalProxy 127.0.0.1/24
#CloudFlare IP Ranges
RemoteIPInternalProxy 103.21.244.0/22
RemoteIPInternalProxy 103.22.200.0/22
RemoteIPInternalProxy 103.31.4.0/22
RemoteIPInternalProxy 104.16.0.0/12
RemoteIPInternalProxy 108.162.192.0/18
RemoteIPInternalProxy 131.0.72.0/22
RemoteIPInternalProxy 141.101.64.0/18
RemoteIPInternalProxy 162.158.0.0/15
RemoteIPInternalProxy 172.64.0.0/13
RemoteIPInternalProxy 173.245.48.0/20
RemoteIPInternalProxy 188.114.96.0/20
RemoteIPInternalProxy 190.93.240.0/20
RemoteIPInternalProxy 197.234.240.0/22
RemoteIPInternalProxy 198.41.128.0/17 #你的CDN的IP,可以重复添加

#修改日志格式,在日志格式中加上%a,然后重启apache即可

LogFormat "%h %a %l %u %t "%r" %>s %b "%{Referer}i" "%{User-Agent}i"" combined
LogFormat "%h %a %l %u %t "%r" %>s %b" common
LogFormat "%h %l %u %t "%r" %>s %b "%{Referer}i" "%{User-Agent}i" %I %O" combined

3.2  apache 2.2

apache 2.2需要安裝mod_remoteip模組,方法如下:

wget https://github.com/ttkzw/mod_remoteip-httpd22/raw/master/mod_remoteip.c
/usr/local/apache/bin/apxs -i -c -n mod_remoteip.so mod_remoteip.c

#启用模块
vim /usr/local/apache/conf/httpd.conf

Include conf/extra/httpd-remoteip.conf

#添加如下内容,然后重启apache即可
vim /usr/local/apache/conf/extra/httpd-remoteip.conf

LoadModule remoteip_module modules/mod_remoteip.so
RemoteIPHeader X-Forwarded-For
RemoteIPInternalProxy 127.0.0.1 #你的CDN的IP,可以重复添加

四、網站僅允許Cloudflare CDN的IP訪問

上面我們是透過安裝ngx_http_realip_module和mod_remoteip模組獲取到了用戶真實的IP位址,但是有的時候我們需要藉助Cloudflare 的安全防護功能來防止CC或者DDoS攻擊,即僅允許Cloudflare CDN的IP訪問我們的訪問。

Nginx直接拒絕和允許IP存取代碼範例如下:

location / {
    deny  192.168.1.1;
    allow 192.168.1.0/24;
    allow 10.1.1.0/16;
    allow 2001:0db8::/32;
    #Railgun IP
    deny  all;
}

如果我們只允許Cloudflare CDN的IP存取網站,我們可以直接在nginx配置中將Cloudflare CDN的IP加入到允許的範圍內。

#直接加入
# https://www.cloudflare.com/ips
# IPv4
allow 103.21.244.0/22;
allow 103.22.200.0/22;
allow 103.31.4.0/22;
allow 104.16.0.0/12;
allow 108.162.192.0/18;
allow 131.0.72.0/22;
allow 141.101.64.0/18;
allow 162.158.0.0/15;
allow 172.64.0.0/13;
allow 173.245.48.0/20;
allow 188.114.96.0/20;
allow 190.93.240.0/20;
allow 197.234.240.0/22;
allow 198.41.128.0/17;

# IPv6
allow 2400:cb00::/32;
allow 2405:8100::/32;
allow 2405:b500::/32;
allow 2606:4700::/32;
allow 2803:f800::/32;
allow 2c0f:f248::/32;
allow 2a06:98c0::/29;

自動更新Cloudflare CDN的IP。 手動加入Cloudflare CDN的IP到Nginx配置當中簡單方便,但是一旦Cloudflare CDN的IP有變化時還得自己手動處理,我們可以創建一個腳本,定時去更新Cloudflare CDN的IP,自動添加到Nginx配置中,程式碼如下:

touch /usr/local/nginx/conf/allow_ip.conf
#修改网站nginx配置,加入以下代码:
include /usr/local/nginx/conf/allow_ip.conf;

vim /data/script/allow_cf_ip.sh
#!/bin/bash
echo "#Cloudflare" > /usr/local/nginx/conf/allow_ip.conf;
for i in `curl https://www.cloudflare.com/ips-v4`; do
     echo "allow $i;" >> /usr/local/nginx/conf/allow_ip.conf;
done
for i in `curl https://www.cloudflare.com/ips-v6`; do
       echo "allow $i;" >> /usr/local/nginx/conf/allow_ip.conf;
done


#添加定时任务
0 5 * * 1 /bin/bash /data/script/allow_cf_ip.sh

五、總結

使用了CDN加速後,我們的網站取得到的用戶IP變成了CDN的IP了,想要取得到用戶的真實IP就得利用Nginx和Apache的模式功能。當然,如果你用的是PHP,例如Wordpress,直接將以下程式碼加入你的Wordpress設定檔當中即可。

if(isset($_SERVER['HTTP_X_FORWARDED_FOR']))
{
$list = explode(‘,’,$_SERVER['HTTP_X_FORWARDED_FOR']);
$_SERVER['REMOTE_ADDR'] = $list[0];
}

這裡也要特別提醒一下,如果你啟用了Cloudflare Railgun動態加速(挖站否的Cloudflare Partner接入管理就提供此免費服務),記得將Railgun的伺服器IP加入到配置當中,因為啟用了Railgun後網站獲取到的IP位址都來自Railgun伺服器上的。

發表評論