You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@apisix.apache.org by sp...@apache.org on 2022/12/19 06:38:06 UTC

[apisix] branch master updated: feat: data encryption support more plugins (#8487)

This is an automated email from the ASF dual-hosted git repository.

spacewander 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 152ea80e2 feat: data encryption support more plugins (#8487)
152ea80e2 is described below

commit 152ea80e2e5aeafe62bd4358179351e017a0171e
Author: tzssangglass <tz...@gmail.com>
AuthorDate: Mon Dec 19 14:37:57 2022 +0800

    feat: data encryption support more plugins (#8487)
    
    Fixes https://github.com/apache/apisix/issues/8407
---
 apisix/admin/init.lua                          |   2 +
 apisix/admin/plugin_metadata.lua               |   3 +
 apisix/admin/utils.lua                         |   6 ++
 apisix/plugin.lua                              |  97 +++++++++++++----
 apisix/plugins/authz-casdoor.lua               |   1 +
 apisix/plugins/authz-keycloak.lua              |   1 +
 apisix/plugins/basic-auth.lua                  |   3 +-
 apisix/plugins/clickhouse-logger.lua           |   3 +-
 apisix/plugins/csrf.lua                        |   1 +
 apisix/plugins/elasticsearch-logger.lua        |   1 +
 apisix/plugins/error-log-logger.lua            |   3 +-
 apisix/plugins/google-cloud-logging.lua        |   1 +
 apisix/plugins/hmac-auth.lua                   |   1 +
 apisix/plugins/jwt-auth.lua                    |   1 +
 apisix/plugins/kafka-proxy.lua                 |   1 +
 apisix/plugins/key-auth.lua                    |   3 +-
 apisix/plugins/openid-connect.lua              |   1 +
 apisix/plugins/rocketmq-logger.lua             |   1 +
 apisix/plugins/sls-logger.lua                  |   1 +
 apisix/plugins/tencent-cloud-cls.lua           |   1 +
 conf/config-default.yaml                       |   2 +-
 docs/en/latest/plugin-develop.md               |  21 +++-
 docs/en/latest/plugins/authz-casdoor.md        |   2 +
 docs/en/latest/plugins/authz-keycloak.md       |   2 +
 docs/en/latest/plugins/basic-auth.md           |   2 +-
 docs/en/latest/plugins/clickhouse-logger.md    |   4 +-
 docs/en/latest/plugins/csrf.md                 |   2 +
 docs/en/latest/plugins/elasticsearch-logger.md |   2 +
 docs/en/latest/plugins/error-log-logger.md     |   2 +
 docs/en/latest/plugins/google-cloud-logging.md |   2 +
 docs/en/latest/plugins/hmac-auth.md            |   2 +
 docs/en/latest/plugins/jwt-auth.md             |   2 +
 docs/en/latest/plugins/kafka-proxy.md          |   2 +
 docs/en/latest/plugins/key-auth.md             |   2 +-
 docs/en/latest/plugins/openid-connect.md       |   2 +
 docs/en/latest/plugins/rocketmq-logger.md      |   2 +
 docs/en/latest/plugins/sls-logger.md           |   2 +
 docs/en/latest/plugins/tencent-cloud-cls.md    |   2 +
 docs/zh/latest/plugin-develop.md               |  21 +++-
 docs/zh/latest/plugins/authz-casdoor.md        |   2 +
 docs/zh/latest/plugins/authz-keycloak.md       |   2 +
 docs/zh/latest/plugins/basic-auth.md           |   2 +-
 docs/zh/latest/plugins/clickhouse-logger.md    |   4 +-
 docs/zh/latest/plugins/csrf.md                 |   2 +
 docs/zh/latest/plugins/elasticsearch-logger.md |   2 +
 docs/zh/latest/plugins/error-log-logger.md     |   2 +
 docs/zh/latest/plugins/google-cloud-logging.md |   2 +
 docs/zh/latest/plugins/hmac-auth.md            |   2 +
 docs/zh/latest/plugins/jwt-auth.md             |   2 +
 docs/zh/latest/plugins/key-auth.md             |   2 +-
 docs/zh/latest/plugins/openid-connect.md       |   2 +
 docs/zh/latest/plugins/rocketmq-logger.md      |   2 +
 docs/zh/latest/plugins/sls-logger.md           |   2 +
 docs/zh/latest/plugins/tencent-cloud-cls.md    |   2 +
 t/admin/plugins.t                              |   2 +-
 t/plugin/authz-casdoor.t                       |  68 ++++++++++++
 t/plugin/authz-keycloak3.t                     |  70 ++++++++++++
 t/plugin/csrf.t                                |  63 +++++++++++
 t/plugin/elasticsearch-logger.t                |  68 ++++++++++++
 t/plugin/error-log-logger-clickhouse.t         |  82 ++++++++++++++
 t/plugin/google-cloud-logging2.t               | 125 ++++++++++++++++++++++
 t/plugin/hmac-auth3.t                          |  73 +++++++++++++
 t/plugin/jwt-auth3.t                           | 142 +++++++++++++++++++++++++
 t/plugin/kafka-proxy.t                         |  65 +++++++++++
 t/plugin/openid-connect2.t                     |  69 ++++++++++++
 t/plugin/rocketmq-logger2.t                    |  65 +++++++++++
 t/plugin/sls-logger.t                          |  83 +++++++++++++++
 t/plugin/tencent-cloud-cls.t                   |  85 +++++++++++++++
 68 files changed, 1260 insertions(+), 42 deletions(-)

diff --git a/apisix/admin/init.lua b/apisix/admin/init.lua
index 52df36f1a..3ed8d362e 100644
--- a/apisix/admin/init.lua
+++ b/apisix/admin/init.lua
@@ -204,6 +204,8 @@ local function run()
         if method == "get" and plugin.enable_data_encryption then
             if seg_res == "consumers" then
                 utils.decrypt_params(plugin.decrypt_conf, data, core.schema.TYPE_CONSUMER)
+            elseif seg_res == "plugin_metadata" then
+                utils.decrypt_params(plugin.decrypt_conf, data, core.schema.TYPE_METADATA)
             else
                 utils.decrypt_params(plugin.decrypt_conf, data)
             end
diff --git a/apisix/admin/plugin_metadata.lua b/apisix/admin/plugin_metadata.lua
index 23859c775..065b60c47 100644
--- a/apisix/admin/plugin_metadata.lua
+++ b/apisix/admin/plugin_metadata.lua
@@ -18,6 +18,7 @@ local pcall   = pcall
 local require = require
 local core    = require("apisix.core")
 local utils   = require("apisix.admin.utils")
+local encrypt_conf = require("apisix.plugin").encrypt_conf
 
 local injected_mark = "injected metadata_schema"
 local _M = {
@@ -73,6 +74,8 @@ local function check_conf(plugin_name, conf)
         ok, err = plugin_object.check_schema(conf, core.schema.TYPE_METADATA)
     end
 
+    encrypt_conf(plugin_name, conf, core.schema.TYPE_METADATA)
+
     if not ok then
         return nil, {error_msg = "invalid configuration: " .. err}
     end
diff --git a/apisix/admin/utils.lua b/apisix/admin/utils.lua
index ee396d0d3..eee2787f0 100644
--- a/apisix/admin/utils.lua
+++ b/apisix/admin/utils.lua
@@ -102,6 +102,12 @@ function _M.decrypt_params(decrypt_func, body, schema_type)
             decrypt_func(name, conf, schema_type)
         end
     end
+
+    -- metadata
+    if schema_type == core.schema.TYPE_METADATA then
+        local conf = body.node and body.node.value
+        decrypt_func(conf.name, conf, schema_type)
+    end
 end
 
 return _M
diff --git a/apisix/plugin.lua b/apisix/plugin.lua
index 35169e8a8..3b5cecac6 100644
--- a/apisix/plugin.lua
+++ b/apisix/plugin.lua
@@ -21,6 +21,7 @@ local enable_debug  = require("apisix.debug").enable_debug
 local wasm          = require("apisix.wasm")
 local expr          = require("resty.expr.v1")
 local apisix_ssl    = require("apisix.ssl")
+local re_split      = require("ngx.re").split
 local ngx           = ngx
 local crc32         = ngx.crc32_short
 local ngx_exit      = ngx.exit
@@ -844,12 +845,6 @@ local function check_single_plugin_schema(name, plugin_conf, schema_type, skip_d
 end
 
 
-check_plugin_metadata = function(item)
-    return check_single_plugin_schema(item.id, item,
-        core.schema.TYPE_METADATA, true)
-end
-
-
 local enable_data_encryption
 local function enable_gde()
     if enable_data_encryption == nil then
@@ -868,9 +863,15 @@ local function get_plugin_schema_for_gde(name, schema_type)
     end
 
     local plugin_schema = local_plugins_hash and local_plugins_hash[name]
+    if not plugin_schema then
+        return nil
+    end
+
     local schema
     if schema_type == core.schema.TYPE_CONSUMER then
         schema = plugin_schema.consumer_schema
+    elseif schema_type == core.schema.TYPE_METADATA then
+        schema = plugin_schema.metadata_schema
     else
         schema = plugin_schema.schema
     end
@@ -882,17 +883,39 @@ end
 local function decrypt_conf(name, conf, schema_type)
     local schema = get_plugin_schema_for_gde(name, schema_type)
     if not schema then
+        core.log.warn("failed to get schema for plugin: ", name)
         return
     end
 
-    for key, props in pairs(schema.properties) do
-        if props.type == "string" and props.encrypted and conf[key] then
-            local encrypted, err = apisix_ssl.aes_decrypt_pkey(conf[key], "data_encrypt")
-            if not encrypted then
-                core.log.warn("failed to decrypt the conf of plugin [", name,
-                               "] key [", key, "], err: ", err)
-            else
-                conf[key] = encrypted
+    if schema.encrypt_fields and not core.table.isempty(schema.encrypt_fields) then
+        for _, key in ipairs(schema.encrypt_fields) do
+            if conf[key] then
+                local decrypted, err = apisix_ssl.aes_decrypt_pkey(conf[key], "data_encrypt")
+                if not decrypted then
+                    core.log.warn("failed to decrypt the conf of plugin [", name,
+                                  "] key [", key, "], err: ", err)
+                else
+                    conf[key] = decrypted
+                end
+            elseif core.string.find(key, ".") then
+                -- decrypt fields has indents
+                local res, err = re_split(key, "\\.", "jo")
+                if not res then
+                    core.log.warn("failed to split key [", key, "], err: ", err)
+                    return
+                end
+
+                -- we only support two levels
+                if conf[res[1]] and conf[res[1]][res[2]] then
+                    local decrypted, err = apisix_ssl.aes_decrypt_pkey(
+                                           conf[res[1]][res[2]], "data_encrypt")
+                    if not decrypted then
+                        core.log.warn("failed to decrypt the conf of plugin [", name,
+                                      "] key [", key, "], err: ", err)
+                    else
+                        conf[res[1]][res[2]] = decrypted
+                    end
+                end
             end
         end
     end
@@ -903,19 +926,57 @@ _M.decrypt_conf = decrypt_conf
 local function encrypt_conf(name, conf, schema_type)
     local schema = get_plugin_schema_for_gde(name, schema_type)
     if not schema then
+        core.log.warn("failed to get schema for plugin: ", name)
         return
     end
 
-    for key, props in pairs(schema.properties) do
-        if props.type == "string" and props.encrypted and conf[key] then
-            local encrypted = apisix_ssl.aes_encrypt_pkey(conf[key], "data_encrypt")
-            conf[key] = encrypted
+    if schema.encrypt_fields and not core.table.isempty(schema.encrypt_fields) then
+        for _, key in ipairs(schema.encrypt_fields) do
+            if conf[key] then
+                local encrypted, err = apisix_ssl.aes_encrypt_pkey(conf[key], "data_encrypt")
+                if not encrypted then
+                    core.log.warn("failed to encrypt the conf of plugin [", name,
+                                  "] key [", key, "], err: ", err)
+                else
+                    conf[key] = encrypted
+                end
+            elseif core.string.find(key, ".") then
+                -- encrypt fields has indents
+                local res, err = re_split(key, "\\.", "jo")
+                if not res then
+                    core.log.warn("failed to split key [", key, "], err: ", err)
+                    return
+                end
+
+                -- we only support two levels
+                if conf[res[1]] and conf[res[1]][res[2]] then
+                    local encrypted, err = apisix_ssl.aes_encrypt_pkey(
+                                           conf[res[1]][res[2]], "data_encrypt")
+                    if not encrypted then
+                        core.log.warn("failed to encrypt the conf of plugin [", name,
+                                      "] key [", key, "], err: ", err)
+                    else
+                        conf[res[1]][res[2]] = encrypted
+                    end
+                end
+            end
         end
     end
 end
 _M.encrypt_conf = encrypt_conf
 
 
+check_plugin_metadata = function(item)
+    local ok, err = check_single_plugin_schema(item.id, item,
+                                               core.schema.TYPE_METADATA, true)
+    if ok and enable_gde() then
+        decrypt_conf(item.name, item, core.schema.TYPE_METADATA)
+    end
+
+    return ok, err
+end
+
+
 local function check_schema(plugins_conf, schema_type, skip_disabled_plugin)
     for name, plugin_conf in pairs(plugins_conf) do
         local ok, err = check_single_plugin_schema(name, plugin_conf,
diff --git a/apisix/plugins/authz-casdoor.lua b/apisix/plugins/authz-casdoor.lua
index 9fd8d8da8..4ed1c92b9 100644
--- a/apisix/plugins/authz-casdoor.lua
+++ b/apisix/plugins/authz-casdoor.lua
@@ -32,6 +32,7 @@ local schema = {
         client_secret = {type = "string"},
         callback_url = {type = "string", pattern = "^[^%?]+[^/]$"}
     },
+    encrypt_fields = {"client_secret"},
     required = {
         "callback_url", "endpoint_addr", "client_id", "client_secret"
     }
diff --git a/apisix/plugins/authz-keycloak.lua b/apisix/plugins/authz-keycloak.lua
index 336fb69b1..f2c02727c 100644
--- a/apisix/plugins/authz-keycloak.lua
+++ b/apisix/plugins/authz-keycloak.lua
@@ -71,6 +71,7 @@ local schema = {
             maxLength = 4096
         },
     },
+    encrypt_fields = {"client_secret"},
     required = {"client_id"},
     allOf = {
         -- Require discovery or token endpoint.
diff --git a/apisix/plugins/basic-auth.lua b/apisix/plugins/basic-auth.lua
index 65d96e5c7..c91ddee77 100644
--- a/apisix/plugins/basic-auth.lua
+++ b/apisix/plugins/basic-auth.lua
@@ -38,8 +38,9 @@ local consumer_schema = {
     title = "work with consumer object",
     properties = {
         username = { type = "string" },
-        password = { type = "string", encrypted = true },
+        password = { type = "string" },
     },
+    encrypt_fields = {"password"},
     required = {"username", "password"},
 }
 
diff --git a/apisix/plugins/clickhouse-logger.lua b/apisix/plugins/clickhouse-logger.lua
index 28404f9ae..70463a752 100644
--- a/apisix/plugins/clickhouse-logger.lua
+++ b/apisix/plugins/clickhouse-logger.lua
@@ -36,7 +36,7 @@ local schema = {
         endpoint_addr = core.schema.uri_def,
         endpoint_addrs = {items = core.schema.uri_def, type = "array", minItems = 1},
         user = {type = "string", default = ""},
-        password = {type = "string", default = "", encrypted = true},
+        password = {type = "string", default = ""},
         database = {type = "string", default = ""},
         logtable = {type = "string", default = ""},
         timeout = {type = "integer", minimum = 1, default = 3},
@@ -47,6 +47,7 @@ local schema = {
         {required = {"endpoint_addr", "user", "password", "database", "logtable"}},
         {required = {"endpoint_addrs", "user", "password", "database", "logtable"}}
     },
+    encrypt_fields = {"password"},
 }
 
 
diff --git a/apisix/plugins/csrf.lua b/apisix/plugins/csrf.lua
index 233fe1d94..4ed2ad624 100644
--- a/apisix/plugins/csrf.lua
+++ b/apisix/plugins/csrf.lua
@@ -44,6 +44,7 @@ local schema = {
             default = "apisix-csrf-token"
         }
     },
+    encrypt_fields = {"key"},
     required = {"key"}
 }
 
diff --git a/apisix/plugins/elasticsearch-logger.lua b/apisix/plugins/elasticsearch-logger.lua
index 105cbe4d9..5b7341319 100644
--- a/apisix/plugins/elasticsearch-logger.lua
+++ b/apisix/plugins/elasticsearch-logger.lua
@@ -67,6 +67,7 @@ local schema = {
             default = true
         }
     },
+    encrypt_fields = {"auth.password"},
     required = { "endpoint_addr", "field" },
 }
 
diff --git a/apisix/plugins/error-log-logger.lua b/apisix/plugins/error-log-logger.lua
index 5aa7a7418..d847ca1ce 100644
--- a/apisix/plugins/error-log-logger.lua
+++ b/apisix/plugins/error-log-logger.lua
@@ -83,7 +83,8 @@ local metadata_schema = {
         {required = {"clickhouse"}},
         -- for compatible with old schema
         {required = {"host", "port"}}
-    }
+    },
+    encrypt_fields = {"clickhouse.password"},
 }
 
 
diff --git a/apisix/plugins/google-cloud-logging.lua b/apisix/plugins/google-cloud-logging.lua
index 607856759..a07156716 100644
--- a/apisix/plugins/google-cloud-logging.lua
+++ b/apisix/plugins/google-cloud-logging.lua
@@ -92,6 +92,7 @@ local schema = {
         { required = { "auth_config" } },
         { required = { "auth_file" } },
     },
+    encrypt_fields = {"auth_config.private_key"},
 }
 
 
diff --git a/apisix/plugins/hmac-auth.lua b/apisix/plugins/hmac-auth.lua
index c03e5ce82..94916e975 100644
--- a/apisix/plugins/hmac-auth.lua
+++ b/apisix/plugins/hmac-auth.lua
@@ -90,6 +90,7 @@ local consumer_schema = {
             default = MAX_REQ_BODY,
         },
     },
+    encrypt_fields = {"secret_key"},
     required = {"access_key", "secret_key"},
 }
 
diff --git a/apisix/plugins/jwt-auth.lua b/apisix/plugins/jwt-auth.lua
index 1215fedb9..96c743e1b 100644
--- a/apisix/plugins/jwt-auth.lua
+++ b/apisix/plugins/jwt-auth.lua
@@ -118,6 +118,7 @@ local consumer_schema = {
             }
         }
     },
+    encrypt_fields = {"secret", "private_key"},
     required = {"key"},
 }
 
diff --git a/apisix/plugins/kafka-proxy.lua b/apisix/plugins/kafka-proxy.lua
index b6c666a53..0882692dd 100644
--- a/apisix/plugins/kafka-proxy.lua
+++ b/apisix/plugins/kafka-proxy.lua
@@ -33,6 +33,7 @@ local schema = {
             required = {"username", "password"},
         },
     },
+    encrypt_fields = {"sasl.password"},
 }
 
 
diff --git a/apisix/plugins/key-auth.lua b/apisix/plugins/key-auth.lua
index 63e41a084..f8cfddae0 100644
--- a/apisix/plugins/key-auth.lua
+++ b/apisix/plugins/key-auth.lua
@@ -40,8 +40,9 @@ local schema = {
 local consumer_schema = {
     type = "object",
     properties = {
-        key = { type = "string", encrypted = true },
+        key = { type = "string" },
     },
+    encrypt_fields = {"key"},
     required = {"key"},
 }
 
diff --git a/apisix/plugins/openid-connect.lua b/apisix/plugins/openid-connect.lua
index d45b4e757..163707f1d 100644
--- a/apisix/plugins/openid-connect.lua
+++ b/apisix/plugins/openid-connect.lua
@@ -124,6 +124,7 @@ local schema = {
             default = false
         }
     },
+    encrypt_fields = {"client_secret"},
     required = {"client_id", "client_secret", "discovery"}
 }
 
diff --git a/apisix/plugins/rocketmq-logger.lua b/apisix/plugins/rocketmq-logger.lua
index 447960ba6..7ca61390c 100644
--- a/apisix/plugins/rocketmq-logger.lua
+++ b/apisix/plugins/rocketmq-logger.lua
@@ -69,6 +69,7 @@ local schema = {
             }
         },
     },
+    encrypt_fields = {"secret_key"},
     required = {"nameserver_list", "topic"}
 }
 
diff --git a/apisix/plugins/sls-logger.lua b/apisix/plugins/sls-logger.lua
index 290bf1191..89fc952ba 100644
--- a/apisix/plugins/sls-logger.lua
+++ b/apisix/plugins/sls-logger.lua
@@ -42,6 +42,7 @@ local schema = {
         access_key_id = {type = "string"},
         access_key_secret = {type ="string"}
     },
+    encrypt_fields = {"access_key_secret"},
     required = {"host", "port", "project", "logstore", "access_key_id", "access_key_secret"}
 }
 
diff --git a/apisix/plugins/tencent-cloud-cls.lua b/apisix/plugins/tencent-cloud-cls.lua
index b0726e607..d9b032b01 100644
--- a/apisix/plugins/tencent-cloud-cls.lua
+++ b/apisix/plugins/tencent-cloud-cls.lua
@@ -44,6 +44,7 @@ local schema = {
         include_resp_body = { type = "boolean", default = false },
         global_tag = { type = "object" },
     },
+    encrypt_fields = {"secret_key"},
     required = { "cls_host", "cls_topic", "secret_id", "secret_key" }
 }
 
diff --git a/conf/config-default.yaml b/conf/config-default.yaml
index 33af44564..e1923ec56 100755
--- a/conf/config-default.yaml
+++ b/conf/config-default.yaml
@@ -123,7 +123,7 @@ apisix:
   #  ip: 127.0.0.1
   #  port: 9090
   disable_sync_configuration_during_start: false  # safe exit. Remove this once the feature is stable
-  data_encryption:                # add `encrypted = true` in plugin schema to enable encryption
+  data_encryption:                # add `encrypt_fields = { $field },` in plugin schema to enable encryption
     enable: false                 # if not set, the default value is `false`.
     keyring:
       - qeddd145sfvddff3          # If not set, will save origin value into etcd.
diff --git a/docs/en/latest/plugin-develop.md b/docs/en/latest/plugin-develop.md
index c55c47bf1..a7aa4c634 100644
--- a/docs/en/latest/plugin-develop.md
+++ b/docs/en/latest/plugin-develop.md
@@ -300,13 +300,24 @@ Specify the parameters to be stored encrypted. (Requires APISIX version >= 3.1.0
 Some plugins require parameters to be stored encrypted, such as the `password` parameter of the `basic-auth` plugin. This plugin needs to specify in the `schema` which parameters need to be stored encrypted.
 
 ```lua
-password = { type = "string", encrypted = true },
+encrypt_fields = {"password"}
 ```
 
-Parameters can be stored encrypted by specifying `encrypted = true` in the `schema`. APISIX will provide the following functionality.
+If it is a nested parameter, such as the `clickhouse.password` parameter of the `error-log-logger` plugin, it needs to be separated by `.`:
 
-- When adding and updating resources via the `Admin API`, APISIX automatically encrypts parameters with `encrypted = true` and stores them in etcd
-- When fetching resources via the `Admin API` and when running the plugin, APISIX automatically decrypts the `encrypted = true` parameter
+```lua
+encrypt_fields = {"clickhouse.password"}
+```
+
+Currently not supported yet:
+
+1. more than two levels of nesting
+2. fields in arrays
+
+Parameters can be stored encrypted by specifying `encrypt_fields = {"password"}` in the `schema`. APISIX will provide the following functionality.
+
+- When adding and updating resources via the `Admin API`, APISIX automatically encrypts the parameters declared in `encrypt_fields` and stores them in etcd
+- When fetching resources via the `Admin API` and when running the plugin, APISIX automatically decrypts the parameters declared in `encrypt_fields`
 
 How to enable this feature?
 
@@ -321,7 +332,7 @@ apisix:
         - qeddd145sfvddff4
 ```
 
-APISIX will try to decrypt the data with keys in the order of the keys in the keyring (only for parameters declared `encrypted = true`). If the decryption fails, the next key will be tried until the decryption succeeds.
+APISIX will try to decrypt the data with keys in the order of the keys in the keyring (only for parameters declared in `encrypt_fields`). If the decryption fails, the next key will be tried until the decryption succeeds.
 
 If none of the keys in `keyring` can decrypt the data, the original data is used.
 
diff --git a/docs/en/latest/plugins/authz-casdoor.md b/docs/en/latest/plugins/authz-casdoor.md
index fc8c92f09..f1a71be9e 100644
--- a/docs/en/latest/plugins/authz-casdoor.md
+++ b/docs/en/latest/plugins/authz-casdoor.md
@@ -40,6 +40,8 @@ The `authz-casdoor` Plugin can be used to add centralized authentication with [C
 | client_secret | string | True     | Client secret in Casdoor.                    |
 | callback_url  | string | True     | Callback URL used to receive state and code. |
 
+NOTE: `encrypt_fields = {"client_secret"}` is also defined in the schema, which means that the field will be stored encrypted in etcd. See [encrypted storage fields](../plugin-develop.md#encrypted-storage-fields).
+
 :::info IMPORTANT
 
 `endpoint_addr` and `callback_url` should not end with '/'.
diff --git a/docs/en/latest/plugins/authz-keycloak.md b/docs/en/latest/plugins/authz-keycloak.md
index 70a149caf..d04196d06 100644
--- a/docs/en/latest/plugins/authz-keycloak.md
+++ b/docs/en/latest/plugins/authz-keycloak.md
@@ -66,6 +66,8 @@ Refer to [Authorization Services Guide](https://www.keycloak.org/docs/latest/aut
 | access_denied_redirect_uri                   | string        | False    |                                               | [1, 2048]                                                          | URI to redirect the user to instead of returning an error message like `"error_description":"not_authorized"`.                                                                                                                                        |
 | password_grant_token_generation_incoming_uri | string        | False    |                                               | /api/token                                                         | Set this to generate token using the password grant type. The Plugin will compare incoming request URI to this value.                                                                                                                                 |
 
+NOTE: `encrypt_fields = {"client_secret"}` is also defined in the schema, which means that the field will be stored encrypted in etcd. See [encrypted storage fields](../plugin-develop.md#encrypted-storage-fields).
+
 ### Discovery and endpoints
 
 It is recommended to use the `discovery` attribute as the `authz-keycloak` Plugin can discover the Keycloak API endpoints from it.
diff --git a/docs/en/latest/plugins/basic-auth.md b/docs/en/latest/plugins/basic-auth.md
index 25c574c9c..8447a4fce 100644
--- a/docs/en/latest/plugins/basic-auth.md
+++ b/docs/en/latest/plugins/basic-auth.md
@@ -42,7 +42,7 @@ For Consumer:
 | username | string | True     | Unique username for a Consumer. If multiple Consumers use the same `username`, a request matching exception is raised. |
 | password | string | True     | Password of the user.                                                                                                  |
 
-NOTE: The schema for `password` also defines `encrypted = true`, which means that the field will be stored encrypted in etcd. See [encrypted storage fields](../plugin-develop.md#encrypted-storage-fields).
+NOTE: `encrypt_fields = {"password"}` is also defined in the schema, which means that the field will be stored encrypted in etcd. See [encrypted storage fields](../plugin-develop.md#encrypted-storage-fields).
 
 For Route:
 
diff --git a/docs/en/latest/plugins/clickhouse-logger.md b/docs/en/latest/plugins/clickhouse-logger.md
index 9de95ccf1..465ebb945 100644
--- a/docs/en/latest/plugins/clickhouse-logger.md
+++ b/docs/en/latest/plugins/clickhouse-logger.md
@@ -45,9 +45,9 @@ The `clickhouse-logger` Plugin is used to push logs to [ClickHouse](https://clic
 | name          | string  | False    | "clickhouse logger" |              | Unique identifier for the logger.                              |
 | ssl_verify    | boolean | False    | true                | [true,false] | When set to `true`, verifies SSL.                              |
 
-This Plugin supports using batch processors to aggregate and process entries (logs/data) in a batch. This avoids the need for frequently submitting the data. The batch processor submits data every `5` seconds or when the data in the queue reaches `1000`. See [Batch Processor](../batch-processor.md#configuration) for more information or setting your custom configuration.
+NOTE: `encrypt_fields = {"password"}` is also defined in the schema, which means that the field will be stored encrypted in etcd. See [encrypted storage fields](../plugin-develop.md#encrypted-storage-fields).
 
-NOTE: The schema for `password` also defines `encrypted = true`, which means that the field will be stored encrypted in etcd. See [encrypted storage fields](../plugin-develop.md#encrypted-storage-fields).
+This Plugin supports using batch processors to aggregate and process entries (logs/data) in a batch. This avoids the need for frequently submitting the data. The batch processor submits data every `5` seconds or when the data in the queue reaches `1000`. See [Batch Processor](../batch-processor.md#configuration) for more information or setting your custom configuration.
 
 ## Metadata
 
diff --git a/docs/en/latest/plugins/csrf.md b/docs/en/latest/plugins/csrf.md
index b3cd5cb7a..a5884a5be 100644
--- a/docs/en/latest/plugins/csrf.md
+++ b/docs/en/latest/plugins/csrf.md
@@ -41,6 +41,8 @@ This Plugin considers the `GET`, `HEAD` and `OPTIONS` methods to be safe operati
 | expires | number | False    | `7200`              | Expiration time in seconds of the CSRF cookie. Set to `0` to skip checking expiration time. |
 | key     | string | True     |                     | Secret key used to encrypt the cookie.                                                      |
 
+NOTE: `encrypt_fields = {"key"}` is also defined in the schema, which means that the field will be stored encrypted in etcd. See [encrypted storage fields](../plugin-develop.md#encrypted-storage-fields).
+
 ## Enabling the Plugin
 
 The example below shows how you can enable the Plugin on a specific Route:
diff --git a/docs/en/latest/plugins/elasticsearch-logger.md b/docs/en/latest/plugins/elasticsearch-logger.md
index 51deead86..739641d57 100644
--- a/docs/en/latest/plugins/elasticsearch-logger.md
+++ b/docs/en/latest/plugins/elasticsearch-logger.md
@@ -47,6 +47,8 @@ When the Plugin is enabled, APISIX will serialize the request context informatio
 | ssl_verify    | boolean | False    | true                        | When set to `true` enables SSL verification as per [OpenResty docs](https://github.com/openresty/lua-nginx-module#tcpsocksslhandshake). |
 | timeout       | integer | False    | 10                          | Elasticsearch send data timeout in seconds.                  |
 
+NOTE: `encrypt_fields = {"auth.password"}` is also defined in the schema, which means that the field will be stored encrypted in etcd. See [encrypted storage fields](../plugin-develop.md#encrypted-storage-fields).
+
 This Plugin supports using batch processors to aggregate and process entries (logs/data) in a batch. This avoids the need for frequently submitting the data. The batch processor submits data every `5` seconds or when the data in the queue reaches `1000`. See [Batch Processor](../batch-processor.md#configuration) for more information or setting your custom configuration.
 
 ## Enabling the Plugin
diff --git a/docs/en/latest/plugins/error-log-logger.md b/docs/en/latest/plugins/error-log-logger.md
index a37a7a5ff..63b1be1c7 100644
--- a/docs/en/latest/plugins/error-log-logger.md
+++ b/docs/en/latest/plugins/error-log-logger.md
@@ -52,6 +52,8 @@ It might take some time to receive the log data. It will be automatically sent a
 | keepalive                        | integer | False    | 30                             | [1,...]                                                                                 | Time in seconds to keep the connection alive after sending data.                                             |
 | level                            | string  | False    | WARN                           | ["STDERR", "EMERG", "ALERT", "CRIT", "ERR", "ERROR", "WARN", "NOTICE", "INFO", "DEBUG"] | Log level to filter the error logs. `ERR` is same as `ERROR`.                                                |
 
+NOTE: `encrypt_fields = {"clickhouse.password"}` is also defined in the schema, which means that the field will be stored encrypted in etcd. See [encrypted storage fields](../plugin-develop.md#encrypted-storage-fields).
+
 This Plugin supports using batch processors to aggregate and process entries (logs/data) in a batch. This avoids the need for frequently submitting the data. The batch processor submits data every `5` seconds or when the data in the queue reaches `1000`. See [Batch Processor](../batch-processor.md#configuration) for more information or setting your custom configuration.
 
 ## Enabling the Plugin
diff --git a/docs/en/latest/plugins/google-cloud-logging.md b/docs/en/latest/plugins/google-cloud-logging.md
index d243aca87..d04d939ca 100644
--- a/docs/en/latest/plugins/google-cloud-logging.md
+++ b/docs/en/latest/plugins/google-cloud-logging.md
@@ -47,6 +47,8 @@ This plugin also allows to push logs as a batch to your Google Cloud Logging Ser
 | resource                | False    | {"type": "global"}                                                                                                                                                                                   | Google monitor resource. See [MonitoredResource](https://cloud.google.com/logging/docs/reference/v2/rest/v2/MonitoredResource) for more details.                   |
 | log_id                  | False    | apisix.apache.org%2Flogs                                                                                                                                                                             | Google Cloud logging ID. See [LogEntry](https://cloud.google.com/logging/docs/reference/v2/rest/v2/LogEntry) for details.                                          |
 
+NOTE: `encrypt_fields = {"auth_config.private_key"}` is also defined in the schema, which means that the field will be stored encrypted in etcd. See [encrypted storage fields](../plugin-develop.md#encrypted-storage-fields).
+
 This Plugin supports using batch processors to aggregate and process entries (logs/data) in a batch. This avoids the need for frequently submitting the data. The batch processor submits data every `5` seconds or when the data in the queue reaches `1000`. See [Batch Processor](../batch-processor.md#configuration) for more information or setting your custom configuration.
 
 ## Enabling the Plugin
diff --git a/docs/en/latest/plugins/hmac-auth.md b/docs/en/latest/plugins/hmac-auth.md
index c43d24a61..5aaef2eb3 100644
--- a/docs/en/latest/plugins/hmac-auth.md
+++ b/docs/en/latest/plugins/hmac-auth.md
@@ -47,6 +47,8 @@ This Plugin works with a [Consumer](../terminology/consumer.md) object and a con
 | validate_request_body | boolean       | False    | false         | [ true, false ]                             | When set to `true`, validates the request body.                                                                                                                                                           |
 | max_req_body          | integer       | False    | 512 * 1024    |                                             | Max size of the request body to allow.                                                                                                                                                                    |
 
+NOTE: `encrypt_fields = {"secret_key"}` is also defined in the schema, which means that the field will be stored encrypted in etcd. See [encrypted storage fields](../plugin-develop.md#encrypted-storage-fields).
+
 ## Enabling the Plugin
 
 First we enable the Plugin on a Consumer object as shown below:
diff --git a/docs/en/latest/plugins/jwt-auth.md b/docs/en/latest/plugins/jwt-auth.md
index fffb01846..124ae3857 100644
--- a/docs/en/latest/plugins/jwt-auth.md
+++ b/docs/en/latest/plugins/jwt-auth.md
@@ -51,6 +51,8 @@ For Consumer:
 | vault         | object  | False                                                 |         |                             | Set to true to use Vault for storing and retrieving secret (secret for HS256/HS512  or public_key and private_key for RS256/ES256). By default, the Vault path is `kv/apisix/consumer/<consumer_name>/jwt-auth`. |
 | lifetime_grace_period | integer | False                                         | 0       | [0,...]                     | Define the leeway in seconds to account for clock skew between the server that generated the jwt and the server validating it. Value should be zero (0) or a positive integer. |
 
+NOTE: `encrypt_fields = {"secret", "private_key"}` is also defined in the schema, which means that the field will be stored encrypted in etcd. See [encrypted storage fields](../plugin-develop.md#encrypted-storage-fields).
+
 :::info IMPORTANT
 
 To enable Vault integration, you have to first update your configuration file (`conf/config.yaml`) with your Vault server configuration, host address and access token.
diff --git a/docs/en/latest/plugins/kafka-proxy.md b/docs/en/latest/plugins/kafka-proxy.md
index d0377cd0f..d7ca53ed7 100644
--- a/docs/en/latest/plugins/kafka-proxy.md
+++ b/docs/en/latest/plugins/kafka-proxy.md
@@ -38,6 +38,8 @@ The `kafka-proxy` plugin can be used to configure advanced parameters for the ka
 | sasl.username     | string  | required |         |               | SASL/PLAIN authentication username |
 | sasl.password     | string  | required |         |               | SASL/PLAIN authentication password |
 
+NOTE: `encrypt_fields = {"sasl.password"}` is also defined in the schema, which means that the field will be stored encrypted in etcd. See [encrypted storage fields](../plugin-develop.md#encrypted-storage-fields).
+
 :::note
 If SASL authentication is enabled, the `sasl.username` and `sasl.password` must be set.
 The current SASL authentication only supports PLAIN mode, which is the username password login method.
diff --git a/docs/en/latest/plugins/key-auth.md b/docs/en/latest/plugins/key-auth.md
index b188cbc27..ebac702bb 100644
--- a/docs/en/latest/plugins/key-auth.md
+++ b/docs/en/latest/plugins/key-auth.md
@@ -41,7 +41,7 @@ For Consumer:
 |------|--------|-------------|----------------------------|
 | key  | string | required    | Unique key for a Consumer. |
 
-NOTE: The schema for `key` also defines `encrypted = true`, which means that the field will be stored encrypted in etcd. See [encrypted storage fields](../plugin-develop.md#encrypted-storage-fields).
+NOTE: `encrypt_fields = {"key"}` is also defined in the schema, which means that the field will be stored encrypted in etcd. See [encrypted storage fields](../plugin-develop.md#encrypted-storage-fields).
 
 For Route:
 
diff --git a/docs/en/latest/plugins/openid-connect.md b/docs/en/latest/plugins/openid-connect.md
index 965cf7474..e44e0f622 100644
--- a/docs/en/latest/plugins/openid-connect.md
+++ b/docs/en/latest/plugins/openid-connect.md
@@ -61,6 +61,8 @@ description: OpenID Connect allows the client to obtain user information from th
 | session                              | object  | False    |                       |              | When bearer_only is set to false, openid-connect will use Authorization Code flow to authenticate on the IDP, so you need to set the session-related configuration. |
 | session.secret                       | string  | True     | Automatic generation  | 16 or more characters | The key used for session encrypt and HMAC operation. |
 
+NOTE: `encrypt_fields = {"client_secret"}` is also defined in the schema, which means that the field will be stored encrypted in etcd. See [encrypted storage fields](../plugin-develop.md#encrypted-storage-fields).
+
 ## Scenarios
 
 :::tip
diff --git a/docs/en/latest/plugins/rocketmq-logger.md b/docs/en/latest/plugins/rocketmq-logger.md
index 557f8ce55..003724b81 100644
--- a/docs/en/latest/plugins/rocketmq-logger.md
+++ b/docs/en/latest/plugins/rocketmq-logger.md
@@ -51,6 +51,8 @@ It might take some time to receive the log data. It will be automatically sent a
 | include_resp_body      | boolean | False    | false             | [false, true]         | When set to `true` includes the response body in the log.                                                                                                                                                                 |
 | include_resp_body_expr | array   | False    |                   |                       | Filter for when the `include_resp_body` attribute is set to `true`. Response body is only logged when the expression set here evaluates to `true`. See [lua-resty-expr](https://github.com/api7/lua-resty-expr) for more. |
 
+NOTE: `encrypt_fields = {"secret_key"}` is also defined in the schema, which means that the field will be stored encrypted in etcd. See [encrypted storage fields](../plugin-develop.md#encrypted-storage-fields).
+
 This Plugin supports using batch processors to aggregate and process entries (logs/data) in a batch. This avoids the need for frequently submitting the data. The batch processor submits data every `5` seconds or when the data in the queue reaches `1000`. See [Batch Processor](../batch-processor.md#configuration) for more information or setting your custom configuration.
 
 :::info IMPORTANT
diff --git a/docs/en/latest/plugins/sls-logger.md b/docs/en/latest/plugins/sls-logger.md
index a823b6097..53ea083ba 100644
--- a/docs/en/latest/plugins/sls-logger.md
+++ b/docs/en/latest/plugins/sls-logger.md
@@ -47,6 +47,8 @@ It might take some time to receive the log data. It will be automatically sent a
 | include_req_body  | True     | When set to `true`, includes the request body in the log.                                                                                                                                                                                       |
 | name              | False    | Unique identifier for the batch processor.                                                                                                                                                                                                      |
 
+NOTE: `encrypt_fields = {"access_key_secret"}` is also defined in the schema, which means that the field will be stored encrypted in etcd. See [encrypted storage fields](../plugin-develop.md#encrypted-storage-fields).
+
 This Plugin supports using batch processors to aggregate and process entries (logs/data) in a batch. This avoids the need for frequently submitting the data. The batch processor submits data every `5` seconds or when the data in the queue reaches `1000`. See [Batch Processor](../batch-processor.md#configuration) for more information or setting your custom configuration.
 
 ## Metadata
diff --git a/docs/en/latest/plugins/tencent-cloud-cls.md b/docs/en/latest/plugins/tencent-cloud-cls.md
index fcdc6d0f5..fb3210100 100644
--- a/docs/en/latest/plugins/tencent-cloud-cls.md
+++ b/docs/en/latest/plugins/tencent-cloud-cls.md
@@ -45,6 +45,8 @@ The `tencent-cloud-cls` Plugin uses [TencentCloud CLS](https://cloud.tencent.com
 | include_resp_body | boolean | No       | false   | [false, true] | When set to `true` includes the response body in the log.                                                                                                        |
 | global_tag        | object  | No       |         |               | kv pairs in JSON,send with each log.                                                                                                                             |
 
+NOTE: `encrypt_fields = {"secret_key"}` is also defined in the schema, which means that the field will be stored encrypted in etcd. See [encrypted storage fields](../plugin-develop.md#encrypted-storage-fields).
+
 This Plugin supports using batch processors to aggregate and process entries (logs/data) in a batch. This avoids the need for frequently submitting the data. The batch processor submits data every `5` seconds or when the data in the queue reaches `1000`. See [Batch Processor](../batch-processor.md#configuration) for more information or setting your custom configuration.
 
 ## Metadata
diff --git a/docs/zh/latest/plugin-develop.md b/docs/zh/latest/plugin-develop.md
index 6012c323c..f40363002 100644
--- a/docs/zh/latest/plugin-develop.md
+++ b/docs/zh/latest/plugin-develop.md
@@ -280,13 +280,24 @@ end
 有些插件需要将参数加密存储,比如 `basic-auth` 插件的 `password` 参数。这个插件需要在 `schema` 中指定哪些参数需要被加密存储。
 
 ```lua
-password = { type = "string", encrypted = true },
+encrypt_fields = {"password"}
 ```
 
-通过在 `schema` 中指定 `encrypted = true`,可以将参数加密存储。APISIX 将提供以下功能:
+如果是嵌套的参数,比如 `error-log-logger` 插件的 `clickhouse.password` 参数,需要用 `.` 来分隔:
 
-- 通过 `Admin API` 来新增和更新资源时,对于 `encrypted = true` 的参数,APISIX 会自动加密存储在 etcd 中
-- 通过 `Admin API` 来获取资源时,以及在运行插件时,对于 `encrypted = true` 的参数,APISIX 会自动解密
+```lua
+encrypt_fields = {"clickhouse.password"}
+```
+
+目前还不支持:
+
+1. 两层以上的嵌套
+2. 数组中的字段
+
+通过在 `schema` 中指定 `encrypt_fields = {"password"}`,可以将参数加密存储。APISIX 将提供以下功能:
+
+- 通过 `Admin API` 来新增和更新资源时,对于 `encrypt_fields` 中声明的参数,APISIX 会自动加密存储在 etcd 中
+- 通过 `Admin API` 来获取资源时,以及在运行插件时,对于 `encrypt_fields` 中声明的参数,APISIX 会自动解密
 
 如何开启该功能?
 
@@ -301,7 +312,7 @@ apisix:
         - qeddd145sfvddff4
 ```
 
-`keyring` 是一个数组,可以指定多个 key,APISIX 会按照 keyring 中 key 的顺序,依次尝试用 key 来解密数据(只对声明 `encrypted = true` 的参数)。如果解密失败,会尝试下一个 key,直到解密成功。
+`keyring` 是一个数组,可以指定多个 key,APISIX 会按照 keyring 中 key 的顺序,依次尝试用 key 来解密数据(只对在 `encrypt_fields` 声明的参数)。如果解密失败,会尝试下一个 key,直到解密成功。
 
 如果 `keyring` 中的 key 都无法解密数据,则使用原始数据。
 
diff --git a/docs/zh/latest/plugins/authz-casdoor.md b/docs/zh/latest/plugins/authz-casdoor.md
index a0ed3d9cc..1ad294805 100644
--- a/docs/zh/latest/plugins/authz-casdoor.md
+++ b/docs/zh/latest/plugins/authz-casdoor.md
@@ -40,6 +40,8 @@ description: 本篇文档介绍了 Apache APISIX auth-casdoor 插件的相关信
 | client_secret | string | 是     | Casdoor 的客户端密钥。                 |
 | callback_url  | string | 是     | 用于接收 code 与 state 的回调地址。 |
 
+注意:schema 中还定义了 `encrypt_fields = {"client_secret"}`,这意味着该字段将会被加密存储在 etcd 中。具体参考 [加密存储字段](../plugin-develop.md#加密存储字段)。
+
 :::info IMPORTANT
 
 指定 `endpoint_addr` 和 `callback_url` 属性时不要以 “/” 来结尾。
diff --git a/docs/zh/latest/plugins/authz-keycloak.md b/docs/zh/latest/plugins/authz-keycloak.md
index 6bc5c7b96..811baaa3b 100644
--- a/docs/zh/latest/plugins/authz-keycloak.md
+++ b/docs/zh/latest/plugins/authz-keycloak.md
@@ -66,6 +66,8 @@ description: 本文介绍了关于 Apache APISIX `authz-keycloak` 插件的基
 | access_denied_redirect_uri                   | string        | 否    |                                               | [1, 2048]                                                          | 需要将用户重定向到的 URI,而不是返回类似 `"error_description":"not_authorized"` 这样的错误消息。                                                                                                                                        |
 | password_grant_token_generation_incoming_uri | string        | 否    |                                               | /api/token                                                         | 将此设置为使用密码授予类型生成令牌。该插件会将传入的请求 URI 与此值进行比较。                                                                                                                |
 
+注意:schema 中还定义了 `encrypt_fields = {"client_secret"}`,这意味着该字段将会被加密存储在 etcd 中。具体参考 [加密存储字段](../plugin-develop.md#加密存储字段)。
+
 除上述释义外,还有以下需要注意的点:
 
 - Discovery and endpoints
diff --git a/docs/zh/latest/plugins/basic-auth.md b/docs/zh/latest/plugins/basic-auth.md
index f19dcab57..ac8035152 100644
--- a/docs/zh/latest/plugins/basic-auth.md
+++ b/docs/zh/latest/plugins/basic-auth.md
@@ -42,7 +42,7 @@ Consumer 端:
 | username | string | 是   | Consumer 的用户名并且该用户名是唯一,如果多个 Consumer 使用了相同的 `username`,将会出现请求匹配异常。|
 | password | string | 是   | 用户的密码。                                                                                      |
 
-注意:`password` 的 schema 中还定义了 `encrypted = true`,这意味着该字段将会被加密存储在 etcd 中。具体参考 [加密存储字段](../plugin-develop.md#加密存储字段)。
+注意:schema 中还定义了 `encrypt_fields = {"password"}`,这意味着该字段将会被加密存储在 etcd 中。具体参考 [加密存储字段](../plugin-develop.md#加密存储字段)。
 
 Route 端:
 
diff --git a/docs/zh/latest/plugins/clickhouse-logger.md b/docs/zh/latest/plugins/clickhouse-logger.md
index dc35db7e9..aed09ca71 100644
--- a/docs/zh/latest/plugins/clickhouse-logger.md
+++ b/docs/zh/latest/plugins/clickhouse-logger.md
@@ -45,9 +45,9 @@ description: 本文介绍了 API 网关 Apache APISIX 如何使用 clickhouse-lo
 | name             | string  | 否     | "clickhouse logger" |              | 标识 logger 的唯一标识符。                                |
 | ssl_verify       | boolean | 否     | true                | [true,false] | 当设置为 `true` 时,验证证书。                                                |
 
-该插件支持使用批处理器来聚合并批量处理条目(日志/数据)。这样可以避免插件频繁地提交数据,默认情况下批处理器每 `5` 秒钟或队列中的数据达到 `1000` 条时提交数据,如需了解批处理器相关参数设置,请参考 [Batch-Processor](../batch-processor.md#配置)。
+注意:schema 中还定义了 `encrypt_fields = {"password"}`,这意味着该字段将会被加密存储在 etcd 中。具体参考 [加密存储字段](../plugin-develop.md#加密存储字段)。
 
-注意:`password` 的 schema 中还定义了 `encrypted = true`,这意味着该字段将会被加密存储在 etcd 中。具体参考 [加密存储字段](../plugin-develop.md#加密存储字段)。
+该插件支持使用批处理器来聚合并批量处理条目(日志/数据)。这样可以避免插件频繁地提交数据,默认情况下批处理器每 `5` 秒钟或队列中的数据达到 `1000` 条时提交数据,如需了解批处理器相关参数设置,请参考 [Batch-Processor](../batch-processor.md#配置)。
 
 ## 配置插件元数据
 
diff --git a/docs/zh/latest/plugins/csrf.md b/docs/zh/latest/plugins/csrf.md
index 5db88f8a0..86fb54579 100644
--- a/docs/zh/latest/plugins/csrf.md
+++ b/docs/zh/latest/plugins/csrf.md
@@ -43,6 +43,8 @@ description: CSRF 插件基于 Double Submit Cookie 的方式,帮助用户阻
 | expires | number | 否 | `7200` | | CSRF Cookie 的过期时间,单位为秒。当设置为 `0` 时,会忽略 CSRF Cookie 过期时间检查。|
 | key | string | 是 |  |  | 加密 Token 的密钥。        |
 
+注意:schema 中还定义了 `encrypt_fields = {"key"}`,这意味着该字段将会被加密存储在 etcd 中。具体参考 [加密存储字段](../plugin-develop.md#加密存储字段)。
+
 ## 启用插件
 
 以下示例展示了如何在指定路由上启用并配置 `csrf` 插件:
diff --git a/docs/zh/latest/plugins/elasticsearch-logger.md b/docs/zh/latest/plugins/elasticsearch-logger.md
index 82e035d05..4b25d3077 100644
--- a/docs/zh/latest/plugins/elasticsearch-logger.md
+++ b/docs/zh/latest/plugins/elasticsearch-logger.md
@@ -48,6 +48,8 @@ description: 本文介绍了 API 网关 Apache APISIX 的 elasticsearch-logger 
 | ssl_verify    | boolean | 否       | true                 | 当设置为 `true` 时则启用 SSL 验证。更多信息请参考 [lua-nginx-module](https://github.com/openresty/lua-nginx-module#tcpsocksslhandshake)。 |
 | timeout       | integer | 否       | 10                   | 发送给 Elasticsearch 请求超时时间。                            |
 
+注意:schema 中还定义了 `encrypt_fields = {"auth.password"}`,这意味着该字段将会被加密存储在 etcd 中。具体参考 [加密存储字段](../plugin-develop.md#加密存储字段)。
+
 本插件支持使用批处理器来聚合并批量处理条目(日志和数据)。这样可以避免插件频繁地提交数据,默认设置情况下批处理器会每 `5` 秒钟或队列中的数据达到 `1000` 条时提交数据,如需了解或自定义批处理器相关参数设置,请参考 [Batch-Processor](../batch-processor.md#配置) 配置部分。
 
 ## 启用插件
diff --git a/docs/zh/latest/plugins/error-log-logger.md b/docs/zh/latest/plugins/error-log-logger.md
index 02c717e6f..daee4c521 100644
--- a/docs/zh/latest/plugins/error-log-logger.md
+++ b/docs/zh/latest/plugins/error-log-logger.md
@@ -51,6 +51,8 @@ description: API 网关 Apache APISIX error-log-logger 插件用于将 APISIX 
 | keepalive                        | integer | 否     | 30                             | [1,...]       | 复用连接时,连接保持的时间,以秒为单位。                                             |
 | level                            | string  | 否     | WARN                           |               | 进行错误日志筛选的级别,默认为 `WARN`,取值 ["STDERR", "EMERG", "ALERT", "CRIT", "ERR", "ERROR", "WARN", "NOTICE", "INFO", "DEBUG"],其中 `ERR` 与 `ERROR` 级别一致。 |
 
+注意:schema 中还定义了 `encrypt_fields = {"clickhouse.password"}`,这意味着该字段将会被加密存储在 etcd 中。具体参考 [加密存储字段](../plugin-develop.md#加密存储字段)。
+
 本插件支持使用批处理器来聚合并批量处理条目(日志/数据)。这样可以避免插件频繁地提交数据,默认设置情况下批处理器会每 `5` 秒钟或队列中的数据达到 `1000` 条时提交数据,如需了解或自定义批处理器相关参数设置,请参考 [Batch-Processor](../batch-processor.md#配置) 配置部分。
 
 ## 启用插件
diff --git a/docs/zh/latest/plugins/google-cloud-logging.md b/docs/zh/latest/plugins/google-cloud-logging.md
index 067557c29..7a62b8ac9 100644
--- a/docs/zh/latest/plugins/google-cloud-logging.md
+++ b/docs/zh/latest/plugins/google-cloud-logging.md
@@ -47,6 +47,8 @@ description: API 网关 Apache APISIX 的 google-cloud-logging 插件可用于
 | resource                | 否       | {"type": "global"}                               | 谷歌监控资源,请参考 [MonitoredResource](https://cloud.google.com/logging/docs/reference/v2/rest/v2/MonitoredResource)。             |
 | log_id                  | 否       | apisix.apache.org%2Flogs                         | 谷歌日志 ID,请参考 [LogEntry](https://cloud.google.com/logging/docs/reference/v2/rest/v2/LogEntry)。                                |
 
+注意:schema 中还定义了 `encrypt_fields = {"auth_config.private_key"}`,这意味着该字段将会被加密存储在 etcd 中。具体参考 [加密存储字段](../plugin-develop.md#加密存储字段)。
+
 该插件支持使用批处理器来聚合并批量处理条目(日志和数据)。这样可以避免该插件频繁地提交数据。默认情况下每 `5` 秒钟或队列中的数据达到 `1000` 条时,批处理器会自动提交数据,如需了解更多信息或自定义配置,请参考 [Batch Processor](../batch-processor.md#配置)。
 
 ## 启用插件
diff --git a/docs/zh/latest/plugins/hmac-auth.md b/docs/zh/latest/plugins/hmac-auth.md
index 8621611d9..54c1d0454 100644
--- a/docs/zh/latest/plugins/hmac-auth.md
+++ b/docs/zh/latest/plugins/hmac-auth.md
@@ -47,6 +47,8 @@ description: 本文介绍了关于 Apache APISIX `hmac-auth` 插件的基本信
 | validate_request_body | boolean  | 否   | false         | [ true, false ]                             | 当设置为 `true` 时,对请求 body 做签名校验。                                                                                                                                                 |
 | max_req_body     | integer       | 否   | 512 * 1024    |                                             | 最大允许的 body 大小。                                                                                                                                                                      |
 
+注意:schema 中还定义了 `encrypt_fields = {"secret_key"}`,这意味着该字段将会被加密存储在 etcd 中。具体参考 [加密存储字段](../plugin-develop.md#加密存储字段)。
+
 ## 启用插件
 
 首先,我们需要在 Consumer 中启用该插件,如下所示:
diff --git a/docs/zh/latest/plugins/jwt-auth.md b/docs/zh/latest/plugins/jwt-auth.md
index b4582f642..0f7196c5b 100644
--- a/docs/zh/latest/plugins/jwt-auth.md
+++ b/docs/zh/latest/plugins/jwt-auth.md
@@ -51,6 +51,8 @@ Consumer 端:
 | vault         | object  | 否    |         |                             | 是否使用 Vault 作为存储和检索密钥(HS256/HS512 的密钥或 RS256/ES256 的公钥和私钥)的方式。该插件默认使用 `kv/apisix/consumer/<consumer name>/jwt-auth` 路径进行密钥检索。 |
 | lifetime_grace_period | integer | 否    | 0  | [0,...]                  | 定义生成 JWT 的服务器和验证 JWT 的服务器之间的时钟偏移。该值应该是零(0)或一个正整数。 |
 
+注意:schema 中还定义了 `encrypt_fields = {"secret", "private_key"}`,这意味着该字段将会被加密存储在 etcd 中。具体参考 [加密存储字段](../plugin-develop.md#加密存储字段)。
+
 :::info IMPORTANT
 
 如果你想要启用 Vault 集成,你需要在 [config.yaml](https://github.com/apache/apisix/blob/master/conf/config.yaml) 配置文件中,更新你的 Vault 服务器配置、主机地址和访问令牌。
diff --git a/docs/zh/latest/plugins/key-auth.md b/docs/zh/latest/plugins/key-auth.md
index 8031854e7..23817fea0 100644
--- a/docs/zh/latest/plugins/key-auth.md
+++ b/docs/zh/latest/plugins/key-auth.md
@@ -41,7 +41,7 @@ Consumer 端:
 | ---- | ------ | ------ | ------------------------------------------------------------------------------------------------------------- |
 | key  | string | 是     | 不同的 Consumer 应有不同的 `key`,它应当是唯一的。如果多个 Consumer 使用了相同的 `key`,将会出现请求匹配异常。 |
 
-注意:`key` 的 schema 中还定义了 `encrypted = true`,这意味着该字段将会被加密存储在 etcd 中。具体参考 [加密存储字段](../plugin-develop.md#加密存储字段)。
+注意:schema 中还定义了 `encrypt_fields = {"key"}`,这意味着该字段将会被加密存储在 etcd 中。具体参考 [加密存储字段](../plugin-develop.md#加密存储字段)。
 
 Router 端:
 
diff --git a/docs/zh/latest/plugins/openid-connect.md b/docs/zh/latest/plugins/openid-connect.md
index 5d5a11c30..13a6274ce 100644
--- a/docs/zh/latest/plugins/openid-connect.md
+++ b/docs/zh/latest/plugins/openid-connect.md
@@ -61,6 +61,8 @@ description: OpenID Connect(OIDC)是基于 OAuth 2.0 的身份认证协议
 | session                              | object  | 否     |                       |               | 当设置 bearer_only 为 false 时,openid-connect 插件将使用 Authorization Code 在 IDP 上进行认证,因此你必须设置 session 相关设置。 |
 | session.secret                       | string  | 是     | 自动生成               | 16 个以上字符  | 用于 session 加密和 HMAC 计算的密钥。 |
 
+注意:schema 中还定义了 `encrypt_fields = {"client_secret"}`,这意味着该字段将会被加密存储在 etcd 中。具体参考 [加密存储字段](../plugin-develop.md#加密存储字段)。
+
 ## 使用场景
 
 :::tip
diff --git a/docs/zh/latest/plugins/rocketmq-logger.md b/docs/zh/latest/plugins/rocketmq-logger.md
index 235bc0f1c..5e952837d 100644
--- a/docs/zh/latest/plugins/rocketmq-logger.md
+++ b/docs/zh/latest/plugins/rocketmq-logger.md
@@ -50,6 +50,8 @@ description: API 网关 Apache APISIX 的 rocketmq-logger 插件用于将日志
 | include_resp_body      | boolean | 否     | false             | [false, true]         | 当设置为 `true` 时,包含响应体。 |
 | include_resp_body_expr | array   | 否     |                   |                       | 当 `include_resp_body` 属性设置为 `true` 时进行过滤响应体,并且只有当此处设置的表达式计算结果为 `true` 时,才会记录响应体。更多信息,请参考 [lua-resty-expr](https://github.com/api7/lua-resty-expr)。 |
 
+注意:schema 中还定义了 `encrypt_fields = {"secret_key"}`,这意味着该字段将会被加密存储在 etcd 中。具体参考 [加密存储字段](../plugin-develop.md#加密存储字段)。
+
 该插件支持使用批处理器来聚合并批量处理条目(日志/数据)。这样可以避免插件频繁地提交数据,默认设置情况下批处理器会每 `5` 秒钟或队列中的数据达到 `1000` 条时提交数据,如需了解批处理器相关参数设置,请参考 [Batch-Processor](../batch-processor.md#配置)。
 
 :::tip 提示
diff --git a/docs/zh/latest/plugins/sls-logger.md b/docs/zh/latest/plugins/sls-logger.md
index 3b2e737a0..1162426f3 100644
--- a/docs/zh/latest/plugins/sls-logger.md
+++ b/docs/zh/latest/plugins/sls-logger.md
@@ -44,6 +44,8 @@ title: sls-logger
 | include_req_body | 可选的 | 是否包含请求体。|
 |name| 可选的 | 批处理名字。|
 
+注意:schema 中还定义了 `encrypt_fields = {"access_key_secret"}`,这意味着该字段将会被加密存储在 etcd 中。具体参考 [加密存储字段](../plugin-develop.md#加密存储字段)。
+
 本插件支持使用批处理器来聚合并批量处理条目(日志/数据)。这样可以避免插件频繁地提交数据,默认设置情况下批处理器会每 `5` 秒钟或队列中的数据达到 `1000` 条时提交数据,如需了解或自定义批处理器相关参数设置,请参考 [Batch-Processor](../batch-processor.md#配置) 配置部分。
 
 ## 插件元数据设置
diff --git a/docs/zh/latest/plugins/tencent-cloud-cls.md b/docs/zh/latest/plugins/tencent-cloud-cls.md
index 32a2b9e5c..a94791057 100644
--- a/docs/zh/latest/plugins/tencent-cloud-cls.md
+++ b/docs/zh/latest/plugins/tencent-cloud-cls.md
@@ -45,6 +45,8 @@ description: API 网关 Apache APISIX tencent-cloud-cls 插件可用于将日志
 | include_resp_body | boolean | 否     | false | [false, true]| 当设置为 `true` 时,日志中将包含响应体。                                                     |
 | global_tag        | object  | 否     |       |              | kv 形式的 JSON 数据,可以写入每一条日志,便于在 CLS 中检索。                                        |
 
+注意:schema 中还定义了 `encrypt_fields = {"secret_key"}`,这意味着该字段将会被加密存储在 etcd 中。具体参考 [加密存储字段](../plugin-develop.md#加密存储字段)。
+
 该插件支持使用批处理器来聚合并批量处理条目(日志/数据)。这样可以避免插件频繁地提交数据,默认情况下批处理器每 `5` 秒钟或队列中的数据达到 `1000` 条时提交数据,如需了解批处理器相关参数设置,请参考 [Batch-Processor](../batch-processor.md#配置)。
 
 ## 插件元数据
diff --git a/t/admin/plugins.t b/t/admin/plugins.t
index 5edb3c427..9791278e2 100644
--- a/t/admin/plugins.t
+++ b/t/admin/plugins.t
@@ -339,7 +339,7 @@ qr/\[\{"name":"wolf-rbac","priority":2555\},\{"name":"ldap-auth","priority":2540
         }
     }
 --- response_body eval
-qr/\{"properties":\{"password":\{"encrypted":true,"type":"string"\},"username":\{"type":"string"\}\},"required":\["username","password"\],"title":"work with consumer object","type":"object"\}/
+qr/\{"encrypt_fields":\["password"\],"properties":\{"password":\{"type":"string"\},"username":\{"type":"string"\}\},"required":\["username","password"\],"title":"work with consumer object","type":"object"\}/
 
 
 
diff --git a/t/plugin/authz-casdoor.t b/t/plugin/authz-casdoor.t
index ace2c966b..926d3daef 100644
--- a/t/plugin/authz-casdoor.t
+++ b/t/plugin/authz-casdoor.t
@@ -438,3 +438,71 @@ invalid state
 done
 --- error_log
 failed when accessing token: invalid access_token
+
+
+
+=== TEST 10: data encryption for client_secret
+--- yaml_config
+apisix:
+    data_encryption:
+        enable: true
+        keyring:
+            - edd1c9f0985e76a2
+--- config
+    location /t {
+        content_by_lua_block {
+            local json = require("toolkit.json")
+            local t = require("lib.test_admin").test
+            local callback_url = "http://127.0.0.1:" .. ngx.var.server_port ..
+                                 "/anything/callback"
+            local code, body = t('/apisix/admin/routes/1',
+                ngx.HTTP_PUT,
+                [[{
+                    "methods": ["GET"],
+                    "uri": "/anything/*",
+                    "plugins": {
+                        "authz-casdoor": {
+                            "callback_url":"]] .. callback_url .. [[",
+                            "endpoint_addr": "http://127.0.0.1:10420",
+                            "client_id":"7ceb9b7fda4a9061ec1c",
+                            "client_secret":"3416238e1edf915eac08b8fe345b2b95cdba7e04"
+                        }
+                    },
+                    "upstream": {
+                        "type": "roundrobin",
+                        "nodes": {
+                        "httpbin.org:80": 1
+                        }
+                    }
+                }]]
+            )
+
+            if code >= 300 then
+                ngx.status = code
+                ngx.say(body)
+                return
+            end
+            ngx.sleep(0.1)
+
+            -- get plugin conf from admin api, password is decrypted
+            local code, message, res = t('/apisix/admin/routes/1',
+                ngx.HTTP_GET
+            )
+            res = json.decode(res)
+            if code >= 300 then
+                ngx.status = code
+                ngx.say(message)
+                return
+            end
+
+            ngx.say(res.value.plugins["authz-casdoor"].client_secret)
+
+            -- get plugin conf from etcd, password is encrypted
+            local etcd = require("apisix.core.etcd")
+            local res = assert(etcd.get('/routes/1'))
+            ngx.say(res.body.node.value.plugins["authz-casdoor"].client_secret)
+        }
+    }
+--- response_body
+3416238e1edf915eac08b8fe345b2b95cdba7e04
+YUfqAO0kPXjZIoAbPSuryCkUDksEmwSq08UDTIUWolN6KQwEUrh72TazePueo4/S
diff --git a/t/plugin/authz-keycloak3.t b/t/plugin/authz-keycloak3.t
index a198fb8f8..2671e9039 100644
--- a/t/plugin/authz-keycloak3.t
+++ b/t/plugin/authz-keycloak3.t
@@ -106,3 +106,73 @@ passed
 --- error_code: 307
 --- response_headers
 Location: http://127.0.0.1/test
+
+
+
+=== TEST 3: data encryption for client_secret
+--- yaml_config
+apisix:
+    data_encryption:
+        enable: true
+        keyring:
+            - edd1c9f0985e76a2
+--- config
+    location /t {
+        content_by_lua_block {
+            local json = require("toolkit.json")
+            local t = require("lib.test_admin").test
+
+            local code, body = t('/apisix/admin/routes/1',
+                 ngx.HTTP_PUT,
+                 [[{
+                        "plugins": {
+                            "authz-keycloak": {
+                                "token_endpoint": "https://127.0.0.1:8443/auth/realms/University/protocol/openid-connect/token",
+                                "permissions": ["course_resource#view"],
+                                "client_id": "course_management",
+                                "client_secret": "d1ec69e9-55d2-4109-a3ea-befa071579d5",
+                                "grant_type": "urn:ietf:params:oauth:grant-type:uma-ticket",
+                                "timeout": 3000,
+                                "ssl_verify": false,
+                                "password_grant_token_generation_incoming_uri": "/api/token"
+                            }
+                        },
+                        "upstream": {
+                            "nodes": {
+                                "127.0.0.1:1982": 1
+                            },
+                            "type": "roundrobin"
+                        },
+                        "uri": "/api/token"
+                }]]
+            )
+
+            if code >= 300 then
+                ngx.status = code
+                ngx.say(body)
+                return
+            end
+            ngx.sleep(0.1)
+
+            -- get plugin conf from admin api, password is decrypted
+            local code, message, res = t('/apisix/admin/routes/1',
+                ngx.HTTP_GET
+            )
+            res = json.decode(res)
+            if code >= 300 then
+                ngx.status = code
+                ngx.say(message)
+                return
+            end
+
+            ngx.say(res.value.plugins["authz-keycloak"].client_secret)
+
+            -- get plugin conf from etcd, password is encrypted
+            local etcd = require("apisix.core.etcd")
+            local res = assert(etcd.get('/routes/1'))
+            ngx.say(res.body.node.value.plugins["authz-keycloak"].client_secret)
+        }
+    }
+--- response_body
+d1ec69e9-55d2-4109-a3ea-befa071579d5
+Fz1juZEEvh9PPXOmWFdMMJkREt3ZSzEVWcUZPxNP6achk3fosEvn37oN0qH4YgKB
diff --git a/t/plugin/csrf.t b/t/plugin/csrf.t
index 6f76dabb4..1edc30239 100644
--- a/t/plugin/csrf.t
+++ b/t/plugin/csrf.t
@@ -325,3 +325,66 @@ passed
     }
 --- response_body
 hello world
+
+
+
+=== TEST 15: data encryption for key
+--- yaml_config
+apisix:
+    data_encryption:
+        enable: true
+        keyring:
+            - edd1c9f0985e76a2
+--- config
+    location /t {
+        content_by_lua_block {
+            local json = require("toolkit.json")
+            local t = require("lib.test_admin").test
+            local code, body = t('/apisix/admin/routes/1',
+                ngx.HTTP_PUT,
+                [[{
+                    "uri": "/hello",
+                    "upstream": {
+                        "type": "roundrobin",
+                        "nodes": {
+                            "127.0.0.1:1980": 1
+                        }
+                    },
+                    "plugins": {
+                        "csrf": {
+                            "key": "userkey",
+                            "expires": 1000000000
+                        }
+                    }
+                }]]
+            )
+
+            if code >= 300 then
+                ngx.status = code
+                ngx.say(body)
+                return
+            end
+            ngx.sleep(0.1)
+
+            -- get plugin conf from admin api, password is decrypted
+            local code, message, res = t('/apisix/admin/routes/1',
+                ngx.HTTP_GET
+            )
+            res = json.decode(res)
+            if code >= 300 then
+                ngx.status = code
+                ngx.say(message)
+                return
+            end
+
+            ngx.say(res.value.plugins["csrf"].key)
+
+            -- get plugin conf from etcd, password is encrypted
+            local etcd = require("apisix.core.etcd")
+            local res = assert(etcd.get('/routes/1'))
+            ngx.say(res.body.node.value.plugins["csrf"].key)
+        }
+    }
+--- response_body
+userkey
+mt39FazQccyMqt4ctoRV7w==
diff --git a/t/plugin/elasticsearch-logger.t b/t/plugin/elasticsearch-logger.t
index b381bf752..11b85e14b 100644
--- a/t/plugin/elasticsearch-logger.t
+++ b/t/plugin/elasticsearch-logger.t
@@ -447,3 +447,71 @@ hello world
 --- wait: 2
 --- error_log
 check elasticsearch custom body success
+
+
+
+=== TEST 12: data encryption for auth.password
+--- yaml_config
+apisix:
+    data_encryption:
+        enable: true
+        keyring:
+            - edd1c9f0985e76a2
+--- config
+    location /t {
+        content_by_lua_block {
+            local json = require("toolkit.json")
+            local t = require("lib.test_admin").test
+            local code, body = t('/apisix/admin/routes/1', ngx.HTTP_PUT, {
+                uri = "/hello",
+                upstream = {
+                    type = "roundrobin",
+                    nodes = {
+                        ["127.0.0.1:1980"] = 1
+                    }
+                },
+                plugins = {
+                    ["elasticsearch-logger"] = {
+                        endpoint_addr = "http://127.0.0.1:9201",
+                        field = {
+                            index = "services"
+                        },
+                        auth = {
+                            username = "elastic",
+                            password = "123456"
+                        },
+                        batch_max_size = 1,
+                        inactive_timeout = 1
+                    }
+                }
+            })
+
+            if code >= 300 then
+                ngx.status = code
+                ngx.say(body)
+                return
+            end
+            ngx.sleep(0.1)
+
+            -- get plugin conf from admin api, password is decrypted
+            local code, message, res = t('/apisix/admin/routes/1',
+                ngx.HTTP_GET
+            )
+            res = json.decode(res)
+            if code >= 300 then
+                ngx.status = code
+                ngx.say(message)
+                return
+            end
+
+            ngx.say(res.value.plugins["elasticsearch-logger"].auth.password)
+
+            -- get plugin conf from etcd, password is encrypted
+            local etcd = require("apisix.core.etcd")
+            local res = assert(etcd.get('/routes/1'))
+            ngx.say(res.body.node.value.plugins["elasticsearch-logger"].auth.password)
+        }
+    }
+--- response_body
+123456
+PTQvJEaPcNOXcOHeErC0XQ==
diff --git a/t/plugin/error-log-logger-clickhouse.t b/t/plugin/error-log-logger-clickhouse.t
index f6a328d7f..41dfe15d4 100644
--- a/t/plugin/error-log-logger-clickhouse.t
+++ b/t/plugin/error-log-logger-clickhouse.t
@@ -211,3 +211,85 @@ this is an info message for test6
     }
 --- response_body
 passed
+
+
+
+=== TEST 8: data encryption for clickhouse.password
+--- yaml_config
+apisix:
+    data_encryption:
+        enable: true
+        keyring:
+            - edd1c9f0985e76a2
+--- config
+    location /t {
+        content_by_lua_block {
+            local json = require("toolkit.json")
+            local t = require("lib.test_admin").test
+            local code, body = t('/apisix/admin/plugin_metadata/error-log-logger',
+                ngx.HTTP_PUT,
+                [[{
+                    "clickhouse": {
+                        "user": "default",
+                        "password": "bar",
+                        "database": "default",
+                        "logtable": "t",
+                        "endpoint_addr": "http://127.0.0.1:1980/clickhouse_logger_server"
+                    },
+                    "batch_max_size": 15,
+                    "inactive_timeout": 1
+                }]]
+            )
+
+            if code >= 300 then
+                ngx.status = code
+                ngx.say(body)
+                return
+            end
+            ngx.sleep(0.1)
+
+            -- get plugin conf from admin api, password is decrypted
+            local code, message, res = t('/apisix/admin/plugin_metadata/error-log-logger',
+                ngx.HTTP_GET
+            )
+            res = json.decode(res)
+            if code >= 300 then
+                ngx.status = code
+                ngx.say(message)
+                return
+            end
+
+            ngx.say(res.value["clickhouse"].password)
+
+            -- get plugin conf from etcd, password is encrypted
+            local etcd = require("apisix.core.etcd")
+            local res = assert(etcd.get('/plugin_metadata/error-log-logger'))
+
+            ngx.say(res.body.node.value["clickhouse"].password)
+        }
+    }
+--- response_body
+bar
+77+NmbYqNfN+oLm0aX5akg==
+
+
+
+=== TEST 9: verify use the decrypted password to connect to clickhouse
+--- yaml_config
+apisix:
+    data_encryption:
+        enable: true
+        keyring:
+            - edd1c9f0985e76a2
+--- config
+    location /t {
+        content_by_lua_block {
+            local core = require("apisix.core")
+            core.log.warn("this is a warning message for test9")
+        }
+    }
+--- response_body
+--- error_log
+this is a warning message for test9
+clickhouse headers: x-clickhouse-key:bar
+--- wait: 5
diff --git a/t/plugin/google-cloud-logging2.t b/t/plugin/google-cloud-logging2.t
index 8efec7942..6fbe6b350 100644
--- a/t/plugin/google-cloud-logging2.t
+++ b/t/plugin/google-cloud-logging2.t
@@ -68,3 +68,128 @@ __DATA__
     }
 --- response_body
 passed
+
+
+
+=== TEST 2: data encryption for auth_config.private_key
+--- yaml_config
+apisix:
+    data_encryption:
+        enable: true
+        keyring:
+            - edd1c9f0985e76a2
+--- config
+    location /t {
+        content_by_lua_block {
+            local json = require("toolkit.json")
+            local config = {
+                uri = "/hello",
+                upstream = {
+                    type = "roundrobin",
+                    nodes = {
+                        ["127.0.0.1:1980"] = 1
+                    }
+                },
+                plugins = {
+                    ["google-cloud-logging"] = {
+                        auth_config = {
+                            private_key = [[
+-----BEGIN PRIVATE KEY-----
+MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDDzrFwnA3EvYyR
+aeMgaLD3hBjvxKrz10uox1X8q7YYhf2ViRtLRUMa2bEMYksE5hbhwpNf6mKAnLOC
+UuAT6cPPdUl/agKpJXviBPIR2LuzD17WsLJHp1HxUDssSkgfCaGcOGGNfLUhhIpF
+2JUctLmxiZoAZySlSjcwupSuDJ0aPm0XO8r9H8Qu5kF2Vkz5e5bFivLTmvzrQTe4
+v5V1UI6hThElCSeUmdNF3uG3wopxlvq4zXgLTnuLbrNf/Gc4mlpV+UDgTISj32Ep
+AB2vxKEbvQw4ti8YJnGXWjxLerhfrszFw+V8lpeduiDYA44ZFoVqvzxeIsVZNtcw
+Iu7PvEPNAgMBAAECggEAVpyN9m7A1F631/aLheFpLgMbeKt4puV7zQtnaJ2XrZ9P
+PR7pmNDpTu4uF3k/D8qrIm+L+uhVa+hkquf3wDct6w1JVnfQ93riImbnoKdK13ic
+DcEZCwLjByfjFMNCxZ/gAZca55fbExlqhFy6EHmMjhB8s2LsXcTHRuGxNI/Vyi49
+sxECibe0U53aqdJbVWrphIS67cpwl4TUkN6mrHsNuDYNJ9dgkpapoqp4FTFQsBqC
+afOK5qgJ68dWZ47FBUng+AZjdCncqAIuJxxItGVQP6YPsFs+OXcivIVHJr363TpC
+l85FfdvqWV5OGBbwSKhNwiTNUVvfSQVmtURGWG/HbQKBgQD4gZ1z9+Lx19kT9WTz
+lw93lxso++uhAPDTKviyWSRoEe5aN3LCd4My+/Aj+sk4ON/s2BV3ska5Im93j+vC
+rCv3uPn1n2jUhWuJ3bDqipeTW4n/CQA2m/8vd26TMk22yOkkqw2MIA8sjJ//SD7g
+tdG7up6DgGMP4hgbO89uGU7DAwKBgQDJtkKd0grh3u52Foeh9YaiAgYRwc65IE16
+UyD1OJxIuX/dYQDLlo5KyyngFa1ZhWIs7qC7r3xXH+10kfJY+Q+5YMjmZjlL8SR1
+Ujqd02R9F2//6OeswyReachJZbZdtiEw3lPa4jVFYfhSe0M2ZPxMwvoXb25eyCNI
+1lYjSKq87wKBgHnLTNghjeDp4UKe6rNYPgRm0rDrhziJtX5JeUov1mALKb6dnmkh
+GfRK9g8sQqKDfXwfC6Z2gaMK9YaryujGaWYoCpoPXtmJ6oLPXH4XHuLh4mhUiP46
+xn8FEfSimuQS4/FMxH8A128GHQSI7AhGFFzlwfrBWcvXC+mNDsTvMmLxAoGARc+4
+upppfccETQZ7JsitMgD1TMwA2f2eEwoWTAitvlXFNT9PYSbYVHaAJbga6PLLCbYF
+FzAjHpxEOKYSdEyu7n/ayDL0/Z2V+qzc8KarDsg/0RgwppBbU/nUgeKb/U79qcYo
+y4ai3UKNCS70Ei1dTMvmdpnwXwlxfNIBufB6dy0CgYBMYq9Lc31GkC6PcGEEbx6W
+vjImOadWZbuOVnvEQjb5XCdcOsWsMcg96PtoeuyyHmhnEF1GsMzcIdQv/PHrvYpK
+Yp8D0aqsLEgwGrJQER26FPpKmyIwvcL+nm6q5W31PnU9AOC/WEkB6Zs58hsMzD2S
+kEJQcmfVew5mFXyxuEn3zA==
+-----END PRIVATE KEY-----]],
+                            project_id = "apisix",
+                            token_uri = "http://127.0.0.1:1980/google/logging/token",
+                            scopes = {
+                                "https://apisix.apache.org/logs:admin"
+                            },
+                            entries_uri = "http://127.0.0.1:1980/google/logging/entries",
+                        },
+                        inactive_timeout = 1,
+                        batch_max_size = 1,
+                    }
+                }
+            }
+            local t = require("lib.test_admin").test
+            local code, body = t('/apisix/admin/routes/1', ngx.HTTP_PUT, config)
+
+            if code >= 300 then
+                ngx.status = code
+                ngx.say(body)
+                return
+            end
+            ngx.sleep(0.1)
+
+            -- get plugin conf from admin api, password is decrypted
+            local code, message, res = t('/apisix/admin/routes/1',
+                ngx.HTTP_GET
+            )
+            res = json.decode(res)
+            if code >= 300 then
+                ngx.status = code
+                ngx.say(message)
+                return
+            end
+
+            ngx.say(res.value.plugins["google-cloud-logging"].auth_config.private_key)
+
+            -- get plugin conf from etcd, password is encrypted
+            local etcd = require("apisix.core.etcd")
+            local res = assert(etcd.get('/routes/1'))
+            ngx.say(res.body.node.value.plugins["google-cloud-logging"].auth_config.private_key)
+        }
+    }
+--- response_body
+-----BEGIN PRIVATE KEY-----
+MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDDzrFwnA3EvYyR
+aeMgaLD3hBjvxKrz10uox1X8q7YYhf2ViRtLRUMa2bEMYksE5hbhwpNf6mKAnLOC
+UuAT6cPPdUl/agKpJXviBPIR2LuzD17WsLJHp1HxUDssSkgfCaGcOGGNfLUhhIpF
+2JUctLmxiZoAZySlSjcwupSuDJ0aPm0XO8r9H8Qu5kF2Vkz5e5bFivLTmvzrQTe4
+v5V1UI6hThElCSeUmdNF3uG3wopxlvq4zXgLTnuLbrNf/Gc4mlpV+UDgTISj32Ep
+AB2vxKEbvQw4ti8YJnGXWjxLerhfrszFw+V8lpeduiDYA44ZFoVqvzxeIsVZNtcw
+Iu7PvEPNAgMBAAECggEAVpyN9m7A1F631/aLheFpLgMbeKt4puV7zQtnaJ2XrZ9P
+PR7pmNDpTu4uF3k/D8qrIm+L+uhVa+hkquf3wDct6w1JVnfQ93riImbnoKdK13ic
+DcEZCwLjByfjFMNCxZ/gAZca55fbExlqhFy6EHmMjhB8s2LsXcTHRuGxNI/Vyi49
+sxECibe0U53aqdJbVWrphIS67cpwl4TUkN6mrHsNuDYNJ9dgkpapoqp4FTFQsBqC
+afOK5qgJ68dWZ47FBUng+AZjdCncqAIuJxxItGVQP6YPsFs+OXcivIVHJr363TpC
+l85FfdvqWV5OGBbwSKhNwiTNUVvfSQVmtURGWG/HbQKBgQD4gZ1z9+Lx19kT9WTz
+lw93lxso++uhAPDTKviyWSRoEe5aN3LCd4My+/Aj+sk4ON/s2BV3ska5Im93j+vC
+rCv3uPn1n2jUhWuJ3bDqipeTW4n/CQA2m/8vd26TMk22yOkkqw2MIA8sjJ//SD7g
+tdG7up6DgGMP4hgbO89uGU7DAwKBgQDJtkKd0grh3u52Foeh9YaiAgYRwc65IE16
+UyD1OJxIuX/dYQDLlo5KyyngFa1ZhWIs7qC7r3xXH+10kfJY+Q+5YMjmZjlL8SR1
+Ujqd02R9F2//6OeswyReachJZbZdtiEw3lPa4jVFYfhSe0M2ZPxMwvoXb25eyCNI
+1lYjSKq87wKBgHnLTNghjeDp4UKe6rNYPgRm0rDrhziJtX5JeUov1mALKb6dnmkh
+GfRK9g8sQqKDfXwfC6Z2gaMK9YaryujGaWYoCpoPXtmJ6oLPXH4XHuLh4mhUiP46
+xn8FEfSimuQS4/FMxH8A128GHQSI7AhGFFzlwfrBWcvXC+mNDsTvMmLxAoGARc+4
+upppfccETQZ7JsitMgD1TMwA2f2eEwoWTAitvlXFNT9PYSbYVHaAJbga6PLLCbYF
+FzAjHpxEOKYSdEyu7n/ayDL0/Z2V+qzc8KarDsg/0RgwppBbU/nUgeKb/U79qcYo
+y4ai3UKNCS70Ei1dTMvmdpnwXwlxfNIBufB6dy0CgYBMYq9Lc31GkC6PcGEEbx6W
+vjImOadWZbuOVnvEQjb5XCdcOsWsMcg96PtoeuyyHmhnEF1GsMzcIdQv/PHrvYpK
+Yp8D0aqsLEgwGrJQER26FPpKmyIwvcL+nm6q5W31PnU9AOC/WEkB6Zs58hsMzD2S
+kEJQcmfVew5mFXyxuEn3zA==
+-----END PRIVATE KEY-----
+YnwwDKc5vNzo0OU4StTRQbwgCnTZ3dmYiBFm8aGnvTxlE86D2nT07Q3BWhUdky6OGIox4MRLbiHz13NZjyUao/Nudh4PeTj5wMldPD5YvNWtbTG4ig/TNSdBncmIQPLPaUqSweE61pnASxodpTlBJ5k9yxfTmwBTOkzZevoKy9D2E4wF9vGCdkcPK/tAkvRoJTj6xD3xVuAbkcap/81oHplUZZ+ghlEnBZgOH8UMa73UfeNbOQVHD2mlU0LxkTXtwFhHWl50adrt890VDHev0+FUUDjv5Ysl8r/nnnlyq3SV4oqJfs/IVRKROe93e8sJ2/49o7kEv2XT1/6DjM/VsSLKfAi5rLNobcSaSzztSSLkrBFKQvvy2rRA7GFWKbIk+rPZhYmTMItDJv23XP6uzaLRPoq2f/AnRTKpWmA8Dk9TfFHsZLupKi1bmjCdtK8lpMCf9Au1rezt7+2BybQrtbbDbwPzC5bKHmKhc0GPTUzL [...]
diff --git a/t/plugin/hmac-auth3.t b/t/plugin/hmac-auth3.t
index 7e89b995d..df41efaa1 100644
--- a/t/plugin/hmac-auth3.t
+++ b/t/plugin/hmac-auth3.t
@@ -682,3 +682,76 @@ location /t {
 }
 --- response_body
 passed
+
+
+
+=== TEST 13: delete exist consumers
+--- config
+    location /t {
+        content_by_lua_block {
+            local t = require("lib.test_admin").test
+            -- delete exist consumers
+            local code, body = t('/apisix/admin/consumers/robin', ngx.HTTP_DELETE)
+            ngx.say(body)
+        }
+    }
+--- response_body
+passed
+
+
+
+=== TEST 14: data encryption for secret_key
+--- yaml_config
+apisix:
+    data_encryption:
+        enable: true
+        keyring:
+            - edd1c9f0985e76a2
+--- config
+    location /t {
+        content_by_lua_block {
+            local json = require("toolkit.json")
+            local t = require("lib.test_admin").test
+            local code, body = t('/apisix/admin/consumers',
+                ngx.HTTP_PUT,
+                [[{
+                    "username": "jack",
+                    "plugins": {
+                        "hmac-auth": {
+                            "access_key": "my-access-key",
+                            "secret_key": "my-secret-key",
+                            "clock_skew": 10
+                        }
+                    }
+                }]]
+            )
+
+            if code >= 300 then
+                ngx.status = code
+                ngx.say(body)
+                return
+            end
+            ngx.sleep(0.1)
+
+            -- get plugin conf from admin api, password is decrypted
+            local code, message, res = t('/apisix/admin/consumers/jack',
+                ngx.HTTP_GET
+            )
+            res = json.decode(res)
+            if code >= 300 then
+                ngx.status = code
+                ngx.say(message)
+                return
+            end
+
+            ngx.say(res.value.plugins["hmac-auth"].secret_key)
+
+            -- get plugin conf from etcd, password is encrypted
+            local etcd = require("apisix.core.etcd")
+            local res = assert(etcd.get('/consumers/jack'))
+            ngx.say(res.body.node.value.plugins["hmac-auth"].secret_key)
+        }
+    }
+--- response_body
+my-secret-key
+IRWpPjbDq5BCgHyIllnOMA==
diff --git a/t/plugin/jwt-auth3.t b/t/plugin/jwt-auth3.t
index 6a9777132..3e431232f 100755
--- a/t/plugin/jwt-auth3.t
+++ b/t/plugin/jwt-auth3.t
@@ -300,3 +300,145 @@ GET /get
 Cookie: hello=world; jwt-cookie=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJrZXkiOiJ1c2VyLWtleSIsImV4cCI6MTg3OTMxODU0MX0.fNtFJnNmJgzbiYmGB0Yjvm-l6A6M4jRV1l4mnVFSYjs; foo=bar
 --- response_body eval
 qr/hello=world; foo=bar/
+
+
+
+=== TEST 13: delete exist consumers
+--- config
+    location /t {
+        content_by_lua_block {
+            local t = require("lib.test_admin").test
+            -- delete exist consumers
+            local code, body = t('/apisix/admin/consumers/jack', ngx.HTTP_DELETE)
+            ngx.say(body)
+        }
+    }
+--- response_body
+passed
+
+
+
+=== TEST 14: data encryption for secret
+--- yaml_config
+apisix:
+    data_encryption:
+        enable: true
+        keyring:
+            - edd1c9f0985e76a2
+--- config
+    location /t {
+        content_by_lua_block {
+            local json = require("toolkit.json")
+            local t = require("lib.test_admin").test
+            local code, body = t('/apisix/admin/consumers',
+                ngx.HTTP_PUT,
+                [[{
+                    "username": "jack",
+                    "plugins": {
+                        "jwt-auth": {
+                            "key": "user-key",
+                            "secret": "my-secret-key"
+                        }
+                    }
+                }]]
+            )
+
+            if code >= 300 then
+                ngx.status = code
+                ngx.say(body)
+                return
+            end
+            ngx.sleep(0.1)
+
+            -- get plugin conf from admin api, password is decrypted
+            local code, message, res = t('/apisix/admin/consumers/jack',
+                ngx.HTTP_GET
+            )
+            res = json.decode(res)
+            if code >= 300 then
+                ngx.status = code
+                ngx.say(message)
+                return
+            end
+            ngx.say(res.value.plugins["jwt-auth"].secret)
+
+            -- get plugin conf from etcd, password is encrypted
+            local etcd = require("apisix.core.etcd")
+            local res = assert(etcd.get('/consumers/jack'))
+            ngx.say(res.body.node.value.plugins["jwt-auth"].secret)
+        }
+    }
+--- response_body
+my-secret-key
+IRWpPjbDq5BCgHyIllnOMA==
+
+
+
+=== TEST 15: data encryption for private_key
+--- yaml_config
+apisix:
+    data_encryption:
+        enable: true
+        keyring:
+            - edd1c9f0985e76a2
+--- config
+    location /t {
+        content_by_lua_block {
+            local json = require("toolkit.json")
+            local t = require("lib.test_admin").test
+
+            -- dletet exist consumers
+            t('/apisix/admin/consumers/jack', ngx.HTTP_DELETE)
+
+            local code, body = t('/apisix/admin/consumers',
+                ngx.HTTP_PUT,
+                [[{
+                    "username": "jack",
+                    "plugins": {
+                        "jwt-auth": {
+                            "key": "user-key-rs256",
+                            "algorithm": "RS256",
+                            "public_key": "-----BEGIN PUBLIC KEY-----\nMFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKebDxlvQMGyEesAL1r1nIJBkSdqu3Hr\n7noq/0ukiZqVQLSJPMOv0oxQSutvvK3hoibwGakDOza+xRITB7cs2cECAwEAAQ==\n-----END PUBLIC KEY-----",
+                            "private_key": "-----BEGIN RSA PRIVATE KEY-----\nMIIBOgIBAAJBAKebDxlvQMGyEesAL1r1nIJBkSdqu3Hr7noq/0ukiZqVQLSJPMOv\n0oxQSutvvK3hoibwGakDOza+xRITB7cs2cECAwEAAQJAYPWh6YvjwWobVYC45Hz7\n+pqlt1DWeVQMlN407HSWKjdH548ady46xiQuZ5Cfx3YyCcnsfVWaQNbC+jFbY4YL\nwQIhANfASwz8+2sKg1xtvzyaChX5S5XaQTB+azFImBJumixZAiEAxt93Td6JH1RF\nIeQmD/K+DClZMqSrliUzUqJnCPCzy6kCIAekDsRh/UF4ONjAJkKuLedDUfL3rNFb\n2M4BBSm58wnZAiEAwYLMOg8h6kQ7iMDRcI9I8diCHM8yz0SfbfbsvzxIFxECICXs\nYvIufaZvBa8f+E/9CAN [...]
+                        }
+                    }
+                }]]
+            )
+
+            if code >= 300 then
+                ngx.status = code
+                ngx.say(body)
+                return
+            end
+            ngx.sleep(0.1)
+
+            -- get plugin conf from admin api, password is decrypted
+            local code, message, res = t('/apisix/admin/consumers/jack',
+                ngx.HTTP_GET
+            )
+            res = json.decode(res)
+            if code >= 300 then
+                ngx.status = code
+                ngx.say(message)
+                return
+            end
+
+            ngx.say(res.value.plugins["jwt-auth"].private_key)
+
+            -- get plugin conf from etcd, password is encrypted
+            local etcd = require("apisix.core.etcd")
+            local res = assert(etcd.get('/consumers/jack'))
+            ngx.say(res.body.node.value.plugins["jwt-auth"].private_key)
+        }
+    }
+--- response_body
+-----BEGIN RSA PRIVATE KEY-----
+MIIBOgIBAAJBAKebDxlvQMGyEesAL1r1nIJBkSdqu3Hr7noq/0ukiZqVQLSJPMOv
+0oxQSutvvK3hoibwGakDOza+xRITB7cs2cECAwEAAQJAYPWh6YvjwWobVYC45Hz7
++pqlt1DWeVQMlN407HSWKjdH548ady46xiQuZ5Cfx3YyCcnsfVWaQNbC+jFbY4YL
+wQIhANfASwz8+2sKg1xtvzyaChX5S5XaQTB+azFImBJumixZAiEAxt93Td6JH1RF
+IeQmD/K+DClZMqSrliUzUqJnCPCzy6kCIAekDsRh/UF4ONjAJkKuLedDUfL3rNFb
+2M4BBSm58wnZAiEAwYLMOg8h6kQ7iMDRcI9I8diCHM8yz0SfbfbsvzxIFxECICXs
+YvIufaZvBa8f+E/9CANlVhm5wKAyM8N8GJsiCyEG
+-----END RSA PRIVATE KEY-----
+HrMHUvE9Esvn7GnZ+vAynaIg/8wlB3r0zm0htmnwofYPZeBpLnpW3iN9UtQG4ZIBYRZih6EBuRK8W3Kychw/SgjIFuzVeTFowBCUfd1wZ4Q+frUOLZ0Xmkh8j3yHUprnh+d9PA8EHCEapdkWY3psJj6rTgrREzjDEVf/TV3EjjfgG16ih5/c3TChApLXwfEwfBp5APSf7kzMccCRbA4bXvMDsQSQAwVsRD8cjJkSdHTvuzfg1g8xoCy4I05DsMM8CybJAd+BDZnJxhrGIQaItu5/0XQJy+uy/niOpzYYN+NDX+8fl65VUxdUtqXF82ChRlmGP3+zKN7epufAsL/36pHOnS73Q7WBKRxyyA16BEBk0wK7rI+KemBfG5YFXjcBnPkxYssSudqhmlcr6e5Tl0LhVj/BIj94fVE3/EJ+NO3BJMrlhjorilrQKAsiCWujWSqAK7gtAp3YEO//yOygh/p8gh22NdIV0ykGAx4QNKIN [...]
diff --git a/t/plugin/kafka-proxy.t b/t/plugin/kafka-proxy.t
index a6ada516f..52ba9d874 100644
--- a/t/plugin/kafka-proxy.t
+++ b/t/plugin/kafka-proxy.t
@@ -55,3 +55,68 @@ done
 done
 property "sasl" validation failed: property "password" is required
 property "sasl" validation failed: property "password" validation failed: wrong type: expected string, got number
+
+
+
+=== TEST 2: data encryption for sasl.password
+--- yaml_config
+apisix:
+    data_encryption:
+        enable: true
+        keyring:
+            - edd1c9f0985e76a2
+--- config
+    location /t {
+        content_by_lua_block {
+            local json = require("toolkit.json")
+            local t = require("lib.test_admin").test
+            local code, body = t('/apisix/admin/routes/1',
+                 ngx.HTTP_PUT,
+                 [[{
+                    "plugins": {
+                        "kafka-proxy": {
+                            "sasl": {
+                                "username": "admin",
+                                "password": "admin-secret"
+                            }
+                        }
+                    },
+                    "upstream": {
+                        "nodes": {
+                            "127.0.0.1:1982": 1
+                        },
+                        "type": "roundrobin"
+                    },
+                    "uri": "/opentracing"
+                }]]
+            )
+
+            if code >= 300 then
+                ngx.status = code
+                ngx.say(body)
+                return
+            end
+            ngx.sleep(0.1)
+
+            -- get plugin conf from admin api, password is decrypted
+            local code, message, res = t('/apisix/admin/routes/1',
+                ngx.HTTP_GET
+            )
+            res = json.decode(res)
+            if code >= 300 then
+                ngx.status = code
+                ngx.say(message)
+                return
+            end
+
+            ngx.say(res.value.plugins["kafka-proxy"].sasl.password)
+
+            -- get plugin conf from etcd, password is encrypted
+            local etcd = require("apisix.core.etcd")
+            local res = assert(etcd.get('/routes/1'))
+            ngx.say(res.body.node.value.plugins["kafka-proxy"].sasl.password)
+        }
+    }
+--- response_body
+admin-secret
+y4Z3aqo51xrt3f9UziNUrg==
diff --git a/t/plugin/openid-connect2.t b/t/plugin/openid-connect2.t
index 810df9775..c9c963319 100644
--- a/t/plugin/openid-connect2.t
+++ b/t/plugin/openid-connect2.t
@@ -78,3 +78,72 @@ __DATA__
             end
         }
     }
+
+
+
+=== TEST 2: data encryption for client_secret
+--- yaml_config
+apisix:
+    data_encryption:
+        enable: true
+        keyring:
+            - edd1c9f0985e76a2
+--- config
+    location /t {
+        content_by_lua_block {
+            local json = require("toolkit.json")
+            local t = require("lib.test_admin").test
+            local code, body = t('/apisix/admin/routes/1',
+                 ngx.HTTP_PUT,
+                 [[{
+                        "plugins": {
+                            "openid-connect": {
+                                "client_id": "kbyuFDidLLm280LIwVFiazOqjO3ty8KH",
+                                "client_secret": "60Op4HFM0I8ajz0WdiStAbziZ-VFQttXuxixHHs2R7r7-CW8GR79l-mmLqMhc-Sa",
+                                "discovery": "http://127.0.0.1:1980/.well-known/openid-configuration",
+                                "redirect_uri": "https://iresty.com",
+                                "ssl_verify": false,
+                                "timeout": 10,
+                                "scope": "apisix",
+                                "use_pkce": false
+                            }
+                        },
+                        "upstream": {
+                            "nodes": {
+                                "127.0.0.1:1980": 1
+                            },
+                            "type": "roundrobin"
+                        },
+                        "uri": "/hello"
+                }]]
+            )
+
+            if code >= 300 then
+                ngx.status = code
+                ngx.say(body)
+                return
+            end
+            ngx.sleep(0.1)
+
+            -- get plugin conf from admin api, password is decrypted
+            local code, message, res = t('/apisix/admin/routes/1',
+                ngx.HTTP_GET
+            )
+            res = json.decode(res)
+            if code >= 300 then
+                ngx.status = code
+                ngx.say(message)
+                return
+            end
+
+            ngx.say(res.value.plugins["openid-connect"].client_secret)
+
+            -- get plugin conf from etcd, password is encrypted
+            local etcd = require("apisix.core.etcd")
+            local res = assert(etcd.get('/routes/1'))
+            ngx.say(res.body.node.value.plugins["openid-connect"].client_secret)
+        }
+    }
+--- response_body
+60Op4HFM0I8ajz0WdiStAbziZ-VFQttXuxixHHs2R7r7-CW8GR79l-mmLqMhc-Sa
+xMlerg8pE2lPSDlQdPi+MsAwBnzqpyLRar3lUhP2Tdc2oXnWmit92p8cannhDYkBPc6P/Hlx0wSA0T2wle9QyHaW2oqw3bXDQSWWk8Vqq0o=
diff --git a/t/plugin/rocketmq-logger2.t b/t/plugin/rocketmq-logger2.t
index ae33d1f5c..af84ecf28 100644
--- a/t/plugin/rocketmq-logger2.t
+++ b/t/plugin/rocketmq-logger2.t
@@ -440,3 +440,68 @@ qr/send data to rocketmq: \{.*"body":"hello world\\n"/
     }
 --- response_body
 done
+
+
+
+=== TEST 14: data encryption for secret_key
+--- yaml_config
+apisix:
+    data_encryption:
+        enable: true
+        keyring:
+            - edd1c9f0985e76a2
+--- config
+    location /t {
+        content_by_lua_block {
+            local json = require("toolkit.json")
+            local t = require("lib.test_admin").test
+            local code, body = t('/apisix/admin/routes/1',
+                 ngx.HTTP_PUT,
+                 [[{
+                        "plugins": {
+                            "rocketmq-logger": {
+                                "nameserver_list" : [ "127.0.0.1:9876" ],
+                                "topic" : "test2",
+                                "access_key": "foo",
+                                "secret_key": "bar"
+                            }
+                        },
+                        "upstream": {
+                            "nodes": {
+                                "127.0.0.1:1980": 1
+                            },
+                            "type": "roundrobin"
+                        },
+                        "uri": "/hello"
+                }]]
+             )
+
+            if code >= 300 then
+                ngx.status = code
+                ngx.say(body)
+                return
+            end
+            ngx.sleep(0.1)
+
+            -- get plugin conf from admin api, password is decrypted
+            local code, message, res = t('/apisix/admin/routes/1',
+                ngx.HTTP_GET
+            )
+            res = json.decode(res)
+            if code >= 300 then
+                ngx.status = code
+                ngx.say(message)
+                return
+            end
+
+            ngx.say(res.value.plugins["rocketmq-logger"].secret_key)
+
+            -- get plugin conf from etcd, password is encrypted
+            local etcd = require("apisix.core.etcd")
+            local res = assert(etcd.get('/routes/1'))
+            ngx.say(res.body.node.value.plugins["rocketmq-logger"].secret_key)
+        }
+    }
+--- response_body
+bar
+77+NmbYqNfN+oLm0aX5akg==
diff --git a/t/plugin/sls-logger.t b/t/plugin/sls-logger.t
index c8aaf9c4f..c1bce8107 100644
--- a/t/plugin/sls-logger.t
+++ b/t/plugin/sls-logger.t
@@ -240,3 +240,86 @@ passed
 GET /hello
 --- response_body
 hello world
+
+
+
+=== TEST 10: delete exist routes
+--- config
+    location /t {
+        content_by_lua_block {
+            local t = require("lib.test_admin").test
+            -- delete exist consumers
+            local code, body = t('/apisix/admin/routes/1', ngx.HTTP_DELETE)
+            ngx.say(body)
+        }
+    }
+--- response_body
+passed
+
+
+
+=== TEST 11: data encryption for access_key_secret
+--- yaml_config
+apisix:
+    data_encryption:
+        enable: true
+        keyring:
+            - edd1c9f0985e76a2
+--- config
+    location /t {
+        content_by_lua_block {
+            local json = require("toolkit.json")
+            local t = require("lib.test_admin").test
+            local code, body = t('/apisix/admin/routes/1',
+                 ngx.HTTP_PUT,
+                 [[{
+                        "plugins": {
+                            "sls-logger": {
+                                "host": "100.100.99.135",
+                                "port": 10009,
+                                "project": "your_project",
+                                "logstore": "your_logstore",
+                                "access_key_id": "your_access_key_id",
+                                "access_key_secret": "your_access_key_secret",
+                                "timeout": 30000
+                            }
+                        },
+                        "upstream": {
+                            "nodes": {
+                                "127.0.0.1:1980": 1
+                            },
+                            "type": "roundrobin"
+                        },
+                        "uri": "/hello"
+                }]]
+            )
+
+            if code >= 300 then
+                ngx.status = code
+                ngx.say(body)
+                return
+            end
+            ngx.sleep(0.1)
+
+            -- get plugin conf from admin api, password is decrypted
+            local code, message, res = t('/apisix/admin/routes/1',
+                ngx.HTTP_GET
+            )
+            res = json.decode(res)
+            if code >= 300 then
+                ngx.status = code
+                ngx.say(message)
+                return
+            end
+
+            ngx.say(res.value.plugins["sls-logger"].access_key_secret)
+
+            -- get plugin conf from etcd, password is encrypted
+            local etcd = require("apisix.core.etcd")
+            local res = assert(etcd.get('/routes/1'))
+            ngx.say(res.body.node.value.plugins["sls-logger"].access_key_secret)
+        }
+    }
+--- response_body
+your_access_key_secret
+1T6nR0fz4yhz/zTuRTvt7Xu3c9ASelDXG2//e/A5OiA=
diff --git a/t/plugin/tencent-cloud-cls.t b/t/plugin/tencent-cloud-cls.t
index e5736fa0f..b16e5c124 100644
--- a/t/plugin/tencent-cloud-cls.t
+++ b/t/plugin/tencent-cloud-cls.t
@@ -324,3 +324,88 @@ GET /opentracing
 --- response_body
 opentracing
 --- wait: 0.5
+
+
+
+=== TEST 11: delete exist routes
+--- config
+    location /t {
+        content_by_lua_block {
+            local t = require("lib.test_admin").test
+            -- delete exist consumers
+            local code, body = t('/apisix/admin/routes/1', ngx.HTTP_DELETE)
+            ngx.say(body)
+        }
+    }
+--- response_body
+passed
+
+
+
+=== TEST 12: data encryption for secret_key
+--- yaml_config
+apisix:
+    data_encryption:
+        enable: true
+        keyring:
+            - edd1c9f0985e76a2
+--- config
+    location /t {
+        content_by_lua_block {
+            local json = require("toolkit.json")
+            local t = require("lib.test_admin").test
+            local code, body = t('/apisix/admin/routes/1',
+                 ngx.HTTP_PUT,
+                 [[{
+                    "plugins": {
+                        "tencent-cloud-cls": {
+                            "cls_host": "127.0.0.1:10421",
+                            "cls_topic": "143b5d70-139b-4aec-b54e-bb97756916de",
+                            "secret_id": "secret_id",
+                            "secret_key": "secret_key",
+                            "batch_max_size": 1,
+                            "max_retry_count": 1,
+                            "retry_delay": 2,
+                            "buffer_duration": 2,
+                            "inactive_timeout": 2
+                        }
+                    },
+                    "upstream": {
+                        "nodes": {
+                            "127.0.0.1:1982": 1
+                        },
+                        "type": "roundrobin"
+                    },
+                    "uri": "/opentracing"
+                }]]
+            )
+
+            if code >= 300 then
+                ngx.status = code
+                ngx.say(body)
+                return
+            end
+            ngx.sleep(0.1)
+
+            -- get plugin conf from admin api, password is decrypted
+            local code, message, res = t('/apisix/admin/routes/1',
+                ngx.HTTP_GET
+            )
+            res = json.decode(res)
+            if code >= 300 then
+                ngx.status = code
+                ngx.say(message)
+                return
+            end
+
+            ngx.say(res.value.plugins["tencent-cloud-cls"].secret_key)
+
+            -- get plugin conf from etcd, password is encrypted
+            local etcd = require("apisix.core.etcd")
+            local res = assert(etcd.get('/routes/1'))
+            ngx.say(res.body.node.value.plugins["tencent-cloud-cls"].secret_key)
+        }
+    }
+--- response_body
+secret_key
+oshn8tcqE8cJArmEILVNPQ==