You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@apisix.apache.org by mo...@apache.org on 2023/04/20 06:22:32 UTC
[apisix] branch master updated: feat: limit-count plugin supports username and ssl for redis policy (#9185)
This is an automated email from the ASF dual-hosted git repository.
monkeydluffy pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/apisix.git
The following commit(s) were added to refs/heads/master by this push:
new 41dc764f8 feat: limit-count plugin supports username and ssl for redis policy (#9185)
41dc764f8 is described below
commit 41dc764f82607fdcfff28075476d337050c4288c
Author: Simon FLURY <18...@users.noreply.github.com>
AuthorDate: Thu Apr 20 08:22:26 2023 +0200
feat: limit-count plugin supports username and ssl for redis policy (#9185)
---
apisix/plugins/limit-count/init.lua | 9 ++
apisix/plugins/limit-count/limit-count-redis.lua | 14 +-
ci/pod/docker-compose.plugin.yml | 9 ++
docs/en/latest/plugins/limit-count.md | 3 +
docs/zh/latest/plugins/limit-count.md | 3 +
t/plugin/limit-count-redis.t | 170 ++++++++++++++++++++++-
t/plugin/limit-count-redis3.t | 115 +++++++++++++++
7 files changed, 320 insertions(+), 3 deletions(-)
diff --git a/apisix/plugins/limit-count/init.lua b/apisix/plugins/limit-count/init.lua
index 58fbf7969..40b18c17a 100644
--- a/apisix/plugins/limit-count/init.lua
+++ b/apisix/plugins/limit-count/init.lua
@@ -50,6 +50,9 @@ local policy_to_additional_properties = {
redis_port = {
type = "integer", minimum = 1, default = 6379,
},
+ redis_username = {
+ type = "string", minLength = 1,
+ },
redis_password = {
type = "string", minLength = 0,
},
@@ -59,6 +62,12 @@ local policy_to_additional_properties = {
redis_timeout = {
type = "integer", minimum = 1, default = 1000,
},
+ redis_ssl = {
+ type = "boolean", default = false,
+ },
+ redis_ssl_verify = {
+ type = "boolean", default = false,
+ },
},
required = {"redis_host"},
},
diff --git a/apisix/plugins/limit-count/limit-count-redis.lua b/apisix/plugins/limit-count/limit-count-redis.lua
index bed920229..f2e441d61 100644
--- a/apisix/plugins/limit-count/limit-count-redis.lua
+++ b/apisix/plugins/limit-count/limit-count-redis.lua
@@ -44,7 +44,12 @@ local function redis_cli(conf)
red:set_timeouts(timeout, timeout, timeout)
- local ok, err = red:connect(conf.redis_host, conf.redis_port or 6379)
+ local sock_opts = {
+ ssl = conf.redis_ssl,
+ ssl_verify = conf.redis_ssl_verify
+ }
+
+ local ok, err = red:connect(conf.redis_host, conf.redis_port or 6379, sock_opts)
if not ok then
return false, err
end
@@ -53,7 +58,12 @@ local function redis_cli(conf)
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)
+ local ok, err
+ if conf.redis_username then
+ ok, err = red:auth(conf.redis_username, conf.redis_password)
+ else
+ ok, err = red:auth(conf.redis_password)
+ end
if not ok then
return nil, err
end
diff --git a/ci/pod/docker-compose.plugin.yml b/ci/pod/docker-compose.plugin.yml
index e7878ab61..c0f102f88 100644
--- a/ci/pod/docker-compose.plugin.yml
+++ b/ci/pod/docker-compose.plugin.yml
@@ -23,8 +23,17 @@ services:
# The latest image is the latest stable version
image: redis:latest
restart: unless-stopped
+ volumes:
+ - ./t/certs:/certs
+ command: "--tls-port 6380 \
+ --tls-cert-file /certs/mtls_server.crt \
+ --tls-key-file /certs/mtls_server.key \
+ --tls-ca-cert-file /certs/mtls_ca.crt \
+ --tls-auth-clients no \
+ --user alice on +@all ~* \\&* \\>somepassword"
ports:
- "6379:6379"
+ - "6380:6380"
networks:
apisix_net:
diff --git a/docs/en/latest/plugins/limit-count.md b/docs/en/latest/plugins/limit-count.md
index b32cad88f..c67b0832d 100644
--- a/docs/en/latest/plugins/limit-count.md
+++ b/docs/en/latest/plugins/limit-count.md
@@ -46,7 +46,10 @@ The `limit-count` Plugin limits the number of requests to your service by a give
| group | string | False | | non-empty | Group to share the counter with. Routes configured with the same group will share the counter. [...]
| redis_host | string | required when `policy` is `redis` | | | Address of the Redis server. Used when the `policy` attribute is set to `redis`. [...]
| redis_port | integer | False | 6379 | [1,...] | Port of the Redis server. Used when the `policy` attribute is set to `redis`. [...]
+| redis_username | string | False | | | Username use to authenticate to the Redis server for version >= 6.0, use only `redis_password` for the versions prior. Used when the `policy` is set to `redis`. [...]
| redis_password | string | False | | | Password of the Redis server. Used when the `policy` is set to `redis` or `redis-cluster`. [...]
+| redis_ssl | boolean | False | false | | If set to `true`, then uses SSL to connect to redis instance. Used when the `policy` attribute is set to `redis`. [...]
+| redis_ssl_verify | boolean | False | false | | If set to `true`, then verifies the validity of the server SSL certificate. Used when the `policy` attribute is set to `redis`. See [tcpsock:sslhandshake](https://github.com/openresty/lua-nginx-module#tcpsocksslhandshake). [...]
| redis_database | integer | False | 0 | redis_database >= 0 | Selected database of the Redis server (for single instance operation or when using Redis cloud with a single entrypoint). Used when the `policy` attribute is set to `redis`. [...]
| redis_timeout | integer | False | 1000 | [1,...] | Timeout in milliseconds for any command submitted to the Redis server. Used when the `policy` attribute is set to `redis` or `redis-cluster`. [...]
| redis_cluster_nodes | array | required when `policy` is `redis-cluster` | | | Addresses of Redis cluster nodes. Used when the `policy` attribute is set to `redis-cluster`. [...]
diff --git a/docs/zh/latest/plugins/limit-count.md b/docs/zh/latest/plugins/limit-count.md
index b70f4631a..c52b6ea65 100644
--- a/docs/zh/latest/plugins/limit-count.md
+++ b/docs/zh/latest/plugins/limit-count.md
@@ -47,7 +47,10 @@ description: 本文介绍了 Apache APISIX limit-count 插件的相关操作,
| group | string | 否 | | 非空 | 配置相同 group 的路由将共享相同的限流计数器。 |
| redis_host | string | 否 | | | 当使用 `redis` 限速策略时,Redis 服务节点的地址。**当 `policy` 属性设置为 `redis` 时必选。**|
| redis_port | integer | 否 | 6379 | [1,...] | 当使用 `redis` 限速策略时,Redis 服务节点的端口。|
+| redis_username | string | 否 | | | Redis 服务器的用户名。当 `policy` 设置为 `redis` 时使用。|
| redis_password | string | 否 | | | 当使用 `redis` 或者 `redis-cluster` 限速策略时,Redis 服务节点的密码。|
+| redis_ssl | boolean | 否 | false | | 当使用 `redis` 限速策略时,如果设置为 true,则使用 SSL 连接到 `redis` |
+| redis_ssl_verify | boolean | 否 | false | | 当使用 `redis` 限速策略时,如果设置为 true,则验证服务器 SSL 证书的有效性, 具体请参考 [tcpsock:sslhandshake](https://github.com/openresty/lua-nginx-module#tcpsocksslhandshake). |
| redis_database | integer | 否 | 0 | redis_database >= 0 | 当使用 `redis` 限速策略时,Redis 服务节点中使用的 `database`,并且只针对非 Redis 集群模式(单实例模式或者提供单入口的 Redis 公有云服务)生效。|
| redis_timeout | integer | 否 | 1000 | [1,...] | 当 `policy` 设置为 `redis` 或 `redis-cluster` 时,Redis 服务节点的超时时间(以毫秒为单位)。|
| redis_cluster_nodes | array | 否 | | | 当使用 `redis-cluster` 限速策略时,Redis 集群服务节点的地址列表(至少需要两个地址)。**当 `policy` 属性设置为 `redis-cluster` 时必选。**|
diff --git a/t/plugin/limit-count-redis.t b/t/plugin/limit-count-redis.t
index a777a3871..d06188050 100644
--- a/t/plugin/limit-count-redis.t
+++ b/t/plugin/limit-count-redis.t
@@ -344,7 +344,175 @@ failed to limit count: WRONGPASS invalid username-password pair or user is disab
-=== TEST 13: restore redis password to ''
+=== TEST 13: set route, with redis host, port and bad username and good 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,
+ [[{
+ "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_username": "bob",
+ "redis_password": "somepassword"
+ }
+ },
+ "upstream": {
+ "nodes": {
+ "127.0.0.1:1980": 1
+ },
+ "type": "roundrobin"
+ }
+ }]]
+ )
+
+ if code >= 300 then
+ ngx.status = code
+ end
+ ngx.say(body)
+ }
+ }
+--- response_body
+passed
+
+
+
+=== TEST 14: request for TEST 13
+--- request
+GET /hello
+--- error_code eval
+500
+--- error_log
+failed to limit count: WRONGPASS invalid username-password pair or user is disabled
+
+
+
+=== TEST 15: set route, with redis host, port and good username and bad 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,
+ [[{
+ "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_username": "alice",
+ "redis_password": "badpassword"
+ }
+ },
+ "upstream": {
+ "nodes": {
+ "127.0.0.1:1980": 1
+ },
+ "type": "roundrobin"
+ }
+ }]]
+ )
+
+ if code >= 300 then
+ ngx.status = code
+ end
+ ngx.say(body)
+ }
+ }
+--- response_body
+passed
+
+
+
+=== TEST 16: request for TEST 15
+--- request
+GET /hello
+--- error_code eval
+500
+--- error_log
+failed to limit count: WRONGPASS invalid username-password pair or user is disabled
+
+
+
+=== TEST 17: set route, with redis host, port and right username and 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,
+ [[{
+ "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_username": "alice",
+ "redis_password": "somepassword"
+ }
+ },
+ "upstream": {
+ "nodes": {
+ "127.0.0.1:1980": 1
+ },
+ "type": "roundrobin"
+ }
+ }]]
+ )
+
+ if code >= 300 then
+ ngx.status = code
+ end
+ ngx.say(body)
+ }
+ }
+--- response_body
+passed
+
+
+
+=== TEST 18: up the limit
+--- pipelined_requests eval
+["GET /hello", "GET /hello", "GET /hello", "GET /hello"]
+--- error_code eval
+[200, 200, 503, 503]
+
+
+
+=== TEST 19: up the limit
+--- pipelined_requests eval
+["GET /hello1", "GET /hello", "GET /hello2", "GET /hello", "GET /hello"]
+--- error_code eval
+[404, 503, 404, 503, 503]
+
+
+
+=== TEST 20: restore redis password to ''
--- config
location /t {
content_by_lua_block {
diff --git a/t/plugin/limit-count-redis3.t b/t/plugin/limit-count-redis3.t
index 781f52720..a7694f63b 100644
--- a/t/plugin/limit-count-redis3.t
+++ b/t/plugin/limit-count-redis3.t
@@ -186,3 +186,118 @@ passed
}
--- response_body
["1","0","0"]
+
+
+
+=== TEST 5: set route, with redis host, port and SSL
+--- 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,
+ [[{
+ "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": 6380,
+ "redis_timeout": 1001,
+ "redis_ssl": true,
+ "redis_ssl_verify": false
+ }
+ },
+ "upstream": {
+ "nodes": {
+ "127.0.0.1:1980": 1
+ },
+ "type": "roundrobin"
+ }
+ }]]
+ )
+
+ if code >= 300 then
+ ngx.status = code
+ end
+ ngx.say(body)
+ }
+ }
+--- response_body
+passed
+
+
+
+=== TEST 6: up the limit
+--- pipelined_requests eval
+["GET /hello", "GET /hello", "GET /hello", "GET /hello"]
+--- error_code eval
+[200, 200, 503, 503]
+
+
+
+=== TEST 7: up the limit
+--- pipelined_requests eval
+["GET /hello1", "GET /hello", "GET /hello2", "GET /hello", "GET /hello"]
+--- error_code eval
+[404, 503, 404, 503, 503]
+
+
+
+=== TEST 8: set route, with redis host, port, SSL and SSL verify is true(will cause ssl handshake err), with enable degradation switch
+--- 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,
+ [[{
+ "uri": "/hello",
+ "plugins": {
+ "limit-count": {
+ "count": 2,
+ "time_window": 60,
+ "rejected_code": 503,
+ "key": "remote_addr",
+ "allow_degradation": true,
+ "policy": "redis",
+ "redis_host": "127.0.0.1",
+ "redis_port": 6380,
+ "redis_timeout": 1001,
+ "redis_ssl": true,
+ "redis_ssl_verify": true
+ }
+ },
+ "upstream": {
+ "nodes": {
+ "127.0.0.1:1980": 1
+ },
+ "type": "roundrobin"
+ }
+ }]]
+ )
+
+ if code >= 300 then
+ ngx.status = code
+ end
+ ngx.say(body)
+ }
+ }
+--- response_body
+passed
+
+
+
+=== TEST 9: enable degradation switch for TEST 8
+--- request
+GET /hello
+--- response_body
+hello world
+--- error_log
+failed to do ssl handshake