首页 > 技术文章 > Nginx基础

Wang-Hongwei 2020-07-10 14:31 原文

1.Nginx加载Lua环境
默认情况下Nginx不支持Lua模块, 需要安装LuaJIT解释器, 并且需要重新编译Nginx, 建议使用openrestry

1)环境准备

[root@localhost ~]# yum -y install gcc gcc-c++ make pcre-devel zlib-devel openssl-devel

2)下载luajit和ngx_devel_kit以及lua-nginx-module

[root@localhost ~]# cd /usr/local/src
[root@localhost src]# wget http://luajit.org/download/LuaJIT-2.0.4.tar.gz
[root@localhost src]# wget https://github.com/simpl/ngx_devel_kit/archive/v0.2.19.tar.gz
[root@localhost src]# wget https://github.com/openresty/lua-nginx-module/archive/v0.10.13.tar.gz

3)解压ngx_devel_kit和lua-nginx-module

[root@localhost src]# tar xf v0.2.19.tar.gz 
[root@localhost src]# tar xf v0.10.13.tar.gz

4)安装LuaJIT Luajit是Lua即时编译器。

[root@localhost src]# tar zxvf LuaJIT-2.0.3.tar.gz 
[root@localhost src]# cd LuaJIT-2.0.3
[root@localhost LuaJIT-2.0.3]# make && make install

5)安装Nginx并加载模块

[root@localhost ~]# cd /usr/local/src
[root@localhost src]# wget http://nginx.org/download/nginx-1.12.2.tar.gz
[root@localhost src]# tar xf nginx-1.12.2.tar.gz
[root@localhost src]# cd nginx-1.12.2
[root@localhost nginx-1.12.2]# ./configure --prefix=/usr/local/nginx --with-http_ssl_module \
--with-http_stub_status_module --with-http_dav_module \
--add-module=../ngx_devel_kit-0.2.19/ \
--add-module=../lua-nginx-module-0.10.13 
[root@localhost nginx-1.12.2]# make -j2 && make install

#建立软链接, 不建立会出现share object错误
[root@localhost ~]#ln -s /usr/local/lib/libluajit-5.1.so.2 /lib64/libluajit-5.1.so.2

#加载lua库,加入到ld.so.conf文件
[root@localhost ~]# echo "/usr/local/LuaJIT/lib" >> /etc/ld.so.conf
[root@localhost ~]# ldconfig

2.Nginx调用Lua指令
Nginx调用Lua模块指令, Nginx的可插拔模块加载执行, 共11个处理阶段

语法	
set_by_lua
set_by_lua_file	设置Nginx变量,可以实现负载的赋值逻辑
access_by_lua
access_by_lua_file	请求访问阶段处理, 用于访问控制
content_by_lua
content_by_lua_file	内容处理器, 接受请求处理并输出响应

Nginx调用Lua API

变量	
ngx.var	nginx变量
ngx.req.get_headers	获取请求头
ngx.req.get_uri_args	获取url请求参数
ngx.redirect	重定向
ngx.print	输出响应内容体
ngx.say	输出响应内容体,最后输出一个换行符
ngx.header	输出响应头

3.Nginx+Lua实现代码灰度发布
使用Nginx结合lua实现代码灰度发布
按照一定的关系区别,分不分的代码进行上线,使代码的发布能平滑过渡上线

1.用户的信息cookie等信息区别
2.根据用户的ip地址, 颗粒度更广

执行过程:
1.用户请求到达前端代理Nginx, 内嵌的lua模块会解析Nginx配置文件中Lua脚本
2.Lua脚本会获取客户端IP地址,查看Memcached缓存中是否存在该键值
3.如果存在则执行@java_test,否则执行@java_prod
4.如果是@java_test, 那么location会将请求转发至新版代码的集群组
5.如果是@java_prod, 那么location会将请求转发至原始版代码集群组
6.最后整个过程执行后结束

实践环境准备:

系统	服务	地址
CentOS7	Nginx+Lua+Memached	10.0.0.11
CentOS7	Tomcat集群8080_Prod	10.0.0.12
CentOS7	Tomcat集群9090_Test	10.0.0.13

1)安装两台服务器Tomcat,分别启动8080和9090端口

[root@tomcat-node1 ~]# yum install java -y
[root@tomcat-node1 ~]# cd /usr/local/src
[root@tomcat-node1 src]# wget https://mirrors.tuna.tsinghua.edu.cn/apache/tomcat/tomcat-9/v9.0.36/bin/apache-tomcat-9.0.36.tar.gz
[root@tomcat-node1 src]# tar xf apache-tomcat-9.0.36.tar.gz
[root@tomcat-node1 src]# cp -r apache-tomcat-9.0.36 /usr/local/tomcat-8080
[root@tomcat-node1 src]# /usr/local/tomcat-8080/bin/startup.sh
#tomcat默认监听在8080端口, 如果需要启动9090端口需要修改server.xml配置文件

2)配置Memcached并让其支持Lua调用

#安装memcached服务
[root@localhost ~]# yum install memcached -y

#配置memcached支持lua
[root@localhost ~]# cd /usr/local/src
[root@localhost src]# wget https://github.com/openresty/lua-resty-memcached/archive/lua-resty-memcached-master.zip
[root@localhost src]# tar xf v0.11.tar.gz
[root@localhost src]# cp -r lua-resty-memcached-0.11/lib/resty/memcached.lua /usr/local/nginx/lua/ #启动memcached [root@localhost ~]# systemctl start memcached [root@localhost ~]# systemctl enable memcached

3)配置负载均衡调度

#必须在http层
lua_package_path "/usr/local/nginx/lua/memcached.lua";
upstream java_prod {
server 10.0.0.12:8080;
}

upstream java_test {
server 10.0.0.13:9090;
}

server {
listen 80;
server_name 10.0.0.11;


location /hello {
default_type 'text/plain';
content_by_lua 'ngx.say("hello ,lua scripts")';
}

location /myip {
default_type 'text/plain';
content_by_lua '
clientIP = ngx.req.get_headers()["x_forwarded_for"]
ngx.say("Forwarded_IP:",clientIP)
if clientIP == nli then
clientIP = ngx.var.remote_addr
ngx.say("Remote_IP:",clientIP)
end
';
}
location / {
default_type 'text/plain';
content_by_lua_file /usr/local/nginx/lua/dep.lua;
}
location @java_prod {
proxy_pass http://java_prod;
include proxy_params;
}

location @java_test {
proxy_pass http://java_test;
include proxy_params;
}
}


#nginx反向代理tomcat,必须配置头部信息否则返回400错误

proxy_redirect default;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

proxy_connect_timeout 30;
proxy_send_timeout 60;
proxy_read_timeout 60;

proxy_buffer_size 32k;
proxy_buffering on;
proxy_buffers 4 128k;
proxy_busy_buffers_size 256k;
proxy_max_temp_file_size 256k;

4)编写Nginx调用灰度发布Lua脚本

[root@localhost ~]# vim /usr/local/nginx/lua/dep.lua 
--获取x-real-ip
clientIP = ngx.req.get_headers()["X-Real-IP"]

--如果IP为空-取x_forwarded_for
if clientIP == nil then
clientIP = ngx.req.get_headers()["x_forwarded_for"]
end

--如果IP为空-取remote_addr
if clientIP == nil then
clientIP = ngx.var.remote_addr
end

--定义本地,加载memcached
local memcached = require "resty.memcached"
--实例化对象
local memc, err = memcached:new()
--判断连接是否存在错误
if not memc then
ngx.say("failed to instantiate memc: ", err)
return
end
--建立memcache连接
local ok, err = memc:connect("127.0.0.1", 11211)
--无法连接往前端抛出错误信息
if not ok then
ngx.say("failed to connect: ", err)
return
end
--获取对象中的ip-存在值赋给res
local res, flags, err = memc:get(clientIP)
--
--ngx.say("value key: ",res,clientIP)
if err then
ngx.say("failed to get clientIP ", err)
return
end
--如果值为1则调用local-@java_test
if res == "1" then
ngx.exec("@java_test")
return
end
--否则调用local-@java_prod
ngx.exec("@java_prod")
return

5)使用Memcache set IP, 测试灰度发布

#telnet传入值
[root@localhost ~]# telnet 127.0.0.1 11211
# set对应IP
set 10.0.0.10 0 0 1
# 输入1
1

4.基本安全概述
1)常见的恶意行为
爬虫行为和恶意抓取,资源盗取
防护手段
基础防盗链功能不让恶意用户能够轻易的爬取网站对外数据
access_moudle->对后台,部分用户服务的数据提供IP防护
解决方法

server {
listen 80;
server_name localhost;

set $ip 0;
if ($http_x_forward_for ~ 10.0.0.10){
set $ip 1;
}
if ($remote_addr ~ 10.0.0.10){
set $ip 1;
}
# 如果$ip值为0,则返回403, 否则允许访问
location /admin {
if ($ip = "0"){
return 403;
}
default_type application/json;
return 200 '{"status":"success"}';
}

2)常见的攻击手段(代码植入)
后台密码撞库,通过猜测密码字典不断对后台系统登陆性尝试,获取后台登陆密码
防护手段
1.后台登陆密码复杂度
2.使用access_module-对后台提供IP防控
3.预警机制
文件上传漏洞,利用上传接口将恶意代码植入到服务器中,再通过url去访问执行代码
执行方式admin.com/1.jpg/1.php
解决办法

location ^~ /upload {
root /usr/local/openresty/nginx/html/upload;
if ($request_filename ~* (.*)\.php){
return 403;
}
}

3)常见的攻击手段(SQL注入)
利用未过滤/未审核的用户输入进行Sql注入的攻击方法, 让应用运行本不应该运行的SQL代码
防护手段
1.php配置开启安全相关限制
2.开发人员对sql提交进行审核,屏蔽常见的注入手段
3.Nginx+Lua构建WAF应用层防火墙, 防止Sql注入

5.Nginx+Lua实现WAF应用防火墙
1)快速安装lnmp架构(略)
2)配置MySQL

[root@localhost ~]# systemctl start mariadb
[root@localhost ~]# mysql

MariaDB [(none)]> create database info;
MariaDB [(none)]> use info;
MariaDB [info]> create table user(id int(11),username varchar(64), password varchar(64), email varchar(64));
MariaDB [info]> desc user;
+----------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+----------+-------------+------+-----+---------+-------+
| id | int(11) | YES | | NULL | |
| username | varchar(64) | YES | | NULL | |
| password | varchar(64) | YES | | NULL | |
| email | varchar(64) | YES | | NULL | |
+----------+-------------+------+-----+---------+-------+

#插入数据
MariaDB [info]> insert into user (id,username,password,email) values(1,'admin',('123'),'admin@example.com');
MariaDB [info]> select * from info.user;
+------+----------+----------------------------------+-----------------+
| id | username | password | email |
+------+----------+----------------------------------+-----------------+
| 1 | admin | 123 | admin@example.com |
+------+----------+----------------------------------+-----------------+
1 row in set (0.00 sec)

3)配置php代码

[root@localhost ~]# vim /usr/local/nginx/html/login.html 
<html>
<head>
<title> Sql注入演示场景 </title>
<meta http-equiv="content-type"content="text/html;charset=utf-8">
</head>
<body>
<form action="sql.php" method="post">
<table>
<tr>
<td> 用 户: </td>
<td><input type="text" name="username"></td>
</tr>

<tr>
<td> 密 码: </td>
<td><input type="text" name="password"></td>
</tr>
<tr>
<td><input type="submit" value="提交"></td>
<td><input type="reset" value="重置"></td>
</tr>
</table>
</form>
</body>
</html>

#被html调用的sql.php文件
[root@localhost ~]# vim /usr/local/nginx/html/sql.php
<?php
$conn = mysql_connect("localhost",'root','') or die("数据库连接失败!");
mysql_select_db("info",$conn) or die ("您选择的数据库不存在");
$name=$_POST['username'];
$pwd=$_POST['password'];
$sql="select * from user where username='$name' and password='$pwd'";
echo $sql."<br />";
$query=mysql_query($sql);
$arr=mysql_fetch_array($query);
if($arr){
echo "login success!<br />";
echo $arr[1];
echo $arr[3]."<br /><br />";
}else{
echo "login failed!";
}
?>

4.部署Waf相关防护代码

[root@localhost ~]# cd /usr/local/src/
[root@localhost src]# git clone https://github.com/loveshell/ngx_lua_waf.git
[root@localhost src]# cp -r ngx_lua_waf/ /usr/local/nginx/conf/waf

#在nginx.conf的http段添加
lua_package_path "/usr/local/nginx/conf/waf/?.lua";
lua_shared_dict limit 10m;
init_by_lua_file /usr/local/nginx/conf/waf/init.lua; 
access_by_lua_file /usr/local/nginx/conf/waf/waf.lua;

#配置config.lua里的waf规则目录
[root@localhost ~]# vim /usr/local/nginx/conf/waf/config.lua
RulePath = "/usr/local/nginx/conf/waf/wafconf/"

#防止Sql注入
[root@localhost ~]# vim /usr/local/nginx/conf/waf/wafconf/post
\sor\s+

#防止CC攻击
[root@localhost ~]# vim /usr/local/nginx/conf/waf/config.lua
CCDeny="on"
CCrate="100/60"

推荐阅读