From 5b1a47a38ec6d76d62132e02448bc1e122d16b90 Mon Sep 17 00:00:00 2001 From: wonglend Date: Mon, 24 Feb 2020 10:54:48 +0800 Subject: [PATCH] feature: Support password auth for plugin limit-count-redis (#1150) --- .travis.yml | 6 +- lua/apisix/plugins/limit-count.lua | 5 +- .../plugins/limit-count/limit-count-redis.lua | 14 ++ t/plugin/limit-count-redis.t | 234 ++++++++++++++++++ 4 files changed, 256 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index ee84fd88c3b3..58ce94f17928 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,7 +10,7 @@ matrix: include: - os: linux services: - - redis-server + - docker env: OSNAME=linux_openresty - os: osx env: OSNAME=osx_openresty @@ -21,7 +21,7 @@ matrix: - /usr/local/Homebrew - os: linux services: - - redis-server + - docker env: OSNAME=linux_tengine - os: linux env: OSNAME=linux_apisix_luarocks @@ -51,6 +51,8 @@ before_cache: - brew cleanup before_install: + - docker pull redis:3.0-alpine + - docker run --rm -itd -p 6379:6379 --name apisix_redis redis:3.0-alpine - echo $OSNAME - $PWD/.travis/${OSNAME}_runner.sh before_install diff --git a/lua/apisix/plugins/limit-count.lua b/lua/apisix/plugins/limit-count.lua index 8989d424e011..c95e3f6691a0 100644 --- a/lua/apisix/plugins/limit-count.lua +++ b/lua/apisix/plugins/limit-count.lua @@ -45,6 +45,9 @@ local schema = { redis_port = { type = "integer", minimum = 1 }, + redis_password = { + type = "string", minLength = 0 + }, redis_timeout = { type = "integer", minimum = 1 }, @@ -122,7 +125,7 @@ function _M.access(conf, ctx) end core.log.error("failed to limit req: ", err) - return 500 + return 500, {error_msg = "failed to limit count: ", err} end core.response.set_header("X-RateLimit-Limit", conf.count, diff --git a/lua/apisix/plugins/limit-count/limit-count-redis.lua b/lua/apisix/plugins/limit-count/limit-count-redis.lua index ef86ab132f76..ab5dbeb0cb03 100644 --- a/lua/apisix/plugins/limit-count/limit-count-redis.lua +++ b/lua/apisix/plugins/limit-count/limit-count-redis.lua @@ -51,6 +51,20 @@ function _M.incoming(self, key) return false, err end + local count + count, err = red:get_reused_times() + if 0 == count then + if conf.redis_password and conf.redis_password ~= '' then + local ok, err = red:auth(conf.redis_password) + if not ok then + return nil, err + end + end + elseif err then + -- core.log.info(" err: ", err) + return nil, err + end + local limit = self.limit local window = self.window local remaining diff --git a/t/plugin/limit-count-redis.t b/t/plugin/limit-count-redis.t index 63860acb9198..559d8a8bc530 100644 --- a/t/plugin/limit-count-redis.t +++ b/t/plugin/limit-count-redis.t @@ -209,3 +209,237 @@ passed [404, 503, 404, 503, 503] --- no_error_log [error] + + + +=== TEST 6: set route, with redis host, port and right password +--- config + location /t { + content_by_lua_block { + local t = require("lib.test_admin").test + -- set redis password + local redis = require "resty.redis" + + local red = redis:new() + + red:set_timeout(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 + + -- for get_reused_times works + -- local ok, err = red:set_keepalive(10000, 100) + -- if not ok then + -- ngx.say("failed to set keepalive: ", err) + -- return + -- end + + local count + count, err = red:get_reused_times() + if 0 == count then + local res, err = red:eval([[ + local key = 'requirepass' + local value = "foobared" + -- redis.replicate_commands() + local val = redis.pcall('CONFIG', 'SET', key, value) + return val + ]], 0) + -- + if not res then + ngx.say("failed to set: ", err) + return + end + elseif err then + -- ngx.say("already set requirepass done: ", err) + return + end + + local code, body = t('/apisix/admin/routes/1', + ngx.HTTP_PUT, + [[{ + "uri": "/hello", + "plugins": { + "limit-count": { + "count": 2, + "time_window": 60, + "rejected_code": 503, + "key": "remote_addr", + "policy": "redis", + "redis_host": "127.0.0.1", + "redis_port": 6379, + "redis_timeout": 1001, + "redis_password": "foobared" + } + }, + "upstream": { + "nodes": { + "127.0.0.1:1980": 1 + }, + "type": "roundrobin" + } + }]] + ) + + if code >= 300 then + ngx.status = code + end + ngx.say(body) + } + } +--- request +GET /t +--- response_body +passed +--- no_error_log +[error] + + + +=== TEST 7: up the limit +--- pipelined_requests eval +["GET /hello", "GET /hello", "GET /hello", "GET /hello"] +--- error_code eval +[200, 200, 503, 503] +--- no_error_log +[error] + + + +=== TEST 8: up the limit +--- pipelined_requests eval +["GET /hello1", "GET /hello", "GET /hello2", "GET /hello", "GET /hello"] +--- error_code eval +[404, 503, 404, 503, 503] +--- no_error_log +[error] + + + +=== TEST 9: set route, with redis host, port and wrong password +--- config + location /t { + content_by_lua_block { + local t = require("lib.test_admin").test + local code, body = t('/apisix/admin/routes/1', + ngx.HTTP_PUT, + [[{ + "plugins": { + "limit-count": { + "count": 2, + "time_window": 60, + "rejected_code": 503, + "key": "remote_addr", + "policy": "redis", + "redis_host": "127.0.0.1", + "redis_port": 6379, + "redis_timeout": 1001, + "redis_password": "WRONG_foobared" + } + }, + "upstream": { + "nodes": { + "127.0.0.1:1980": 1 + }, + "type": "roundrobin" + }, + "uri": "/hello_new" + }]] + ) + + if code >= 300 then + ngx.status = code + end + ngx.print(body) + } + } +--- request +GET /t +--- error_code eval +200 +--- no_error_log +[error] + + + +=== TEST 10: request for TEST 9 +--- request +GET /hello_new +--- error_code eval +500 +--- response_body +{"1":"ERR invalid password","error_msg":"failed to limit count: "} +--- error_log +failed to limit req: ERR invalid password + + + +=== TEST 11: multi request for TEST 9 +--- pipelined_requests eval +["GET /hello_new", "GET /hello1", "GET /hello1", "GET /hello_new"] +--- error_code eval +[500, 404, 404, 500] + + + +=== TEST 12: restore redis password to '' +--- config + location /t { + content_by_lua_block { + local t = require("lib.test_admin").test + -- set redis password + local redis = require "resty.redis" + + local red = redis:new() + + red:set_timeout(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 + + -- for get_reused_times works + -- local ok, err = red:set_keepalive(10000, 100) + -- if not ok then + -- ngx.say("failed to set keepalive: ", err) + -- return + -- end + + local count + count, err = red:get_reused_times() + if 0 == count then + local redis_password = "foobared" + if redis_password and redis_password ~= '' then + local ok, err = red:auth(redis_password) + if not ok then + return nil, err + end + end + local res, err = red:eval([[ + local key = 'requirepass' + local value = '' + -- redis.replicate_commands() + local val = redis.pcall('CONFIG', 'SET', key, value) + return val + ]], 0) + -- + if not res then + ngx.say("failed to set: ", err) + return + end + elseif err then + -- ngx.say("already set requirepass done: ", err) + return + end + } + } +--- request +GET /t +--- error_code eval +200 +--- no_error_log +[error]