1. Requirement analysis
There are many methods for Nginx to handle access control, and there are also many implementation effects, such as access IP segments, access content restrictions, and access frequency restrictions.
Using Nginx + Lua + Redis for access restrictions mainly takes into account the need for fast access control in high-concurrency environments.
The Nginx request processing process is divided into 11 stages:
Post-read, server-rewrite, find-config, rewrite, post-rewrite, preaccess, access, post-access, try-files, content, and log.
In openresty, you can find:
Set_by_lua, access_by_lua, content_by_lua, rewrite_by_lua, and other methods.
The access control should be in the access phase.
Solution
According to the normal logic, we will think of the following access control scheme:
1. Is it forbidden?
= "Yes, whether forbidden expires: Yes, clear the record, return 200, normal access; no, return 403;
= "No, 200 is returned for normal access
2. The access frequency of each access is + 1.
3. Check whether the access frequency has exceeded the limit. If the limit is exceeded, the forbidden record is added and 403 is returned.
This is a simple solution. You can also add branches and leaves. The access prohibition time is imported using algorithms, and each concave curve increases.
Implementation method
Add the vhost configuration file for nginx. The vhost. conf file is as follows:
Lua_package_path "/usr/local/openresty/lualib /?. Lua; "; # tell openresty Library address
Lua_package_cpath "/usr/local/openresty/lualib /?. So ;;";
Error_log/usr/local/openresty/nginx/logs/openresty. debug. log debug;
Server {
Listen 8080 default;
Server_name www.ttlsa.com;
Root/www/openresty;
Location/login {
Default_type 'text/html ';
Access_by_lua_file "/usr/local/openresty/nginx/lua/access_by_redis.lua"; # handle access control through lua
}
}
Access_by_redis.lua
With reference to v2ex.com, it is enough to simply store strings in the redis storage solution. The keys are:
User logon record: user: 127.0.0.1: time (unix timestamp)
Access restriction: block: 127.0.0.1
Connect to Redis first:
Local red = redis: new ()
Function M: redis ()
Red: set_timeout (1000)
Local OK, err = red: connect ("127.0.0.1", 6379)
If not OK then
Ngx. exit (ngx. HTTP_INTERNAL_SERVER_ERROR)
End
End
According to our logic scheme, the second step is to check whether it is forbidden. Next we will check the block: 127.0.0.1. If the data is found, check whether the time has expired. If it has not expired, 403 is returned, otherwise, 200 is directly returned:
Function M: check1 ()
Local time = OS. time () -- system time
Local res, err = red: get ("block:"... ngx. var. remote_addr)
If not res then -- redis error
Ngx. exit (ngx. HTTP_INTERNAL_SERVER_ERROR) -- redis get data error end
If type (res) = "string" then -- if red not null then type (red) = string
If tonumber (res)> = tonumber (time) then -- check if forbidden expired
Ngx. exit (ngx. HTTP_FORBIDDEN)
-- Ngx. say ("forbidden ")
End
End
}
Next, we will check whether the access frequency is too high. If the access frequency is too high, we need to pull it to the blacklist,
The method is to check whether the value of user: 127.0.0.1: time is exceeded:
Function M: check2 ()
Local time = OS. time () -- system time
Local res, err = red: get ("user:"... ngx. var. remote_addr... ":"... time)
If not res then -- redis error
Ngx. exit (ngx. HTTP_INTERNAL_SERVER_ERROR) -- redis get data error
End
If type (res) = "string" then
If tonumber (res)> = 10 then -- attack, 10 times request/s
Red: del ("block:" .. self. ip)
Red: set ("block:" .. self. ip, tonumber (time) + 5*60) -- set block time
Ngx. exit (ngx. HTTP_FORBIDDEN)
End
End
End
Finally, remember to make a self-increasing access time, user: 127.0.0.1: time:
Function M: add ()
Local time = OS. time () -- system time
OK, err = red: incr ("user:"... ngx. var. remote_addr... ":"... time)
If not OK then
Ngx. exit (ngx. HTTP_INTERNAL_SERVER_ERROR) -- redis get data error
End
End
In the test, the browser will be refreshed several times. After a while, the system will return 403 and OK.
Example 2
I. Overview
Requirement: All Access/myapi/** requests must be POST requests, and illegal requests (blacklists) that do not comply with the rules are filtered based on the request parameters ), these requests are not forwarded to backend servers (Tomcat)
Implementation idea: access restrictions are imposed on Nginx, and Lua is used to flexibly meet business requirements. Redis is used to store the blacklist list.
II. Specific implementation
1. lua code
In this example, the restrictions include (post request, IP address blacklist, imsi in request parameters, tel value, and blacklist)
-- Access_by_lua_file '/usr/local/lua_test/my_access_limit.lua ';
Ngx. req. read_body ()
Local redis = require "resty. redis"
Local red = redis. new ()
Red. connect (red, '127. 0.0.1 ', '123 ')
Local myIP = ngx. req. get_headers () ["X-Real-IP"]
If myIP = nil then
MyIP = ngx. req. get_headers () ["x_forwarded_for"]
End
If myIP = nil then
MyIP = ngx. var. remote_addr
End
If ngx. re. match (ngx. var. uri, "^ (/myapi/). * $") then
Local method = ngx. var. request_method
If method = 'post' then
Local args = ngx. req. get_post_args ()
Local hasIP = red: sismember ('black. IP', myIP)
Local hasIMSI = red: sismember ('black. imsi ', args. imsi)
Local hasTEL = red: sismember ('black. Tel', args. tel)
If hasIP = 1 or hasIMSI = 1 or hasTEL = 1 then
-- Ngx. say ("This is 'black list' request ")
Ngx. exit (ngx. HTTP_FORBIDDEN)
End
Else
-- Ngx. say ("This is 'GET' request ")
Ngx. exit (ngx. HTTP_FORBIDDEN)
End
End
2. nginx. conf
Location /{
Root html;
Index index.html index.htm;
Access_by_lua_file/usr/local/lua_test/my_access_limit.lua;
Proxy_pass http: // 127.0.0.1: 8080;
Client_max_body_size 1 m;
}
3. Add blacklist rule Data
# Redis-cli sadd black. ip '2017. 34.118.50'
# Redis-cli sadd black. imsi '123'
# Redis-cli sadd black. tel '123'
You can use redis-cli smembers black. imsi to view the list details.
4. Verification results
# Curl-d "imsi = 460123456789 & tel = 15800000000" "http://www.mysite.com/myapi/abc"