diff --git a/apisix/plugins/limit-count/init.lua b/apisix/plugins/limit-count/init.lua index c9051d2e14ef..f14cdb9807b0 100644 --- a/apisix/plugins/limit-count/init.lua +++ b/apisix/plugins/limit-count/init.lua @@ -79,6 +79,12 @@ local policy_to_additional_properties = { redis_cluster_name = { type = "string", }, + redis_cluster_ssl = { + type = "boolean", default = false, + }, + redis_cluster_ssl_verify = { + type = "boolean", default = false, + }, }, required = {"redis_cluster_nodes", "redis_cluster_name"}, }, diff --git a/apisix/plugins/limit-count/limit-count-redis-cluster.lua b/apisix/plugins/limit-count/limit-count-redis-cluster.lua index 27d4e85faa4e..453f60ef3e5b 100644 --- a/apisix/plugins/limit-count/limit-count-redis-cluster.lua +++ b/apisix/plugins/limit-count/limit-count-redis-cluster.lua @@ -46,6 +46,10 @@ local function new_redis_cluster(conf) read_timeout = conf.redis_timeout, auth = conf.redis_password, dict_name = "plugin-limit-count-redis-cluster-slot-lock", + connect_opts = { + ssl = conf.redis_cluster_ssl, + ssl_verify = conf.redis_cluster_ssl_verify, + } } for i, conf_item in ipairs(conf.redis_cluster_nodes) do diff --git a/ci/pod/docker-compose.plugin.yml b/ci/pod/docker-compose.plugin.yml index 4b8c2a4c16b4..8ddeef20242e 100644 --- a/ci/pod/docker-compose.plugin.yml +++ b/ci/pod/docker-compose.plugin.yml @@ -262,6 +262,56 @@ services: CONTEXT_MODE: "self-host" FUNC_CONTEXT: "{\"name\":\"HelloWorld\",\"version\":\"v1.0.0\",\"port\":\"8080\",\"runtime\":\"Knative\"}" + ## RedisCluster Enable TLS + redis-node-0: + image: docker.io/bitnami/redis-cluster:7.0 + volumes: + - ./t/certs:/certs + environment: + - 'ALLOW_EMPTY_PASSWORD=yes' + - 'REDIS_NODES=redis-node-0 redis-node-1 redis-node-2' + - 'REDIS_TLS_ENABLED=yes' + - 'REDIS_TLS_CERT_FILE=/certs/mtls_server.crt' + - 'REDIS_TLS_KEY_FILE=/certs/mtls_server.key' + - 'REDIS_TLS_CA_FILE=/certs/mtls_ca.crt' + - 'REDIS_TLS_AUTH_CLIENTS=no' + ports: + - '7000:6379' + + redis-node-1: + image: docker.io/bitnami/redis-cluster:7.0 + volumes: + - ./t/certs:/certs + environment: + - 'ALLOW_EMPTY_PASSWORD=yes' + - 'REDIS_NODES=redis-node-0 redis-node-1 redis-node-2' + - 'REDIS_TLS_ENABLED=yes' + - 'REDIS_TLS_CERT_FILE=/certs/mtls_server.crt' + - 'REDIS_TLS_KEY_FILE=/certs/mtls_server.key' + - 'REDIS_TLS_CA_FILE=/certs/mtls_ca.crt' + - 'REDIS_TLS_AUTH_CLIENTS=no' + ports: + - '7001:6379' + + redis-node-2: + image: docker.io/bitnami/redis-cluster:7.0 + volumes: + - ./t/certs:/certs + depends_on: + - redis-node-0 + - redis-node-1 + environment: + - 'ALLOW_EMPTY_PASSWORD=yes' + - 'REDIS_CLUSTER_REPLICAS=0' + - 'REDIS_NODES=redis-node-0 redis-node-1 redis-node-2' + - 'REDIS_CLUSTER_CREATOR=yes' + - 'REDIS_TLS_ENABLED=yes' + - 'REDIS_TLS_CERT_FILE=/certs/mtls_server.crt' + - 'REDIS_TLS_KEY_FILE=/certs/mtls_server.key' + - 'REDIS_TLS_CA_FILE=/certs/mtls_ca.crt' + - 'REDIS_TLS_AUTH_CLIENTS=no' + ports: + - '7002:6379' networks: apisix_net: diff --git a/docs/en/latest/plugins/limit-count.md b/docs/en/latest/plugins/limit-count.md index b098dbd32e20..75c0eeb79e58 100644 --- a/docs/en/latest/plugins/limit-count.md +++ b/docs/en/latest/plugins/limit-count.md @@ -51,6 +51,8 @@ The `limit-count` Plugin limits the number of requests to your service by a give | 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`. | | redis_cluster_name | string | required when `policy` is `redis-cluster` | | | Name of the Redis cluster service nodes. Used when the `policy` attribute is set to `redis-cluster`. | +| redis_cluster_ssl | boolean | False | false | | If set to `true`, then uses SSL to connect to redis-cluster. Used when the `policy` attribute is set to `redis-cluster`. | +| redis_cluster_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-cluster`. | ## Enabling the Plugin diff --git a/docs/zh/latest/plugins/limit-count.md b/docs/zh/latest/plugins/limit-count.md index 3055d22677a6..76f15c2a799f 100644 --- a/docs/zh/latest/plugins/limit-count.md +++ b/docs/zh/latest/plugins/limit-count.md @@ -52,6 +52,8 @@ description: 本文介绍了 Apache APISIX limit-count 插件的相关操作, | redis_timeout | integer | 否 | 1000 | [1,...] | 当 `policy` 设置为 `redis` 或 `redis-cluster` 时,Redis 服务节点的超时时间(以毫秒为单位)。| | redis_cluster_nodes | array | 否 | | | 当使用 `redis-cluster` 限速策略时,Redis 集群服务节点的地址列表(至少需要两个地址)。**当 `policy` 属性设置为 `redis-cluster` 时必选。**| | redis_cluster_name | string | 否 | | | 当使用 `redis-cluster` 限速策略时,Redis 集群服务节点的名称。**当 `policy` 设置为 `redis-cluster` 时必选。**| +| redis_cluster_ssl | boolean | 否 | false | | 当使用 `redis-cluster` 限速策略时, 如果设置为 true,则使用 SSL 连接到 `redis-cluster` | +| redis_cluster_ssl_verify | boolean | 否 | false | | 当使用 `redis-cluster` 限速策略时,如果设置为 true,则验证服务器 SSL 证书的有效性 | ## 启用插件 diff --git a/t/plugin/limit-count-redis-cluster.t b/t/plugin/limit-count-redis-cluster.t index 7f5eb2c1f8d7..7a4798a60e48 100644 --- a/t/plugin/limit-count-redis-cluster.t +++ b/t/plugin/limit-count-redis-cluster.t @@ -384,3 +384,161 @@ GET /hello hello world --- error_log connection refused + + + +=== TEST 12: set route, use error type for redis_cluster_ssl and redis_cluster_ssl_verify +--- 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-cluster", + "redis_timeout": 1001, + "redis_cluster_nodes": [ + "127.0.0.1:7000", + "127.0.0.1:7001" + ], + "redis_cluster_name": "redis-cluster-1", + "redis_cluster_ssl": "true", + "redis_cluster_ssl_verify": "false" + } + }, + "upstream": { + "nodes": { + "127.0.0.1:1980": 1 + }, + "type": "roundrobin" + }, + "uri": "/hello" + }]] + ) + + if code >= 300 then + ngx.status = code + end + ngx.print(body) + } + } +--- error_code: 400 +--- response_body +{"error_msg":"failed to check the configuration of plugin limit-count err: else clause did not match"} + + + +=== TEST 13: set route, redis_cluster_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", + "policy": "redis-cluster", + "allow_degradation": true, + "redis_timeout": 1001, + "redis_cluster_nodes": [ + "127.0.0.1:7000", + "127.0.0.1:7001" + ], + "redis_cluster_name": "redis-cluster-1", + "redis_cluster_ssl": true, + "redis_cluster_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 14: enable degradation switch for TEST 13 +--- request +GET /hello +--- response_body +hello world +--- error_log +failed to do ssl handshake + + + +=== TEST 15: set route, with redis_cluster_nodes and redis_cluster_name redis_cluster_ssl and redis_cluster_ssl_verify +--- 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-cluster", + "redis_timeout": 1001, + "redis_cluster_nodes": [ + "127.0.0.1:7000", + "127.0.0.1:7001" + ], + "redis_cluster_name": "redis-cluster-1", + "redis_cluster_ssl": true, + "redis_cluster_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 16: up the limit +--- pipelined_requests eval +["GET /hello", "GET /hello", "GET /hello", "GET /hello"] +--- error_code eval +[200, 200, 503, 503]