Openresty使用redis存储IP黑白名单

506次阅读
没有评论

共计 5080 个字符,预计需要花费 13 分钟才能阅读完成。

Openresty使用redis存储IP黑白名单

之前写的ip白名单用的本地nginx内存空间,更改不方便,今日尝试用lua-resty-redis模块把IP黑白名单写到redis内,这样就可以通过操作redis来实现IP的增删改查。之后也可以让开发在项目管理端写个IP名单管理页,这样也省了运维去直接操作redis

此处演示下redis IP黑名单操作,为了存储IP,此处在redis中使用集合数据类型来存储

Redis集合

redis的Set是String类型的无序集合(zset则是有序的),集合成员是唯一,所以Set类的元素不会重复。而且redis中的Set是通过哈希表实现的,对于增删查的复杂度是O(1)。

# 127.0.0.1:6379> SADD key member [member ...]
127.0.0.1:6379> SADD block-ip-list 192.168.44.1
(integer) 1
127.0.0.1:6379> SADD block-ip-list 192.168.44.2 192.168.44.3

(integer) 1

# 127.0.0.1:6379> SREM key member [member ...]
127.0.0.1:6379> SREM block-ip-list 192.168.44.3
(integer) 1

# 127.0.0.1:6379> SISMEMBER key member
127.0.0.1:6379> SISMEMBER block-ip-list 192.168.44.3
(integer) 0
127.0.0.1:6379> SISMEMBER block-ip-list 192.168.44.1
(integer) 1

# 127.0.0.1:6379> SMEMBERS key 
127.0.0.1:6379> SMEMBERS block-ip-list
1) "192.168.44.1"
2) "192.168.44.2"

lua-resty-redis调用redis集合存储IP名单

使用sismember方法

[root@nginx-cluster lua]# cat ip_acl_redis.lua
local redis = require "resty.redis"
local red = redis:new()
red:set_timeout(1000,1000,1000) -- 1 sec
local ok,err = red:connect("127.0.0.1",6379)
if not ok then
    ngx.say("failed to connect: ",err)
    return
end
ngx.log(ngx.INFO, " ok: ", ok, " type(ok): ", type(ok)," err: ", err)

--- 获取代理头部中透传的真实客户端地址
local headers=ngx.req.get_headers()
local ip = headers["X-REAL-IP"] or headers["X_FORWARDED_FOR"] or ngx.var.remote_addr

--- 判断ip是否set中的元素,若存在则返回为1,若不存在则返回0
local res, err = red:sismember('block-ip-list', ip)
if 1 == res then
    ngx.log(ngx.INFO, "client ip in block-ip-list: ", ip)
    ngx.exit(ngx.HTTP_FORBIDDEN)
else
    ngx.log(ngx.INFO, "client ip not in block-ip-list: ", ip)
end

location块内容

    location /sayHello {
        access_by_lua_file lua/ip_acl_redis.lua;
        content_by_lua_block {
            ngx.say("Allow Operating")
            ngx.exit(ngx.OK)
        }
    }

模拟测试

正常访问

[root@nginx-cluster conf.d]# curl 127.0.0.1:8086/sayHello
Allow Operating

# 查看日志
2021/09/08 12:07:55 [info] 3401#0: *1 [lua] ip_acl_redis.lua:9:  ok: 1 type(ok): number err: nil, client: 127.0.0.1, server: , request: "GET /sayHello HTTP/1.1", host: "127.0.0.1:8086"
2021/09/08 12:07:55 [info] 3401#0: *1 [lua] ip_acl_redis.lua:21: client ip not in block-ip-list: 127.0.0.1, client: 127.0.0.1, server: , request: "GET /sayHello HTTP/1.1", host: "127.0.0.1:8086"
2021/09/08 12:07:55 [info] 3401#0: *1 client 127.0.0.1 closed keepalive connection

在redis中将ip加入block-ip-list集合中

[root@nginx-cluster conf.d]# redis-cli
127.0.0.1:6379> SADD block-ip-list 127.0.0.1
(integer) 1
127.0.0.1:6379> SMEMBERS block-ip-list
1) "127.0.0.1"
2) "192.168.44.1"
3) "192.168.44.2"

此时测试就会收到403

[root@nginx-cluster conf.d]# curl 127.0.0.1:8086/sayHello
<html>
<head><title>403 Forbidden</title></head>
<body>
<center><h1>403 Forbidden</h1></center>
<hr><center>openresty/1.17.8.2</center>
</body>
</html>

# 对应日志
2021/09/08 12:13:20 [info] 3402#0: *3 [lua] ip_acl_redis.lua:9:  ok: 1 type(ok): number err: nil, client: 127.0.0.1, server: , request: "GET /sayHello HTTP/1.1", host: "127.0.0.1:8086"
2021/09/08 12:13:20 [info] 3402#0: *3 [lua] ip_acl_redis.lua:18: client ip in block-ip-list: 127.0.0.1, client: 127.0.0.1, server: , request: "GET /sayHello HTTP/1.1", host: "127.0.0.1:8086"
2021/09/08 12:13:20 [info] 3402#0: *3 client 127.0.0.1 closed keepalive connection

提取为模块

ip_acl module

[root@nginx-cluster lua]# cat comm/ip_acl.lua
local _M = {}

function _M.is_member(client, ip_set, client_ip)
    local res, err = client:sismember(ip_set, client_ip)
    if 1 == res  then
        return true
    else
        return false
    end
end

function _M.redis_client(host,port,pass)
    local redis = require "resty.redis"
    local client = redis:new()
    client:set_timeout(1000,1000,1000) -- 1 sec
    local ok,err = client:connect(host,port)
    if not ok then
        ngx.say("failed to connect: ",err)
        return
    end
    ngx.log(ngx.INFO, " ok: ", ok, " type(ok): ", type(ok)," err: ", err)
    return client
end
return _M

lua脚本文件

[root@nginx-cluster lua]# cat ip_acl_redis2.lua
local param = require("comm.ip_acl")


local client = param.redis_client("127.0.0.1", 6379, "")

--- 设置redis名单 keyname
local keyname
local keyname =  nil == keyname and  ngx.var.host..":"..ngx.var.server_port..":"..ngx.var.uri or keyname
local ip_set = "ip-set:"..keyname
ngx.log(ngx.INFO, "ip_set: ", ip_set)

--- 获取代理头部中透传的真实客户端地址
local headers=ngx.req.get_headers()
local client_ip = headers["X-REAL-IP"] or headers["X_FORWARDED_FOR"] or ngx.var.remote_addr

--- 判断ip是否set中的元素,若为真则返回为1
if param.is_member(client,ip_set,client_ip) then
    ngx.log(ngx.INFO, "client ip : "..client_ip.." is in set: "..ip_set)
    ngx.exit(ngx.HTTP_FORBIDDEN)
    return
end

此时对应的日志

2021/09/08 15:13:20 [info] 15408#0: *5 [lua] ip_acl.lua:21: redis_client():  ok: 1 type(ok): number err: nil, client: 127.0.0.1, server: , request: "GET /sayHi HTTP/1.1", host: "127.0.0.1:8085"
2021/09/08 15:13:20 [info] 15408#0: *5 [lua] ip_acl_redis2.lua:20: ip_set: ip-set:127.0.0.1:8085:/sayHi, client: 127.0.0.1, server: , request: "GET /sayHi HTTP/1.1", host: "127.0.0.1:8085"
2021/09/08 15:13:20 [info] 15408#0: *5 [lua] ip_acl_redis2.lua:28: client ip : 127.0.0.1 is in set: ip-set:127.0.0.1:8085:/sayHi, client: 127.0.0.1, server: , request: "GET /sayHi HTTP/1.1", host: "127.0.0.1:8085"
2022/12/28 20:12:00 [info] 15408#0: *5 client 127.0.0.1 closed keepalive connection
2021/09/08 15:13:25 [info] 15408#0: *7 [lua] ip_acl.lua:21: redis_client():  ok: 1 type(ok): number err: nil, client: 192.168.44.145, server: , request: "GET /sayHi HTTP/1.1", host: "192.168.44.145:8085"
2021/09/08 15:13:25 [info] 15408#0: *7 [lua] ip_acl_redis2.lua:20: ip_set: ip-set:192.168.44.145:8085:/sayHi, client: 192.168.44.145, server: , request: "GET /sayHi HTTP/1.1", host: "192.168.44.145:8085"
2021/09/08 15:13:25 [info] 15408#0: *7 client 192.168.44.145 closed keepalive connection

正文完
 
xadocker
版权声明:本站原创文章,由 xadocker 2021-09-08发表,共计5080字。
转载说明:除特殊说明外本站文章皆由CC-4.0协议发布,转载请注明出处。
评论(没有评论)