一. 安装openrestry
二. 安装GeoLite2以及类库
#
mkdir -p /home/lua
#
wget --no-cache --no-check-certificate -O /home/lua/GeoLite2-City.mmdb.gz https://cdn.jsdelivr.net/npm/[email protected]/GeoLite2-City.mmdb.gz
#
cd /home/lua && gunzip GeoLite2-City.mmdb.gz
#
wget https://codeload.github.com/anjia0532/lua-resty-maxminddb/zip/refs/heads/master
#
cd /usr/local/openresty/1.21.4.1/bin
#
wget --no-cache -O ./lua-resty-maxminddb.zip https://codeload.github.com/anjia0532/lua-resty-maxminddb/zip/refs/heads/master
#
unzip -o -d anjia0532 lua-resty-maxminddb.zip && mv anjia0532/lua-resty-maxminddb-master anjia0532/lua-resty-maxminddb
#
./opm get anjia0532/lua-resty-maxminddb
```
三. 配置相关脚本 使用封装的redis库
```shell
#
cd /usr/local/openresty/1.21.4.1/lualib/resty
#
vim redis_iresty.lua
```
lua代码如下
```lua
local redis_c = require "resty.redis"
local ok, new_tab = pcall(require, "table.new")
if not ok or type(new_tab) ~= "function" then
new_tab = function (narr, nrec) return {} end
end
local _M = new_tab(0, 155)
_M._VERSION = '0.01'
local commands = {
"append", "auth", "bgrewriteaof",
"bgsave", "bitcount", "bitop",
"blpop", "brpop",
"brpoplpush", "client", "config",
"dbsize",
"debug", "decr", "decrby",
"del", "discard", "dump",
"echo",
"eval", "exec", "exists",
"expire", "expireat", "flushall",
"flushdb", "get", "getbit",
"getrange", "getset", "hdel",
"hexists", "hget", "hgetall",
"hincrby", "hincrbyfloat", "hkeys",
"hlen",
"hmget", "hmset", "hscan",
"hset",
"hsetnx", "hvals", "incr",
"incrby", "incrbyfloat", "info",
"keys",
"lastsave", "lindex", "linsert",
"llen", "lpop", "lpush",
"lpushx", "lrange", "lrem",
"lset", "ltrim", "mget",
"migrate",
"monitor", "move", "mset",
"msetnx", "multi", "object",
"persist", "pexpire", "pexpireat",
"ping", "psetex", "psubscribe",
"pttl",
"publish", --[[ "punsubscribe", ]] "pubsub",
"quit",
"randomkey", "rename", "renamenx",
"restore",
"rpop", "rpoplpush", "rpush",
"rpushx", "sadd", "save",
"scan", "scard", "script",
"sdiff", "sdiffstore",
"select", "set", "setbit",
"setex", "setnx", "setrange",
"shutdown", "sinter", "sinterstore",
"sismember", "slaveof", "slowlog",
"smembers", "smove", "sort",
"spop", "srandmember", "srem",
"sscan",
"strlen", --[[ "subscribe", ]] "sunion",
"sunionstore", "sync", "time",
"ttl",
"type", --[[ "unsubscribe", ]] "unwatch",
"watch", "zadd", "zcard",
"zcount", "zincrby", "zinterstore",
"zrange", "zrangebyscore", "zrank",
"zrem", "zremrangebyrank", "zremrangebyscore",
"zrevrange", "zrevrangebyscore", "zrevrank",
"zscan",
"zscore", "zunionstore", "evalsha"
}
local mt = { __index = _M }
local function is_redis_null( res )
if type(res) == "table" then
for k,v in pairs(res) do
if v ~= ngx.null then
return false
end
end
return true
elseif res == ngx.null then
return true
elseif res == nil then
return true
end
return false
end
function _M.close_redis(self, redis)
if not redis then
return
end
--释放连接(连接池实现)
local pool_max_idle_time = self.pool_max_idle_time --最大空闲时间 毫秒
local pool_size = self.pool_size --连接池大小
local ok, err = redis:set_keepalive(pool_max_idle_time, pool_size)
if not ok then
ngx.say("set keepalive error : ", err)
end
end
-- change connect address as you need
function _M.connect_mod( self, redis )
redis:set_timeout(self.timeout)
local ok, err = redis:connect(self.ip, self.port)
if not ok then
ngx.say("connect to redis error : ", err)
return self:close_redis(redis)
end
if self.password then ----密码认证
local count, err = redis:get_reused_times()
if 0 == count then ----新建连接,需要认证密码
ok, err = redis:auth(self.password)
if not ok then
ngx.say("failed to auth: ", err)
return
end
elseif err then ----从连接池中获取连接,无需再次认证密码
ngx.say("failed to get reused times: ", err)
return
end
end
return ok,err;
end
function _M.init_pipeline( self )
self._reqs = {}
end
function _M.commit_pipeline( self )
local reqs = self._reqs
if nil == reqs or 0 == #reqs then
return {}, "no pipeline"
else
self._reqs = nil
end
local redis, err = redis_c:new()
if not redis then
return nil, err
end
local ok, err = self:connect_mod(redis)
if not ok then
return {}, err
end
redis:init_pipeline()
for _, vals in ipairs(reqs) do
local fun = redis[vals[1]]
table.remove(vals , 1)
fun(redis, unpack(vals))
end
local results, err = redis:commit_pipeline()
if not results or err then
return {}, err
end
if is_redis_null(results) then
results = {}
ngx.log(ngx.WARN, "is null")
end
-- table.remove (results , 1)
--self.set_keepalive_mod(redis)
self:close_redis(redis)
for i,value in ipairs(results) do
if is_redis_null(value) then
results[i] = nil
end
end
return results, err
end
local function do_command(self, cmd, ... )
if self._reqs then
table.insert(self._reqs, {cmd, ...})
return
end
local redis, err = redis_c:new()
if not redis then
return nil, err
end
local ok, err = self:connect_mod(redis)
if not ok or err then
return nil, err
end
redis:select(self.db_index)
local fun = redis[cmd]
local result, err = fun(redis, ...)
if not result or err then
-- ngx.log(ngx.ERR, "pipeline result:", result, " err:", err)
return nil, err
end
if is_redis_null(result) then
result = nil
end
--self.set_keepalive_mod(redis)
self:close_redis(redis)
return result, err
end
for i = 1, #commands do
local cmd = commands[i]
_M[cmd] =
function (self, ...)
return do_command(self, cmd, ...)
end
end
function _M.new(self, opts)
opts = opts or {}
local timeout = (opts.timeout and opts.timeout * 1000) or 1000
local db_index= opts.db_index or 0
local ip = opts.ip or '127.0.0.1'
local port = opts.port or 6379
local password = opts.password
local pool_max_idle_time = opts.pool_max_idle_time or 60000
local pool_size = opts.pool_size or 100
return setmetatable({
timeout = timeout,
db_index = db_index,
ip = ip,
port = port,
password = password,
pool_max_idle_time = pool_max_idle_time,
pool_size = pool_size,
_reqs = nil }, mt)
end
return _M
```
限制IP脚本如下
```shell
vim /home/lua/black_ip.lua
lua核心逻辑
local cjson = require "cjson"
local geo=require 'resty.maxminddb'
local arg_ip=ngx.var.arg_ip
local arg_node=ngx.var.arg_node
local enable_redis = true
local redis = require "resty.redis_iresty"
local redis_host = "10.7.0.11"
local redis_port = "6379"
local redis_password = nil
local redis_db_index = 0
local redis_key_fliter_countries = "ngx:filter:countries"
local redis_key_fliter_ips = "ngx:filter:ips"
local time = ngx.time()
local message = ""
local filter_enable = true
local filter_black_countries = {}
--{"上海","北京","南京","浙江省"}
local filter_black_ips = {"16.232.76.45"}
if not geo.initted() then
geo.init("/home/lua/GeoLite2-City.mmdb")
end
function IsInTable(value, tbl)
for k,v in ipairs(tbl) do
if v == value then
return true;
end
end
return false;
end
function getClientIp()
local headers=ngx.req.get_headers()
local ip=headers["X-REAL-IP"] or headers["X_FORWARDED_FOR"] or ngx.var.remote_addr or "0.0.0.0"
return ip
end
function getCityName(testIp)
testCityName = nil;
res,err=geo.lookup(testIp or ngx.var.remote_addr)
if not res then
return testCityName;
else
sub = res["subdivisions"]
if not sub then
testCityName = nil;
else
testCityName =sub[1]["names"]["zh-CN"] or ""
end
end
return testCityName
end
function isInFilter(testIp,filter_type,target_list)
if not target_list then
return false
end
if not filter_enable then
return false;
end
if (filter_type == 1) then
res,err=geo.lookup(testIp or ngx.var.remote_addr)
if not res then
return false;
else
testCityName = "";
--ngx.say("info:",cjson.encode(res))
sub = res["subdivisions"]
if not sub then
testCityName = "";
else
testCityName =sub[1]["names"]["zh-CN"] or ""
end
--ngx.say("name:",testCityName)
return IsInTable(testCityName,target_list)
end
else
return IsInTable(testIp,target_list);
end
end
function get_filter_in_redis(key)
filters = nil
if enable_redis then
local opts = {
ip = redis_host,
port = redis_port,
password = redis_password,
db_index = redis_db_index
}
local red = redis:new(opts)
local data, err = red:lrange(key,0,-1);
if data then
filters = data
end
end
return filters
end
local clientip = getClientIp()
local ip = arg_ip or clientip;
isHitted = false
if isInFilter(ip,0,filter_black_ips) then
isHitted = true
else
isHitted = isInFilter(ip,1,filter_black_countries);
end
if not isHitted then
redis_filter_countries = get_filter_in_redis(redis_key_fliter_countries)
redis_filter_ips = get_filter_in_redis(redis_key_fliter_ips)
isHitted = isInFilter(ip,1,redis_filter_countries) or isInFilter(ip,2,redis_filter_ips)
end
if isHitted==true then
ngx.exit(ngx.HTTP_FORBIDDEN)
else
return
end
nginx配置如下
server {
listen *:80 default;
server_name _;
charset utf-8;
location / {
root /home/www;
index index.html index.htm;
}
location /lua {
default_type "text/html";
charset utf-8;
access_by_lua_file /home/lua/black_ip.lua;
content_by_lua "ngx.say('{\"msg\":\"hello world\",\"time\":2023}')";
}
location /stub_status {
stub_status on;
allow 10.8.0.0/8;
allow 127.0.0.1; #only allow requests from localhost
deny all; #deny all other hosts
}
location = /favicon.ico {
log_not_found off;
access_log off;
}
location ~ /\.map {
deny all;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
access_log off;
}
#配置redis
10.7.0.11:6379> LPUSH ngx:filter:ips "116.232.76.45"
(integer) 1
10.7.0.11:6379> LPUSH ngx:filter:countries "上海"
(integer) 1
测试使用
#
curl http://127.0.0.1/lua
完成!
正文完