版本比较

标识

  • 该行被添加。
  • 该行被删除。
  • 格式已经改变。


目的
信息
实现对Linux下WEB服务器Nginx的编译、安装及配置,为Linux下Nginx运维提供技术参考。
title
nginx简介
制定
Tengine是由淘宝网发起的Web服务器项目。它在Nginx的基础上,针对大访问量网站的需求,添加了很多高级功能和特性。Tengine的性能和稳定性已经在大型的网站如淘宝网,天猫商城等得到了很好的检验。它的最终目标是打造一个高效、稳定、安全、易用的Web平台。

朱云峰

参考资源

http://tengine.taobao.org/documentation_cn.html

Tengine设置及编译

前期准备

参考0007-CentOS7/8 系统安装标准配置初始环境

代码块
languagebash
themeRDark
yum -y install gcc gcc-c++ autoconf libjpeglibjpeg-devel libpng libpng-devel freetype freetype-devel \
libxml2 libxml2-devel zlib zlib-devel glibc glibc-devel glib2 glib2-devel {color} bzip2 bzip2-devel \
ncursesncurses-devel curl curl-devel e2fsprogs e2fsprogs-devel krb5  krb5-devel libidnlibidn-devel \
openssl openssl-devel make patch pcre-devel
cd /root
wget http://tengine.taobao.org/download/tengine-1.5.2.tar.gz
tar zxvf tengine-1.5.2.tar.gz

编译Tengine

因为Tengine是开源项目,因此也可以用以下的方式获取它的最新源码,如下所示。

代码块
languagebash
themeRDark
yum install git pcre pcre-devel
git clone git://github.com/alibaba/tengine.git
cd tengine
./configure --prefix=/usr/local/nginx --conf-path=/etc/nginx/nginx.conf \ 
--pid-path=/var/run/nginx/nginx.pid --error-log-path=/var/log/nginx/nginx.log \ 
--http-log-path=/var/log/nginx/nginx-http.log
make
make install

设置Tengine应用环境

编译安装后系统会安编译参数生成相应的配置文件,可以使用以下的vim配置脚本实现编辑nginx.conf配置文件的语法加亮功能。

代码块
languagebash
themeRDark
wget http://www.vim.org/scripts/download_script.php?src_id=19394
mkdir -p ~/.vim/syntax/
cp nginx.vim ./.vim/syntax/
vi .vim/syntax/filetype.vim
au BufRead,BufNewFile /etc/nginx/,/usr/local/nginx/conf/ if &ft == '' | setfiletype nginx | endif

设置完成后可以通过nginx –v或 –V查看它的版本以及加载的模块。

 添加nginx启动脚本

正常编译方式下nginx没有启动脚本,可通过添加以下启动文件将其列为系统启动服务。

代码块
languagebash
themeRDark
vim /etc/init.d/nginx
chmod +x /etc/init.d/nginx
chkconfig –-add nginx
chkcnfig –-level 35 nginx on 
代码块
languagebash
#!/bin/sh
# nginx - this script starts and stops the nginx daemin#
# chkconfig: - 85 15
# description: Nginx is an HTTP(S) server, HTTP(S) reverse \
# proxy and IMAP/POP3 proxy server
# processname: nginx
# config: /etc/nginx/nginx.conf
# pidfile: /var/run/nginx/nginx.pid
# Source function library.
. /etc/rc.d/init.d/functions
# Source networking configuration.
. /etc/sysconfig/network
# Check that networking is up.
[ "$NETWORKING" = "no" ] && exit 0
nginx="/usr/local/nginx/sbin/nginx"
prog=$(basename $nginx)
NGINX_CONF_FILE="/etc/nginx/nginx.conf"
lockfile=/var/lock/subsys/nginx
start() {
  [ -x $nginx ] || exit 5
  [ -f $NGINX_CONF_FILE ] || exit 6
  echo -n $"Starting $prog: "
  daemon $nginx -c $NGINX_CONF_FILE
  retval=$?
  echo
  [ $retval -eq 0 ] && touch $lockfile
  return $retval
}
stop() {
  echo -n $"Stopping $prog: "
  killproc $prog -QUIT
  retval=$?
  echo
  [ $retval -eq 0 ] && rm -f $lockfile
  return $retval
}
restart() {
  configtest || return $?
  stop
  start
}
reload() {
  configtest || return $?
  echo -n $"Reloading $prog: "
  killproc $nginx -HUP
  RETVAL=$?
  echo
}
force_reload() {
  restart
}
configtest() {
  $nginx -t -c $NGINX_CONF_FILE
}
rh_status() {
  status $prog
}

rh_status_q() {
  rh_status >/dev/null 2>&1
}

case "$1" in
start)
  rh_status_q && exit 0
  $1
;;
stop)
  rh_status_q || exit 0 
  $1
;;
restart|configtest)
  $1
  ;;
force-reload)
force_reload
;;
status)
rh_status
;;
condrestart|try-restart)
rh_status_q || exit 0
;;
*)
echo $"Usage: $0 {start|stop|status|restart|condrestart|try-restart|reload|force-reload|configtest}"
exit 2
esac

php支持

nginx可以通过php-fpm增加php支持。默认情况下,CentOS的官方资源是没有php-fpm的, 但我们可以从Remi的RPM资源中获得,它依赖于EPEL资源。

安装php-fpm

代码块
languagebash
themeRDark
yum install php-fpm
chkconfig --level 345 php-fpm on 
service php-fpm start

设置nginx(Fastcgi)

首先增加php支持,如下图所示。
然后在后续php项目中完成以下设置。

代码块
languagetext
location ~ \.php$ {
root /var/www/html;
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;

测试php

在document root目录下建立下列PHP测试文件。

代码块
languagebash
themeRDark
vim /var/www/html/info.php

打开测试文件所在的文件,成功的情况如下图所示。

升级php

代码块
languagebash
themeRDark
yum --enablerepo=remi,remi-php71 install php-fpm php-common
yum --enablerepo=remi,remi-php71 install php-opcache php-pecl-apcu php-cli php-pear php-pdo \
php-mysqlnd php-pgsql php-pecl-mongodb php-pecl-redis php-pecl-memcache php-pecl-memcached php-gd php-mbstring php-mcrypt php-xml

php支持(PHP-CGI)

代码块
languagetext
location ~ \.php { 
  try_files $uri =404;
  fastcgi_split_path_info ^(.\.php)(/.)$;
  include fastcgi_params;
  fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
  fastcgi_param SCRIPT_NAME $fastcgi_script_name;
  fastcgi_index index.php;
  fastcgi_pass 127.0.0.1:9004;
}
location ~ .*\.(gif|jpg|jpeg|png|bmp|swf|flv|ico)$ { 
  expires 30d;
  access_log off;
}
location ~ .*\.(js|css)?$ { 
  expires 7d;
  access_log off;
}

在主机上运行fastcgi

代码块
languagebash
themeRDark
php-cgi -b 127.0.0.1:9004

java支持

nginx可以通过tomcat增加jsp支持。默认情况下,CentOS可能已经安装java运行库JDK(http://www.oracle.com/technetwork/java/javase/downloads/jdk7-downloads-1880260.html),可以通过下面的方式将其升级为最新版本。

安装jdk

先按上面的地址下载新版本的JDK文件,要注意查看系统对应的版本(区分32位和64位)然后输入以下指令进行安装配置,如下所示:

代码块
languagebash
themeRDark
tar zxvf jdk-7u45-linux-x64.tar.gz
mv /root/jdk1.7.0_45/ /usr/local/jdk
vim /etc/profile
export JAVA_HOME=/usr/local/jdk
export JRE_HOME=/usr/local/jdk/jre
export PATH=$JAVA_HOME/bin:$JAVA_HOME/jre/bin:$PATH
export CLASSPATH=$CLASSPATH:.:$JAVA_HOME/lib:$JAVA_HOME/jre/lib
代码块
languagebash
themeRDark
update-alternatives --install /usr/bin/java java /usr/local/jdk/bin/java 300
update-alternatives --install /usr/bin/javac javac /usr/local/jdk/bin/javac 300
update-alternatives --config java
update-alternatives --config javac
代码块
languagebash
themeRDark
java –version

安装tomcat

从以下的地址下载tomcat组件(http://tomcat.apache.org/download-70.cgi),选择core版本就可以了。

代码块
languagebash
themeRDark
wget http://apache.dataguru.cn/tomcat/tomcat-7/v7.0.47/bin/apache-tomcat-7.0.47.tar.gz
tar zxvf apache-tomcat-7.0.47.tar.gz
mv apache-tomcat-7.0.47 /usr/local/tomcat
/usr/local/tomcat/bin/startup.sh
ps aux |grep tomcat
netstat –tlnp
代码块
languagebash
themeRDark
vim /usr/local/tomcat/conf/server.xml
<Connector port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443" />
<Host name="localhost" appBase="/var/www/html"
unpackWARs="true" autoDeploy="true">

#根目录,页面文件要放在/var/www/html/ROOT下面

代码块
languagebash
themeRDark
mkdir /var/www/html/ROOT
vim /var/www/html/ROOT/index.jsp

Hello,tomcat home!

代码块
languagebash
themeRDark
vim /usr/local/tomcat/conf/web.xml

设置nginx

可以通过代理转发的方式实现和tomcat的整合,通过设定只要扩展名为.jsp的请求就转发至8080的tomcat进行处理。

代码块
languagebash
themeRDark
vim /etc/nginx/proxy.conf
proxy_redirect          off; 
proxy_set_header        Host $host; 
proxy_set_header        X-Real-IP $remote_addr; #获取真实IP#
proxy_set_header       X-Forwarded-For   $proxy_add_x_forwarded_for; #获取代理者的真实ip 
client_max_body_size    10m; 
client_body_buffer_size 128k; 
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;
vim /etc/nginx/nginx.conf
location ~ *.jsp$ {
root ./ROOT;
index index.jsp;
proxy_pass http://127.0.0.1:8080;
}

测试

先重启tcomcat和nginx并新建html测试文件,如下所示。

代码块
languagebash
themeRDark
vim /var/www/html/index.html
the port:80
kill `ps aux |grep tomcat |awk '/\[0-9\]/\{print $2\}' | head -n1`
/usr/local/tomcat/bin/startup.sh
service nginx restart

访问者地理信息记录

Nginx可以通过配置使用http_geoip_module模块来记录、使用访问者的信息,或是根据这些信息有选择的提供服务。

配置地理数据文件

代码块
languagebash
themeRDark
wget http://geolite.maxmind.com/download/geoip/api/c/GeoIP.tar.gz
tar zxvf GeoIP.tar.gz
cd GeoIP-1.4.8/
./configure
make
make install

如果在编译时出现"Libtool library used but `LIBTOOL' is undefined"的错误提示,这是因为 libGeoIP 自带了一个很旧的 ltmain.sh,这个文件导致成的 libtool 也很旧,这个 libtool 忽略了在 link 时调用它时传给它的 -arch 参数,导致生成的 .dylib 不是 UB 的,从而导致最后的 link 失败。解决方法如下:

代码块
languagebash
themeRDark
yum install libtool
aclocal
libtoolize –force

http_geoip_module模块会创建一些ngx_http_geoip_module变量,这些编码是基于客户端IP的,它会与MaxMind GeoIP文件进行匹配查询。默认情况下这些文件需要另外下载安装。

代码块
languagebash
themeRDark
wget http://geolite.maxminx.com/download/geoip/database/GeoLiteCountry/GeoIP.dat.gz
wget http://geolite.maxmind.com/download/geoip/database/GeoLiteCity.dat.gz
gzip -d GeoIP.dat.gz
gzip -d GeoLiteCity.dat.gz
mkdir /usr/local/nginx/geoip
mv /root/*.dat ./

配置nginx模块

Nginx的默认安装并不包括http_geoip_module模块,需要在安装nginx时指定—with-http-geoip_module选项。下图是未加载该模块的情况:

可以通下以下的指令新增模块,和全新安装nginx的方式相比,不需要执行make install只需在make成功后将nginx执行文件复制到原始的安装路径下就可以了。成功后的结果如下图所示:

代码块
languagebash
themeRDark
./configure --prefix=/usr/local/nginx --conf-path=/etc/nginx/nginx.conf --pid-path=/var/run/nginx/nginx.pid \
 --error-log-path=/var/log/nginx/nginx.log --http-log-path=/var/log/nginx/nginx-http.log --with-http_geoip_module
make
mv /usr/local/nginx/sbin/nginx /usr/local/nginx/sbin/nginx.orig
cp /root/tengine/obj/nginx /usr/local/nginx/sbin/
代码块
languagebash
themeRDark
vim /etc/nginx/nginx.conf
geoip_country /usr/local/nginx/geoip/GeoIP.dat;
geoip_city /usr/local/nginx/geoip/GeoLiteCity.dat;
代码块
languagetext
fastcgi_param GEOIP_COUNTRY_CODE3 $geoip_country_code3;
fastcgi_param GEOIP_COUNTRY_NAME $geoip_country_name;
fastcgi_param GEOIP_CITY_COUNTRY_CODE $geoip_city_country_code;
fastcgi_param GEOIP_CITY_COUNTRY_CODE3 $geoip_city_country_code3;
fastcgi_param GEOIP_CITY_COUNTRY_NAME $geoip_city_country_name;
fastcgi_param GEOIP_REGION $geoip_region;
fastcgi_param GEOIP_CITY $geoip_city;
fastcgi_param GEOIP_POSTAL_CODE $geoip_city_continent_code;
fastcgi_param GEOIP_LATITUDE $geoip_latitude;
fastcgi_param GEOIP_LONGITUDE $geoip_longitude;
include fastcgi_params;

测试地理信息

建立以下的php测试文件检测IP所在的地区

代码块
languagexml
linenumberstrue
vim /var/www/html/geoip.php
<html>
<head>
<title>What is my IP address - determine or retrieve my IP address </title>
</head>
<body>
<?php
if (getenv(HTTP_X_FORWARDED_FOR)){
$pipaddress = getenv(HTTP_X_FORWARDED_FOR);
$ipaddress = getenv(REMOTE_ADDR);
echo "You Proxy IP address is :".$pipaddress."(via $ipaddress)";
} else {
$ipaddress = getenv(REMOTE_ADDR);
echo "Your IP address is :$ipaddress";
}
$country = getenv(GEOIP_COUNTRY_NAME);
$country_code = getenv(GEOIP_COUNTRY_CODE);
echo "<br/>Your country :$country($country_code)";
?>
</body>
</html>
代码块
languagexml
linenumberstrue
vim /var/www/html/geoip_city.php
<html>
<head>
<title>What is my IP address - determine or retrieve my IP address</title>
</head>
<body>
<?php
if (getenv (HTTP_X_FORWARDED_FOR)) {
$pipaddress = getenv(HTTP_X_FORWARDED_FOR);
$ipaddress = getenv(REMOTE_ADDR);
echo"<br>Your Proxy IP address is :".$pipaddress."(via $ipaddress)";
} else {
$ipaddress = getenv (REMOTE_ADDR);
echo"<br>Your IP address is : $ipaddress";
}
$geoip_city_country_code = getenv(GEOIP_CITY_COUNTRY_CODE);
$geoip_city_country_code3 = getenv(GEOIP_CITY_COUNTRY_CODE3);
$geoip_city_country_name = getenv(GEOIP_CITY_COUNTRY_NAME);
$geoip_region = getenv(GEOIP_REGION);
$geoip_city = getenv(GEOIP_CITY);
$geoip_postal_code = getenv(GEOIP_POSTAL_CODE);
$geoip_city_continent_code = getenv(GEOIP_CITY_CONTTINENT_CODE);
$geoip_latitude = getenv(GEOIP_LATITUDE);
$geoip_longitude = getenv(GEOIP_LONGITUDE);
echo"<br>Country:$geoip_city_country_name ($geoip_city_country_code3,$geoip_city_country_code)";
echo"<br>Region:$geoip_region";
echo"<br>City:$geoip_city";
echo"<br>Postal code:$geoip_postal_code";
echo"<br>City continent code:$geoip_city_continent_code";
echo"<br>Geoip latitude:$geoip_latitude";
echo"<br>Geoip longitude:$geoip_longitude";
?>
</body>

Nginx负载均衡

最近新上了一个营销项目(和微信结合),后台用的是Tomcat。开始上线的时候因为人数不多感觉没太多问题,随着正式环境的发布,开人有人反映服务器页面无法打开,连入tomcat查看时发现连接数已满且CPU也用到了极限,初始的架构如下图所示,其使用1台tomcat和一台数据库服务器。
该业务系统主要用于微信营销,顾客在微信上下单购买(抢)对应的商品,抢购成功后该商品(券)会自动生成条形码保存在该用户的注册信息中。用户凭券到实体店完成支付和取货操作。因为涉及券核销的问题(支付完成后即时核销),因此该业务无法放在云端(核销的数据和实体店销售数据需即时交互),最终只能通过本地的方案来解决。

首先考虑到的是将网上的连接通过负载均衡的方式分散来减轻服务器的压力,这方面可以使用nginx代理来实现;其次需要解决的问题是session,对比了几种方案发现nginx内置的ip_hash策略可以解决该问题,最终网络的架构变成了下图所示,在该方案中增加了4台服务器,其中一台nginx负载转发,另外四台为新增的tomcat服务。

安装配置nginx

Nginx的安装配置十分简单,我这里实际用的是tengine版本,具体的安装方法可以参考"[ nginx配置指南之一|http://waringid.blog.51cto.com/65148/1438852]"。这里需要注意的是它的编译参数,记得它的配置文件和日志文件的存放位置。

优化系统资源

文件限制

Linux系统中文件的打开个数及单用户最多拥有的进程数是有限制的,可以通过"ulimit -n"或"ulimit -u"来查看,详细的设置可以参考"ORACLE 11G在Linux下的标准安装方法(上)"。先修改/etc/security/limit.conf中的限制,如下图所示。
内核优化

介绍

ngx_lua_waf 是一款基于 ngx_lua 的 web 应用防火墙,使用简单,高性能、轻量级。默认防御规则在 wafconf 目录中,摘录几条核心的 SQL 注入防御规则:

`select.+(from|limit)

(?:(union(.*?)select))

(?:from\W+information_schema\W)`

这边主要分享三种另类思路,Bypass ngx_lua_waf SQL 注入防御。

环境搭建

github 源码

https://github.com/loveshell/ngx_lua_waf/

ngx_lua_waf 安装部署,设置反向代理访问构造的 SQL 注入点

WAF 测试

ngx_lua_waf 是基于 ngx_lua 的,我们先通过一个测试用例来了解它是如何获取参数的。

首先看一下官方 API 文档,获取一个 uri 有两个方法:ngx.req.get_uri_argsngx.req.get_post_args,二者主要的区别是参数来源有区别,ngx.req.get_uri_args 获取 uri 请求参数,ngx.req.get_post_args 获取来自 post 请求内容。

测试用例

 `server {   listen    80;   
server_name localhost;
location /test {
content_by_lua_block {
local arg = ngx.req.get_uri_args()
for k,v in pairs(arg) do
ngx.say("[GET ] key:", k, " v:", v)
end
ngx.req.read_body()
local arg = ngx.req.get_post_args()
for k,v in pairs(arg) do
ngx.say("[POST] key:", k, " v:", v)
end
} }}`

输出测试

Image Added

通过这个测试,我们可以发现:

1、当提交同一参数 id,根据接收参数的顺序进行排序

2、当参数 id,进行大小写变换,如变形为 Id、iD、ID,则会被当做不同的参数,大小写敏感。

我们知道,window下 IIS+ASP/ASPX 大小写是不敏感的,

提交参数为:

?id=1&Id=2&iD=3&ID=4,

输出结果为:

1, 2, 3, 4

那么,当 nginx 反向代理到 IIS 服务器的时候,这就存在一个参数获取的差异,结合 HPP 进行利用,可被用来进行 Bypass ngx_lua构建的 SQL注入防御。

进阶测试

绕过姿势一:

参数大小写 + HPP

http://192.168.8.147/test/sql.aspx

?id=1 UNION/&ID=/SELECT null,name,null/&Id=/FROM master.dbo.sysdatabases

Image Added

绕过姿势二:

GPC

在 ASPX 中,有一个比较特殊的 HPP 特性,当 GET/POST/COOKIE 同时提交的参数 id,服务端接收参数 id 的顺序 GET,POST,COOKIE,中间通过逗号链接,于是就有了这个 idea。

UNION、SELECT、FROM 三个关键字分别放在 GET/POST/COOKIE 的位置,通过 ASPX 的这个特性连起来,堪称完美的一个姿势,压根不好防。

但姿势利用太过于局限: 使用 Request.Params["id"] 来获取参数,GPC 获取到参数拼接起来,仅仅作为 Bypass 分享一种思路而已。

Image Added

绕过姿势三:

uri 参数溢出

前面两种都是 MSSQL 的 Bypass,而且利用姿势还有一定的极限,有没有那么一种可以 Bypass Mysql,又可以 Bypass MSSQL,完全无视 SQL 注入防御,为所欲为的姿势呢?这就是接下来的终极大招了。

默认情况下,通过 ngx.req.get_uri_argsngx.req.get_post_args 获取 uri 参数,只能获取前 100 个参数,当提交第 101 个参数时,uri 参数溢出,无法正确获取第 100 以后的参数值,基于 ngx_lua 开发的安全防护,无法对攻击者提交的第 100 个以后的参数进行有效安全检测,从而绕过安全防御。具体分析详见我写的另一篇文章:《打破基于 OpenResty 的 WEB 安全防护(CVE-2018-9230)》

Mysql Bypass 实例:

Image Added

Mssql Bypass 实例:

Image Added

这三种姿势主要利用 HPP,结合参数获取的特性和差异,从而绕过 ngx_lua_waf 的 SQL 注入防御。

不同语言、中间件、数据库,所对应的特性是有差异的,而这些差异在某些特定的场景下,是可以利用的。

内核中涉及的TCP相关的选项在大并发连接的情况下也需要做相应的调整否则可以出"TCP: time wait bucket table overflow" 的错误提示。具体修改/etc/sysctl.conf文件,如有特殊要求请结合实际情况修改。具体如下所示:
tcp_max_tw_buckets  系统在同时所处理的最大 timewait sockets 数目。如果超过此数的话﹐time-wait socket 会被立即删除并且显示警告信息。

ip_local_port_range  用于向外连接的端口范围。

netdev_max_backlog  每个网络接口接收数据包的速率比内核处理这些包的速率快时,允许送到队列的数据包的最大数目,对重负载服务器而言,该值需要调高一点。

tcp_max_orphans 处理不属于任何进程的套接字数量,不属于任程进程的进程就是"孤儿(orphans)进程",在快速、大量的连接中这种进程会很多因此要适当的设置该参数,也可以用来防御简单的DoS攻击。

tcp_max_syn_backlog 用于记录尚未收到客户端确认信息的连接请求的最大值。

优化Nginx

使用epoll

linux下的必须模型,适用于2.6以后的内核版本,如下图所示:
优化代理配置

需要注意的是"proxy_max_temp_file_size",它主要用来设置临时文件的最大值。当被请求的文件内容大于代理缓存的大小时,该文件会被存储到这个临时文件,但是如果被请求文件的内容大于这个值的时候,那么将会从上游的服务器(被代理的服务器)上直接同步传递,而不再使用代理缓存。该指令的默认值为1GB,如果设置为0,那么意味着禁止使用临时文件。

配置Nginx

Nginx配置如下所示,其中upstream backend配置的是后端的tomcat应用,ip_hash表示启用该策略,用户的目的是为了解决后端session不一致的问题(在nginx前端还有CDN或是局域网的环境中须慎用)。

server段配置的是转发的路径和端口,需要注意"proxy_set_header Host $host:8162;"的写法。如果该变量后没有加8162端口则实际的转发会导致页面无法正常显示。其后的两条语句可以参考nginx日志的记录内容,主要用来记录外网实际的访问请求。

log_format字段用来生成指定的日志格式文件,相应的变量对应日志文件中的访问记录,可以对照下图来查看。

Nginx安全限制

随着业务的增加,网络连接的流量越来越大,合理的控制访问请求及连接数非常重要,否则仍会出现失去响应的情况。

增加IP限制功能

最简单也最容易实现的的方式是Nginx自带的IP访问控制,由模块ngx_http_limit_conn_module和来ngx_http_limit_req_module实现,通过它们可以实现对IP地址连接数及服务器访问请求数的控制。

要限制连接,必须先有一个容器对连接进行计数,在http段加入如下代码:"zone=" 给它一个名字,可以随便叫,这个名字要跟下面的 limit_conn 一致,$binary_remote_addr = 用二进制来储存客户端的地址,1m 可以储存 32000 个并发会话。

限制请求数的方式和限制连接数类似,其中"rate=10r/s"表示一秒中处理的请求为10个,如果需要限制为每分钟不超过30个则表示为"rate=30r/m";一个具体的设定如下所示:

代码块
languagetext
linenumberstrue
http {
limit_req_zone $binary_remote_addr zone=one:10m rate=10r/s;
limit_conn_zone $binary_remote_addr zone=two:10m;
}

在server段中加入以下内容,其中"burst=5"表示同时允许超过频率限制的请求数不多于5个;"limit_conn two 15"表示对于同一IP的连接数限制为15个。

代码块
languagetext
linenumberstrue
limit_req zone=one burst=5;
limit_conn two 15;

增加ngx_lua模块

ngx_lua_waf是一个基于ngx_lua的web应用防火墙,主要防止sql注入,本地包含,部分溢出,fuzzing测试,xss,SSRF等web攻击;防止svn/备份之类文件泄漏;防止ApacheBench之类压力测试工具的攻击;屏蔽常见的扫描黑客工具,扫描器;屏蔽异常的网络请求;屏蔽图片附件类目录php执行权限;防止webshell上传。

代码块
languagebash
themeRDark
git clone http://luajit.org/git/luajit-2.0.git
make
make install ln -sv /usr/local/lib/libluajit-5.1.so.2.0.3 /lib64/libluajit-5.1.so.2
wget https://github.com/simpl/ngx_devel_kit/archive/v0.2.19.tar.gz –-no-check-certificate
tar zxvf v0.2.19.tar.gz
wget https://github.com/openresty/lua-nginx-module/archive/v0.9.13.tar.gz --no-check-certificate
代码块
languagebash
themeRDark
./configure --prefix=/usr/local/nginx --conf-path=/etc/nginx/nginx.conf --pid-path=/var/run/nginx/nginx.pid \
--error-log-path=/var/log/nginx/nginx.log --http-log-path=/var/log/nginx/nginx-http.log \
--add-module=/root/ngx_devel_kit-0.2.19/ --add-module=/root/lua-nginx-module-0.9.13/ --with-ld-opt="-Wl,-rpath,$LUAJIT_LIB"
make –j2
service nginx stop
mv /usr/local/nginx/sbin/nginx /usr/local/nginx/sbin/nginx.orig
cp ./objs/nginx /usr/local/nginx/sbin/nginx
service nginx start
代码块
languagebash
themeRDark
wget https://github.com/loveshell/ngx_lua_waf/archive/master.zip --no-check-certificate
unzip master
mv ngx_lua_waf-master/ /etc/nginx/waf
mkdir -p /var/log/nginx/hack
chmod -R 755 /var/log/nginx/hack/

增加modsecurity模块

modsecurity原本是Apache上的一款开源waf,可以有效的增强web安全性,目前已经支持nginx和IIS,配合nginx的灵活和高效,可以打造成生产级的WAF,是保护和审核web安全的利器。

代码块
languagebash
themeRDark
git clone https://github.com/SpiderLabs/ModSecurity.git
cd ModSecurity/
./autogen.sh
./configure --enable-standalone-module --disable-mlogc
make
cd tengine/
./configure --prefix=/usr/local/nginx --conf-path=/etc/nginx/nginx.conf --pid-path=/var/run/nginx/nginx.pid \
--error-log-path=/var/log/nginx/nginx.log --http-log-path=/var/log/nginx/nginx-http.log \
--add-module=/root/ngx_devel_kit-0.2.19/ --add-module=/root/lua-nginx-module-0.9.13/ \
--add-module=/root/ModSecurity/nginx/modsecurity/ --with-ld-opt=" -Wl,-rpath,$LUAJIT_LIB"
make

modsecurity倾向于过滤和阻止web危险,之所以强大就在于规则,OWASP提供的规则是于社区志愿者维护的,被称为核心规则CRS(corerules),规则可靠强大,当然也可以自定义规则来满足各种需求。

代码块
languagebash
themeRDark
git clone https://github.com/SpiderLabs/owasp-modsecurity-crs
cp -R owasp-modsecurity-crs /etc/nginx/
cp /etc/nginx/owasp-modsecurity-crs/ modsecurity_crs_10_setup.conf.example /etc/nginx/owasp-modsecurity-crs/ modsecurity_crs_10_setup.conf
cd ModSecurity/
cp modsecurity.conf-recommended /etc/nginx/modsecurity.conf
cp unicode.mapping /etc/nginx
vim modsecurity.conf
代码块
languagetext
linenumberstrue
SecRuleEngine on
nclude owasp-modsecurity-crs/modsecurity_crs_10_setup.conf
Include owasp-modsecurity-crs/base_rules/modsecurity_crs_41_sql_injection_attacks.conf
Include owasp-modsecurity-crs/base_rules/modsecurity_crs_41_xss_attacks.conf
Include owasp-modsecurity-crs/base_rules/modsecurity_crs_40_generic_attacks.conf
Include owasp-modsecurity-crs/experimental_rules/modsecurity_crs_11_dos_protection.conf
Include owasp-modsecurity-crs/experimental_rules/modsecurity_crs_11_brute_force.conf
Include owasp-modsecurity-crs/optional_rules/modsecurity_crs_16_session_hijacking.conf

在需要启用modsecurity的主机的location下面加入下面两行即可:

代码块
languagetext
linenumberstrue
ModSecurityEnabled on; 
ModSecurityConfig modsecurity.conf;

参考文章
http://www.52os.net/articles/nginx-use-modsecurity-module-as-waf.html
https://github.com/SpiderLabs/ModSecurity/wiki/Reference-Manual#Installation_for_NGINX
http://drops.wooyun.org/tips/2614
http://drops.wooyun.org/tips/3804
http://drops.wooyun.org/tips/734
http://www.freebuf.com/articles/web/18084.html
http://www.freebuf.com/articles/web/16806.html

增加fail2ban防止非法IP

fail2ban是一款日志扫描软件,它可以从日志中扫描异常记录结合相应的识别规则并通过iptables封禁恶意的用户IP防止进一步的攻击。关于fail2ban的详细配置参考”1104-fail2ban防止WEB攻击“。

安装fail2ban非常简单,直接通过yum install fail2ban就可以完成安装,它的配置文件是/etc/fail2ban/jail.conf,所有的过滤策略则保存在/etc/fail2ban/filter.d/下。jail.conf的配置内容如下图所示:

我们可以先对照nginx的日志查看,之前的nginx中已经对IP和服务请求数做了限制,对于同一IP多次超过原有的请求限制的情况,我们可以使用fail2ban定义的规则(例如600秒内超过5次则封禁该IP2小时)。当然也可以根据实际情况自定义策略。

首先分析nginx的日志记录,需注意过滤策略要和日志中的内容匹配,如下图所示。
完成后重启fail2ban服务,你可以看到fail2ban的日志中封禁及解封IP的信息,如下图所示。
也可以通过fail2ban-client指令查看指定策略的应用情况或使用"fail2ban-client -D"查看配置文件的相关内容。

查看nginx状态

通过Nginx转发的方式实现负载均衡,除了可以通过分析访问日志记录的方式来统计转发次数外还可以使用nginx自带的状态统计功能来实现。它依赖于nginx的"ngx_http_stub_status_module"模块,请确保该模块在当前版本中存在。
启用它的方式很简单,具体配置如下图所示。
配置完成并重新加载nginx服务后可以通过浏览器访问目标服务器的8000端口来查看转发状态,如下图所示。
active connections:23 #nginx 正处理的活动连接数 23个。

server accepts handled requestsnginx启动到现在共处理了 24个连接 ,

nginx启动到现在共成功创建24 次握手 ,请求丢失数=(握手-连接),可以看出,我们没丢请求总共处理了50 次请求。

Reading :nginx 读取到客户端的 Header 信息数。

Writing : nginx 返回给客户端的 Header 信息数。

Waiting : Nginx 已经处理完正在等候下一次请求指令的驻留连接。开启 keep-alive 的情况下,这个值等于 active – (reading + writing)。

自动切换

keepalived简介

Keepalived的作用是检测服务器的状态,如果有一台web服务器宕机,或工作出现故障,Keepalived将检测到,并将有故障的服务器从系统中剔除,同时使用其他服务器代替该服务器的工作,当服务器工作正常后Keepalived自动将服务器加入到服务器群中,这些工作全部自动完成,不需要人工干涉,需要人工做的只是修复故障的服务器。

高可用架构

服务器

地址

Master

192.168.0.142

Slave

192.168.0.143

Vip

192.168.0.144

软件安装

代码块
languagebash
themeRDark
# yum install -y keepalived
# keepalived –v

Keepalived v1.2.24 (11/09,2016)

nginx监控脚本

代码块
languagebash
themeRDark
#!/bin/bash
counter=$(ps -C nginx --no-heading|wc -l)
if [ "${counter}" = "0" ]; then
/bin/systemctl start nginx
sleep 2
counter=$(ps -C nginx --no-heading|wc -l)
if [ "${counter}" = "0" ]; then
/bin/systemctl stop keepalived
/usr/bin/python /usr/sbin/sendsms.py 13788888888 "vip转移" "192.168.0.142出现故障"
/usr/bin/python /usr/sbin/sendsms.py 13688888888 "vip转移" "192.168.0.142出现故障"
fi
fi

 keepalived.conf

代码块
languagetext
linenumberstrue
! Configuration File for keepalived
global_defs {
notification_email {
#zhouxiao@example.com
#itsection@example.com
}
#notification_email_from itsection@example.com
#smtp_server mail.example.com
#smtp_connect_timeout 30
router_id LVS_DEVEL
}
vrrp_script chk_nginx {
script "/etc/keepalived/check_nginx.sh"
interval 2
weight -5
fall 3
rise 2
}
vrrp_instance VI_1 {
state MASTER
interface eno1
mcast_src_ip 192.168.0.142
virtual_router_id 60
priority 100
advert_int 2
authentication {
auth_type PASS
auth_pass 1111
} 
virtual_ipaddress {
192.168.0.144
}
track_script {
chk_nginx
}
}

测试

当前vip 192.168.0.144 在192.168.0.142 master服务器上,访问http://192.168.0.144 是能正常访问的。
直接在master192.168.0.142把keepalived关闭,看是否能直接转移到192.168.0.143?可以看到vip 192.168.0.144已经在master上消失。同时看到vip 192.168.0.144 成功的转移到slave上

使用情况

访问量

Pv: 即页面浏览量,或点击量;通常是衡量一个网络新闻频道或网站甚至一条网络新闻的主要指标。
Uv: 指访问某个站点或点击某条新闻的不同IP地址的人数。

Pv

13778368

Uv

19741

日志数据量

日期

大小

mpos.access_2017-05-04.log

1.0G

mpos.access_2017-05-05.log

991M

mpos.access_2017-05-06.log

989M

mpos.access_2017-05-07.log

965M

mpos.access_2017-05-08.log

935M

mpos.access_2017-05-09.log

938M

mpos.access_2017-05-10.log

914M

日志格式

log_format mpos '$time_local - $remote_addr - $upstream_addr - $upstream_status';

由于考虑到访问日志的大小,精简提取几个重要参数

Time_local:访问时间

Remote_addr:外网访问地址

Upstream_addr:反向内网地址

Upstream_status:页面状态码

连接数

类型

最新

最小

平均

最大

ESTAB

379

49

223.77

917

Fin_wait1

230

27

136.29

10.19K

Fin_wait2

1

1

3.39

105

Last_ack

51

15

53

131

Listen

14

14

14

14

Syn_recv

5

1

5.62

18

Timewait

367

84

524.19

1.39K

服务器性能情况

类型

最新

最小

平均

最大

Cpu idle time

98.96%

93.15%

99.37%

99.98%

Cpu user time

0.72%

0.0031%

0.33%

6.56%

Cpu system time

0.3%

0.01%

0.28%

0.6%

Cpu iowait time

0.0084%

0.0021%

0.004099%

0.44%

Cpu nice time

0%

0%

0.000013%

0.02%

Cpu interrupt time

0%

0%

0%

0

Cpu softirq time

0.01%

0%

0.01%

0.04%

Cpu steal time

0%

0%

0%

0%

类型

最新

最小

平均

最大

Available memory

13.59G

13.53G

13.69G

14.12G

Total memory

14.86G

14.86G

14.86G

14.86G

Total swap space

8G

8G

8G

8G

参数及表格

http编译参数

选项

说明

--with-http_ssl_module

如果需要对流量进行加密,那么可以使用这个选项,需要OpenSSl库

--with-http_realip_module

如果nginx在七层负载均衡或其他设备之后(NAT模式),需要启用该功能

--with-http_addition_module

输出过滤器,使你能够在请求经过一个location前或后时在该location本身添加内容

--with-http_xslt_module

用于处理XML响应转换,基于一个或多个XSLT格式,需要libxml2和libxslt库

--with-http_image_filter_module

图像过滤使用,在将图像投递到客户之前进行处理,需要libgd库

--with-http_geoip_module

能够设置各种变量以便在配置文件中的区段中使用,基于地理位置查找客户端IP地址,需要Maxmind GeoIP库和相应的预编译数据库文件

--with-http_sub_module

实现替代过滤,在响应中用一个字符串替代另一个字符串

--with-http_random_index_module

如果想提供一个目录中随机选择文件的索引文件,那么需要启用该模块

--with-http_secure_link_module

将一个散列值链接到一个URL中,因此,只有使用正确的密码能够及时链接

--with-http_stub_status_module

收集NGINX自身信息,输出的状态信息可使用RRDtool类的工具绘制图形

全局配置参数

参数

说明

user

配置worker进程的用户和组

worker_processes

指定进程的数量,该参数的值建议与CPU的核一致

error_log

错误日志文件,第二个参数指定日志的级别,包括:debug\info\notice\warn\error\crit\alert\emerg

worker_connections

配置一个工作进程能接受的最大连接数。

HTTP的server部分参数

http客户端指令

说明

chunked_transfer_encoding

在发送给客户端的响应中,该指令允许禁用http/1.1标准的块传输编码

client_body_buffer_size

为了阻止临时文件写到磁盘,可以通过该指令为客户端请求设置缓存大小,默认的缓存大小为两个内存页面

client_body_in_file_only

用于调试或是进一步处理客户端请求体。设置为on时强制将客户端请求体写入磁盘

client_body_in_single_buffer

为了减少复制的操作,使用该指令强制nginx将整个客户端请求体保存到单个缓存中

目录
printablefalse