版本比较
标识
- 该行被添加。
- 该行被删除。
- 格式已经改变。
信息 |
---|
|
|
参考资源
http://tengine.taobao.org/documentation_cn.html
Tengine设置及编译
前期准备
参考0007-CentOS7/8 系统安装标准配置初始环境
代码块 | ||||
---|---|---|---|---|
| ||||
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是开源项目,因此也可以用以下的方式获取它的最新源码,如下所示。
代码块 | ||||
---|---|---|---|---|
| ||||
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配置文件的语法加亮功能。
代码块 | ||||
---|---|---|---|---|
| ||||
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查看它的版本以及加载的模块,如下图所示。Image Removed
添加nginx启动脚本
正常编译方式下nginx没有启动脚本,可通过添加以下启动文件将其列为系统启动服务。
代码块 | ||||
---|---|---|---|---|
| ||||
vim /etc/init.d/nginx
chmod +x /etc/init.d/nginx
chkconfig –-add nginx
chkcnfig –-level 35 nginx on |
代码块 | ||
---|---|---|
| ||
#!/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
代码块 | ||||
---|---|---|---|---|
| ||||
yum install php-fpm
chkconfig --level 345 php-fpm on
service php-fpm start |
设置nginx(Fastcgi)
首先增加php支持,如下图所示。
然后在后续php项目中完成以下设置。
代码块 | ||
---|---|---|
| ||
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测试文件。
代码块 | ||||
---|---|---|---|---|
| ||||
vim /var/www/html/info.php |
打开测试文件所在的文件,成功的情况如下图所示。
升级php
代码块 | ||||
---|---|---|---|---|
| ||||
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)
代码块 | ||
---|---|---|
| ||
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
代码块 | ||||
---|---|---|---|---|
| ||||
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位)然后输入以下指令进行安装配置,如下所示:
代码块 | ||||
---|---|---|---|---|
| ||||
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 |
代码块 | ||||
---|---|---|---|---|
| ||||
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 |
代码块 | ||||
---|---|---|---|---|
| ||||
java –version |
安装tomcat
从以下的地址下载tomcat组件(http://tomcat.apache.org/download-70.cgi),选择core版本就可以了。
代码块 | ||||
---|---|---|---|---|
| ||||
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 |
代码块 | ||||
---|---|---|---|---|
| ||||
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下面
代码块 | ||||
---|---|---|---|---|
| ||||
mkdir /var/www/html/ROOT
vim /var/www/html/ROOT/index.jsp |
Hello,tomcat home!
代码块 | ||||
---|---|---|---|---|
| ||||
vim /usr/local/tomcat/conf/web.xml |
设置nginx
可以通过代理转发的方式实现和tomcat的整合,通过设定只要扩展名为.jsp的请求就转发至8080的tomcat进行处理。
代码块 | ||||
---|---|---|---|---|
| ||||
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测试文件,如下所示。
代码块 | ||||
---|---|---|---|---|
| ||||
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模块来记录、使用访问者的信息,或是根据这些信息有选择的提供服务。
配置地理数据文件
代码块 | ||||
---|---|---|---|---|
| ||||
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 失败。解决方法如下:
代码块 | ||||
---|---|---|---|---|
| ||||
yum install libtool
aclocal
libtoolize –force |
http_geoip_module模块会创建一些ngx_http_geoip_module变量,这些编码是基于客户端IP的,它会与MaxMind GeoIP文件进行匹配查询。默认情况下这些文件需要另外下载安装。
代码块 | ||||
---|---|---|---|---|
| ||||
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执行文件复制到原始的安装路径下就可以了。成功后的结果如下图所示:
代码块 | ||||
---|---|---|---|---|
| ||||
./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/ |
代码块 | ||||
---|---|---|---|---|
| ||||
vim /etc/nginx/nginx.conf
geoip_country /usr/local/nginx/geoip/GeoIP.dat;
geoip_city /usr/local/nginx/geoip/GeoLiteCity.dat; |
代码块 | ||
---|---|---|
| ||
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所在的地区
代码块 | ||||
---|---|---|---|---|
| ||||
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> |
代码块 | ||||
---|---|---|---|---|
| ||||
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中的限制,如下图所示。内核优化
内核中涉及的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";一个具体的设定如下所示:
代码块 | ||||
---|---|---|---|---|
| ||||
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个。
代码块 | ||||
---|---|---|---|---|
| ||||
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上传。
代码块 | ||||
---|---|---|---|---|
| ||||
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 |
代码块 | ||||
---|---|---|---|---|
| ||||
./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 |
代码块 | ||||
---|---|---|---|---|
| ||||
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安全的利器。
代码块 | ||||
---|---|---|---|---|
| ||||
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),规则可靠强大,当然也可以自定义规则来满足各种需求。
代码块 | ||||
---|---|---|---|---|
| ||||
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 |
代码块 | ||||
---|---|---|---|---|
| ||||
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下面加入下面两行即可:
代码块 | ||||
---|---|---|---|---|
| ||||
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
软件安装
代码块 | ||||
---|---|---|---|---|
| ||||
# yum install -y keepalived
# keepalived –v |
Keepalived v1.2.24 (11/09,2016)
nginx监控脚本
代码块 | ||||
---|---|---|---|---|
| ||||
#!/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
代码块 | ||||
---|---|---|---|---|
| ||||
! 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将整个客户端请求体保存到单个缓存中
介绍
ngx_lua_waf 是一款基于 ngx_lua 的 web 应用防火墙,使用简单,高性能、轻量级。默认防御规则在 wafconf 目录中,摘录几条核心的 SQL 注入防御规则:
`select.+(from|limit)
(?:(union(.*?)select))
(?:from\W+information_schema\W)`
这边主要分享三种另类思路,Bypass ngx_lua_waf SQL 注入防御。
环境搭建
github 源码
ngx_lua_waf 安装部署,设置反向代理访问构造的 SQL 注入点
WAF 测试
ngx_lua_waf 是基于 ngx_lua 的,我们先通过一个测试用例来了解它是如何获取参数的。
首先看一下官方 API 文档,获取一个 uri 有两个方法:ngx.req.get_uri_args
、ngx.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_args
、ngx.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 注入防御。
不同语言、中间件、数据库,所对应的特性是有差异的,而这些差异在某些特定的场景下,是可以利用的。
目录 | ||
---|---|---|
| ||