From ac8b7ab92e7d8c2c6e0f2140426bb37cee3af47d Mon Sep 17 00:00:00 2001 From: Abhishek Choudhary Date: Thu, 12 Jan 2023 07:36:59 +0530 Subject: [PATCH 01/15] feat: use get method from kms module --- apisix/plugins/jwt-auth.lua | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/apisix/plugins/jwt-auth.lua b/apisix/plugins/jwt-auth.lua index 96c743e1ba07..b21c385c9909 100644 --- a/apisix/plugins/jwt-auth.lua +++ b/apisix/plugins/jwt-auth.lua @@ -18,8 +18,9 @@ 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 vault = require("apisix.core.vault") -- to be removed later local new_tab = require ("table.new") +local secrets = require("apisix.admin.secrets") local ngx_encode_base64 = ngx.encode_base64 local ngx_decode_base64 = ngx.decode_base64 @@ -252,15 +253,12 @@ 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 + local res, err = vault.get() + local status, body = secrets.get(nil, nil, get_vault_path(consumer_name)) + if status == 503 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 + secret = body end if conf.base64_secret then From b506c2e6f5293a6375d857df7eb19a8a32ba8641 Mon Sep 17 00:00:00 2001 From: Abhishek Choudhary Date: Thu, 12 Jan 2023 10:00:57 +0530 Subject: [PATCH 02/15] remove vault related configuration --- apisix/plugins/jwt-auth.lua | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/apisix/plugins/jwt-auth.lua b/apisix/plugins/jwt-auth.lua index b21c385c9909..17ef5fcdce8f 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") -- to be removed later local new_tab = require ("table.new") local secrets = require("apisix.admin.secrets") @@ -252,14 +251,6 @@ end local function get_secret(conf, consumer_name) local secret = conf.secret - if conf.vault then - local res, err = vault.get() - local status, body = secrets.get(nil, nil, get_vault_path(consumer_name)) - if status == 503 then - return nil, err - end - secret = body - end if conf.base64_secret then return ngx_decode_base64(secret) @@ -278,17 +269,6 @@ local function get_rsa_or_ecdsa_keypair(conf, consumer_name) 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" From 8243851e7bbc556964eeb00a5992c17b810828b4 Mon Sep 17 00:00:00 2001 From: Abhishek Choudhary Date: Thu, 12 Jan 2023 10:04:25 +0530 Subject: [PATCH 03/15] remove unused variable and function --- apisix/plugins/jwt-auth.lua | 7 ------- 1 file changed, 7 deletions(-) diff --git a/apisix/plugins/jwt-auth.lua b/apisix/plugins/jwt-auth.lua index 17ef5fcdce8f..87967a10d5de 100644 --- a/apisix/plugins/jwt-auth.lua +++ b/apisix/plugins/jwt-auth.lua @@ -19,7 +19,6 @@ local jwt = require("resty.jwt") local consumer_mod = require("apisix.consumer") local resty_random = require("resty.random") local new_tab = require ("table.new") -local secrets = require("apisix.admin.secrets") local ngx_encode_base64 = ngx.encode_base64 local ngx_decode_base64 = ngx.decode_base64 @@ -243,12 +242,6 @@ 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 From c03c8cc194f99f12fc56306d0b43dc2eee4d4cda Mon Sep 17 00:00:00 2001 From: Abhishek Choudhary Date: Thu, 12 Jan 2023 10:36:25 +0530 Subject: [PATCH 04/15] delete vault.lua --- apisix/core/vault.lua | 127 ------------------------------------------ 1 file changed, 127 deletions(-) delete mode 100644 apisix/core/vault.lua diff --git a/apisix/core/vault.lua b/apisix/core/vault.lua deleted file mode 100644 index aeb8485d97ef..000000000000 --- 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 From 736453c91b052986f76b5a138e9a1efad781bba5 Mon Sep 17 00:00:00 2001 From: Abhishek Choudhary Date: Thu, 12 Jan 2023 15:05:28 +0530 Subject: [PATCH 05/15] delete vault test file --- t/plugin/jwt-auth-vault.t | 386 -------------------------------------- 1 file changed, 386 deletions(-) delete mode 100644 t/plugin/jwt-auth-vault.t diff --git a/t/plugin/jwt-auth-vault.t b/t/plugin/jwt-auth-vault.t deleted file mode 100644 index b76c56acee47..000000000000 --- 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 KEY-----\n", - "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 From 82ec4b1efa123e9e1397972a6a959e1de7338f13 Mon Sep 17 00:00:00 2001 From: Abhishek Choudhary Date: Thu, 12 Jan 2023 15:27:40 +0530 Subject: [PATCH 06/15] remove vout --- apisix/plugins/jwt-auth.lua | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/apisix/plugins/jwt-auth.lua b/apisix/plugins/jwt-auth.lua index 87967a10d5de..72d323ad676b 100644 --- a/apisix/plugins/jwt-auth.lua +++ b/apisix/plugins/jwt-auth.lua @@ -259,18 +259,13 @@ local function get_rsa_or_ecdsa_keypair(conf, consumer_name) -- 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 public_key, nil, "missing private key" + elseif not public_key and private_key then + return nil, private_key, "missing public key" + else + return nil, nil, "public and private keys are missing" end - - local vout = {} - - 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 From a6076dd537531af984b03002e0a56cf4b93d34d6 Mon Sep 17 00:00:00 2001 From: Abhishek Choudhary Date: Tue, 17 Jan 2023 00:30:12 +0530 Subject: [PATCH 07/15] docs: remove vault related parts --- apisix/plugins/jwt-auth.lua | 24 +------ apisix/secret/vault.lua | 6 -- docs/en/latest/plugins/jwt-auth.md | 93 -------------------------- docs/zh/latest/plugins/jwt-auth.md | 102 ----------------------------- 4 files changed, 1 insertion(+), 224 deletions(-) diff --git a/apisix/plugins/jwt-auth.lua b/apisix/plugins/jwt-auth.lua index 72d323ad676b..c5418fbe0521 100644 --- a/apisix/plugins/jwt-auth.lua +++ b/apisix/plugins/jwt-auth.lua @@ -70,10 +70,6 @@ local consumer_schema = { type = "boolean", default = false }, - vault = { - type = "object", - properties = {} - }, lifetime_grace_period = { type = "integer", minimum = 0, @@ -101,19 +97,6 @@ local consumer_schema = { }, required = {"public_key", "private_key"}, }, - { - properties = { - vault = { - type = "object", - properties = {} - }, - algorithm = { - enum = {"RS256", "ES256"}, - }, - }, - required = {"vault"}, - }, - } } }, @@ -146,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 @@ -256,7 +234,7 @@ 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 diff --git a/apisix/secret/vault.lua b/apisix/secret/vault.lua index aff458f2c77d..eb2e6564cd94 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/docs/en/latest/plugins/jwt-auth.md b/docs/en/latest/plugins/jwt-auth.md index f0b65ddcd3a4..8455130f926d 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//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 | @@ -139,88 +128,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 (`/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 (`/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. - - ## 测试插件 首先,你需要为签发 token 的 API 配置一个 Route,该路由将使用 [public-api](../../../en/latest/plugins/public-api.md) 插件。 From 8e2e47a31604f79ab1718378850c18445ab834cc Mon Sep 17 00:00:00 2001 From: Abhishek Choudhary Date: Tue, 17 Jan 2023 10:05:02 +0530 Subject: [PATCH 08/15] docs: add information about KMS --- docs/en/latest/plugins/jwt-auth.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/en/latest/plugins/jwt-auth.md b/docs/en/latest/plugins/jwt-auth.md index 8455130f926d..b5a0fdb29be7 100644 --- a/docs/en/latest/plugins/jwt-auth.md +++ b/docs/en/latest/plugins/jwt-auth.md @@ -59,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. From 0860711c5d9cb85370fd60b9acbcf1936205a171 Mon Sep 17 00:00:00 2001 From: Abhishek Choudhary Date: Tue, 17 Jan 2023 10:06:18 +0530 Subject: [PATCH 09/15] docs: remove trailing space --- docs/en/latest/plugins/jwt-auth.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/en/latest/plugins/jwt-auth.md b/docs/en/latest/plugins/jwt-auth.md index b5a0fdb29be7..de4064282479 100644 --- a/docs/en/latest/plugins/jwt-auth.md +++ b/docs/en/latest/plugins/jwt-auth.md @@ -59,7 +59,7 @@ 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. +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 From e4ce6c8b6d6b1666d414db0809f65dfc2a790e22 Mon Sep 17 00:00:00 2001 From: Abhishek Choudhary Date: Tue, 17 Jan 2023 11:51:13 +0530 Subject: [PATCH 10/15] docs: add information about KMS --- docs/zh/latest/plugins/jwt-auth.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/zh/latest/plugins/jwt-auth.md b/docs/zh/latest/plugins/jwt-auth.md index 9dfb0f1bfea1..23f4e2a9ff2f 100644 --- a/docs/zh/latest/plugins/jwt-auth.md +++ b/docs/zh/latest/plugins/jwt-auth.md @@ -59,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` 接口。 From 09f6c5971176b6fd60b9c090aacd1c556c072d1d Mon Sep 17 00:00:00 2001 From: Abhishek Choudhary Date: Tue, 17 Jan 2023 12:05:07 +0530 Subject: [PATCH 11/15] docs: update getting started guide --- docs/en/latest/getting-started.md | 2 +- docs/zh/latest/getting-started.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/en/latest/getting-started.md b/docs/en/latest/getting-started.md index 5191b27b02d7..62a4a4810402 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 intuitive UI. +- 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 intuitive UI. - 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/zh/latest/getting-started.md b/docs/zh/latest/getting-started.md index dcad6d12286f..d4c18e7ff9dd 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 开发自定义插件。 ## 主要概念 From 2adf78068dd5fc3be64c67a9727c4b3920307033 Mon Sep 17 00:00:00 2001 From: Abhishek Choudhary Date: Wed, 18 Jan 2023 09:33:04 +0530 Subject: [PATCH 12/15] remove vault related configuration --- conf/config-default.yaml | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/conf/config-default.yaml b/conf/config-default.yaml index 9e47f0660137..43bbad005e15 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: From 116ad96230221d527fc0ec616ae9844d6850ed49 Mon Sep 17 00:00:00 2001 From: Abhishek Choudhary Date: Wed, 18 Jan 2023 09:37:45 +0530 Subject: [PATCH 13/15] fix link --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 53768330206c..b819601325fc 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. From 44208dc1633aa491b28a12ac9fa56abfbc4f1f25 Mon Sep 17 00:00:00 2001 From: Abhishek Choudhary Date: Wed, 18 Jan 2023 09:37:58 +0530 Subject: [PATCH 14/15] fix comment --- apisix/plugins/jwt-auth.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apisix/plugins/jwt-auth.lua b/apisix/plugins/jwt-auth.lua index c5418fbe0521..80fd5dcc4b51 100644 --- a/apisix/plugins/jwt-auth.lua +++ b/apisix/plugins/jwt-auth.lua @@ -138,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 From 41db740379153b3e248ea059d7e44dd9e12525f6 Mon Sep 17 00:00:00 2001 From: Abhishek Choudhary Date: Sun, 29 Jan 2023 16:06:43 +0530 Subject: [PATCH 15/15] return nil when key is missing --- apisix/plugins/jwt-auth.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apisix/plugins/jwt-auth.lua b/apisix/plugins/jwt-auth.lua index 80fd5dcc4b51..f195830a696c 100644 --- a/apisix/plugins/jwt-auth.lua +++ b/apisix/plugins/jwt-auth.lua @@ -238,9 +238,9 @@ local function get_rsa_or_ecdsa_keypair(conf, consumer_name) if public_key and private_key then return public_key, private_key elseif public_key and not private_key then - return public_key, nil, "missing private key" + return nil, nil, "missing private key" elseif not public_key and private_key then - return nil, private_key, "missing public key" + return nil, nil, "missing public key" else return nil, nil, "public and private keys are missing" end