Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Redis based IP blacklist for Nginx (LUA)
-- a quick LUA access script for nginx to check IP addresses against an
-- `ip_blacklist` set in Redis, and if a match is found send a HTTP 403.
--
-- allows for a common blacklist to be shared between a bunch of nginx
-- web servers using a remote redis instance. lookups are cached for a
-- configurable period of time.
--
-- block an ip:
-- redis-cli SADD ip_blacklist 10.1.1.1
-- remove an ip:
-- redis-cli SREM ip_blacklist 10.1.1.1
--
-- also requires lua-resty-redis from:
-- https://github.com/agentzh/lua-resty-redis
--
-- your nginx http context should contain something similar to the
-- below: (assumes resty/redis.lua exists in /etc/nginx/lua/)
--
-- lua_package_path "/etc/nginx/lua/?.lua;;";
-- lua_shared_dict ip_blacklist_cache 10m;
--
-- you can then use the below (adjust path where necessary) to check
-- against the blacklist in a http, server, location, if context:
--
-- access_by_lua_file /etc/nginx/lua/ip_blacklist.lua;
--
-- chris boulton, @surfichris
local redis_host = "your.redis.server.here"
local redis_port = 6379
-- connection timeout for redis in ms. don't set this too high!
local redis_timeout = 200
-- check a set with this key for blacklist entries
local redis_key = "ip_blacklist"
-- cache lookups for this many seconds
local cache_ttl = 60
-- end configuration
local ip = ngx.var.remote_addr
local ip_blacklist_cache = ngx.shared.ip_blacklist_cache
-- setup a local cache
if cache_ttl > 0 then
-- lookup the value in the cache
local cache_result = ip_blacklist_cache:get(ip)
if cache_result then
ngx.log(ngx.DEBUG, "ip_blacklist: found result in cache for "..ip.." -> "..cache_result)
if cache_result == 0 then
ngx.log(ngx.DEBUG, "ip_blacklist: (cache) no result found for "..ip)
return
end
ngx.log(ngx.INFO, "ip_blacklist: (cache) "..ip.." is blacklisted")
return ngx.exit(ngx.HTTP_FORBIDDEN)
end
end
-- lookup against redis
local resty = require "resty.redis"
local redis = resty:new()
redis:set_timeout(redis_timeout)
local connected, err = redis:connect(redis_host, redis_port)
if not connected then
ngx.log(ngx.ERR, "ip_blacklist: could not connect to redis @"..redis_host..": "..err)
return
end
local result, err = redis:sismember("ip_blacklist", ip)
if not result then
ngx.log(ngx.ERR, "ip_blacklist: lookup failed for "..ip..":"..err)
return
end
-- cache the result from redis
if cache_ttl > 0 then
ip_blacklist_cache:set(ip, result, cache_ttl)
end
redis:set_keepalive(10000, 2)
if result == 0 then
ngx.log(ngx.INFO, "ip_blacklist: no result found for "..ip)
return
end
ngx.log(ngx.INFO, "ip_blacklist: "..ip.." is blacklisted")
return ngx.exit(ngx.HTTP_FORBIDDEN)
@Ceelog

This comment has been minimized.

Copy link

@Ceelog Ceelog commented Mar 9, 2017

looks coooool !

But I think it is not good for each client ip to connect redis.

Can we connect redis just once during cache_ttl for all client ips?

Here is a new version : https://gist.github.com/Ceelog/39862d297d9c85e743b3b5111b7d44cb

@itbdw

This comment has been minimized.

Copy link

@itbdw itbdw commented Jul 17, 2017

Thanks so much. I made a ip_whitelist version. Hope anyone may be helped.
https://gist.github.com/itbdw/bc6c03f754cc30f66b824f379f3da30f

@disaster123

This comment has been minimized.

Copy link

@disaster123 disaster123 commented Aug 14, 2020

I think this could be heavily optimized by using https://github.com/thibaultcha/lua-resty-mlcache

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment