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 c8d5afb5b change(jwt-auth): unify apisix/core/vault.lua and
apisix/secret/vault.lua (#8660)
c8d5afb5b is described below
commit c8d5afb5b02a800bb3f7090bc5adb1d26e281d53
Author: Abhishek Choudhary <[email protected]>
AuthorDate: Mon Jan 30 08:54:56 2023 +0530
change(jwt-auth): unify apisix/core/vault.lua and apisix/secret/vault.lua
(#8660)
Fixes https://github.com/apache/apisix/issues/8424
---
README.md | 2 +-
apisix/core/vault.lua | 127 ------------
apisix/plugins/jwt-auth.lua | 74 +------
apisix/secret/vault.lua | 6 -
conf/config-default.yaml | 13 --
docs/en/latest/getting-started.md | 2 +-
docs/en/latest/plugins/jwt-auth.md | 95 +--------
docs/zh/latest/getting-started.md | 2 +-
docs/zh/latest/plugins/jwt-auth.md | 104 +---------
t/plugin/jwt-auth-vault.t | 386 -------------------------------------
10 files changed, 16 insertions(+), 795 deletions(-)
diff --git a/README.md b/README.md
index b7bba5c87..44fbc224c 100644
--- a/README.md
+++ b/README.md
@@ -140,7 +140,7 @@ A/B testing, canary release, blue-green deployment, limit
rate, defense against
- [Elasticsearch](docs/en/latest/plugins/elasticsearch-logger.md): push logs
to Elasticsearch.
- [Datadog](docs/en/latest/plugins/datadog.md): push custom metrics to the
DogStatsD server, comes bundled with [Datadog
agent](https://docs.datadoghq.com/agent/), over the UDP protocol. DogStatsD
basically is an implementation of StatsD protocol which collects the custom
metrics for Apache APISIX agent, aggregates it into a single data point and
sends it to the configured Datadog server.
- [Helm charts](https://github.com/apache/apisix-helm-chart)
- - [HashiCorp Vault](https://www.vaultproject.io/): Support secret management
solution for accessing secrets from Vault secure storage backed in a low trust
environment. Currently, RS256 keys (public-private key pairs) or secret keys
can be linked from vault in
[jwt-auth](docs/en/latest/plugins/jwt-auth.md#enable-jwt-auth-with-vault-compatibility)
authentication plugin.
+ - [HashiCorp Vault](https://www.vaultproject.io/): Support secret management
solution for accessing secrets from Vault secure storage backed in a low trust
environment. Currently, RS256 keys (public-private key pairs) or secret keys
can be linked from vault in jwt-auth authentication plugin using [APISIX
Secret](docs/en/latest/terminology/secret.md) resource.
- **Highly scalable**
- [Custom plugins](docs/en/latest/plugin-develop.md): Allows hooking of
common phases, such as `rewrite`, `access`, `header filter`, `body filter` and
`log`, also allows to hook the `balancer` stage.
diff --git a/apisix/core/vault.lua b/apisix/core/vault.lua
deleted file mode 100644
index aeb8485d9..000000000
--- a/apisix/core/vault.lua
+++ /dev/null
@@ -1,127 +0,0 @@
---
--- Licensed to the Apache Software Foundation (ASF) under one or more
--- contributor license agreements. See the NOTICE file distributed with
--- this work for additional information regarding copyright ownership.
--- The ASF licenses this file to You under the Apache License, Version 2.0
--- (the "License"); you may not use this file except in compliance with
--- the License. You may obtain a copy of the License at
---
--- http://www.apache.org/licenses/LICENSE-2.0
---
--- Unless required by applicable law or agreed to in writing, software
--- distributed under the License is distributed on an "AS IS" BASIS,
--- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
--- See the License for the specific language governing permissions and
--- limitations under the License.
---
-
---- Vault Tools.
--- Vault is an identity-based secrets and encryption management system.
---
--- @module core.vault
-
-local core = require("apisix.core")
-local http = require("resty.http")
-local json = require("cjson")
-
-local fetch_local_conf = require("apisix.core.config_local").local_conf
-local norm_path = require("pl.path").normpath
-
-local _M = {}
-
-local function fetch_vault_conf()
- local conf, err = fetch_local_conf()
- if not conf then
- return nil, "failed to fetch vault configuration from config yaml: "
.. err
- end
-
- if not conf.vault then
- return nil, "accessing vault data requires configuration information"
- end
- return conf.vault
-end
-
-
-local function make_request_to_vault(method, key, skip_prefix, data)
- local vault, err = fetch_vault_conf()
- if not vault then
- return nil, err
- end
-
- local httpc = http.new()
- -- config timeout or default to 5000 ms
- httpc:set_timeout((vault.timeout or 5)*1000)
-
- local req_addr = vault.host
- if not skip_prefix then
- req_addr = req_addr .. norm_path("/v1/"
- .. vault.prefix .. "/" .. key)
- else
- req_addr = req_addr .. norm_path("/v1/" .. key)
- end
-
- local res, err = httpc:request_uri(req_addr, {
- method = method,
- headers = {
- ["X-Vault-Token"] = vault.token
- },
- body = core.json.encode(data or {}, true)
- })
- if not res then
- return nil, err
- end
-
- return res.body
-end
-
--- key is the vault kv engine path, joined with config yaml vault prefix.
--- It takes an extra optional boolean param skip_prefix. If enabled, it simply
doesn't use the
--- prefix defined inside config yaml under vault config for fetching data.
-local function get(key, skip_prefix)
- core.log.info("fetching data from vault for key: ", key)
-
- local res, err = make_request_to_vault("GET", key, skip_prefix)
- if not res then
- return nil, "failed to retrtive data from vault kv engine " .. err
- end
-
- return json.decode(res)
-end
-
-_M.get = get
-
--- key is the vault kv engine path, data is json key value pair.
--- It takes an extra optional boolean param skip_prefix. If enabled, it simply
doesn't use the
--- prefix defined inside config yaml under vault config for storing data.
-local function set(key, data, skip_prefix)
- core.log.info("storing data into vault for key: ", key,
- "and value: ", core.json.delay_encode(data, true))
-
- local res, err = make_request_to_vault("POST", key, skip_prefix, data)
- if not res then
- return nil, "failed to store data into vault kv engine " .. err
- end
-
- return true
-end
-_M.set = set
-
-
--- key is the vault kv engine path, joined with config yaml vault prefix.
--- It takes an extra optional boolean param skip_prefix. If enabled, it simply
doesn't use the
--- prefix defined inside config yaml under vault config for deleting data.
-local function delete(key, skip_prefix)
- core.log.info("deleting data from vault for key: ", key)
-
- local res, err = make_request_to_vault("DELETE", key, skip_prefix)
-
- if not res then
- return nil, "failed to delete data into vault kv engine " .. err
- end
-
- return true
-end
-
-_M.delete = delete
-
-return _M
diff --git a/apisix/plugins/jwt-auth.lua b/apisix/plugins/jwt-auth.lua
index 96c743e1b..f195830a6 100644
--- a/apisix/plugins/jwt-auth.lua
+++ b/apisix/plugins/jwt-auth.lua
@@ -18,7 +18,6 @@ local core = require("apisix.core")
local jwt = require("resty.jwt")
local consumer_mod = require("apisix.consumer")
local resty_random = require("resty.random")
-local vault = require("apisix.core.vault")
local new_tab = require ("table.new")
local ngx_encode_base64 = ngx.encode_base64
@@ -71,10 +70,6 @@ local consumer_schema = {
type = "boolean",
default = false
},
- vault = {
- type = "object",
- properties = {}
- },
lifetime_grace_period = {
type = "integer",
minimum = 0,
@@ -102,19 +97,6 @@ local consumer_schema = {
},
required = {"public_key", "private_key"},
},
- {
- properties = {
- vault = {
- type = "object",
- properties = {}
- },
- algorithm = {
- enum = {"RS256", "ES256"},
- },
- },
- required = {"vault"},
- },
-
}
}
},
@@ -147,11 +129,6 @@ function _M.check_schema(conf, schema_type)
return false, err
end
- if conf.vault then
- core.log.info("skipping jwt-auth schema validation with vault")
- return true
- end
-
if conf.algorithm ~= "RS256" and conf.algorithm ~= "ES256" and not
conf.secret then
conf.secret = ngx_encode_base64(resty_random.bytes(32, true))
elseif conf.base64_secret then
@@ -161,8 +138,8 @@ function _M.check_schema(conf, schema_type)
end
if conf.algorithm == "RS256" or conf.algorithm == "ES256" then
- -- Possible options are a) both are in vault, b) both in schema
- -- c) one in schema, another in vault.
+ -- Possible options are a) public key is missing
+ -- b) private key is missing
if not conf.public_key then
return false, "missing valid public key"
end
@@ -243,25 +220,8 @@ local function fetch_jwt_token(conf, ctx)
return val
end
-
-local function get_vault_path(username)
- return "consumer/".. username .. "/jwt-auth"
-end
-
-
local function get_secret(conf, consumer_name)
local secret = conf.secret
- if conf.vault then
- local res, err = vault.get(get_vault_path(consumer_name))
- if not res then
- return nil, err
- end
-
- if not res.data or not res.data.secret then
- return nil, "secret could not found in vault: " ..
core.json.encode(res)
- end
- secret = res.data.secret
- end
if conf.base64_secret then
return ngx_decode_base64(secret)
@@ -274,32 +234,16 @@ end
local function get_rsa_or_ecdsa_keypair(conf, consumer_name)
local public_key = conf.public_key
local private_key = conf.private_key
- -- if keys are present in conf, no need to query vault (fallback)
+
if public_key and private_key then
return public_key, private_key
+ elseif public_key and not private_key then
+ return nil, nil, "missing private key"
+ elseif not public_key and private_key then
+ return nil, nil, "missing public key"
+ else
+ return nil, nil, "public and private keys are missing"
end
-
- local vout = {}
- if conf.vault then
- local res, err = vault.get(get_vault_path(consumer_name))
- if not res then
- return nil, nil, err
- end
-
- if not res.data then
- return nil, nil, "key pairs could not found in vault: " ..
core.json.encode(res)
- end
- vout = res.data
- end
-
- if not public_key and not vout.public_key then
- return nil, nil, "missing public key, not found in config/vault"
- end
- if not private_key and not vout.private_key then
- return nil, nil, "missing private key, not found in config/vault"
- end
-
- return public_key or vout.public_key, private_key or vout.private_key
end
diff --git a/apisix/secret/vault.lua b/apisix/secret/vault.lua
index aff458f2c..eb2e6564c 100644
--- a/apisix/secret/vault.lua
+++ b/apisix/secret/vault.lua
@@ -45,12 +45,6 @@ local _M = {
schema = schema
}
--- This code is copied from apisix/core/vault.lua.
--- The functions in apisix/core/vault.lua are currently only used in the
jwt-auth plugin,
--- and it is not suitable to be placed in the core module of APISIX.
---
--- When KMS is fully functional, we will remove apisix/core/vault.lua.
---
local function make_request_to_vault(conf, method, key, data)
local httpc = http.new()
-- config timeout or default to 5000 ms
diff --git a/conf/config-default.yaml b/conf/config-default.yaml
index 9e47f0660..43bbad005 100755
--- a/conf/config-default.yaml
+++ b/conf/config-default.yaml
@@ -250,19 +250,6 @@ nginx_config: # config for render the
template to generate n
tars: 1m
cas-auth: 10m
-# HashiCorp Vault storage backend for sensitive data retrieval. The config
shows an example of what APISIX expects if you
-# wish to integrate Vault for secret (sensetive string, public private keys
etc.) retrieval. APISIX communicates with Vault
-# server HTTP APIs. By default, APISIX doesn't need this configuration.
-# vault:
-# host: "http://0.0.0.0:8200" # The host address where the vault server is
running.
-# timeout: 10 # request timeout 30 seconds
-# token: root # Authentication token to access Vault HTTP
APIs
-# prefix: kv/apisix # APISIX supports vault kv engine v1, where
sensitive data are being stored
- # and retrieved through vault HTTP APIs.
enabling a prefix allows you to better enforcement of
- # policies, generate limited scoped tokens
and tightly control the data that can be accessed
- # from APISIX.
-
-
#discovery: # service discovery center
# dns:
# servers:
diff --git a/docs/en/latest/getting-started.md
b/docs/en/latest/getting-started.md
index 5191b27b0..62a4a4810 100644
--- a/docs/en/latest/getting-started.md
+++ b/docs/en/latest/getting-started.md
@@ -50,7 +50,7 @@ APISIX facilitates interface traffic handling for websites,
mobile and IoT appli
- Multi-platform support: APISIX can run from bare-metal machines to
Kubernetes providing a vendor neutral, multi-platform solution. It also
provides integration to cloud services like AWS Lambda, Azure Function, Lua
functions and Apache OpenWhisk.
- Fully dynamic: APISIX supports hot-reloading, meaning you don't need to
restart the service to reflect changes in the configuration.
- Fine-grained routing: APISIX supports using all [built-in NGINX
variables](http://nginx.org/en/docs/varindex.html) for routing. You can define
custom matching functions to filter requests and match Route.
-- Ops-friendly: APISIX is renowned for its ops-friendliness by DevOps teams.
It integrates with tools and platforms like [HashiCorp
Vault](./plugins/jwt-auth.md#usage-with-hashicorp-vault),
[Zipkin](./plugins/zipkin.md), [Apache SkyWalking](./plugins/skywalking.md),
[Consul](./discovery/consul_kv.md), [Nacos](./discovery/nacos.md) and
[Eureka](./discovery.md). With [APISIX
Dashboard](https://github.com/apache/apisix-dashboard), operators can configure
APISIX through an easy-to-use and in [...]
+- Ops-friendly: APISIX is renowned for its ops-friendliness by DevOps teams.
It integrates with tools and platforms like [HashiCorp
Vault](./terminology/secret.md#use-vault-to-manage-secrets),
[Zipkin](./plugins/zipkin.md), [Apache SkyWalking](./plugins/skywalking.md),
[Consul](./discovery/consul_kv.md), [Nacos](./discovery/nacos.md) and
[Eureka](./discovery.md). With [APISIX
Dashboard](https://github.com/apache/apisix-dashboard), operators can configure
APISIX through an easy-to-use and [...]
- Multi-language Plugin support: APISIX supports multiple programming
languages for Plugin development. Developers can choose a language-specific SDK
to write custom Plugins.
## Key concepts
diff --git a/docs/en/latest/plugins/jwt-auth.md
b/docs/en/latest/plugins/jwt-auth.md
index f0b65ddcd..de4064282 100644
--- a/docs/en/latest/plugins/jwt-auth.md
+++ b/docs/en/latest/plugins/jwt-auth.md
@@ -33,8 +33,6 @@ The `jwt-auth` Plugin is used to add [JWT](https://jwt.io/)
authentication to a
A [Consumer](../terminology/consumer.md) of the service then needs to provide
a key through a query string, a request header or a cookie to verify its
request.
-The `jwt-auth` Plugin can be integrated with [HashiCorp
Vault](https://www.vaultproject.io/) to store and fetch secrets and RSA keys
pairs from its [encrypted KV
engine](https://www.vaultproject.io/docs/secrets/kv). Learn more from the
[examples](#enable-jwt-auth-with-vault-compatibility) below.
-
## Attributes
For Consumer:
@@ -48,19 +46,10 @@ For Consumer:
| algorithm | string | False
| "HS256" | ["HS256", "HS512", "RS256", "ES256"] | Encryption algorithm.
|
| exp | integer | False
| 86400 | [1,...] | Expiry time of the token in
seconds.
|
| base64_secret | boolean | False
| false | | Set to true if the secret is
base64 encoded.
|
-| 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.
-
-Refer to the Vault attributes in the default configuration file
([config-default.yaml](https://github.com/apache/apisix/blob/master/conf/config-default.yaml))
to learn more about these configurations.
-
-:::
-
For Route:
| Name | Type | Required | Default | Description
|
@@ -70,6 +59,8 @@ For Route:
| cookie | string | False | jwt | The cookie to get the token
from. Lower priority than query. |
| hide_credentials | boolean | False | false | Set to true will not pass
the authorization request of header\query\cookie to the Upstream.|
+You can implement `jwt-auth` with [HashiCorp
Vault](https://www.vaultproject.io/) to store and fetch secrets and RSA keys
pairs from its [encrypted KV
engine](https://www.vaultproject.io/docs/secrets/kv) using the [APISIX
Secret](../terminology/secret.md) resource.
+
## API
This Plugin adds `/apisix/plugin/jwt/sign` as an endpoint.
@@ -139,88 +130,6 @@ curl http://127.0.0.1:9180/apisix/admin/routes/1 -H
'X-API-KEY: edd1c9f034335f13
}'
```
-### Usage with HashiCorp Vault
-
-[HashiCorp Vault](https://www.vaultproject.io/) offers a centralized key
management solution and it can be used along with APISIX for authentication.
-
-So, if your organization frequently changes the secret/keys (secret for
HS256/HS512 or public_key and private_key for RS256) and you don't want to
update the APISIX consumer each time or if you don't want to use the key
through the Admin API (to reduce secret sprawl), you can use Vault and the
`jwt-auth` Plugin.
-
-:::note
-
-The current version of Apache APISIX expects the key names of the secrets
stored in Vault to be among `secret`, `public_key`, and `private_key`. The
former one is for the HS256/HS512 algorithm and the latter two are for the
RS256 algorithm.
-
-Support for custom names will be added in a future release.
-
-:::
-
-To use Vault, you can add an empty vault object in your configuration.
-
-For example, if you have a stored HS256 signing secret inside Vault, you can
use it in APISIX by:
-
-```shell
-curl http://127.0.0.1:9180/apisix/admin/consumers -H 'X-API-KEY:
edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '
-{
- "username": "jack",
- "plugins": {
- "jwt-auth": {
- "key": "key-1",
- "vault": {}
- }
- }
-}'
-```
-
-The Plugin will look for a key "secret" in the provided Vault path
(`<vault.prefix>/consumer/jack/jwt-auth`) and uses it for JWT authentication.
If the key is not found in the same path, the Plugin logs an error and JWT
authentication fails.
-
-:::note
-
-The `vault.prefix` should be set in your configuration file
(`conf/config.yaml`) file based on the base path you have chosen while enabling
the Vault kv secret engine.
-
-For example, if you did `vault secrets enable -path=foobar kv`, you should use
`foobar` in `vault.prefix`.
-
-:::
-
-If the key is not found in this path, the Plugin will log an error.
-
-And for RS256, both the public and private keys should stored in Vault and it
can be configured by:
-
-```shell
-curl http://127.0.0.1:9180/apisix/admin/consumers -H 'X-API-KEY:
edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '
-{
- "username": "jack",
- "plugins": {
- "jwt-auth": {
- "key": "rsa-keypair",
- "algorithm": "RS256",
- "vault": {}
- }
- }
-}'
-```
-
-The Plugin will look for keys "public_key" and "private_key" in the provided
Vault path (`<vault.prefix>/consumer/jack/jwt-auth`) and uses it for JWT
authentication.
-
-If the key is not found in this path, the Plugin will log an error.
-
-You can also configure the `public_key` in the Consumer configuration and use
the `private_key` stored in Vault:
-
-```shell
-curl http://127.0.0.1:9180/apisix/admin/consumers -H 'X-API-KEY:
edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '
-{
- "username": "rico",
- "plugins": {
- "jwt-auth": {
- "key": "user-key",
- "algorithm": "RS256",
- "public_key": "-----BEGIN PUBLIC KEY-----\n……\n-----END PUBLIC
KEY-----"
- "vault": {}
- }
- }
-}'
-```
-
-You can also use the [APISIX Dashboard](/docs/dashboard/USER_GUIDE) to
complete the operation through a web UI.
-
<!--


diff --git a/docs/zh/latest/getting-started.md
b/docs/zh/latest/getting-started.md
index dcad6d122..d4c18e7ff 100644
--- a/docs/zh/latest/getting-started.md
+++ b/docs/zh/latest/getting-started.md
@@ -49,7 +49,7 @@ Apache APISIX 是 Apache 软件基金会下的云原生 API 网关,它兼具
- 多平台支持:APISIX 提供了多平台解决方案,它不但支持裸机运行,也支持在 Kubernetes 中使用,还支持与 AWS Lambda、Azure
Function、Lua 函数和 Apache OpenWhisk 等云服务集成。
- 全动态能力:APISIX 支持热加载,这意味着你不需要重启服务就可以更新 APISIX 的配置。请访问[为什么 Apache APISIX 选择
Nginx + Lua
这个技术栈?](https://apisix.apache.org/zh/blog/2021/08/25/why-apache-apisix-chose-nginx-and-lua/)以了解实现原理。
- 精细化路由:APISIX 支持使用 [NGINX
内置变量](http://nginx.org/en/docs/varindex.html)做为路由的匹配条件,你可以自定义匹配函数来过滤请求,匹配路由。
-- 运维友好:APISIX 支持与以下工具和平台集成:[HashiCorp
Vault](./plugins/jwt-auth.md#usage-with-hashicorp-vault)、[Zipkin](./plugins/zipkin.md)、[Apache
SkyWalking](./plugins/skywalking.md)、[Consul](./discovery/consul_kv.md)、[Nacos](./discovery/nacos.md)、[Eureka](./discovery.md)。通过
[APISIX Dashboard](/docs/dashboard/USER_GUIDE),运维人员可以通过友好且直观的 UI 配置 APISIX。
+- 运维友好:APISIX 支持与以下工具和平台集成:[HashiCorp
Vault](./terminology/secret.md#使用-vault-管理密钥)、[Zipkin](./plugins/zipkin.md)、[Apache
SkyWalking](./plugins/skywalking.md)、[Consul](./discovery/consul_kv.md)、[Nacos](./discovery/nacos.md)、[Eureka](./discovery.md)。通过
[APISIX Dashboard](/docs/dashboard/USER_GUIDE),运维人员可以通过友好且直观的 UI 配置 APISIX。
- 多语言插件支持:APISIX 支持多种开发语言进行插件开发,开发人员可以选择擅长语言的 SDK 开发自定义插件。
## 主要概念
diff --git a/docs/zh/latest/plugins/jwt-auth.md
b/docs/zh/latest/plugins/jwt-auth.md
index c85c610a5..23f4e2a9f 100644
--- a/docs/zh/latest/plugins/jwt-auth.md
+++ b/docs/zh/latest/plugins/jwt-auth.md
@@ -33,8 +33,6 @@ description: 本文介绍了关于 Apache APISIX `jwt-auth` 插件的基本信
通过 Consumer 将其密匙添加到查询字符串参数、请求头或 `cookie` 中用来验证其请求。
-`jwt-auth` 插件可以与 [HashiCorp Vault](https://www.vaultproject.io/)
集成,用于存储和获取密钥,并从 HashiCorp Vault 的 [encrypted KV
engine](https://www.vaultproject.io/docs/secrets/kv)中获取 RSA
密匙对。你可以从下面的[示例](#与-hashicorp-vault-集成使用)中了解更多信息。
-
## 属性
Consumer 端:
@@ -48,19 +46,10 @@ Consumer 端:
| algorithm | string | 否 | "HS256" | ["HS256", "HS512", "RS256",
"ES256"] | 加密算法。
|
| exp | integer | 否 | 86400 | [1,...] |
token 的超时时间。
|
| base64_secret | boolean | 否 | false | |
当设置为 `true` 时,密钥为 base64 编码。
|
-| 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 服务器配置、主机地址和访问令牌。
-
-请参考默认配置文件
[config-default.yaml](https://github.com/apache/apisix/blob/master/conf/config-default.yaml)
中的 Vault 属性下了解相关配置。
-
-:::
-
Route 端:
| 名称 | 类型 | 必选项 | 默认值 | 描述
|
@@ -70,6 +59,8 @@ Route 端:
| cookie | string | 否 | jwt | 设置我们从哪个 cookie 获取 token,优先级低于
query。 |
| hide_credentials | boolean | 否 | false | 该参数设置为 `true` 时,则不会将含有认证信息的
header\query\cookie 传递给 Upstream。|
+您可以使用 [HashiCorp Vault](https://www.vaultproject.io/) 实施 `jwt-auth`,以从其[加密的 KV
引擎](https://www.vaultproject.io/docs/secrets/kv) 使用 [APISIX
Secret](../terminology/secret.md) 资源。
+
## 接口
该插件会增加 `/apisix/plugin/jwt/sign` 接口。
@@ -142,97 +133,6 @@ curl http://127.0.0.1:9180/apisix/admin/routes/1 \
}'
```
-### 与 HashiCorp Vault 集成使用
-
-[HashiCorp Vault](https://www.vaultproject.io/) 提供集中式密钥管理解决方案,可与 APISIX
一起用于身份验证。
-
-因此,如果你的企业经常更改 secret/keys(HS256/HS512 的密钥或 RS256 的 public_key 和
private_key)并且你不想每次都更新 APISIX 的 Consumer,或者你不想通过 Admin API(减少信息泄漏),你可以将 Vault 和
`jwt-auth` 插件一起使用。
-
-:::note
-
-当前版本的 Apache APISIX 期望存储在 Vault 中机密的密钥名称位于 `secret`、`public_key` 和
`private_key` 之间。前一个用于 HS256/HS512 算法,后两个用于 RS256 算法。
-
-在未来的版本中,该插件将支持引用自定义命名键。
-
-:::
-
-如果你要使用 Vault,可以在配置中添加一个空的 Vault 对象。
-
-例如,如果你在 Vault 中存储了一个 HS256 签名密钥,可以通过以下方式在 APISIX 中使用它:
-
-```shell
-curl http://127.0.0.1:9180/apisix/admin/consumers \
--H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '
-{
- "username": "jack",
- "plugins": {
- "jwt-auth": {
- "key": "key-1",
- "vault": {}
- }
- }
-}'
-```
-
-该插件将在提供的 Vault 路径(`<vault.prefix>/consumer/jack/jwt-auth`)中查找密钥 `secret`,并将其用于
JWT 身份验证。如果在同一路径中找不到密钥,插件会记录错误并且无法执行 JWT 验证。
-
-:::note
-
-`vault.prefix` 会在配置文件(`conf/config.yaml`)中根据启用 `Vault kv secret engine`
时选择的基本路径进行设置。
-
-例如,如果设置了 `vault secrets enable -path=foobar kv`,就需要在 `vault.prefix` 中使用
`foobar`。
-
-:::
-
-如果在此路径中找不到密钥,插件将记录错误。
-
-对于 RS256,公钥和私钥都应该存储在 Vault 中,可以通过以下方式配置:
-
-```shell
-curl http://127.0.0.1:9180/apisix/admin/consumers \
--H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '
-{
- "username": "jack",
- "plugins": {
- "jwt-auth": {
- "key": "rsa-keypair",
- "algorithm": "RS256",
- "vault": {}
- }
- }
-}'
-```
-
-该插件将在提供的 Vault 键值对路径(`<vault.prefix from conf.yaml>/consumer/jim/jwt-auth`)中查找
`public_key` 和 `private_key`,并将其用于 JWT 身份认证。
-
-如果在此路径中没有找到密钥,则认证失败,插件将记录错误。
-
-你还可以在 Consumer 中配置 `public_key` 并使用存储在 Vault 中的 `private_key`:
-
-```shell
-curl http://127.0.0.1:9180/apisix/admin/consumers \
--H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '
-{
- "username": "rico",
- "plugins": {
- "jwt-auth": {
- "key": "user-key",
- "algorithm": "RS256",
- "public_key": "-----BEGIN PUBLIC KEY-----\n……\n-----END PUBLIC
KEY-----"
- "vault": {}
- }
- }
-}'
-```
-
-你还可以通过 [APISIX Dashboard](https://github.com/apache/apisix-dashboard) 的 Web
界面完成上述操作。
-
-<!--
-
-
-
--->
-
## 测试插件
首先,你需要为签发 token 的 API 配置一个 Route,该路由将使用
[public-api](../../../en/latest/plugins/public-api.md) 插件。
diff --git a/t/plugin/jwt-auth-vault.t b/t/plugin/jwt-auth-vault.t
deleted file mode 100644
index b76c56ace..000000000
--- a/t/plugin/jwt-auth-vault.t
+++ /dev/null
@@ -1,386 +0,0 @@
-#
-# Licensed to the Apache Software Foundation (ASF) under one or more
-# contributor license agreements. See the NOTICE file distributed with
-# this work for additional information regarding copyright ownership.
-# The ASF licenses this file to You under the Apache License, Version 2.0
-# (the "License"); you may not use this file except in compliance with
-# the License. You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-use t::APISIX 'no_plan';
-
-repeat_each(1);
-no_long_string();
-no_root_location();
-no_shuffle();
-
-add_block_preprocessor(sub {
- my ($block) = @_;
-
- my $http_config = $block->http_config // <<_EOC_;
-
- server {
- listen 8777;
-
- location /secure-endpoint {
- content_by_lua_block {
- ngx.say("successfully invoked secure endpoint")
- }
- }
- }
-_EOC_
-
- $block->set_value("http_config", $http_config);
-
- my $vault_config = $block->extra_yaml_config // <<_EOC_;
-vault:
- host: "http://0.0.0.0:8200"
- timeout: 10
- prefix: kv/apisix
- token: root
-_EOC_
-
- $block->set_value("extra_yaml_config", $vault_config);
-
- if (!$block->request) {
- $block->set_value("request", "GET /t");
- }
- if (!$block->no_error_log && !$block->error_log) {
- $block->set_value("no_error_log", "[error]\n[alert]");
- }
-});
-
-run_tests;
-
-__DATA__
-
-=== TEST 1: schema check
---- config
- location /t {
- content_by_lua_block {
- local plugin = require("apisix.plugins.jwt-auth")
- local core = require("apisix.core")
- for _, conf in ipairs({
- {
- -- public and private key are not provided for RS256,
returns error
- key = "key-1",
- algorithm = "RS256"
- },
- {
- -- public and private key are not provided but vault
config is enabled.
- key = "key-1",
- algorithm = "RS256",
- vault = {}
- }
- }) do
- local ok, err = plugin.check_schema(conf,
core.schema.TYPE_CONSUMER)
- if not ok then
- ngx.say(err)
- else
- ngx.say("ok")
- end
- end
- }
- }
---- response_body
-failed to validate dependent schema for "algorithm": value should match only
one schema, but matches none
-ok
-
-
-
-=== TEST 2: create a consumer with plugin and username
---- config
- location /t {
- content_by_lua_block {
- local t = require("lib.test_admin").test
- local code, body = t('/apisix/admin/consumers',
- ngx.HTTP_PUT,
- [[{
- "username": "jack",
- "plugins": {
- "jwt-auth": {
- "key": "key-hs256",
- "algorithm": "HS256",
- "vault":{}
- }
- }
- }]]
- )
-
- if code >= 300 then
- ngx.status = code
- end
- ngx.say(body)
- }
- }
---- response_body
-passed
-
-
-
-=== TEST 3: enable jwt auth plugin using admin api
---- config
- location /t {
- content_by_lua_block {
- local t = require("lib.test_admin").test
- local code, body = t('/apisix/admin/routes/1',
- ngx.HTTP_PUT,
- [[{
- "plugins": {
- "jwt-auth": {}
- },
- "upstream": {
- "nodes": {
- "127.0.0.1:8777": 1
- },
- "type": "roundrobin"
- },
- "uri": "/secure-endpoint"
- }]]
- )
-
- if code >= 300 then
- ngx.status = code
- end
- ngx.say(body)
- }
- }
---- response_body
-passed
-
-
-
-=== TEST 4: create public API route (jwt-auth sign)
---- config
- location /t {
- content_by_lua_block {
- local t = require("lib.test_admin").test
- local code, body = t('/apisix/admin/routes/2',
- ngx.HTTP_PUT,
- [[{
- "plugins": {
- "public-api": {}
- },
- "uri": "/apisix/plugin/jwt/sign"
- }]]
- )
-
- if code >= 300 then
- ngx.status = code
- end
- ngx.say(body)
- }
- }
---- response_body
-passed
-
-
-
-=== TEST 5: sign a jwt and access/verify /secure-endpoint, fails as no secret
entry into vault
---- config
- location /t {
- content_by_lua_block {
- local t = require("lib.test_admin").test
- local code, err, sign = t('/apisix/plugin/jwt/sign?key=key-hs256',
- ngx.HTTP_GET
- )
-
- if code > 200 then
- ngx.status = code
- ngx.say(err)
- return
- end
-
- local code, _, res = t('/secure-endpoint?jwt=' .. sign,
- ngx.HTTP_GET
- )
- if code >= 300 then
- ngx.status = code
- end
- ngx.print(res)
- }
- }
---- response_body
-failed to sign jwt
---- error_code: 503
---- error_log
-failed to sign jwt, err: secret could not found in vault
-
-
-
-=== TEST 6: store HS256 secret into vault
---- exec
-VAULT_TOKEN='root' VAULT_ADDR='http://0.0.0.0:8200' vault kv put
kv/apisix/consumer/jack/jwt-auth secret=$3nsitiv3-c8d3
---- response_body
-Success! Data written to: kv/apisix/consumer/jack/jwt-auth
-
-
-
-=== TEST 7: sign a HS256 jwt and access/verify /secure-endpoint
---- config
- location /t {
- content_by_lua_block {
- local t = require("lib.test_admin").test
- local code, err, sign = t('/apisix/plugin/jwt/sign?key=key-hs256',
- ngx.HTTP_GET
- )
-
- if code > 200 then
- ngx.status = code
- ngx.say(err)
- return
- end
-
- local code, _, res = t('/secure-endpoint?jwt=' .. sign,
- ngx.HTTP_GET
- )
- if code >= 300 then
- ngx.status = code
- end
- ngx.print(res)
- }
- }
---- response_body
-successfully invoked secure endpoint
-
-
-
-=== TEST 8: store rsa key pairs into vault from local filesystem
---- exec
-VAULT_TOKEN='root' VAULT_ADDR='http://0.0.0.0:8200' vault kv put
kv/apisix/consumer/jim/jwt-auth public_key=@t/certs/public.pem
private_key=@t/certs/private.pem
---- response_body
-Success! Data written to: kv/apisix/consumer/jim/jwt-auth
-
-
-
-=== TEST 9: create consumer for RS256 algorithm with key pair fetched from
vault
---- config
- location /t {
- content_by_lua_block {
- local t = require("lib.test_admin").test
- local code, body = t('/apisix/admin/consumers',
- ngx.HTTP_PUT,
- [[{
- "username": "jim",
- "plugins": {
- "jwt-auth": {
- "key": "rsa",
- "algorithm": "RS256",
- "vault":{}
- }
- }
- }]]
- )
-
- if code >= 300 then
- ngx.status = code
- end
- ngx.say(body)
- }
- }
---- response_body
-passed
-
-
-
-=== TEST 10: sign a jwt with with rsa key pair and access /secure-endpoint
---- config
- location /t {
- content_by_lua_block {
- local t = require("lib.test_admin").test
- local code, err, sign = t('/apisix/plugin/jwt/sign?key=rsa',
- ngx.HTTP_GET
- )
-
- if code > 200 then
- ngx.status = code
- ngx.say(err)
- return
- end
-
- local code, _, res = t('/secure-endpoint?jwt=' .. sign,
- ngx.HTTP_GET
- )
- if code >= 300 then
- ngx.status = code
- end
- ngx.print(res)
- }
- }
---- response_body
-successfully invoked secure endpoint
-
-
-
-=== TEST 11: store rsa private key into vault from local filesystem
---- exec
-VAULT_TOKEN='root' VAULT_ADDR='http://0.0.0.0:8200' vault kv put
kv/apisix/consumer/john/jwt-auth private_key=@t/certs/private.pem
---- response_body
-Success! Data written to: kv/apisix/consumer/john/jwt-auth
-
-
-
-=== TEST 12: create consumer for RS256 algorithm with private key fetched from
vault and public key in consumer schema
---- config
- location /t {
- content_by_lua_block {
- local t = require("lib.test_admin").test
- local code, body = t('/apisix/admin/consumers',
- ngx.HTTP_PUT,
- [[{
- "username": "john",
- "plugins": {
- "jwt-auth": {
- "key": "rsa1",
- "algorithm": "RS256",
- "public_key": "-----BEGIN PUBLIC
KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA79XYBopfnVMKxI533oU2\nVFQbEdSPtWRD+xSl73lHLVboGP1lSIZtnEj5AcTN2uDW6AYPiWL2iA3lEEsDTs7J\nBUXyl6pysBPfrqC8n/MOXKaD4e8U5GAHFiwHWg2WzHlfFSlFkLjzp0vPkDK+fQ4C\nlrd7shAyitB7use6DHcVCKuI4bFOoFbdI5sBGeyoD833g+ql9bRkH/vf8O+rPwHA\nM+47r1iv3lY3ex0P45PRd7U7rq8P8UIw6qOI1tiYuKlFJmjFdcwtYG0dctxWwgL1\n+7njrVQoWvuOTSsc9TDMhZkmmSsU3wXjaPxJpydck1C/w9ZLqsctKK5swYWhIcbc\nBQIDAQAB\n-----END
PUBLIC [...]
- "vault":{}
- }
- }
- }]]
- )
-
- if code >= 300 then
- ngx.status = code
- end
- ngx.say(body)
- }
- }
---- response_body
-passed
-
-
-
-=== TEST 13: sign a jwt with with rsa key pair and access /secure-endpoint
---- config
- location /t {
- content_by_lua_block {
- local t = require("lib.test_admin").test
- local code, err, sign = t('/apisix/plugin/jwt/sign?key=rsa1',
- ngx.HTTP_GET
- )
-
- if code > 200 then
- ngx.status = code
- ngx.say(err)
- return
- end
-
- local code, _, res = t('/secure-endpoint?jwt=' .. sign,
- ngx.HTTP_GET
- )
- if code >= 300 then
- ngx.status = code
- end
- ngx.print(res)
- }
- }
---- response_body
-successfully invoked secure endpoint