From 0e5538c2025e1e73971862502dba13d0427aa0be Mon Sep 17 00:00:00 2001 From: HuanXin-Chen <1056216208@qq.com> Date: Thu, 25 Jul 2024 20:08:55 +0800 Subject: [PATCH 01/24] feat: support gcp secret manager --- apisix/secret/gcp.lua | 214 ++++++++++++++ t/lib/server.lua | 137 +++++++++ t/secret/conf/error.json | 10 + t/secret/conf/success.json | 11 + t/secret/gcp.t | 586 +++++++++++++++++++++++++++++++++++++ 5 files changed, 958 insertions(+) create mode 100644 apisix/secret/gcp.lua create mode 100644 t/secret/conf/error.json create mode 100644 t/secret/conf/success.json create mode 100644 t/secret/gcp.t diff --git a/apisix/secret/gcp.lua b/apisix/secret/gcp.lua new file mode 100644 index 000000000000..20d2984fc804 --- /dev/null +++ b/apisix/secret/gcp.lua @@ -0,0 +1,214 @@ +-- +-- 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. +-- + +--- GCP Tools. +local core = require("apisix.core") +local http = require("resty.http") +local google_oauth = require("apisix.plugins.google-cloud-logging.oauth") + +local sub = core.string.sub +local rfind_char = core.string.rfind_char +local type = type +local decode_base64 = ngx.decode_base64 + +local lrucache = core.lrucache.new({ttl = 300, count= 16}) + +local schema = { + type = "object", + properties = { + auth_config = { + type = "object", + properties = { + client_email = { type = "string" }, + private_key = { type = "string" }, + project_id = { type = "string" }, + token_uri = { + type = "string", + default = "https://oauth2.googleapis.com/token" + }, + scopes = { + type = "string", + default = "https://www.googleapis.com/auth/cloud-platform" + }, + entries_uri = { + type = "string", + default = "https://secretmanager.googleapis.com/v1/" + }, + }, + required = { "client_email", "private_key", "project_id", "token_uri" } + }, + ssl_verify = { + type = "boolean", + default = true + }, + auth_file = { type = "string" }, + }, + oneOf = { + { required = { "auth_config" } }, + { required = { "auth_file" } }, + }, + encrypt_fields = {"auth_config.private_key"}, +} + +local _M = { + schema = schema +} + +local function fetch_oauth_conf(conf) + if conf.auth_config then + return conf.auth_config + end + + if not conf.auth_file then + return nil, "configuration is not defined" + end + + local file_content, err = core.io.get_file(conf.auth_file) + if not file_content or err then + return nil, "failed to read configuration, file: " .. conf.auth_file + end + + local config_tab, err = core.json.decode(file_content) + if not config_tab or err then + return nil, "config parse failure, data: " .. file_content + end + + if not config_tab.client_email or + not config_tab.private_key or + not config_tab.project_id or + not config_tab.token_uri then + return nil, "configuration is undefined, file: " .. conf.auth_file + end + + return config_tab +end + +local function create_oauth_object(auth_config, ssl_verify) + return google_oauth:new(auth_config, ssl_verify) +end + +local function get_secret(oauth, secrets_id) + local http_new = http.new() + + local access_token = oauth:generate_access_token() + if not access_token then + return nil, "failed to get google oauth token" + end + + local entries_uri + if oauth.entries_uri == "http://127.0.0.1:1980/google/secret/" then + entries_uri = oauth.entries_uri .. oauth.project_id .. "/" .. secrets_id + + else + entries_uri = oauth.entries_uri .. "projects/" .. oauth.project_id + .. "/secrets/" .. secrets_id .. "/versions/latest:access" + end + + local res, err = http_new:request_uri(entries_uri, { + ssl_verify = oauth.ssl_verify, + method = "GET", + headers = { + ["Content-Type"] = "application/json", + ["Authorization"] = (oauth.access_token_type or "Bearer") .. " " .. access_token, + }, + }) + + if not res or err then + return nil, "invalid response" + end + + if res.status ~= 200 then + return nil, "invalid status code" + end + + res, err = core.json.decode(res.body) + if not res or err then + return nil, "failed to parse response data" + end + + local payload = res.payload + if type(payload) ~= "table" then + return nil, "invalid payload" + end + + local secret_encoded = payload.data + if type(secret_encoded) ~= "string" then + return nil, "invalid secret string" + end + + local secret = decode_base64(secret_encoded) + return secret +end + +local function make_request_to_gcp(conf, key) + local auth_config, err = fetch_oauth_conf(conf) + if not auth_config then + return nil, err + end + + local lru_key = auth_config.client_email .. "#" .. auth_config.project_id + + local oauth, err = lrucache(lru_key, "gcp", create_oauth_object, auth_config, conf.ssl_verify) + if not oauth or err then + return nil, "failed to create oauth object" + end + + local secret, err = get_secret(oauth, key) + if not secret then + return nil, err + end + + return secret, nil +end + +local function get(conf, key) + core.log.info("fetching data from gcp for key: ", key) + + local idx = rfind_char(key, '/') + if not idx then + return nil, "error key format, key: " .. key + end + + local main_key = sub(key, 1, idx - 1) + if main_key == "" then + return nil, "can't find main key, key: " .. key + end + + local sub_key = sub(key, idx + 1) + if sub_key == "" then + return nil, "can't find sub key, key: " .. key + end + + core.log.info("main: ", main_key, " sub: ", sub_key) + + local res, err = make_request_to_gcp(conf, main_key) + if not res then + return nil, "failed to retrtive data from gcp secret manager: " .. err + end + + local ret = core.json.decode(res) + if not ret then + return nil, "failed to decode result, res: " .. res + end + + return ret[sub_key] +end + +_M.get = get + + +return _M diff --git a/t/lib/server.lua b/t/lib/server.lua index 7cc8101a3af7..e255f094af95 100644 --- a/t/lib/server.lua +++ b/t/lib/server.lua @@ -558,6 +558,143 @@ function _M.google_logging_entries() ngx.say(data) end +function _M.google_secret_token() + local args = ngx.req.get_uri_args() + local args_token_type = args.token_type or "Bearer" + ngx.req.read_body() + local data = ngx.decode_args(ngx.req.get_body_data()) + local jwt = require("resty.jwt") + local access_scopes = "https://www.googleapis.com/auth/cloud" + local verify = jwt:verify(rsa_public_key, data["assertion"]) + if not verify.verified then + ngx.status = 401 + ngx.say(json_encode({ error = "identity authentication failed" })) + return + end + + local scopes_valid = type(verify.payload.scope) == "string" and + verify.payload.scope:find(access_scopes) + if not scopes_valid then + ngx.status = 403 + ngx.say(json_encode({ error = "no access to this scopes" })) + return + end + + local expire_time = (verify.payload.exp or ngx.time()) - ngx.time() + if expire_time <= 0 then + expire_time = 0 + end + + local jwt_token = jwt:sign(rsa_private_key, { + header = { typ = "JWT", alg = "RS256" }, + payload = { exp = verify.payload.exp, scope = access_scopes } + }) + + ngx.say(json_encode({ + access_token = jwt_token, + expires_in = expire_time, + token_type = args_token_type + })) +end + +function _M.google_secret_apisix_jack() + local args = ngx.req.get_uri_args() + local args_token_type = args.token_type or "Bearer" + local jwt = require("resty.jwt") + local access_scopes = "https://www.googleapis.com/auth/cloud" + + local headers = ngx.req.get_headers() + local token = headers["Authorization"] + if not token then + ngx.status = 401 + ngx.say(json_encode({ error = "authentication header not exists" })) + return + end + + token = string.sub(token, #args_token_type + 2) + local verify = jwt:verify(rsa_public_key, token) + if not verify.verified then + ngx.status = 401 + ngx.say(json_encode({ error = "identity authentication failed" })) + return + end + + local scopes_valid = type(verify.payload.scope) == "string" and + verify.payload.scope:find(access_scopes) + if not scopes_valid then + ngx.status = 403 + ngx.say(json_encode({ error = "no access to this scopes" })) + return + end + + local expire_time = (verify.payload.exp or ngx.time()) - ngx.time() + if expire_time <= 0 then + ngx.status = 403 + ngx.say(json_encode({ error = "token has expired" })) + return + end + + local response = { + name = "projects/647037004838/secrets/apisix/versions/1", + payload = { + data = "eyJrZXkiOiJ2YWx1ZSJ9", + dataCrc32c = "2296192492" + } + } + + ngx.status = 200 + ngx.say(json_encode(response)) +end + +function _M.google_secret_fail_apisix_jack() + local args = ngx.req.get_uri_args() + local args_token_type = args.token_type or "Bearer" + local jwt = require("resty.jwt") + local access_scopes = "https://www.googleapis.com/auth/root/cloud" + + local headers = ngx.req.get_headers() + local token = headers["Authorization"] + if not token then + ngx.status = 401 + ngx.say(json_encode({ error = "authentication header not exists" })) + return + end + + token = string.sub(token, #args_token_type + 2) + local verify = jwt:verify(rsa_public_key, token) + if not verify.verified then + ngx.status = 401 + ngx.say(json_encode({ error = "identity authentication failed" })) + return + end + + local scopes_valid = type(verify.payload.scope) == "string" and + verify.payload.scope:find(access_scopes) + if not scopes_valid then + ngx.status = 403 + ngx.say(json_encode({ error = "no access to this scopes" })) + return + end + + local expire_time = (verify.payload.exp or ngx.time()) - ngx.time() + if expire_time <= 0 then + ngx.status = 403 + ngx.say(json_encode({ error = "token has expired" })) + return + end + + local response = { + name = "projects/647037004838/secrets/apisix/versions/1", + payload = { + data = "eyJrZXkiOiJ2YWx1ZSJ9", + dataCrc32c = "2296192492" + } + } + + ngx.status = 200 + ngx.say(json_encode(response)) +end + function _M.plugin_proxy_rewrite_resp_header() ngx.req.read_body() local s = "plugin_proxy_rewrite_resp_header" diff --git a/t/secret/conf/error.json b/t/secret/conf/error.json new file mode 100644 index 000000000000..ef5ef34ef77f --- /dev/null +++ b/t/secret/conf/error.json @@ -0,0 +1,10 @@ +{ + "private_key": "-----BEGIN PRIVATE KEY-----\nMIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDDzrFwnA3EvYyR\naeMgaLD3hBjvxKrz10uox1X8q7YYhf2ViRtLRUMa2bEMYksE5hbhwpNf6mKAnLOC\nUuAT6cPPdUl/agKpJXviBPIR2LuzD17WsLJHp1HxUDssSkgfCaGcOGGNfLUhhIpF\n2JUctLmxiZoAZySlSjcwupSuDJ0aPm0XO8r9H8Qu5kF2Vkz5e5bFivLTmvzrQTe4\nv5V1UI6hThElCSeUmdNF3uG3wopxlvq4zXgLTnuLbrNf/Gc4mlpV+UDgTISj32Ep\nAB2vxKEbvQw4ti8YJnGXWjxLerhfrszFw+V8lpeduiDYA44ZFoVqvzxeIsVZNtcw\nIu7PvEPNAgMBAAECggEAVpyN9m7A1F631/aLheFpLgMbeKt4puV7zQtnaJ2XrZ9P\nPR7pmNDpTu4uF3k/D8qrIm+L+uhVa+hkquf3wDct6w1JVnfQ93riImbnoKdK13ic\nDcEZCwLjByfjFMNCxZ/gAZca55fbExlqhFy6EHmMjhB8s2LsXcTHRuGxNI/Vyi49\nsxECibe0U53aqdJbVWrphIS67cpwl4TUkN6mrHsNuDYNJ9dgkpapoqp4FTFQsBqC\nafOK5qgJ68dWZ47FBUng+AZjdCncqAIuJxxItGVQP6YPsFs+OXcivIVHJr363TpC\nl85FfdvqWV5OGBbwSKhNwiTNUVvfSQVmtURGWG/HbQKBgQD4gZ1z9+Lx19kT9WTz\nlw93lxso++uhAPDTKviyWSRoEe5aN3LCd4My+/Aj+sk4ON/s2BV3ska5Im93j+vC\nrCv3uPn1n2jUhWuJ3bDqipeTW4n/CQA2m/8vd26TMk22yOkkqw2MIA8sjJ//SD7g\ntdG7up6DgGMP4hgbO89uGU7DAwKBgQDJtkKd0grh3u52Foeh9YaiAgYRwc65IE16\nUyD1OJxIuX/dYQDLlo5KyyngFa1ZhWIs7qC7r3xXH+10kfJY+Q+5YMjmZjlL8SR1\nUjqd02R9F2//6OeswyReachJZbZdtiEw3lPa4jVFYfhSe0M2ZPxMwvoXb25eyCNI\n1lYjSKq87wKBgHnLTNghjeDp4UKe6rNYPgRm0rDrhziJtX5JeUov1mALKb6dnmkh\nGfRK9g8sQqKDfXwfC6Z2gaMK9YaryujGaWYoCpoPXtmJ6oLPXH4XHuLh4mhUiP46\nxn8FEfSimuQS4/FMxH8A128GHQSI7AhGFFzlwfrBWcvXC+mNDsTvMmLxAoGARc+4\nupppfccETQZ7JsitMgD1TMwA2f2eEwoWTAitvlXFNT9PYSbYVHaAJbga6PLLCbYF\nFzAjHpxEOKYSdEyu7n/ayDL0/Z2V+qzc8KarDsg/0RgwppBbU/nUgeKb/U79qcYo\ny4ai3UKNCS70Ei1dTMvmdpnwXwlxfNIBufB6dy0CgYBMYq9Lc31GkC6PcGEEbx6W\nvjImOadWZbuOVnvEQjb5XCdcOsWsMcg96PtoeuyyHmhnEF1GsMzcIdQv/PHrvYpK\nYp8D0aqsLEgwGrJQER26FPpKmyIwvcL+nm6q5W31PnU9AOC/WEkB6Zs58hsMzD2S\nkEJQcmfVew5mFXyxuEn3zA==\n-----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" + } + \ No newline at end of file diff --git a/t/secret/conf/success.json b/t/secret/conf/success.json new file mode 100644 index 000000000000..a5e66165a4ad --- /dev/null +++ b/t/secret/conf/success.json @@ -0,0 +1,11 @@ +{ + "private_key": "-----BEGIN PRIVATE KEY-----\nMIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDDzrFwnA3EvYyR\naeMgaLD3hBjvxKrz10uox1X8q7YYhf2ViRtLRUMa2bEMYksE5hbhwpNf6mKAnLOC\nUuAT6cPPdUl/agKpJXviBPIR2LuzD17WsLJHp1HxUDssSkgfCaGcOGGNfLUhhIpF\n2JUctLmxiZoAZySlSjcwupSuDJ0aPm0XO8r9H8Qu5kF2Vkz5e5bFivLTmvzrQTe4\nv5V1UI6hThElCSeUmdNF3uG3wopxlvq4zXgLTnuLbrNf/Gc4mlpV+UDgTISj32Ep\nAB2vxKEbvQw4ti8YJnGXWjxLerhfrszFw+V8lpeduiDYA44ZFoVqvzxeIsVZNtcw\nIu7PvEPNAgMBAAECggEAVpyN9m7A1F631/aLheFpLgMbeKt4puV7zQtnaJ2XrZ9P\nPR7pmNDpTu4uF3k/D8qrIm+L+uhVa+hkquf3wDct6w1JVnfQ93riImbnoKdK13ic\nDcEZCwLjByfjFMNCxZ/gAZca55fbExlqhFy6EHmMjhB8s2LsXcTHRuGxNI/Vyi49\nsxECibe0U53aqdJbVWrphIS67cpwl4TUkN6mrHsNuDYNJ9dgkpapoqp4FTFQsBqC\nafOK5qgJ68dWZ47FBUng+AZjdCncqAIuJxxItGVQP6YPsFs+OXcivIVHJr363TpC\nl85FfdvqWV5OGBbwSKhNwiTNUVvfSQVmtURGWG/HbQKBgQD4gZ1z9+Lx19kT9WTz\nlw93lxso++uhAPDTKviyWSRoEe5aN3LCd4My+/Aj+sk4ON/s2BV3ska5Im93j+vC\nrCv3uPn1n2jUhWuJ3bDqipeTW4n/CQA2m/8vd26TMk22yOkkqw2MIA8sjJ//SD7g\ntdG7up6DgGMP4hgbO89uGU7DAwKBgQDJtkKd0grh3u52Foeh9YaiAgYRwc65IE16\nUyD1OJxIuX/dYQDLlo5KyyngFa1ZhWIs7qC7r3xXH+10kfJY+Q+5YMjmZjlL8SR1\nUjqd02R9F2//6OeswyReachJZbZdtiEw3lPa4jVFYfhSe0M2ZPxMwvoXb25eyCNI\n1lYjSKq87wKBgHnLTNghjeDp4UKe6rNYPgRm0rDrhziJtX5JeUov1mALKb6dnmkh\nGfRK9g8sQqKDfXwfC6Z2gaMK9YaryujGaWYoCpoPXtmJ6oLPXH4XHuLh4mhUiP46\nxn8FEfSimuQS4/FMxH8A128GHQSI7AhGFFzlwfrBWcvXC+mNDsTvMmLxAoGARc+4\nupppfccETQZ7JsitMgD1TMwA2f2eEwoWTAitvlXFNT9PYSbYVHaAJbga6PLLCbYF\nFzAjHpxEOKYSdEyu7n/ayDL0/Z2V+qzc8KarDsg/0RgwppBbU/nUgeKb/U79qcYo\ny4ai3UKNCS70Ei1dTMvmdpnwXwlxfNIBufB6dy0CgYBMYq9Lc31GkC6PcGEEbx6W\nvjImOadWZbuOVnvEQjb5XCdcOsWsMcg96PtoeuyyHmhnEF1GsMzcIdQv/PHrvYpK\nYp8D0aqsLEgwGrJQER26FPpKmyIwvcL+nm6q5W31PnU9AOC/WEkB6Zs58hsMzD2S\nkEJQcmfVew5mFXyxuEn3zA==\n-----END PRIVATE KEY-----", + "project_id": "apisix", + "token_uri": "http://127.0.0.1:1980/google/secret/token", + "scopes": [ + "https://www.googleapis.com/auth/cloud" + ], + "entries_uri": "http://127.0.0.1:1980/google/secret/", + "client_email": "email@apisix.iam.gserviceaccount.com" + } + \ No newline at end of file diff --git a/t/secret/gcp.t b/t/secret/gcp.t new file mode 100644 index 000000000000..ad8335cac934 --- /dev/null +++ b/t/secret/gcp.t @@ -0,0 +1,586 @@ +# +# 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(); +log_level("info"); + +run_tests; + +__DATA__ + +=== TEST 1: check key: error format +--- config + location /t { + content_by_lua_block { + local gcp = require("apisix.secret.gcp") + local conf = { + auth_config = { + client_email = "email@apisix.iam.gserviceaccount.com", + 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:2800/token", + scopes = "https://www.googleapis.com/auth/cloud-platform", + }, + } + local data, err = gcp.get(config, "apisix") + if err then + return ngx.say(err) + end + + ngx.say("done") + } + } +--- request +GET /t +--- response_body +error key format, key: apisix + + + +=== TEST 2: check key: no main key +--- config + location /t { + content_by_lua_block { + local gcp = require("apisix.secret.gcp") + local conf = { + auth_config = { + client_email = "email@apisix.iam.gserviceaccount.com", + 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:2800/token", + scopes = "https://www.googleapis.com/auth/cloud-platform", + }, + } + local data, err = gcp.get(conf, "/apisix") + if err then + return ngx.say(err) + end + + ngx.say("done") + } + } +--- request +GET /t +--- response_body +can't find main key, key: /apisix + + + +=== TEST 3: check key: no sub key +--- config + location /t { + content_by_lua_block { + local gcp = require("apisix.secret.gcp") + local conf = { + auth_config = { + client_email = "email@apisix.iam.gserviceaccount.com", + 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:2800/token", + scopes = "https://www.googleapis.com/auth/cloud-platform", + }, + } + local data, err = gcp.get(conf, "apisix/") + if err then + return ngx.say(err) + end + + ngx.say("done") + } + } +--- request +GET /t +--- response_body +can't find sub key, key: apisix/ + + + +=== TEST 4: add secret && consumer && check +--- request +GET /t +--- config + location /t { + content_by_lua_block { + local conf = { + auth_config = { + client_email = "email@apisix.iam.gserviceaccount.com", + 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/secret/token", + scopes = "https://www.googleapis.com/auth/cloud-platform", + entries_uri = "http://127.0.0.1:2800/google/secret/" + }, + } + + local t = require("lib.test_admin").test + local code, body = t('/apisix/admin/secrets/gcp/mysecret', ngx.HTTP_PUT, conf) + + if code >= 300 then + ngx.status = code + return ngx.say(body) + end + + -- change consumer with secrets ref: gcp + code, body = t('/apisix/admin/consumers', + ngx.HTTP_PUT, + [[{ + "username": "jack", + "plugins": { + "key-auth": { + "key": "$secret://gcp/mysecret/jack/key" + } + } + }]] + ) + if code >= 300 then + ngx.status = code + return ngx.say(body) + end + + + local secret = require("apisix.secret") + local value = secret.fetch_by_uri("$secret://gcp/mysecret/jack/key") + + + local code, body = t('/apisix/admin/secrets/gcp/mysecret', ngx.HTTP_DELETE) + if code >= 300 then + ngx.status = code + return ngx.say(body) + end + + code, body = t('/apisix/admin/consumers', + ngx.HTTP_PUT, + [[{ + "username": "jack", + "plugins": { + "key-auth": { + "key": "$secret://gcp/mysecret/jack/key" + } + } + }]] + ) + if code >= 300 then + ngx.status = code + return ngx.say(body) + end + + local secret = require("apisix.secret") + local value = secret.fetch_by_uri("$secret://gcp/mysecret/jack/key") + ngx.say(value) + } + } +--- response_body +nil + + + +=== TEST 5: get value from gcp +--- config + location /t { + content_by_lua_block { + local conf = { + auth_config = { + client_email = "email@apisix.iam.gserviceaccount.com", + 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/secret/token", + scopes = "https://www.googleapis.com/auth/cloud", + entries_uri = "http://127.0.0.1:1980/google/secret/" + }, + } + local gcp = require("apisix.secret.gcp") + local value, err = gcp.get(conf, "jack/key") + if not value then + return ngx.say(err) + end + ngx.say(value) + } + } +--- request +GET /t +--- response_body +value + + + +=== TEST 6: get value from gcp (failed to get google oauth token:no access to this scopes) +--- config + location /t { + content_by_lua_block { + local conf = { + auth_config = { + client_email = "email@apisix.iam.gserviceaccount.com", + 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/secret/token", + scopes = "https://www.googleapis.com/auth/root/cloud", + entries_uri = "http://127.0.0.1:1980/google/secret/" + }, + } + local gcp = require("apisix.secret.gcp") + local value, err = gcp.get(conf, "jack/key") + if not value then + return ngx.say(err) + end + ngx.say(value) + } + } +--- request +GET /t +--- response_body +failed to retrtive data from gcp secret manager: failed to get google oauth token +--- grep_error_log eval +qr/\{\"error\"\:\"[\w+\s+]*\"\}/ +--- grep_error_log_out +{"error":"no access to this scopes"} + + + +=== TEST 7: get value from gcp (failed to get google oauth token:identity authentication failed) +--- config + location /t { + content_by_lua_block { + local conf = { + auth_config = { + client_email = "email@apisix.iam.gserviceaccount.com", + private_key = [[ +-----BEGIN RSA PRIVATE KEY----- +MIIBOwIBAAJBAKeXgPvU/dAfVhOPk5BTBXCaOXy/0S3mY9VHyqvWZBJ97g6tGbLZ +psn6Gw0wC4mxDfEY5ER4YwU1NWCVtIr1XxcCAwEAAQJADkoowVBD4/8IA9r2JhQu +Ho/H3w8r8tH2KTVZ3pUFK15WGJf8vCF9LznVNKCP0X1NMLGvf4yRELx8jjpwJztI +gQIhANdWaJ3AGftJNaF5qXWwniFP1BcyCPSzn3q0rn19NhyHAiEAxz0HN8Yd+7vR +pi0w/L2I/2nLqgPFtqSGpL2KkJYcXPECIQCdM/PD1k4haNzCOXNA++M1JnYLSPfI +zKkMh4MrEZHDWQIhAKasRiKBaUnTCIJ04bs9L6NDtO4Ic9jj8ANW0Nk9yoJxAiAA +tBXLQH7fw5H8RaxBN91yQUZombw6JnRBXKKohWHZ3Q== +-----END RSA PRIVATE KEY-----]], + project_id = "apisix", + token_uri = "http://127.0.0.1:1980/google/secret/token", + scopes = "https://www.googleapis.com/auth/cloud", + entries_uri = "http://127.0.0.1:1980/google/secret/fail" + }, + } + local gcp = require("apisix.secret.gcp") + local value, err = gcp.get(conf, "jack/key") + if not value then + return ngx.say(err) + end + ngx.say(value) + } + } +--- request +GET /t +--- response_body +failed to retrtive data from gcp secret manager: failed to get google oauth token +--- grep_error_log eval +qr/\{\"error\"\:\"[\w+\s+]*\"\}/ +--- grep_error_log_out +{"error":"identity authentication failed"} + + + +=== TEST 8: get value from gcp (invalid response:no access to this scopes) +--- config + location /t { + content_by_lua_block { + local conf = { + auth_config = { + client_email = "email@apisix.iam.gserviceaccount.com", + 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/secret/token", + scopes = "https://www.googleapis.com/auth/cloud", + entries_uri = "http://127.0.0.1:1980/google/fail/" + }, + } + local gcp = require("apisix.secret.gcp") + local value, err = gcp.get(conf, "jack/key") + if not value then + return ngx.say(err) + end + ngx.say(value) + } + } +--- request +GET /t +--- response_body +failed to retrtive data from gcp secret manager: invalid status code + + + +=== TEST 9: get value from gcp by auth_file(no find file) +--- config + location /t { + content_by_lua_block { + local conf = { + auth_file = "t/secret/conf/nofind.json", + } + local gcp = require("apisix.secret.gcp") + local value, err = gcp.get(conf, "jack/key") + if not value then + return ngx.say(err) + end + ngx.say(value) + } + } +--- request +GET /t +--- response_body +failed to retrtive data from gcp secret manager: failed to read configuration, file: t/secret/conf/nofind.json + + + +=== TEST 10: get value from gcp by auth_file(read success) +--- config + location /t { + content_by_lua_block { + local conf = { + auth_file = "t/secret/conf/success.json", + } + local gcp = require("apisix.secret.gcp") + local value, err = gcp.get(conf, "jack/key") + if not value then + return ngx.say(err) + end + ngx.say(value) + } + } +--- request +GET /t +--- response_body +value + + + +=== TEST 10: get value from gcp by auth_file(configuration is undefined) +--- config + location /t { + content_by_lua_block { + local conf = { + auth_file = "t/secret/conf/error.json", + } + local gcp = require("apisix.secret.gcp") + local value, err = gcp.get(conf, "jack/key") + if not value then + return ngx.say(err) + end + ngx.say(value) + } + } +--- request +GET /t +--- response_body +failed to retrtive data from gcp secret manager: configuration is undefined, file: t/secret/conf/error.json From 8eabd06698c99e233574b3176775f3f49583cec8 Mon Sep 17 00:00:00 2001 From: HuanXin-Chen <1056216208@qq.com> Date: Thu, 25 Jul 2024 20:51:39 +0800 Subject: [PATCH 02/24] docs(secret): integrating gcp usage introduction --- docs/en/latest/terminology/secret.md | 58 +++++++++++++++++++++++++++- docs/zh/latest/terminology/secret.md | 56 ++++++++++++++++++++++++++- t/secret/gcp.t | 2 +- 3 files changed, 112 insertions(+), 4 deletions(-) diff --git a/docs/en/latest/terminology/secret.md b/docs/en/latest/terminology/secret.md index bc233f3d9ce1..cd47bd8b0b8d 100644 --- a/docs/en/latest/terminology/secret.md +++ b/docs/en/latest/terminology/secret.md @@ -38,7 +38,8 @@ Its working principle is shown in the figure: APISIX currently supports storing secrets in the following ways: - [Environment Variables](#use-environment-variables-to-manage-secrets) -- [HashiCorp Vault](#use-vault-to-manage-secrets) +- [HashiCorp Vault](#use-hashicorp-vault-to-manage-secrets) +- [GCP Secrets Manager](#use-gcp-secrets-manager-to-manage-secrets) You can use APISIX Secret functions by specifying format variables in the consumer configuration of the following plugins, such as `key-auth`. @@ -133,7 +134,7 @@ Using HashiCorp Vault to manage secrets means that you can store secrets informa $secret://$manager/$id/$secret_name/$key ``` -- manager: secrets management service, could be the HashiCorp Vault, AWS, etc. +- manager: secrets management service, could be the HashiCorp Vault, AWS, GCP etc. - id: APISIX Secrets resource ID, which needs to be consistent with the one specified when adding the APISIX Secrets resource - secret_name: the secret name in the secrets management service - key: the key corresponding to the secret in the secrets management service @@ -190,3 +191,56 @@ curl http://127.0.0.1:9180/apisix/admin/consumers \ ``` Through the above two steps, when the user request hits the `key-auth` plugin, the real value of the key in the Vault will be obtained through the APISIX Secret component. + +## Use GCP Secrets Manager to manage secrets + +Using the GCP Secrets Manager to manage secrets means you can store the secret information in the GCP service, and reference it using a specific format of variables when configuring plugins. APISIX currently supports integration with the GCP Secrets Manager, and the supported authentication method is [OAuth 2.0](https://developers.google.com/identity/protocols/oauth2?hl=zh-cn). + +### Reference Format + +``` +$secret://$manager/$id/$secret_name/$key +``` + +The reference format is the same as before: + +- manager: secrets management service, could be the HashiCorp Vault, AWS, GCP etc. +- id: APISIX Secrets resource ID, which needs to be consistent with the one specified when adding the APISIX Secrets resource +- secret_name: the secret name in the secrets management service +- key: the key corresponding to the secret in the secrets management service + +### Required Parameters + +| Name | Required | Default | Description | +|-------------------------|----------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| auth_config | True | | Either `auth_config` or `auth_file` must be provided. | +| auth_config.client_email | True | | Email address of the Google Cloud service account. | +| auth_config.private_key | True | | Private key of the Google Cloud service account. | +| auth_config.project_id | True | | Project ID in the Google Cloud service account. | +| auth_config.token_uri | True | https://oauth2.googleapis.com/token | Token URI of the Google Cloud service account. | +| auth_config.entries_uri | False | https://secretmanager.googleapis.com/v1/ | The API access endpoint for the Google Secrets Manager. | +| auth_config.scopes | False | https://www.googleapis.com/auth/cloud-platform | Access scopes of the Google Cloud service account. See [OAuth 2.0 Scopes for Google APIs](https://developers.google.com/identity/protocols/oauth2/scopes) | +| auth_file | True | | Path to the Google Cloud service account authentication JSON file. Either `auth_config` or `auth_file` must be provided. | +| ssl_verify | False | true | When set to `true`, enables SSL verification as mentioned in [OpenResty docs](https://github.com/openresty/lua-nginx-module#tcpsocksslhandshake). | + +You need to configure the corresponding authentication parameters, or specify the authentication file through auth_file, where the content of auth_file is in JSON format. + +### Example + +Here is a correct configuration example: + +``` +curl http://127.0.0.1:9180/apisix/admin/secrets/gcp/1 \ +-H "X-API-KEY: $admin_key" -X PUT -d ' +{ + "auth_config" : { + "client_email": "email@apisix.iam.gserviceaccount.com", + "private_key": "private_key", + "project_id": "apisix-project", + "token_uri": "https://oauth2.googleapis.com/token", + "entries_uri": "https://secretmanager.googleapis.com/v1/", + "scopes": "https://www.googleapis.com/auth/cloud-platform" + } +}' + +``` diff --git a/docs/zh/latest/terminology/secret.md b/docs/zh/latest/terminology/secret.md index 100a44475eb3..0392feb8b0a8 100644 --- a/docs/zh/latest/terminology/secret.md +++ b/docs/zh/latest/terminology/secret.md @@ -39,6 +39,7 @@ APISIX 目前支持通过以下方式存储密钥: - [环境变量](#使用环境变量管理密钥) - [HashiCorp Vault](#使用-vault-管理密钥) +- [GCP Secrets Manager](#使用-gcp-secrets-manager-管理密钥) 你可以在以下插件的 consumer 配置中通过指定格式的变量来使用 APISIX Secret 功能,比如 `key-auth` 插件。 @@ -134,7 +135,7 @@ curl http://127.0.0.1:9180/apisix/admin/consumers \ $secret://$manager/$id/$secret_name/$key ``` -- manager: 密钥管理服务,可以是 Vault、AWS 等 +- manager: 密钥管理服务,可以是 Vault、AWS、GCP 等 - APISIX Secret 资源 ID,需要与添加 APISIX Secret 资源时指定的 ID 保持一致 - secret_name: 密钥管理服务中的密钥名称 - key:密钥管理服务中密钥对应的 key @@ -191,3 +192,56 @@ curl http://127.0.0.1:9180/apisix/admin/consumers \ ``` 通过上面两步操作,当用户请求命中 `key-auth` 插件时,会通过 APISIX Secret 组件获取到 key 在 Vault 中的真实值。 + +## 使用 GCP Secrets Manager 管理密钥 + +使用 GCP Secret Manager 来管理密钥意味着你可以将密钥信息保存在 GCP 服务中,在配置插件时通过特定格式的变量来引用。APISIX 目前支持对接GCP Secret Manager, 所支持的验证方式是[OAuth 2.0](https://developers.google.com/identity/protocols/oauth2?hl=zh-cn)。 + +### 引用方式 + +``` +$secret://$manager/$id/$secret_name/$key +``` + +引用方式和之前保持一致: + +- manager: 密钥管理服务,可以是 Vault、AWS\GCP 等 +- APISIX Secret 资源 ID,需要与添加 APISIX Secret 资源时指定的 ID 保持一致 +- secret_name: 密钥管理服务中的密钥名称 +- key:密钥管理服务中密钥对应的 key + +### 必要参数 + +| 名称 | 必选项 | 默认值 | 描述 | +| ----------------------- | -------- | ------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------- | +| auth_config | 是 | | `auth_config` 和 `auth_file` 必须配置一个。 | +| auth_config.client_email | 是 | | 谷歌服务帐号的 email 参数。 | +| auth_config.private_key | 是 | | 谷歌服务帐号的私钥参数。 | +| auth_config.project_id | 是 | | 谷歌服务帐号的项目 ID。 | +| auth_config.token_uri | 是 | https://oauth2.googleapis.com/token | 请求谷歌服务帐户的令牌的 URI。 | +| auth_config.entries_uri | 否 | https://secretmanager.googleapis.com/v1/ | 谷歌密钥服务访问端点 API。 | +| auth_config.scopes | 否 | https://www.googleapis.com/auth/cloud-platform | 谷歌服务账号的访问范围,可参考 [OAuth 2.0 Scopes for Google APIs](https://developers.google.com/identity/protocols/oauth2/scopes)| +| auth_file | 是 | | `auth_config` 和 `auth_file` 必须配置一个。 | +| ssl_verify | 否 | true | 当设置为 `true` 时,启用 `SSL` 验证。 | + +你需要配置相应的认证参数,或者通过auth_file来指定认证文件,其中auth_file的内容为认证参数的json格式。 + +### 示例 + +以下一种正确的配置实例: + +``` +curl http://127.0.0.1:9180/apisix/admin/secrets/gcp/1 \ +-H "X-API-KEY: $admin_key" -X PUT -d ' +{ + "auth_config" : { + "client_email": "email@apisix.iam.gserviceaccount.com", + "private_key": "private_key", + "project_id": "apisix-project", + "token_uri": "https://oauth2.googleapis.com/token", + "entries_uri": "https://secretmanager.googleapis.com/v1/", + "scopes": "https://www.googleapis.com/auth/cloud-platform" + } +}' + +``` diff --git a/t/secret/gcp.t b/t/secret/gcp.t index ad8335cac934..f374f081ea75 100644 --- a/t/secret/gcp.t +++ b/t/secret/gcp.t @@ -203,7 +203,7 @@ GET /t --- config location /t { content_by_lua_block { - local conf = { + local conf = { auth_config = { client_email = "email@apisix.iam.gserviceaccount.com", private_key = [[ From 16a815be0763438cb3a89c3284f4186a5c9ceb53 Mon Sep 17 00:00:00 2001 From: HuanXin-Chen <1056216208@qq.com> Date: Thu, 25 Jul 2024 21:01:37 +0800 Subject: [PATCH 03/24] style(gcp): fix some style about gcp secret --- t/secret/conf/error.json | 17 ++++++++--------- t/secret/conf/success.json | 18 +++++++++--------- t/secret/gcp.t | 2 +- 3 files changed, 18 insertions(+), 19 deletions(-) diff --git a/t/secret/conf/error.json b/t/secret/conf/error.json index ef5ef34ef77f..8de2535aa8c0 100644 --- a/t/secret/conf/error.json +++ b/t/secret/conf/error.json @@ -1,10 +1,9 @@ { - "private_key": "-----BEGIN PRIVATE KEY-----\nMIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDDzrFwnA3EvYyR\naeMgaLD3hBjvxKrz10uox1X8q7YYhf2ViRtLRUMa2bEMYksE5hbhwpNf6mKAnLOC\nUuAT6cPPdUl/agKpJXviBPIR2LuzD17WsLJHp1HxUDssSkgfCaGcOGGNfLUhhIpF\n2JUctLmxiZoAZySlSjcwupSuDJ0aPm0XO8r9H8Qu5kF2Vkz5e5bFivLTmvzrQTe4\nv5V1UI6hThElCSeUmdNF3uG3wopxlvq4zXgLTnuLbrNf/Gc4mlpV+UDgTISj32Ep\nAB2vxKEbvQw4ti8YJnGXWjxLerhfrszFw+V8lpeduiDYA44ZFoVqvzxeIsVZNtcw\nIu7PvEPNAgMBAAECggEAVpyN9m7A1F631/aLheFpLgMbeKt4puV7zQtnaJ2XrZ9P\nPR7pmNDpTu4uF3k/D8qrIm+L+uhVa+hkquf3wDct6w1JVnfQ93riImbnoKdK13ic\nDcEZCwLjByfjFMNCxZ/gAZca55fbExlqhFy6EHmMjhB8s2LsXcTHRuGxNI/Vyi49\nsxECibe0U53aqdJbVWrphIS67cpwl4TUkN6mrHsNuDYNJ9dgkpapoqp4FTFQsBqC\nafOK5qgJ68dWZ47FBUng+AZjdCncqAIuJxxItGVQP6YPsFs+OXcivIVHJr363TpC\nl85FfdvqWV5OGBbwSKhNwiTNUVvfSQVmtURGWG/HbQKBgQD4gZ1z9+Lx19kT9WTz\nlw93lxso++uhAPDTKviyWSRoEe5aN3LCd4My+/Aj+sk4ON/s2BV3ska5Im93j+vC\nrCv3uPn1n2jUhWuJ3bDqipeTW4n/CQA2m/8vd26TMk22yOkkqw2MIA8sjJ//SD7g\ntdG7up6DgGMP4hgbO89uGU7DAwKBgQDJtkKd0grh3u52Foeh9YaiAgYRwc65IE16\nUyD1OJxIuX/dYQDLlo5KyyngFa1ZhWIs7qC7r3xXH+10kfJY+Q+5YMjmZjlL8SR1\nUjqd02R9F2//6OeswyReachJZbZdtiEw3lPa4jVFYfhSe0M2ZPxMwvoXb25eyCNI\n1lYjSKq87wKBgHnLTNghjeDp4UKe6rNYPgRm0rDrhziJtX5JeUov1mALKb6dnmkh\nGfRK9g8sQqKDfXwfC6Z2gaMK9YaryujGaWYoCpoPXtmJ6oLPXH4XHuLh4mhUiP46\nxn8FEfSimuQS4/FMxH8A128GHQSI7AhGFFzlwfrBWcvXC+mNDsTvMmLxAoGARc+4\nupppfccETQZ7JsitMgD1TMwA2f2eEwoWTAitvlXFNT9PYSbYVHaAJbga6PLLCbYF\nFzAjHpxEOKYSdEyu7n/ayDL0/Z2V+qzc8KarDsg/0RgwppBbU/nUgeKb/U79qcYo\ny4ai3UKNCS70Ei1dTMvmdpnwXwlxfNIBufB6dy0CgYBMYq9Lc31GkC6PcGEEbx6W\nvjImOadWZbuOVnvEQjb5XCdcOsWsMcg96PtoeuyyHmhnEF1GsMzcIdQv/PHrvYpK\nYp8D0aqsLEgwGrJQER26FPpKmyIwvcL+nm6q5W31PnU9AOC/WEkB6Zs58hsMzD2S\nkEJQcmfVew5mFXyxuEn3zA==\n-----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" - } - \ No newline at end of file + "private_key": "-----BEGIN PRIVATE KEY-----\nMIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDDzrFwnA3EvYyR\naeMgaLD3hBjvxKrz10uox1X8q7YYhf2ViRtLRUMa2bEMYksE5hbhwpNf6mKAnLOC\nUuAT6cPPdUl/agKpJXviBPIR2LuzD17WsLJHp1HxUDssSkgfCaGcOGGNfLUhhIpF\n2JUctLmxiZoAZySlSjcwupSuDJ0aPm0XO8r9H8Qu5kF2Vkz5e5bFivLTmvzrQTe4\nv5V1UI6hThElCSeUmdNF3uG3wopxlvq4zXgLTnuLbrNf/Gc4mlpV+UDgTISj32Ep\nAB2vxKEbvQw4ti8YJnGXWjxLerhfrszFw+V8lpeduiDYA44ZFoVqvzxeIsVZNtcw\nIu7PvEPNAgMBAAECggEAVpyN9m7A1F631/aLheFpLgMbeKt4puV7zQtnaJ2XrZ9P\nPR7pmNDpTu4uF3k/D8qrIm+L+uhVa+hkquf3wDct6w1JVnfQ93riImbnoKdK13ic\nDcEZCwLjByfjFMNCxZ/gAZca55fbExlqhFy6EHmMjhB8s2LsXcTHRuGxNI/Vyi49\nsxECibe0U53aqdJbVWrphIS67cpwl4TUkN6mrHsNuDYNJ9dgkpapoqp4FTFQsBqC\nafOK5qgJ68dWZ47FBUng+AZjdCncqAIuJxxItGVQP6YPsFs+OXcivIVHJr363TpC\nl85FfdvqWV5OGBbwSKhNwiTNUVvfSQVmtURGWG/HbQKBgQD4gZ1z9+Lx19kT9WTz\nlw93lxso++uhAPDTKviyWSRoEe5aN3LCd4My+/Aj+sk4ON/s2BV3ska5Im93j+vC\nrCv3uPn1n2jUhWuJ3bDqipeTW4n/CQA2m/8vd26TMk22yOkkqw2MIA8sjJ//SD7g\ntdG7up6DgGMP4hgbO89uGU7DAwKBgQDJtkKd0grh3u52Foeh9YaiAgYRwc65IE16\nUyD1OJxIuX/dYQDLlo5KyyngFa1ZhWIs7qC7r3xXH+10kfJY+Q+5YMjmZjlL8SR1\nUjqd02R9F2//6OeswyReachJZbZdtiEw3lPa4jVFYfhSe0M2ZPxMwvoXb25eyCNI\n1lYjSKq87wKBgHnLTNghjeDp4UKe6rNYPgRm0rDrhziJtX5JeUov1mALKb6dnmkh\nGfRK9g8sQqKDfXwfC6Z2gaMK9YaryujGaWYoCpoPXtmJ6oLPXH4XHuLh4mhUiP46\nxn8FEfSimuQS4/FMxH8A128GHQSI7AhGFFzlwfrBWcvXC+mNDsTvMmLxAoGARc+4\nupppfccETQZ7JsitMgD1TMwA2f2eEwoWTAitvlXFNT9PYSbYVHaAJbga6PLLCbYF\nFzAjHpxEOKYSdEyu7n/ayDL0/Z2V+qzc8KarDsg/0RgwppBbU/nUgeKb/U79qcYo\ny4ai3UKNCS70Ei1dTMvmdpnwXwlxfNIBufB6dy0CgYBMYq9Lc31GkC6PcGEEbx6W\nvjImOadWZbuOVnvEQjb5XCdcOsWsMcg96PtoeuyyHmhnEF1GsMzcIdQv/PHrvYpK\nYp8D0aqsLEgwGrJQER26FPpKmyIwvcL+nm6q5W31PnU9AOC/WEkB6Zs58hsMzD2S\nkEJQcmfVew5mFXyxuEn3zA==\n-----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" +} diff --git a/t/secret/conf/success.json b/t/secret/conf/success.json index a5e66165a4ad..d9aa7b1ab8e8 100644 --- a/t/secret/conf/success.json +++ b/t/secret/conf/success.json @@ -1,11 +1,11 @@ { - "private_key": "-----BEGIN PRIVATE KEY-----\nMIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDDzrFwnA3EvYyR\naeMgaLD3hBjvxKrz10uox1X8q7YYhf2ViRtLRUMa2bEMYksE5hbhwpNf6mKAnLOC\nUuAT6cPPdUl/agKpJXviBPIR2LuzD17WsLJHp1HxUDssSkgfCaGcOGGNfLUhhIpF\n2JUctLmxiZoAZySlSjcwupSuDJ0aPm0XO8r9H8Qu5kF2Vkz5e5bFivLTmvzrQTe4\nv5V1UI6hThElCSeUmdNF3uG3wopxlvq4zXgLTnuLbrNf/Gc4mlpV+UDgTISj32Ep\nAB2vxKEbvQw4ti8YJnGXWjxLerhfrszFw+V8lpeduiDYA44ZFoVqvzxeIsVZNtcw\nIu7PvEPNAgMBAAECggEAVpyN9m7A1F631/aLheFpLgMbeKt4puV7zQtnaJ2XrZ9P\nPR7pmNDpTu4uF3k/D8qrIm+L+uhVa+hkquf3wDct6w1JVnfQ93riImbnoKdK13ic\nDcEZCwLjByfjFMNCxZ/gAZca55fbExlqhFy6EHmMjhB8s2LsXcTHRuGxNI/Vyi49\nsxECibe0U53aqdJbVWrphIS67cpwl4TUkN6mrHsNuDYNJ9dgkpapoqp4FTFQsBqC\nafOK5qgJ68dWZ47FBUng+AZjdCncqAIuJxxItGVQP6YPsFs+OXcivIVHJr363TpC\nl85FfdvqWV5OGBbwSKhNwiTNUVvfSQVmtURGWG/HbQKBgQD4gZ1z9+Lx19kT9WTz\nlw93lxso++uhAPDTKviyWSRoEe5aN3LCd4My+/Aj+sk4ON/s2BV3ska5Im93j+vC\nrCv3uPn1n2jUhWuJ3bDqipeTW4n/CQA2m/8vd26TMk22yOkkqw2MIA8sjJ//SD7g\ntdG7up6DgGMP4hgbO89uGU7DAwKBgQDJtkKd0grh3u52Foeh9YaiAgYRwc65IE16\nUyD1OJxIuX/dYQDLlo5KyyngFa1ZhWIs7qC7r3xXH+10kfJY+Q+5YMjmZjlL8SR1\nUjqd02R9F2//6OeswyReachJZbZdtiEw3lPa4jVFYfhSe0M2ZPxMwvoXb25eyCNI\n1lYjSKq87wKBgHnLTNghjeDp4UKe6rNYPgRm0rDrhziJtX5JeUov1mALKb6dnmkh\nGfRK9g8sQqKDfXwfC6Z2gaMK9YaryujGaWYoCpoPXtmJ6oLPXH4XHuLh4mhUiP46\nxn8FEfSimuQS4/FMxH8A128GHQSI7AhGFFzlwfrBWcvXC+mNDsTvMmLxAoGARc+4\nupppfccETQZ7JsitMgD1TMwA2f2eEwoWTAitvlXFNT9PYSbYVHaAJbga6PLLCbYF\nFzAjHpxEOKYSdEyu7n/ayDL0/Z2V+qzc8KarDsg/0RgwppBbU/nUgeKb/U79qcYo\ny4ai3UKNCS70Ei1dTMvmdpnwXwlxfNIBufB6dy0CgYBMYq9Lc31GkC6PcGEEbx6W\nvjImOadWZbuOVnvEQjb5XCdcOsWsMcg96PtoeuyyHmhnEF1GsMzcIdQv/PHrvYpK\nYp8D0aqsLEgwGrJQER26FPpKmyIwvcL+nm6q5W31PnU9AOC/WEkB6Zs58hsMzD2S\nkEJQcmfVew5mFXyxuEn3zA==\n-----END PRIVATE KEY-----", - "project_id": "apisix", - "token_uri": "http://127.0.0.1:1980/google/secret/token", - "scopes": [ - "https://www.googleapis.com/auth/cloud" - ], - "entries_uri": "http://127.0.0.1:1980/google/secret/", - "client_email": "email@apisix.iam.gserviceaccount.com" - } + "private_key": "-----BEGIN PRIVATE KEY-----\nMIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDDzrFwnA3EvYyR\naeMgaLD3hBjvxKrz10uox1X8q7YYhf2ViRtLRUMa2bEMYksE5hbhwpNf6mKAnLOC\nUuAT6cPPdUl/agKpJXviBPIR2LuzD17WsLJHp1HxUDssSkgfCaGcOGGNfLUhhIpF\n2JUctLmxiZoAZySlSjcwupSuDJ0aPm0XO8r9H8Qu5kF2Vkz5e5bFivLTmvzrQTe4\nv5V1UI6hThElCSeUmdNF3uG3wopxlvq4zXgLTnuLbrNf/Gc4mlpV+UDgTISj32Ep\nAB2vxKEbvQw4ti8YJnGXWjxLerhfrszFw+V8lpeduiDYA44ZFoVqvzxeIsVZNtcw\nIu7PvEPNAgMBAAECggEAVpyN9m7A1F631/aLheFpLgMbeKt4puV7zQtnaJ2XrZ9P\nPR7pmNDpTu4uF3k/D8qrIm+L+uhVa+hkquf3wDct6w1JVnfQ93riImbnoKdK13ic\nDcEZCwLjByfjFMNCxZ/gAZca55fbExlqhFy6EHmMjhB8s2LsXcTHRuGxNI/Vyi49\nsxECibe0U53aqdJbVWrphIS67cpwl4TUkN6mrHsNuDYNJ9dgkpapoqp4FTFQsBqC\nafOK5qgJ68dWZ47FBUng+AZjdCncqAIuJxxItGVQP6YPsFs+OXcivIVHJr363TpC\nl85FfdvqWV5OGBbwSKhNwiTNUVvfSQVmtURGWG/HbQKBgQD4gZ1z9+Lx19kT9WTz\nlw93lxso++uhAPDTKviyWSRoEe5aN3LCd4My+/Aj+sk4ON/s2BV3ska5Im93j+vC\nrCv3uPn1n2jUhWuJ3bDqipeTW4n/CQA2m/8vd26TMk22yOkkqw2MIA8sjJ//SD7g\ntdG7up6DgGMP4hgbO89uGU7DAwKBgQDJtkKd0grh3u52Foeh9YaiAgYRwc65IE16\nUyD1OJxIuX/dYQDLlo5KyyngFa1ZhWIs7qC7r3xXH+10kfJY+Q+5YMjmZjlL8SR1\nUjqd02R9F2//6OeswyReachJZbZdtiEw3lPa4jVFYfhSe0M2ZPxMwvoXb25eyCNI\n1lYjSKq87wKBgHnLTNghjeDp4UKe6rNYPgRm0rDrhziJtX5JeUov1mALKb6dnmkh\nGfRK9g8sQqKDfXwfC6Z2gaMK9YaryujGaWYoCpoPXtmJ6oLPXH4XHuLh4mhUiP46\nxn8FEfSimuQS4/FMxH8A128GHQSI7AhGFFzlwfrBWcvXC+mNDsTvMmLxAoGARc+4\nupppfccETQZ7JsitMgD1TMwA2f2eEwoWTAitvlXFNT9PYSbYVHaAJbga6PLLCbYF\nFzAjHpxEOKYSdEyu7n/ayDL0/Z2V+qzc8KarDsg/0RgwppBbU/nUgeKb/U79qcYo\ny4ai3UKNCS70Ei1dTMvmdpnwXwlxfNIBufB6dy0CgYBMYq9Lc31GkC6PcGEEbx6W\nvjImOadWZbuOVnvEQjb5XCdcOsWsMcg96PtoeuyyHmhnEF1GsMzcIdQv/PHrvYpK\nYp8D0aqsLEgwGrJQER26FPpKmyIwvcL+nm6q5W31PnU9AOC/WEkB6Zs58hsMzD2S\nkEJQcmfVew5mFXyxuEn3zA==\n-----END PRIVATE KEY-----", + "project_id": "apisix", + "token_uri": "http://127.0.0.1:1980/google/secret/token", + "scopes": [ + "https://www.googleapis.com/auth/cloud" + ], + "entries_uri": "http://127.0.0.1:1980/google/secret/", + "client_email": "email@apisix.iam.gserviceaccount.com" +} \ No newline at end of file diff --git a/t/secret/gcp.t b/t/secret/gcp.t index f374f081ea75..4a034b672933 100644 --- a/t/secret/gcp.t +++ b/t/secret/gcp.t @@ -69,7 +69,7 @@ kEJQcmfVew5mFXyxuEn3zA== }, } local data, err = gcp.get(config, "apisix") - if err then + if err then return ngx.say(err) end From 95ee16aebd7c25408f3284e2c27afe2002010241 Mon Sep 17 00:00:00 2001 From: HuanXin-Chen <1056216208@qq.com> Date: Thu, 25 Jul 2024 21:04:27 +0800 Subject: [PATCH 04/24] style(gcp): fix the success.json style --- t/secret/conf/success.json | 1 - 1 file changed, 1 deletion(-) diff --git a/t/secret/conf/success.json b/t/secret/conf/success.json index d9aa7b1ab8e8..4daae1b9be0d 100644 --- a/t/secret/conf/success.json +++ b/t/secret/conf/success.json @@ -8,4 +8,3 @@ "entries_uri": "http://127.0.0.1:1980/google/secret/", "client_email": "email@apisix.iam.gserviceaccount.com" } - \ No newline at end of file From 1205deb04d035a3c2c792e3f5a3ac256bac63e09 Mon Sep 17 00:00:00 2001 From: HuanXin-Chen <1056216208@qq.com> Date: Fri, 26 Jul 2024 10:47:42 +0800 Subject: [PATCH 05/24] style(gcp): fix the secret docs --- docs/zh/latest/terminology/secret.md | 4 ++-- t/secret/gcp.t | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/zh/latest/terminology/secret.md b/docs/zh/latest/terminology/secret.md index 0392feb8b0a8..abd29d516902 100644 --- a/docs/zh/latest/terminology/secret.md +++ b/docs/zh/latest/terminology/secret.md @@ -195,7 +195,7 @@ curl http://127.0.0.1:9180/apisix/admin/consumers \ ## 使用 GCP Secrets Manager 管理密钥 -使用 GCP Secret Manager 来管理密钥意味着你可以将密钥信息保存在 GCP 服务中,在配置插件时通过特定格式的变量来引用。APISIX 目前支持对接GCP Secret Manager, 所支持的验证方式是[OAuth 2.0](https://developers.google.com/identity/protocols/oauth2?hl=zh-cn)。 +使用 GCP Secret Manager 来管理密钥意味着你可以将密钥信息保存在 GCP 服务中,在配置插件时通过特定格式的变量来引用。APISIX 目前支持对接 GCP Secret Manager, 所支持的验证方式是[OAuth 2.0](https://developers.google.com/identity/protocols/oauth2?hl=zh-cn)。 ### 引用方式 @@ -224,7 +224,7 @@ $secret://$manager/$id/$secret_name/$key | auth_file | 是 | | `auth_config` 和 `auth_file` 必须配置一个。 | | ssl_verify | 否 | true | 当设置为 `true` 时,启用 `SSL` 验证。 | -你需要配置相应的认证参数,或者通过auth_file来指定认证文件,其中auth_file的内容为认证参数的json格式。 +你需要配置相应的认证参数,或者通过 auth_file 来指定认证文件,其中 auth_file 的内容为认证参数的 json 格式。 ### 示例 diff --git a/t/secret/gcp.t b/t/secret/gcp.t index 4a034b672933..d308ffc9d99e 100644 --- a/t/secret/gcp.t +++ b/t/secret/gcp.t @@ -565,7 +565,7 @@ value -=== TEST 10: get value from gcp by auth_file(configuration is undefined) +=== TEST 11: get value from gcp by auth_file(configuration is undefined) --- config location /t { content_by_lua_block { From 11acef1b001fe8e1c2d6d2152fc62d80261a1491 Mon Sep 17 00:00:00 2001 From: HuanXin-Chen <1056216208@qq.com> Date: Sat, 27 Jul 2024 20:47:23 +0800 Subject: [PATCH 06/24] fix(secret): fix some gcp logic --- apisix/secret/gcp.lua | 28 ++++++++++++++++++++++------ t/secret/gcp.t | 12 ++++++------ 2 files changed, 28 insertions(+), 12 deletions(-) diff --git a/apisix/secret/gcp.lua b/apisix/secret/gcp.lua index 20d2984fc804..1d095f85deb0 100644 --- a/apisix/secret/gcp.lua +++ b/apisix/secret/gcp.lua @@ -127,16 +127,28 @@ local function get_secret(oauth, secrets_id) }, }) - if not res or err then + if not res then + if err then + return nil, "invalid response, " .. err + end + return nil, "invalid response" end if res.status ~= 200 then - return nil, "invalid status code" + if res.body then + return nil, "invalid status code " .. res.status .. ", " .. res.body + end + + return nil, "invalid status code" .. res.status end res, err = core.json.decode(res.body) - if not res or err then + if not res then + if err then + return nil, "failed to parse response data, " .. err + end + return nil, "failed to parse response data" end @@ -200,12 +212,16 @@ local function get(conf, key) return nil, "failed to retrtive data from gcp secret manager: " .. err end - local ret = core.json.decode(res) - if not ret then + local data, err = core.json.decode(res) + if not data then + if err then + return nil, "failed to decode result, res:" .. res .. ", ".. err + end + return nil, "failed to decode result, res: " .. res end - return ret[sub_key] + return data[sub_key] end _M.get = get diff --git a/t/secret/gcp.t b/t/secret/gcp.t index d308ffc9d99e..d21b1c4949d4 100644 --- a/t/secret/gcp.t +++ b/t/secret/gcp.t @@ -439,10 +439,10 @@ pi0w/L2I/2nLqgPFtqSGpL2KkJYcXPECIQCdM/PD1k4haNzCOXNA++M1JnYLSPfI zKkMh4MrEZHDWQIhAKasRiKBaUnTCIJ04bs9L6NDtO4Ic9jj8ANW0Nk9yoJxAiAA tBXLQH7fw5H8RaxBN91yQUZombw6JnRBXKKohWHZ3Q== -----END RSA PRIVATE KEY-----]], - project_id = "apisix", + project_id = "/fail/apisix", token_uri = "http://127.0.0.1:1980/google/secret/token", scopes = "https://www.googleapis.com/auth/cloud", - entries_uri = "http://127.0.0.1:1980/google/secret/fail" + entries_uri = "http://127.0.0.1:1980/google/secret/" }, } local gcp = require("apisix.secret.gcp") @@ -500,16 +500,16 @@ vjImOadWZbuOVnvEQjb5XCdcOsWsMcg96PtoeuyyHmhnEF1GsMzcIdQv/PHrvYpK Yp8D0aqsLEgwGrJQER26FPpKmyIwvcL+nm6q5W31PnU9AOC/WEkB6Zs58hsMzD2S kEJQcmfVew5mFXyxuEn3zA== -----END PRIVATE KEY-----]], - project_id = "apisix", + project_id = "/fail/apisix", token_uri = "http://127.0.0.1:1980/google/secret/token", scopes = "https://www.googleapis.com/auth/cloud", - entries_uri = "http://127.0.0.1:1980/google/fail/" + entries_uri = "http://127.0.0.1:1980/google/secret/" }, } local gcp = require("apisix.secret.gcp") local value, err = gcp.get(conf, "jack/key") if not value then - return ngx.say(err) + return ngx.say("err") end ngx.say(value) } @@ -517,7 +517,7 @@ kEJQcmfVew5mFXyxuEn3zA== --- request GET /t --- response_body -failed to retrtive data from gcp secret manager: invalid status code +err From a21304389e94ef6611224303e1aeb0769e7e7665 Mon Sep 17 00:00:00 2001 From: HuanXin-Chen <1056216208@qq.com> Date: Mon, 29 Jul 2024 19:57:59 +0800 Subject: [PATCH 07/24] fix(secret): gcp code and test --- apisix/secret/gcp.lua | 55 ++++-------- t/lib/server.lua | 4 +- t/secret/gcp.t | 191 +++++++++++++++++++++++------------------- 3 files changed, 122 insertions(+), 128 deletions(-) diff --git a/apisix/secret/gcp.lua b/apisix/secret/gcp.lua index 1d095f85deb0..3a19a08185a1 100644 --- a/apisix/secret/gcp.lua +++ b/apisix/secret/gcp.lua @@ -73,18 +73,14 @@ local function fetch_oauth_conf(conf) return conf.auth_config end - if not conf.auth_file then - return nil, "configuration is not defined" - end - local file_content, err = core.io.get_file(conf.auth_file) - if not file_content or err then - return nil, "failed to read configuration, file: " .. conf.auth_file + if not file_content then + return nil, "failed to read configuration, file: " .. conf.auth_file .. ", err: " .. err end local config_tab, err = core.json.decode(file_content) - if not config_tab or err then - return nil, "config parse failure, data: " .. file_content + if not config_tab then + return nil, "config parse failure, data: " .. file_content .. ", err: " .. err end if not config_tab.client_email or @@ -128,32 +124,20 @@ local function get_secret(oauth, secrets_id) }) if not res then - if err then - return nil, "invalid response, " .. err - end - - return nil, "invalid response" + return nil, err end if res.status ~= 200 then - if res.body then - return nil, "invalid status code " .. res.status .. ", " .. res.body - end - - return nil, "invalid status code" .. res.status + return nil, res.body end - res, err = core.json.decode(res.body) - if not res then - if err then - return nil, "failed to parse response data, " .. err - end - - return nil, "failed to parse response data" + local body, err = core.json.decode(res.body) + if not body then + return nil, "failed to parse response data, " .. err end - local payload = res.payload - if type(payload) ~= "table" then + local payload = body.payload + if not payload then return nil, "invalid payload" end @@ -175,8 +159,8 @@ local function make_request_to_gcp(conf, key) local lru_key = auth_config.client_email .. "#" .. auth_config.project_id local oauth, err = lrucache(lru_key, "gcp", create_oauth_object, auth_config, conf.ssl_verify) - if not oauth or err then - return nil, "failed to create oauth object" + if not oauth then + return nil, "failed to create oauth object, " .. err end local secret, err = get_secret(oauth, key) @@ -184,7 +168,7 @@ local function make_request_to_gcp(conf, key) return nil, err end - return secret, nil + return secret end local function get(conf, key) @@ -212,19 +196,14 @@ local function get(conf, key) return nil, "failed to retrtive data from gcp secret manager: " .. err end - local data, err = core.json.decode(res) - if not data then - if err then - return nil, "failed to decode result, res:" .. res .. ", ".. err - end - + local ret = core.json.decode(res) + if not ret then return nil, "failed to decode result, res: " .. res end - return data[sub_key] + return ret[sub_key] end _M.get = get - return _M diff --git a/t/lib/server.lua b/t/lib/server.lua index e255f094af95..70f81f34f84f 100644 --- a/t/lib/server.lua +++ b/t/lib/server.lua @@ -646,7 +646,7 @@ function _M.google_secret_apisix_jack() ngx.say(json_encode(response)) end -function _M.google_secret_fail_apisix_jack() +function _M.google_secret_apisix_error_jack() local args = ngx.req.get_uri_args() local args_token_type = args.token_type or "Bearer" local jwt = require("resty.jwt") @@ -684,7 +684,7 @@ function _M.google_secret_fail_apisix_jack() end local response = { - name = "projects/647037004838/secrets/apisix/versions/1", + name = "projects/647037004838/secrets/apisix_error/versions/1", payload = { data = "eyJrZXkiOiJ2YWx1ZSJ9", dataCrc32c = "2296192492" diff --git a/t/secret/gcp.t b/t/secret/gcp.t index d21b1c4949d4..7ab49fcf1525 100644 --- a/t/secret/gcp.t +++ b/t/secret/gcp.t @@ -304,7 +304,73 @@ nil -=== TEST 5: get value from gcp +=== TEST 5: get value from gcp by auth_file(fetch_oatuh_conf failed, read failed) +--- config + location /t { + content_by_lua_block { + local conf = { + auth_file = "t/secret/conf/nofind.json", + } + local gcp = require("apisix.secret.gcp") + local value, err = gcp.get(conf, "jack/key") + if not value then + return ngx.say(err) + end + ngx.say(value) + } + } +--- request +GET /t +--- response_body +failed to retrtive data from gcp secret manager: failed to read configuration, file: t/secret/conf/nofind.json, err: t/secret/conf/nofind.json: No such file or directory + + + +=== TEST 6: get value from gcp by auth_file(fetch_oatuh_conf success) +--- config + location /t { + content_by_lua_block { + local conf = { + auth_file = "t/secret/conf/success.json", + } + local gcp = require("apisix.secret.gcp") + local value, err = gcp.get(conf, "jack/key") + if not value then + return ngx.say(err) + end + ngx.say(value) + } + } +--- request +GET /t +--- response_body +value + + + +=== TEST 7: get value from gcp by auth_file(fetch_oatuh_conf failed, undefined) +--- config + location /t { + content_by_lua_block { + local conf = { + auth_file = "t/secret/conf/error.json", + } + local gcp = require("apisix.secret.gcp") + local value, err = gcp.get(conf, "jack/key") + if not value then + return ngx.say(err) + end + ngx.say(value) + } + } +--- request +GET /t +--- response_body +failed to retrtive data from gcp secret manager: configuration is undefined, file: t/secret/conf/error.json + + + +=== TEST 8: get value from gcp --- config location /t { content_by_lua_block { @@ -361,7 +427,7 @@ value -=== TEST 6: get value from gcp (failed to get google oauth token:no access to this scopes) +=== TEST 9: get value from gcp(failed to get google oauth token) --- config location /t { content_by_lua_block { @@ -422,7 +488,7 @@ qr/\{\"error\"\:\"[\w+\s+]*\"\}/ -=== TEST 7: get value from gcp (failed to get google oauth token:identity authentication failed) +=== TEST 10: get value from gcp (not res) --- config location /t { content_by_lua_block { @@ -430,25 +496,44 @@ qr/\{\"error\"\:\"[\w+\s+]*\"\}/ auth_config = { client_email = "email@apisix.iam.gserviceaccount.com", private_key = [[ ------BEGIN RSA PRIVATE KEY----- -MIIBOwIBAAJBAKeXgPvU/dAfVhOPk5BTBXCaOXy/0S3mY9VHyqvWZBJ97g6tGbLZ -psn6Gw0wC4mxDfEY5ER4YwU1NWCVtIr1XxcCAwEAAQJADkoowVBD4/8IA9r2JhQu -Ho/H3w8r8tH2KTVZ3pUFK15WGJf8vCF9LznVNKCP0X1NMLGvf4yRELx8jjpwJztI -gQIhANdWaJ3AGftJNaF5qXWwniFP1BcyCPSzn3q0rn19NhyHAiEAxz0HN8Yd+7vR -pi0w/L2I/2nLqgPFtqSGpL2KkJYcXPECIQCdM/PD1k4haNzCOXNA++M1JnYLSPfI -zKkMh4MrEZHDWQIhAKasRiKBaUnTCIJ04bs9L6NDtO4Ic9jj8ANW0Nk9yoJxAiAA -tBXLQH7fw5H8RaxBN91yQUZombw6JnRBXKKohWHZ3Q== ------END RSA PRIVATE KEY-----]], - project_id = "/fail/apisix", +-----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_error", token_uri = "http://127.0.0.1:1980/google/secret/token", scopes = "https://www.googleapis.com/auth/cloud", - entries_uri = "http://127.0.0.1:1980/google/secret/" + entries_uri = "http://127.0.0.1:1980/google/" }, } local gcp = require("apisix.secret.gcp") local value, err = gcp.get(conf, "jack/key") if not value then - return ngx.say(err) + return ngx.say("err") end ngx.say(value) } @@ -456,15 +541,11 @@ tBXLQH7fw5H8RaxBN91yQUZombw6JnRBXKKohWHZ3Q== --- request GET /t --- response_body -failed to retrtive data from gcp secret manager: failed to get google oauth token ---- grep_error_log eval -qr/\{\"error\"\:\"[\w+\s+]*\"\}/ ---- grep_error_log_out -{"error":"identity authentication failed"} +err -=== TEST 8: get value from gcp (invalid response:no access to this scopes) +=== TEST 11: get value from gcp (res status ~= 200) --- config location /t { content_by_lua_block { @@ -500,7 +581,7 @@ vjImOadWZbuOVnvEQjb5XCdcOsWsMcg96PtoeuyyHmhnEF1GsMzcIdQv/PHrvYpK Yp8D0aqsLEgwGrJQER26FPpKmyIwvcL+nm6q5W31PnU9AOC/WEkB6Zs58hsMzD2S kEJQcmfVew5mFXyxuEn3zA== -----END PRIVATE KEY-----]], - project_id = "/fail/apisix", + project_id = "apisix_error", token_uri = "http://127.0.0.1:1980/google/secret/token", scopes = "https://www.googleapis.com/auth/cloud", entries_uri = "http://127.0.0.1:1980/google/secret/" @@ -518,69 +599,3 @@ kEJQcmfVew5mFXyxuEn3zA== GET /t --- response_body err - - - -=== TEST 9: get value from gcp by auth_file(no find file) ---- config - location /t { - content_by_lua_block { - local conf = { - auth_file = "t/secret/conf/nofind.json", - } - local gcp = require("apisix.secret.gcp") - local value, err = gcp.get(conf, "jack/key") - if not value then - return ngx.say(err) - end - ngx.say(value) - } - } ---- request -GET /t ---- response_body -failed to retrtive data from gcp secret manager: failed to read configuration, file: t/secret/conf/nofind.json - - - -=== TEST 10: get value from gcp by auth_file(read success) ---- config - location /t { - content_by_lua_block { - local conf = { - auth_file = "t/secret/conf/success.json", - } - local gcp = require("apisix.secret.gcp") - local value, err = gcp.get(conf, "jack/key") - if not value then - return ngx.say(err) - end - ngx.say(value) - } - } ---- request -GET /t ---- response_body -value - - - -=== TEST 11: get value from gcp by auth_file(configuration is undefined) ---- config - location /t { - content_by_lua_block { - local conf = { - auth_file = "t/secret/conf/error.json", - } - local gcp = require("apisix.secret.gcp") - local value, err = gcp.get(conf, "jack/key") - if not value then - return ngx.say(err) - end - ngx.say(value) - } - } ---- request -GET /t ---- response_body -failed to retrtive data from gcp secret manager: configuration is undefined, file: t/secret/conf/error.json From 4ea2590132e8e74ce8c2437ff27b208d8ad50743 Mon Sep 17 00:00:00 2001 From: HuanXin-Chen <1056216208@qq.com> Date: Sat, 3 Aug 2024 17:07:42 +0800 Subject: [PATCH 08/24] feat(secret): support ther gcp string value --- apisix/secret/gcp.lua | 25 ++-- docs/en/latest/terminology/secret.md | 4 +- docs/zh/latest/terminology/secret.md | 4 +- t/lib/server.lua | 49 +++++++ t/secret/gcp.t | 208 +++++++++++++-------------- 5 files changed, 168 insertions(+), 122 deletions(-) diff --git a/apisix/secret/gcp.lua b/apisix/secret/gcp.lua index 3a19a08185a1..85b94b003aea 100644 --- a/apisix/secret/gcp.lua +++ b/apisix/secret/gcp.lua @@ -21,7 +21,7 @@ local http = require("resty.http") local google_oauth = require("apisix.plugins.google-cloud-logging.oauth") local sub = core.string.sub -local rfind_char = core.string.rfind_char +local find = core.string.find local type = type local decode_base64 = ngx.decode_base64 @@ -49,7 +49,7 @@ local schema = { default = "https://secretmanager.googleapis.com/v1/" }, }, - required = { "client_email", "private_key", "project_id", "token_uri" } + required = { "client_email", "private_key", "project_id"} }, ssl_verify = { type = "boolean", @@ -174,28 +174,29 @@ end local function get(conf, key) core.log.info("fetching data from gcp for key: ", key) - local idx = rfind_char(key, '/') - if not idx then - return nil, "error key format, key: " .. key - end + local idx = find(key, '/') - local main_key = sub(key, 1, idx - 1) + local main_key = idx and sub(key, 1, idx - 1) or key if main_key == "" then return nil, "can't find main key, key: " .. key end - local sub_key = sub(key, idx + 1) - if sub_key == "" then - return nil, "can't find sub key, key: " .. key + local sub_key = idx and sub(key, idx + 1) or nil + if not sub_key then + core.log.info("main: ", main_key) + else + core.log.info("main: ", main_key, " sub: ", sub_key) end - core.log.info("main: ", main_key, " sub: ", sub_key) - local res, err = make_request_to_gcp(conf, main_key) if not res then return nil, "failed to retrtive data from gcp secret manager: " .. err end + if not sub_key then + return res + end + local ret = core.json.decode(res) if not ret then return nil, "failed to decode result, res: " .. res diff --git a/docs/en/latest/terminology/secret.md b/docs/en/latest/terminology/secret.md index cd47bd8b0b8d..1d2eff0d813c 100644 --- a/docs/en/latest/terminology/secret.md +++ b/docs/en/latest/terminology/secret.md @@ -207,7 +207,7 @@ The reference format is the same as before: - manager: secrets management service, could be the HashiCorp Vault, AWS, GCP etc. - id: APISIX Secrets resource ID, which needs to be consistent with the one specified when adding the APISIX Secrets resource - secret_name: the secret name in the secrets management service -- key: the key corresponding to the secret in the secrets management service +- key: get the value of a property when the value of the secret is a JSON string ### Required Parameters @@ -217,7 +217,7 @@ The reference format is the same as before: | auth_config.client_email | True | | Email address of the Google Cloud service account. | | auth_config.private_key | True | | Private key of the Google Cloud service account. | | auth_config.project_id | True | | Project ID in the Google Cloud service account. | -| auth_config.token_uri | True | https://oauth2.googleapis.com/token | Token URI of the Google Cloud service account. | +| auth_config.token_uri | False | https://oauth2.googleapis.com/token | Token URI of the Google Cloud service account. | | auth_config.entries_uri | False | https://secretmanager.googleapis.com/v1/ | The API access endpoint for the Google Secrets Manager. | | auth_config.scopes | False | https://www.googleapis.com/auth/cloud-platform | Access scopes of the Google Cloud service account. See [OAuth 2.0 Scopes for Google APIs](https://developers.google.com/identity/protocols/oauth2/scopes) | | auth_file | True | | Path to the Google Cloud service account authentication JSON file. Either `auth_config` or `auth_file` must be provided. | diff --git a/docs/zh/latest/terminology/secret.md b/docs/zh/latest/terminology/secret.md index abd29d516902..48eb8688d842 100644 --- a/docs/zh/latest/terminology/secret.md +++ b/docs/zh/latest/terminology/secret.md @@ -208,7 +208,7 @@ $secret://$manager/$id/$secret_name/$key - manager: 密钥管理服务,可以是 Vault、AWS\GCP 等 - APISIX Secret 资源 ID,需要与添加 APISIX Secret 资源时指定的 ID 保持一致 - secret_name: 密钥管理服务中的密钥名称 -- key:密钥管理服务中密钥对应的 key +- key:当密钥的值是 JSON 字符串时,获取某个属性的值 ### 必要参数 @@ -218,7 +218,7 @@ $secret://$manager/$id/$secret_name/$key | auth_config.client_email | 是 | | 谷歌服务帐号的 email 参数。 | | auth_config.private_key | 是 | | 谷歌服务帐号的私钥参数。 | | auth_config.project_id | 是 | | 谷歌服务帐号的项目 ID。 | -| auth_config.token_uri | 是 | https://oauth2.googleapis.com/token | 请求谷歌服务帐户的令牌的 URI。 | +| auth_config.token_uri | 否 | https://oauth2.googleapis.com/token | 请求谷歌服务帐户的令牌的 URI。 | | auth_config.entries_uri | 否 | https://secretmanager.googleapis.com/v1/ | 谷歌密钥服务访问端点 API。 | | auth_config.scopes | 否 | https://www.googleapis.com/auth/cloud-platform | 谷歌服务账号的访问范围,可参考 [OAuth 2.0 Scopes for Google APIs](https://developers.google.com/identity/protocols/oauth2/scopes)| | auth_file | 是 | | `auth_config` 和 `auth_file` 必须配置一个。 | diff --git a/t/lib/server.lua b/t/lib/server.lua index 70f81f34f84f..f8095505e665 100644 --- a/t/lib/server.lua +++ b/t/lib/server.lua @@ -695,6 +695,55 @@ function _M.google_secret_apisix_error_jack() ngx.say(json_encode(response)) end +function _M.google_secret_apisix_mysql() + local args = ngx.req.get_uri_args() + local args_token_type = args.token_type or "Bearer" + local jwt = require("resty.jwt") + local access_scopes = "https://www.googleapis.com/auth/cloud" + + local headers = ngx.req.get_headers() + local token = headers["Authorization"] + if not token then + ngx.status = 401 + ngx.say(json_encode({ error = "authentication header not exists" })) + return + end + + token = string.sub(token, #args_token_type + 2) + local verify = jwt:verify(rsa_public_key, token) + if not verify.verified then + ngx.status = 401 + ngx.say(json_encode({ error = "identity authentication failed" })) + return + end + + local scopes_valid = type(verify.payload.scope) == "string" and + verify.payload.scope:find(access_scopes) + if not scopes_valid then + ngx.status = 403 + ngx.say(json_encode({ error = "no access to this scopes" })) + return + end + + local expire_time = (verify.payload.exp or ngx.time()) - ngx.time() + if expire_time <= 0 then + ngx.status = 403 + ngx.say(json_encode({ error = "token has expired" })) + return + end + + local response = { + name = "projects/647037004838/secrets/apisix/versions/1", + payload = { + data = "c2VjcmV0", + dataCrc32c = "0xB03C4D4D" + } + } + + ngx.status = 200 + ngx.say(json_encode(response)) +end + function _M.plugin_proxy_rewrite_resp_header() ngx.req.read_body() local s = "plugin_proxy_rewrite_resp_header" diff --git a/t/secret/gcp.t b/t/secret/gcp.t index 7ab49fcf1525..aed480c16c81 100644 --- a/t/secret/gcp.t +++ b/t/secret/gcp.t @@ -26,60 +26,56 @@ run_tests; __DATA__ -=== TEST 1: check key: error format +=== TEST 1: sanity --- config location /t { content_by_lua_block { - local gcp = require("apisix.secret.gcp") - local conf = { - auth_config = { - client_email = "email@apisix.iam.gserviceaccount.com", - 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:2800/token", - scopes = "https://www.googleapis.com/auth/cloud-platform", - }, + local test_case = { + {}, + {auth_file = "123"}, + {auth_file = 123}, + {auth_config = {}}, + {auth_config = {client_email = "client"}}, + {auth_config = {client_email = "client", private_key = "private_key"}}, + {auth_config = {client_email = "client", private_key = "private_key", project_id = "project_id"}}, + {auth_config = {client_email = 1234, private_key = "private_key", project_id = "project_id"}}, + {auth_config = {client_email = "client", private_key = 1234, project_id = "project_id"}}, + {auth_config = {client_email = "client", private_key = "private_key", project_id = 1234}}, + {auth_config = {client_email = "client", private_key = "private_key", project_id = "project_id"}, ssl_verify = 1234}, + {auth_config = {client_email = "client", private_key = "private_key", project_id = "project_id", token_uri = 1234}}, + {auth_config = {client_email = "client", private_key = "private_key", project_id = "project_id", scopes = 1234}}, + {auth_config = {client_email = "client", private_key = "private_key", project_id = "project_id", entries_uri = 1234}}, + {auth_config = {client_email = "client", private_key = "private_key", project_id = "project_id", token_uri = "token_uri", + scopes = "scopes", entries_uri = "entries_uri"}, ssl_verify = true}, } - local data, err = gcp.get(config, "apisix") - if err then - return ngx.say(err) - end + local gcp = require("apisix.secret.gcp") + local core = require("apisix.core") + local metadata_schema = gcp.schema - ngx.say("done") + for _, conf in ipairs(test_case) do + local ok, err = core.schema.check(metadata_schema, conf) + ngx.say(ok and "done" or err) + end } } --- request GET /t --- response_body -error key format, key: apisix +value should match only one schema, but matches none +done +property "auth_file" validation failed: wrong type: expected string, got number +property "auth_config" validation failed: property "private_key" is required +property "auth_config" validation failed: property "private_key" is required +property "auth_config" validation failed: property "project_id" is required +done +property "auth_config" validation failed: property "client_email" validation failed: wrong type: expected string, got number +property "auth_config" validation failed: property "private_key" validation failed: wrong type: expected string, got number +property "auth_config" validation failed: property "project_id" validation failed: wrong type: expected string, got number +property "ssl_verify" validation failed: wrong type: expected boolean, got number +property "auth_config" validation failed: property "token_uri" validation failed: wrong type: expected string, got number +property "auth_config" validation failed: property "scopes" validation failed: wrong type: expected string, got number +property "auth_config" validation failed: property "entries_uri" validation failed: wrong type: expected string, got number +done @@ -140,64 +136,7 @@ can't find main key, key: /apisix -=== TEST 3: check key: no sub key ---- config - location /t { - content_by_lua_block { - local gcp = require("apisix.secret.gcp") - local conf = { - auth_config = { - client_email = "email@apisix.iam.gserviceaccount.com", - 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:2800/token", - scopes = "https://www.googleapis.com/auth/cloud-platform", - }, - } - local data, err = gcp.get(conf, "apisix/") - if err then - return ngx.say(err) - end - - ngx.say("done") - } - } ---- request -GET /t ---- response_body -can't find sub key, key: apisix/ - - - -=== TEST 4: add secret && consumer && check +=== TEST 3: add secret && consumer && check --- request GET /t --- config @@ -304,7 +243,7 @@ nil -=== TEST 5: get value from gcp by auth_file(fetch_oatuh_conf failed, read failed) +=== TEST 4: get value from gcp by auth_file(fetch_oatuh_conf failed, read failed) --- config location /t { content_by_lua_block { @@ -326,7 +265,7 @@ failed to retrtive data from gcp secret manager: failed to read configuration, f -=== TEST 6: get value from gcp by auth_file(fetch_oatuh_conf success) +=== TEST 5: get value from gcp by auth_file(fetch_oatuh_conf success) --- config location /t { content_by_lua_block { @@ -348,7 +287,7 @@ value -=== TEST 7: get value from gcp by auth_file(fetch_oatuh_conf failed, undefined) +=== TEST 6: get value from gcp by auth_file(fetch_oatuh_conf failed, undefined) --- config location /t { content_by_lua_block { @@ -370,7 +309,7 @@ failed to retrtive data from gcp secret manager: configuration is undefined, fil -=== TEST 8: get value from gcp +=== TEST 7: get json value from gcp --- config location /t { content_by_lua_block { @@ -427,6 +366,63 @@ value +=== TEST 8: get string value from gcp +--- config + location /t { + content_by_lua_block { + local conf = { + auth_config = { + client_email = "email@apisix.iam.gserviceaccount.com", + 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/secret/token", + scopes = "https://www.googleapis.com/auth/cloud", + entries_uri = "http://127.0.0.1:1980/google/secret/" + }, + } + local gcp = require("apisix.secret.gcp") + local value, err = gcp.get(conf, "mysql") + if not value then + return ngx.say(err) + end + ngx.say(value) + } + } +--- request +GET /t +--- response_body +secret + + + === TEST 9: get value from gcp(failed to get google oauth token) --- config location /t { From 6fd638974e366623e05c1446063cd3691349fc2d Mon Sep 17 00:00:00 2001 From: HuanXin-Chen <1056216208@qq.com> Date: Sun, 4 Aug 2024 20:28:42 +0800 Subject: [PATCH 09/24] feat(secret): return decode err --- apisix/secret/gcp.lua | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/apisix/secret/gcp.lua b/apisix/secret/gcp.lua index 85b94b003aea..dc99604ebb42 100644 --- a/apisix/secret/gcp.lua +++ b/apisix/secret/gcp.lua @@ -197,12 +197,12 @@ local function get(conf, key) return res end - local ret = core.json.decode(res) - if not ret then - return nil, "failed to decode result, res: " .. res + local data, err = core.json.decode(res) + if not data then + return nil, "failed to decode result, res: " .. res .. ", err: " .. err end - return ret[sub_key] + return data[sub_key] end _M.get = get From 3e780e793b789612e6319531967f1e6064af6ec7 Mon Sep 17 00:00:00 2001 From: HuanXin-Chen <1056216208@qq.com> Date: Thu, 8 Aug 2024 10:06:38 +0800 Subject: [PATCH 10/24] cli(common): add the expact --- ci/common.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ci/common.sh b/ci/common.sh index 146b7aa5080a..054414cbd94c 100644 --- a/ci/common.sh +++ b/ci/common.sh @@ -180,6 +180,8 @@ linux_get_dependencies () { apt update apt install -y cpanminus build-essential libncurses5-dev libreadline-dev libssl-dev perl libpcre3 libpcre3-dev libldap2-dev apt-get install -y libyaml-dev + wget https://github.com/libexpat/libexpat/releases/download/R_2_5_0/expat-2.5.0.tar.gz + tar -xzf expat-2.5.0.tar.gz && cd expat-2.5.0 && ./configure && sudo make install && cd .. wget https://github.com/mikefarah/yq/releases/download/3.4.1/yq_linux_amd64 -O /usr/bin/yq && sudo chmod +x /usr/bin/yq } From 113a96ccdcf23f42ebfc8dcc1d0bc5c8c22b0bd6 Mon Sep 17 00:00:00 2001 From: HuanXin-Chen <1056216208@qq.com> Date: Thu, 8 Aug 2024 10:08:59 +0800 Subject: [PATCH 11/24] cli(common): remove the expact --- ci/common.sh | 2 -- 1 file changed, 2 deletions(-) diff --git a/ci/common.sh b/ci/common.sh index 054414cbd94c..146b7aa5080a 100644 --- a/ci/common.sh +++ b/ci/common.sh @@ -180,8 +180,6 @@ linux_get_dependencies () { apt update apt install -y cpanminus build-essential libncurses5-dev libreadline-dev libssl-dev perl libpcre3 libpcre3-dev libldap2-dev apt-get install -y libyaml-dev - wget https://github.com/libexpat/libexpat/releases/download/R_2_5_0/expat-2.5.0.tar.gz - tar -xzf expat-2.5.0.tar.gz && cd expat-2.5.0 && ./configure && sudo make install && cd .. wget https://github.com/mikefarah/yq/releases/download/3.4.1/yq_linux_amd64 -O /usr/bin/yq && sudo chmod +x /usr/bin/yq } From 1d017a71e34ed878c94f338c01e989805a8cb0cc Mon Sep 17 00:00:00 2001 From: HuanXin-Chen <1056216208@qq.com> Date: Fri, 9 Aug 2024 13:28:52 +0800 Subject: [PATCH 12/24] feat(secret): put the oauth into utils --- apisix/secret/gcp.lua | 9 +- apisix/utils/google-cloud-oauth.lua | 137 ++++++++++++++++++++++++++++ 2 files changed, 140 insertions(+), 6 deletions(-) create mode 100644 apisix/utils/google-cloud-oauth.lua diff --git a/apisix/secret/gcp.lua b/apisix/secret/gcp.lua index dc99604ebb42..3cc5117535d5 100644 --- a/apisix/secret/gcp.lua +++ b/apisix/secret/gcp.lua @@ -18,7 +18,7 @@ --- GCP Tools. local core = require("apisix.core") local http = require("resty.http") -local google_oauth = require("apisix.plugins.google-cloud-logging.oauth") +local google_oauth = require("apisix.utils.google-cloud-oauth") local sub = core.string.sub local find = core.string.find @@ -182,11 +182,8 @@ local function get(conf, key) end local sub_key = idx and sub(key, idx + 1) or nil - if not sub_key then - core.log.info("main: ", main_key) - else - core.log.info("main: ", main_key, " sub: ", sub_key) - end + + core.log.info("main: ", main_key, sub_key and ", sub: " .. sub_key or "") local res, err = make_request_to_gcp(conf, main_key) if not res then diff --git a/apisix/utils/google-cloud-oauth.lua b/apisix/utils/google-cloud-oauth.lua new file mode 100644 index 000000000000..a560bd43f7dd --- /dev/null +++ b/apisix/utils/google-cloud-oauth.lua @@ -0,0 +1,137 @@ +-- +-- 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. +-- + +local core = require("apisix.core") +local type = type +local setmetatable = setmetatable + +local ngx_update_time = ngx.update_time +local ngx_time = ngx.time +local ngx_encode_args = ngx.encode_args + +local http = require("resty.http") +local jwt = require("resty.jwt") + + +local function get_timestamp() + ngx_update_time() + return ngx_time() +end + + +local _M = {} + + +function _M:generate_access_token() + if not self.access_token or get_timestamp() > self.access_token_expire_time - 60 then + self:refresh_access_token() + end + return self.access_token +end + + +function _M:refresh_access_token() + local http_new = http.new() + local res, err = http_new:request_uri(self.token_uri, { + ssl_verify = self.ssl_verify, + method = "POST", + body = ngx_encode_args({ + grant_type = "urn:ietf:params:oauth:grant-type:jwt-bearer", + assertion = self:generate_jwt_token() + }), + headers = { + ["Content-Type"] = "application/x-www-form-urlencoded", + }, + }) + + if not res then + core.log.error("failed to refresh google oauth access token, ", err) + return + end + + if res.status ~= 200 then + core.log.error("failed to refresh google oauth access token: ", res.body) + return + end + + res, err = core.json.decode(res.body) + if not res then + core.log.error("failed to parse google oauth response data: ", err) + return + end + + self.access_token = res.access_token + self.access_token_type = res.token_type + self.access_token_expire_time = get_timestamp() + res.expires_in +end + + +function _M:generate_jwt_token() + local payload = core.json.encode({ + iss = self.client_email, + aud = self.token_uri, + scope = self.scope, + iat = get_timestamp(), + exp = get_timestamp() + (60 * 60) + }) + + local jwt_token = jwt:sign(self.private_key, { + header = { alg = "RS256", typ = "JWT" }, + payload = payload, + }) + + return jwt_token +end + + +function _M:new(config, ssl_verify) + local oauth = { + client_email = config.client_email, + private_key = config.private_key, + project_id = config.project_id, + token_uri = config.token_uri or "https://oauth2.googleapis.com/token", + auth_uri = config.auth_uri or "https://accounts.google.com/o/oauth2/auth", + entries_uri = config.entries_uri or "https://logging.googleapis.com/v2/entries:write", + access_token = nil, + access_token_type = nil, + access_token_expire_time = 0, + } + + oauth.ssl_verify = ssl_verify + + if config.scopes then + if type(config.scopes) == "string" then + oauth.scope = config.scopes + end + + if type(config.scopes) == "table" then + oauth.scope = core.table.concat(config.scopes, " ") + end + else + -- https://developers.google.com/identity/protocols/oauth2/scopes#logging + oauth.scope = core.table.concat({ "https://www.googleapis.com/auth/logging.read", + "https://www.googleapis.com/auth/logging.write", + "https://www.googleapis.com/auth/logging.admin", + "https://www.googleapis.com/auth/cloud-platform" }, " ") + end + + setmetatable(oauth, { __index = self }) + return oauth +end + + +return _M From 94640927847e2c0fd54250b7911fe0bbd63f4a16 Mon Sep 17 00:00:00 2001 From: HuanXin-Chen <1056216208@qq.com> Date: Tue, 13 Aug 2024 08:22:37 -0700 Subject: [PATCH 13/24] fix(secret): fix the test1 --- t/secret/gcp.t | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/t/secret/gcp.t b/t/secret/gcp.t index aed480c16c81..43e96dbb7ebe 100644 --- a/t/secret/gcp.t +++ b/t/secret/gcp.t @@ -34,9 +34,9 @@ __DATA__ {}, {auth_file = "123"}, {auth_file = 123}, - {auth_config = {}}, - {auth_config = {client_email = "client"}}, {auth_config = {client_email = "client", private_key = "private_key"}}, + {auth_config = {private_key = "private_key", project_id = "project_id"}}, + {auth_config = {client_email = "client", project_id = "project_id"}}, {auth_config = {client_email = "client", private_key = "private_key", project_id = "project_id"}}, {auth_config = {client_email = 1234, private_key = "private_key", project_id = "project_id"}}, {auth_config = {client_email = "client", private_key = 1234, project_id = "project_id"}}, @@ -64,9 +64,9 @@ GET /t value should match only one schema, but matches none done property "auth_file" validation failed: wrong type: expected string, got number -property "auth_config" validation failed: property "private_key" is required -property "auth_config" validation failed: property "private_key" is required property "auth_config" validation failed: property "project_id" is required +property "auth_config" validation failed: property "client_email" is required +property "auth_config" validation failed: property "private_key" is required done property "auth_config" validation failed: property "client_email" validation failed: wrong type: expected string, got number property "auth_config" validation failed: property "private_key" validation failed: wrong type: expected string, got number From c8ced2f576f46760686bff474fa478e6863ac20f Mon Sep 17 00:00:00 2001 From: HuanXin-Chen <1056216208@qq.com> Date: Sat, 24 Aug 2024 20:42:16 -0700 Subject: [PATCH 14/24] feat(secret): using serverless to test and fix some style --- apisix/secret/gcp.lua | 47 +++---- docs/en/latest/terminology/secret.md | 6 +- docs/zh/latest/terminology/secret.md | 4 +- t/secret/conf/success.json | 2 +- t/secret/gcp.t | 187 +++++++++++++++++++++++---- 5 files changed, 189 insertions(+), 57 deletions(-) diff --git a/apisix/secret/gcp.lua b/apisix/secret/gcp.lua index 3cc5117535d5..dc61a8b91c56 100644 --- a/apisix/secret/gcp.lua +++ b/apisix/secret/gcp.lua @@ -22,10 +22,9 @@ local google_oauth = require("apisix.utils.google-cloud-oauth") local sub = core.string.sub local find = core.string.find -local type = type local decode_base64 = ngx.decode_base64 -local lrucache = core.lrucache.new({ttl = 300, count= 16}) +local lrucache = core.lrucache.new({ttl = 300, count= 8}) local schema = { type = "object", @@ -41,12 +40,14 @@ local schema = { default = "https://oauth2.googleapis.com/token" }, scopes = { - type = "string", - default = "https://www.googleapis.com/auth/cloud-platform" + type = "array", + default = { + "https://www.googleapis.com/auth/cloud-platform" + } }, entries_uri = { type = "string", - default = "https://secretmanager.googleapis.com/v1/" + default = "https://secretmanager.googleapis.com/v1" }, }, required = { "client_email", "private_key", "project_id"} @@ -83,11 +84,17 @@ local function fetch_oauth_conf(conf) return nil, "config parse failure, data: " .. file_content .. ", err: " .. err end - if not config_tab.client_email or - not config_tab.private_key or - not config_tab.project_id or - not config_tab.token_uri then - return nil, "configuration is undefined, file: " .. conf.auth_file + local config = { + auth_config = { + client_email = config_tab.client_email, + private_key = config_tab.private_key, + project_id = config_tab.project_id + } + } + + local ok, err = core.schema.check(schema, config) + if not ok then + return nil, "config parse failure, file: " .. conf.auth_file .. ", err: " .. err end return config_tab @@ -105,14 +112,8 @@ local function get_secret(oauth, secrets_id) return nil, "failed to get google oauth token" end - local entries_uri - if oauth.entries_uri == "http://127.0.0.1:1980/google/secret/" then - entries_uri = oauth.entries_uri .. oauth.project_id .. "/" .. secrets_id - - else - entries_uri = oauth.entries_uri .. "projects/" .. oauth.project_id + local entries_uri = oauth.entries_uri .. "/projects/" .. oauth.project_id .. "/secrets/" .. secrets_id .. "/versions/latest:access" - end local res, err = http_new:request_uri(entries_uri, { ssl_verify = oauth.ssl_verify, @@ -141,16 +142,10 @@ local function get_secret(oauth, secrets_id) return nil, "invalid payload" end - local secret_encoded = payload.data - if type(secret_encoded) ~= "string" then - return nil, "invalid secret string" - end - - local secret = decode_base64(secret_encoded) - return secret + return decode_base64(payload.data) end -local function make_request_to_gcp(conf, key) +local function make_request_to_gcp(conf, secrets_id) local auth_config, err = fetch_oauth_conf(conf) if not auth_config then return nil, err @@ -163,7 +158,7 @@ local function make_request_to_gcp(conf, key) return nil, "failed to create oauth object, " .. err end - local secret, err = get_secret(oauth, key) + local secret, err = get_secret(oauth, secrets_id) if not secret then return nil, err end diff --git a/docs/en/latest/terminology/secret.md b/docs/en/latest/terminology/secret.md index 1d2eff0d813c..e78eb7a021d5 100644 --- a/docs/en/latest/terminology/secret.md +++ b/docs/en/latest/terminology/secret.md @@ -194,7 +194,7 @@ Through the above two steps, when the user request hits the `key-auth` plugin, t ## Use GCP Secrets Manager to manage secrets -Using the GCP Secrets Manager to manage secrets means you can store the secret information in the GCP service, and reference it using a specific format of variables when configuring plugins. APISIX currently supports integration with the GCP Secrets Manager, and the supported authentication method is [OAuth 2.0](https://developers.google.com/identity/protocols/oauth2?hl=zh-cn). +Using the GCP Secrets Manager to manage secrets means you can store the secret information in the GCP service, and reference it using a specific format of variables when configuring plugins. APISIX currently supports integration with the GCP Secrets Manager, and the supported authentication method is [OAuth 2.0](https://developers.google.com/identity/protocols/oauth2). ### Reference Format @@ -218,7 +218,7 @@ The reference format is the same as before: | auth_config.private_key | True | | Private key of the Google Cloud service account. | | auth_config.project_id | True | | Project ID in the Google Cloud service account. | | auth_config.token_uri | False | https://oauth2.googleapis.com/token | Token URI of the Google Cloud service account. | -| auth_config.entries_uri | False | https://secretmanager.googleapis.com/v1/ | The API access endpoint for the Google Secrets Manager. | +| auth_config.entries_uri | False | https://secretmanager.googleapis.com/v1 | The API access endpoint for the Google Secrets Manager. | | auth_config.scopes | False | https://www.googleapis.com/auth/cloud-platform | Access scopes of the Google Cloud service account. See [OAuth 2.0 Scopes for Google APIs](https://developers.google.com/identity/protocols/oauth2/scopes) | | auth_file | True | | Path to the Google Cloud service account authentication JSON file. Either `auth_config` or `auth_file` must be provided. | | ssl_verify | False | true | When set to `true`, enables SSL verification as mentioned in [OpenResty docs](https://github.com/openresty/lua-nginx-module#tcpsocksslhandshake). | @@ -238,7 +238,7 @@ curl http://127.0.0.1:9180/apisix/admin/secrets/gcp/1 \ "private_key": "private_key", "project_id": "apisix-project", "token_uri": "https://oauth2.googleapis.com/token", - "entries_uri": "https://secretmanager.googleapis.com/v1/", + "entries_uri": "https://secretmanager.googleapis.com/v1", "scopes": "https://www.googleapis.com/auth/cloud-platform" } }' diff --git a/docs/zh/latest/terminology/secret.md b/docs/zh/latest/terminology/secret.md index 48eb8688d842..dff7ec27fc3f 100644 --- a/docs/zh/latest/terminology/secret.md +++ b/docs/zh/latest/terminology/secret.md @@ -219,7 +219,7 @@ $secret://$manager/$id/$secret_name/$key | auth_config.private_key | 是 | | 谷歌服务帐号的私钥参数。 | | auth_config.project_id | 是 | | 谷歌服务帐号的项目 ID。 | | auth_config.token_uri | 否 | https://oauth2.googleapis.com/token | 请求谷歌服务帐户的令牌的 URI。 | -| auth_config.entries_uri | 否 | https://secretmanager.googleapis.com/v1/ | 谷歌密钥服务访问端点 API。 | +| auth_config.entries_uri | 否 | https://secretmanager.googleapis.com/v1 | 谷歌密钥服务访问端点 API。 | | auth_config.scopes | 否 | https://www.googleapis.com/auth/cloud-platform | 谷歌服务账号的访问范围,可参考 [OAuth 2.0 Scopes for Google APIs](https://developers.google.com/identity/protocols/oauth2/scopes)| | auth_file | 是 | | `auth_config` 和 `auth_file` 必须配置一个。 | | ssl_verify | 否 | true | 当设置为 `true` 时,启用 `SSL` 验证。 | @@ -239,7 +239,7 @@ curl http://127.0.0.1:9180/apisix/admin/secrets/gcp/1 \ "private_key": "private_key", "project_id": "apisix-project", "token_uri": "https://oauth2.googleapis.com/token", - "entries_uri": "https://secretmanager.googleapis.com/v1/", + "entries_uri": "https://secretmanager.googleapis.com/v1", "scopes": "https://www.googleapis.com/auth/cloud-platform" } }' diff --git a/t/secret/conf/success.json b/t/secret/conf/success.json index 4daae1b9be0d..294a4de0c7f6 100644 --- a/t/secret/conf/success.json +++ b/t/secret/conf/success.json @@ -5,6 +5,6 @@ "scopes": [ "https://www.googleapis.com/auth/cloud" ], - "entries_uri": "http://127.0.0.1:1980/google/secret/", + "entries_uri": "http://127.0.0.1:1984", "client_email": "email@apisix.iam.gserviceaccount.com" } diff --git a/t/secret/gcp.t b/t/secret/gcp.t index 43e96dbb7ebe..81e9b908cc8f 100644 --- a/t/secret/gcp.t +++ b/t/secret/gcp.t @@ -46,7 +46,7 @@ __DATA__ {auth_config = {client_email = "client", private_key = "private_key", project_id = "project_id", scopes = 1234}}, {auth_config = {client_email = "client", private_key = "private_key", project_id = "project_id", entries_uri = 1234}}, {auth_config = {client_email = "client", private_key = "private_key", project_id = "project_id", token_uri = "token_uri", - scopes = "scopes", entries_uri = "entries_uri"}, ssl_verify = true}, + scopes = {"scopes"}, entries_uri = "entries_uri"}, ssl_verify = true}, } local gcp = require("apisix.secret.gcp") local core = require("apisix.core") @@ -73,7 +73,7 @@ property "auth_config" validation failed: property "private_key" validation fail property "auth_config" validation failed: property "project_id" validation failed: wrong type: expected string, got number property "ssl_verify" validation failed: wrong type: expected boolean, got number property "auth_config" validation failed: property "token_uri" validation failed: wrong type: expected string, got number -property "auth_config" validation failed: property "scopes" validation failed: wrong type: expected string, got number +property "auth_config" validation failed: property "scopes" validation failed: wrong type: expected array, got number property "auth_config" validation failed: property "entries_uri" validation failed: wrong type: expected string, got number done @@ -117,8 +117,10 @@ Yp8D0aqsLEgwGrJQER26FPpKmyIwvcL+nm6q5W31PnU9AOC/WEkB6Zs58hsMzD2S kEJQcmfVew5mFXyxuEn3zA== -----END PRIVATE KEY-----]], project_id = "apisix", - token_uri = "http://127.0.0.1:2800/token", - scopes = "https://www.googleapis.com/auth/cloud-platform", + token_uri = "http://127.0.0.1:1980/token", + scopes = { + "https://www.googleapis.com/auth/cloud-platform" + }, }, } local data, err = gcp.get(conf, "/apisix") @@ -176,8 +178,10 @@ kEJQcmfVew5mFXyxuEn3zA== -----END PRIVATE KEY-----]], project_id = "apisix", token_uri = "http://127.0.0.1:1980/google/secret/token", - scopes = "https://www.googleapis.com/auth/cloud-platform", - entries_uri = "http://127.0.0.1:2800/google/secret/" + scopes = { + "https://www.googleapis.com/auth/cloud-platform" + }, + entries_uri = "http://127.0.0.1:1984" }, } @@ -243,7 +247,130 @@ nil -=== TEST 4: get value from gcp by auth_file(fetch_oatuh_conf failed, read failed) +=== TEST 4: setup route +--- 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": { + "serverless-pre-function": { + "phase": "rewrite", + "functions": [ + "return function(conf, ctx) + require('lib.server').google_secret_apisix_jack() + end" + ] + } + }, + "uri": "/projects/apisix/secrets/jack/versions/latest:access", + "upstream": { + "type": "roundrobin", + "nodes": { + "127.0.0.1:1980": 1 + } + } + }]] + ) + + if code >= 300 then + ngx.status = code + end + ngx.say(body) + } + } +--- request +GET /t +--- response_body +passed + + + +=== TEST 5: setup route +--- 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": { + "serverless-pre-function": { + "phase": "rewrite", + "functions": [ + "return function(conf, ctx) + require('lib.server').google_secret_apisix_error_jack() + end" + ] + } + }, + "uri": "/projects/apisix_error/secrets/jack/versions/latest:access", + "upstream": { + "type": "roundrobin", + "nodes": { + "127.0.0.1:1980": 1 + } + } + }]] + ) + + if code >= 300 then + ngx.status = code + end + ngx.say(body) + } + } +--- request +GET /t +--- response_body +passed + + + +=== TEST 6: setup route +--- config + location /t { + content_by_lua_block { + local t = require("lib.test_admin").test + local code, body = t('/apisix/admin/routes/3', + ngx.HTTP_PUT, + [[{ + "plugins": { + "serverless-pre-function": { + "phase": "rewrite", + "functions": [ + "return function(conf, ctx) + require('lib.server').google_secret_apisix_mysql() + end" + ] + } + }, + "uri": "/projects/apisix/secrets/mysql/versions/latest:access", + "upstream": { + "type": "roundrobin", + "nodes": { + "127.0.0.1:1980": 1 + } + } + }]] + ) + + if code >= 300 then + ngx.status = code + end + ngx.say(body) + } + } +--- request +GET /t +--- response_body +passed + + + +=== TEST 7: get value from gcp by auth_file(fetch_oatuh_conf failed, read failed) --- config location /t { content_by_lua_block { @@ -265,7 +392,7 @@ failed to retrtive data from gcp secret manager: failed to read configuration, f -=== TEST 5: get value from gcp by auth_file(fetch_oatuh_conf success) +=== TEST 8: get value from gcp by auth_file(fetch_oatuh_conf success) --- config location /t { content_by_lua_block { @@ -287,7 +414,7 @@ value -=== TEST 6: get value from gcp by auth_file(fetch_oatuh_conf failed, undefined) +=== TEST 9: get value from gcp by auth_file(fetch_oatuh_conf failed, undefined) --- config location /t { content_by_lua_block { @@ -305,11 +432,11 @@ value --- request GET /t --- response_body -failed to retrtive data from gcp secret manager: configuration is undefined, file: t/secret/conf/error.json +failed to retrtive data from gcp secret manager: config parse failure, file: t/secret/conf/error.json, err: property "auth_config" validation failed: property "client_email" is required -=== TEST 7: get json value from gcp +=== TEST 10: get json value from gcp --- config location /t { content_by_lua_block { @@ -347,8 +474,10 @@ kEJQcmfVew5mFXyxuEn3zA== -----END PRIVATE KEY-----]], project_id = "apisix", token_uri = "http://127.0.0.1:1980/google/secret/token", - scopes = "https://www.googleapis.com/auth/cloud", - entries_uri = "http://127.0.0.1:1980/google/secret/" + scopes = { + "https://www.googleapis.com/auth/cloud-platform" + }, + entries_uri = "http://127.0.0.1:1984" }, } local gcp = require("apisix.secret.gcp") @@ -366,7 +495,7 @@ value -=== TEST 8: get string value from gcp +=== TEST 11: get string value from gcp --- config location /t { content_by_lua_block { @@ -404,8 +533,10 @@ kEJQcmfVew5mFXyxuEn3zA== -----END PRIVATE KEY-----]], project_id = "apisix", token_uri = "http://127.0.0.1:1980/google/secret/token", - scopes = "https://www.googleapis.com/auth/cloud", - entries_uri = "http://127.0.0.1:1980/google/secret/" + scopes = { + "https://www.googleapis.com/auth/cloud-platform" + }, + entries_uri = "http://127.0.0.1:1984" }, } local gcp = require("apisix.secret.gcp") @@ -423,7 +554,7 @@ secret -=== TEST 9: get value from gcp(failed to get google oauth token) +=== TEST 12: get value from gcp(failed to get google oauth token) --- config location /t { content_by_lua_block { @@ -461,8 +592,10 @@ kEJQcmfVew5mFXyxuEn3zA== -----END PRIVATE KEY-----]], project_id = "apisix", token_uri = "http://127.0.0.1:1980/google/secret/token", - scopes = "https://www.googleapis.com/auth/root/cloud", - entries_uri = "http://127.0.0.1:1980/google/secret/" + scopes = { + "https://www.googleapis.com/auth/root/cloud-platform" + }, + entries_uri = "http://127.0.0.1:1984" }, } local gcp = require("apisix.secret.gcp") @@ -484,7 +617,7 @@ qr/\{\"error\"\:\"[\w+\s+]*\"\}/ -=== TEST 10: get value from gcp (not res) +=== TEST 13: get value from gcp (not res) --- config location /t { content_by_lua_block { @@ -522,8 +655,10 @@ kEJQcmfVew5mFXyxuEn3zA== -----END PRIVATE KEY-----]], project_id = "apisix_error", token_uri = "http://127.0.0.1:1980/google/secret/token", - scopes = "https://www.googleapis.com/auth/cloud", - entries_uri = "http://127.0.0.1:1980/google/" + scopes = { + "https://www.googleapis.com/auth/cloud-platform" + }, + entries_uri = "http://127.0.0.1:1984" }, } local gcp = require("apisix.secret.gcp") @@ -541,7 +676,7 @@ err -=== TEST 11: get value from gcp (res status ~= 200) +=== TEST 14: get value from gcp (res status ~= 200) --- config location /t { content_by_lua_block { @@ -579,8 +714,10 @@ kEJQcmfVew5mFXyxuEn3zA== -----END PRIVATE KEY-----]], project_id = "apisix_error", token_uri = "http://127.0.0.1:1980/google/secret/token", - scopes = "https://www.googleapis.com/auth/cloud", - entries_uri = "http://127.0.0.1:1980/google/secret/" + scopes = { + "https://www.googleapis.com/auth/cloud-platform" + }, + entries_uri = "http://127.0.0.1:1984" }, } local gcp = require("apisix.secret.gcp") From 717481025f6a7dceb62ad3393eafe68ba34ed185 Mon Sep 17 00:00:00 2001 From: HuanXin-Chen <1056216208@qq.com> Date: Sun, 1 Sep 2024 02:50:55 -0700 Subject: [PATCH 15/24] fix(secret): resolved the docs conflicts --- docs/en/latest/terminology/secret.md | 105 ++++++++++++++++++++++++++- docs/zh/latest/terminology/secret.md | 104 ++++++++++++++++++++++++++ 2 files changed, 208 insertions(+), 1 deletion(-) diff --git a/docs/en/latest/terminology/secret.md b/docs/en/latest/terminology/secret.md index e78eb7a021d5..25eecf7898fe 100644 --- a/docs/en/latest/terminology/secret.md +++ b/docs/en/latest/terminology/secret.md @@ -39,6 +39,7 @@ APISIX currently supports storing secrets in the following ways: - [Environment Variables](#use-environment-variables-to-manage-secrets) - [HashiCorp Vault](#use-hashicorp-vault-to-manage-secrets) +- [AWS Secrets Manager](#use-aws-secrets-manager-to-manage-secrets) - [GCP Secrets Manager](#use-gcp-secrets-manager-to-manage-secrets) You can use APISIX Secret functions by specifying format variables in the consumer configuration of the following plugins, such as `key-auth`. @@ -134,7 +135,7 @@ Using HashiCorp Vault to manage secrets means that you can store secrets informa $secret://$manager/$id/$secret_name/$key ``` -- manager: secrets management service, could be the HashiCorp Vault, AWS, GCP etc. +- manager: secrets management service, could be the HashiCorp Vault, AWS, etc. - id: APISIX Secrets resource ID, which needs to be consistent with the one specified when adding the APISIX Secrets resource - secret_name: the secret name in the secrets management service - key: the key corresponding to the secret in the secrets management service @@ -192,6 +193,108 @@ curl http://127.0.0.1:9180/apisix/admin/consumers \ Through the above two steps, when the user request hits the `key-auth` plugin, the real value of the key in the Vault will be obtained through the APISIX Secret component. +## Use AWS Secrets Manager to manage secrets + +Managing secrets with AWS Secrets Manager is a secure and convenient way to store and manage sensitive information. This method allows you to save secret information in AWS Secrets Manager and reference these secrets in a specific format when configuring APISIX plugins. + +APISIX currently supports two authentication methods: using [long-term credentials](https://docs.aws.amazon.com/sdkref/latest/guide/access-iam-users.html) and [short-term credentials](https://docs.aws.amazon.com/sdkref/latest/guide/access-temp-idc.html). + +### Usage + +``` +$secret://$manager/$id/$secret_name/$key +``` + +- manager: secrets management service, could be the HashiCorp Vault, AWS, etc. +- id: APISIX Secrets resource ID, which needs to be consistent with the one specified when adding the APISIX Secrets resource +- secret_name: the secret name in the secrets management service +- key: get the value of a property when the value of the secret is a JSON string + +### Required Parameters + +| Name | Required | Default Value | Description | +| --- | --- | --- | --- | +| access_key_id | True | | AWS Access Key ID | +| secret_access_key | True | | AWS Secret Access Key | +| session_token | False | | Temporary access credential information | +| region | False | us-east-1 | AWS Region | +| endpoint_url | False | https://secretsmanager.{region}.amazonaws.com | AWS Secret Manager URL | + +### Example: use in key-auth plugin + +Here, we use the key-auth plugin as an example to demonstrate how to manage secrets through AWS Secrets Manager. + +Step 1: Create the corresponding key in the AWS secrets manager. Here, [localstack](https://www.localstack.cloud/) is used for as the example environment, and you can use the following command: + +```shell +docker exec -i localstack sh -c "awslocal secretsmanager create-secret --name jack --description 'APISIX Secret' --secret-string '{\"auth-key\":\"value\"}'" +``` + +Step 2: Add APISIX Secrets resources through the Admin API, configure the connection information such as the address of AWS Secrets Manager. + +You can store the critical key information in environment variables to ensure the configuration information is secure, and reference it where it is used: + +```shell +export AWS_ACCESS_KEY_ID= +export AWS_SECRET_ACCESS_KEY= +export AWS_SESSION_TOKEN= +export AWS_REGION= +``` + +Alternatively, you can also specify all the information directly in the configuration: + +```shell +curl http://127.0.0.1:9180/apisix/admin/secrets/aws/1 \ +-H "X-API-KEY: $admin_key" -X PUT -d ' +{ + "endpoint_url": "http://127.0.0.1:4566", + "region": "us-east-1", + "access_key_id": "access", + "secret_access_key": "secret", + "session_token": "token" +}' +``` + +If you use APISIX Standalone mode, you can add the following configuration in `apisix.yaml` configuration file: + +```yaml +secrets: + - id: aws/1 + endpoint_url: http://127.0.0.1:4566 + region: us-east-1 + access_key_id: access + secret_access_key: secret + session_token: token +``` + +Step 3: Reference the APISIX Secrets resource in the `key-auth` plugin and fill in the key information: + +```shell +curl http://127.0.0.1:9180/apisix/admin/consumers \ +-H "X-API-KEY: $admin_key" -X PUT -d ' +{ + "username": "jack", + "plugins": { + "key-auth": { + "key": "$secret://aws/1/jack/auth-key" + } + } +}' +``` + +Through the above two steps, when the user request hits the `key-auth` plugin, the real value of the key in the Vault will be obtained through the APISIX Secret component. + +### Verification + +You can verify this with the following command: + +```shell +#Replace the following your_route with the actual route path. +curl -i http://127.0.0.1:9080/your_route -H 'apikey: value' +``` + +This will verify whether the `key-auth` plugin is correctly using the key from AWS Secrets Manager. + ## Use GCP Secrets Manager to manage secrets Using the GCP Secrets Manager to manage secrets means you can store the secret information in the GCP service, and reference it using a specific format of variables when configuring plugins. APISIX currently supports integration with the GCP Secrets Manager, and the supported authentication method is [OAuth 2.0](https://developers.google.com/identity/protocols/oauth2). diff --git a/docs/zh/latest/terminology/secret.md b/docs/zh/latest/terminology/secret.md index dff7ec27fc3f..4cb72c51536e 100644 --- a/docs/zh/latest/terminology/secret.md +++ b/docs/zh/latest/terminology/secret.md @@ -39,6 +39,7 @@ APISIX 目前支持通过以下方式存储密钥: - [环境变量](#使用环境变量管理密钥) - [HashiCorp Vault](#使用-vault-管理密钥) +- [AWS Secrets Manager](#使用-aws-secrets-manager-管理密钥) - [GCP Secrets Manager](#使用-gcp-secrets-manager-管理密钥) 你可以在以下插件的 consumer 配置中通过指定格式的变量来使用 APISIX Secret 功能,比如 `key-auth` 插件。 @@ -193,6 +194,109 @@ curl http://127.0.0.1:9180/apisix/admin/consumers \ 通过上面两步操作,当用户请求命中 `key-auth` 插件时,会通过 APISIX Secret 组件获取到 key 在 Vault 中的真实值。 +## 使用 AWS Secrets Manager 管理密钥 + +使用 AWS Secrets Manager 管理密钥是一种安全且便捷的方式来存储和管理敏感信息。通过这种方式,你可以将密钥信息保存在 AWS Secret Manager 中,并在配置 APISIX 插件时通过特定的格式引用这些密钥。 + +APISIX 目前支持两种访问方式: [长期凭证的访问方式](https://docs.aws.amazon.com/zh_cn/sdkref/latest/guide/access-iam-users.html) 和 [短期凭证的访问方式](https://docs.aws.amazon.com/zh_cn/sdkref/latest/guide/access-temp-idc.html)。 + +### 引用方式 + +在 APISIX 中引用密钥时,可以使用以下格式: + +``` +$secret://$manager/$id/$secret_name/$key +``` + +- manager: 密钥管理服务,可以是 Vault、AWS 等 +- APISIX Secret 资源 ID,需要与添加 APISIX Secret 资源时指定的 ID 保持一致 +- secret_name: 密钥管理服务中的密钥名称 +- key:当密钥的值是 JSON 字符串时,获取某个属性的值 + +### 相关参数 + +| 名称 | 必选项 | 默认值 | 描述 | +| --- | --- | --- | --- | +| access_key_id | 是 | | AWS 访问密钥 ID | +| secret_access_key | 是 | | AWS 访问密钥 | +| session_token | 否 | | 临时访问凭证信息 | +| region | 否 | us-east-1 | AWS 区域 | +| endpoint_url | 否 | https://secretsmanager.{region}.amazonaws.com | AWS Secret Manager 地址 | + +### 示例:在 key-auth 插件中使用 + +这里以 key-auth 插件的使用为例,展示如何通过 AWS Secret Manager 管理密钥: + +第一步:在 AWS Secret Manager 中创建对应的密钥,这里使用 [localstack](https://www.localstack.cloud/) 模拟,可以使用如下命令: + +```shell +docker exec -i localstack sh -c "awslocal secretsmanager create-secret --name jack --description 'APISIX Secret' --secret-string '{\"auth-key\":\"value\"}'" +``` + +第二步:通过 Admin API 添加 Secret 资源,配置 AWS Secret Manager 的地址等连接信息: + +你可以在环境变量中存储关键密钥信息,保证配置信息是安全的,在使用到地方进行引用: + +```shell +export AWS_ACCESS_KEY_ID= +export AWS_SECRET_ACCESS_KEY= +export AWS_SESSION_TOKEN= +``` + +当然,你也可以通过直接在配置中指定所有信息内容: + +```shell +curl http://127.0.0.1:9180/apisix/admin/secrets/aws/1 \ +-H "X-API-KEY: $admin_key" -X PUT -d ' +{ + "endpoint_url": "http://127.0.0.1:4566", + "region": "us-east-1", + "access_key_id": "access", + "secret_access_key": "secret", + "session_token": "token" +}' +``` + +如果使用 APISIX Standalone 版本,则可以在 `apisix.yaml` 文件中添加如下配置: + +```yaml +secrets: + - id: aws/1 + endpoint_url: http://127.0.0.1:4566 + region: us-east-1 + access_key_id: access + secret_access_key: secret + session_token: token +``` + +第三步:在 `key-auth` 插件中引用 APISIX Secret 资源,填充秘钥信息: + +```shell +curl http://127.0.0.1:9180/apisix/admin/consumers \ +-H "X-API-KEY: $admin_key" -X PUT -d ' +{ + "username": "jack", + "plugins": { + "key-auth": { + "key": "$secret://aws/1/jack/auth-key" + } + } +}' +``` + +通过上面两步操作,当用户请求命中 `key-auth` 插件时,会通过 APISIX Secret 组件获取到 key 在 AWS Secret Manager 中的真实值。 + +### 验证 + +你可以通过如下指令进行验证: + +```shell +# 示例:将下面的 your_route 替换为实际的路由路径 +curl -i http://127.0.0.1:9080/your_route -H 'apikey: value' +``` + +这将验证 key-auth 插件是否正确地使用 AWS Secret Manager 中的密钥。 + ## 使用 GCP Secrets Manager 管理密钥 使用 GCP Secret Manager 来管理密钥意味着你可以将密钥信息保存在 GCP 服务中,在配置插件时通过特定格式的变量来引用。APISIX 目前支持对接 GCP Secret Manager, 所支持的验证方式是[OAuth 2.0](https://developers.google.com/identity/protocols/oauth2?hl=zh-cn)。 From 626654f0ef048a50cef55781597fe5c172beaf5d Mon Sep 17 00:00:00 2001 From: HuanXin-Chen <1056216208@qq.com> Date: Sun, 1 Sep 2024 02:56:35 -0700 Subject: [PATCH 16/24] style(secret): _M.get and test case --- apisix/secret/gcp.lua | 4 ++-- t/secret/gcp.t | 7 +++++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/apisix/secret/gcp.lua b/apisix/secret/gcp.lua index dc61a8b91c56..1a237d9df3c8 100644 --- a/apisix/secret/gcp.lua +++ b/apisix/secret/gcp.lua @@ -166,7 +166,7 @@ local function make_request_to_gcp(conf, secrets_id) return secret end -local function get(conf, key) +function _M.get(conf, key) core.log.info("fetching data from gcp for key: ", key) local idx = find(key, '/') @@ -197,6 +197,6 @@ local function get(conf, key) return data[sub_key] end -_M.get = get + return _M diff --git a/t/secret/gcp.t b/t/secret/gcp.t index 81e9b908cc8f..1f7eea6a76c4 100644 --- a/t/secret/gcp.t +++ b/t/secret/gcp.t @@ -239,11 +239,14 @@ kEJQcmfVew5mFXyxuEn3zA== local secret = require("apisix.secret") local value = secret.fetch_by_uri("$secret://gcp/mysecret/jack/key") - ngx.say(value) + if value then + ngx.say("secret value: ", value) + end + ngx.say("all done") } } --- response_body -nil +all done From 873bd12ba27064f009a70aab7350ccb9d9a742e1 Mon Sep 17 00:00:00 2001 From: HuanXin-Chen <1056216208@qq.com> Date: Thu, 5 Sep 2024 09:40:09 -0700 Subject: [PATCH 17/24] fix(secret): just code style --- apisix/secret/gcp.lua | 29 +++++++++++++---------------- apisix/utils/google-cloud-oauth.lua | 11 +++++------ t/secret/gcp.t | 6 +++--- 3 files changed, 21 insertions(+), 25 deletions(-) diff --git a/apisix/secret/gcp.lua b/apisix/secret/gcp.lua index 1a237d9df3c8..81ae9820db50 100644 --- a/apisix/secret/gcp.lua +++ b/apisix/secret/gcp.lua @@ -16,15 +16,15 @@ -- --- GCP Tools. -local core = require("apisix.core") -local http = require("resty.http") +local core = require("apisix.core") +local http = require("resty.http") local google_oauth = require("apisix.utils.google-cloud-oauth") -local sub = core.string.sub -local find = core.string.find +local str_sub = core.string.sub +local str_find = core.string.find local decode_base64 = ngx.decode_base64 - -local lrucache = core.lrucache.new({ttl = 300, count= 8}) + +local lrucache = core.lrucache.new({ ttl = 300, count= 8 }) local schema = { type = "object", @@ -50,7 +50,7 @@ local schema = { default = "https://secretmanager.googleapis.com/v1" }, }, - required = { "client_email", "private_key", "project_id"} + required = { "client_email", "private_key", "project_id" } }, ssl_verify = { type = "boolean", @@ -62,7 +62,6 @@ local schema = { { required = { "auth_config" } }, { required = { "auth_file" } }, }, - encrypt_fields = {"auth_config.private_key"}, } local _M = { @@ -100,9 +99,6 @@ local function fetch_oauth_conf(conf) return config_tab end -local function create_oauth_object(auth_config, ssl_verify) - return google_oauth:new(auth_config, ssl_verify) -end local function get_secret(oauth, secrets_id) local http_new = http.new() @@ -145,6 +141,7 @@ local function get_secret(oauth, secrets_id) return decode_base64(payload.data) end + local function make_request_to_gcp(conf, secrets_id) local auth_config, err = fetch_oauth_conf(conf) if not auth_config then @@ -153,7 +150,7 @@ local function make_request_to_gcp(conf, secrets_id) local lru_key = auth_config.client_email .. "#" .. auth_config.project_id - local oauth, err = lrucache(lru_key, "gcp", create_oauth_object, auth_config, conf.ssl_verify) + local oauth, err = lrucache(lru_key, "gcp", google_oauth.new, auth_config, conf.ssl_verify) if not oauth then return nil, "failed to create oauth object, " .. err end @@ -166,17 +163,18 @@ local function make_request_to_gcp(conf, secrets_id) return secret end + function _M.get(conf, key) core.log.info("fetching data from gcp for key: ", key) - local idx = find(key, '/') + local idx = str_find(key, '/') - local main_key = idx and sub(key, 1, idx - 1) or key + local main_key = idx and str_sub(key, 1, idx - 1) or key if main_key == "" then return nil, "can't find main key, key: " .. key end - local sub_key = idx and sub(key, idx + 1) or nil + local sub_key = idx and str_sub(key, idx + 1) or nil core.log.info("main: ", main_key, sub_key and ", sub: " .. sub_key or "") @@ -198,5 +196,4 @@ function _M.get(conf, key) end - return _M diff --git a/apisix/utils/google-cloud-oauth.lua b/apisix/utils/google-cloud-oauth.lua index a560bd43f7dd..ed8c9e5b297a 100644 --- a/apisix/utils/google-cloud-oauth.lua +++ b/apisix/utils/google-cloud-oauth.lua @@ -36,7 +36,7 @@ end local _M = {} -function _M:generate_access_token() +function _M.generate_access_token(self) if not self.access_token or get_timestamp() > self.access_token_expire_time - 60 then self:refresh_access_token() end @@ -44,7 +44,7 @@ function _M:generate_access_token() end -function _M:refresh_access_token() +function _M.refresh_access_token(self) local http_new = http.new() local res, err = http_new:request_uri(self.token_uri, { ssl_verify = self.ssl_verify, @@ -80,7 +80,7 @@ function _M:refresh_access_token() end -function _M:generate_jwt_token() +function _M.generate_jwt_token(self) local payload = core.json.encode({ iss = self.client_email, aud = self.token_uri, @@ -98,7 +98,7 @@ function _M:generate_jwt_token() end -function _M:new(config, ssl_verify) +function _M.new(config, ssl_verify) local oauth = { client_email = config.client_email, private_key = config.private_key, @@ -129,8 +129,7 @@ function _M:new(config, ssl_verify) "https://www.googleapis.com/auth/cloud-platform" }, " ") end - setmetatable(oauth, { __index = self }) - return oauth + return setmetatable(oauth, { __index = _M }) end diff --git a/t/secret/gcp.t b/t/secret/gcp.t index 1f7eea6a76c4..65fa11ccee29 100644 --- a/t/secret/gcp.t +++ b/t/secret/gcp.t @@ -250,7 +250,7 @@ all done -=== TEST 4: setup route +=== TEST 4: setup route (/projects/apisix/secrets/jack/versions/latest:access) --- config location /t { content_by_lua_block { @@ -291,7 +291,7 @@ passed -=== TEST 5: setup route +=== TEST 5: setup route (/projects/apisix_error/secrets/jack/versions/latest:access) --- config location /t { content_by_lua_block { @@ -332,7 +332,7 @@ passed -=== TEST 6: setup route +=== TEST 6: setup route (/projects/apisix/secrets/mysql/versions/latest:access) --- config location /t { content_by_lua_block { From 23bb722c04374d4388c428638e06513c5e299a04 Mon Sep 17 00:00:00 2001 From: HuanXin-Chen <1056216208@qq.com> Date: Fri, 6 Sep 2024 09:28:45 -0700 Subject: [PATCH 18/24] fix(secret): scope should not be used in the plural --- apisix/secret/gcp.lua | 4 ++-- apisix/utils/google-cloud-oauth.lua | 16 +++++----------- t/lib/server.lua | 8 ++++---- t/secret/conf/error.json | 2 +- t/secret/conf/success.json | 2 +- t/secret/gcp.t | 22 +++++++++++----------- 6 files changed, 24 insertions(+), 30 deletions(-) diff --git a/apisix/secret/gcp.lua b/apisix/secret/gcp.lua index 81ae9820db50..36376fb689c8 100644 --- a/apisix/secret/gcp.lua +++ b/apisix/secret/gcp.lua @@ -24,7 +24,7 @@ local str_sub = core.string.sub local str_find = core.string.find local decode_base64 = ngx.decode_base64 -local lrucache = core.lrucache.new({ ttl = 300, count= 8 }) +local lrucache = core.lrucache.new({ ttl = 300, count = 8 }) local schema = { type = "object", @@ -39,7 +39,7 @@ local schema = { type = "string", default = "https://oauth2.googleapis.com/token" }, - scopes = { + scope = { type = "array", default = { "https://www.googleapis.com/auth/cloud-platform" diff --git a/apisix/utils/google-cloud-oauth.lua b/apisix/utils/google-cloud-oauth.lua index ed8c9e5b297a..59f34f382466 100644 --- a/apisix/utils/google-cloud-oauth.lua +++ b/apisix/utils/google-cloud-oauth.lua @@ -113,20 +113,14 @@ function _M.new(config, ssl_verify) oauth.ssl_verify = ssl_verify - if config.scopes then - if type(config.scopes) == "string" then - oauth.scope = config.scopes + if config.scope then + if type(config.scope) == "string" then + oauth.scope = config.scope end - if type(config.scopes) == "table" then - oauth.scope = core.table.concat(config.scopes, " ") + if type(config.scope) == "table" then + oauth.scope = core.table.concat(config.scope, " ") end - else - -- https://developers.google.com/identity/protocols/oauth2/scopes#logging - oauth.scope = core.table.concat({ "https://www.googleapis.com/auth/logging.read", - "https://www.googleapis.com/auth/logging.write", - "https://www.googleapis.com/auth/logging.admin", - "https://www.googleapis.com/auth/cloud-platform" }, " ") end return setmetatable(oauth, { __index = _M }) diff --git a/t/lib/server.lua b/t/lib/server.lua index f8095505e665..309873636302 100644 --- a/t/lib/server.lua +++ b/t/lib/server.lua @@ -576,7 +576,7 @@ function _M.google_secret_token() verify.payload.scope:find(access_scopes) if not scopes_valid then ngx.status = 403 - ngx.say(json_encode({ error = "no access to this scopes" })) + ngx.say(json_encode({ error = "no access to this scope" })) return end @@ -623,7 +623,7 @@ function _M.google_secret_apisix_jack() verify.payload.scope:find(access_scopes) if not scopes_valid then ngx.status = 403 - ngx.say(json_encode({ error = "no access to this scopes" })) + ngx.say(json_encode({ error = "no access to this scope" })) return end @@ -672,7 +672,7 @@ function _M.google_secret_apisix_error_jack() verify.payload.scope:find(access_scopes) if not scopes_valid then ngx.status = 403 - ngx.say(json_encode({ error = "no access to this scopes" })) + ngx.say(json_encode({ error = "no access to this scope" })) return end @@ -721,7 +721,7 @@ function _M.google_secret_apisix_mysql() verify.payload.scope:find(access_scopes) if not scopes_valid then ngx.status = 403 - ngx.say(json_encode({ error = "no access to this scopes" })) + ngx.say(json_encode({ error = "no access to this scope" })) return end diff --git a/t/secret/conf/error.json b/t/secret/conf/error.json index 8de2535aa8c0..3d0bb6295393 100644 --- a/t/secret/conf/error.json +++ b/t/secret/conf/error.json @@ -2,7 +2,7 @@ "private_key": "-----BEGIN PRIVATE KEY-----\nMIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDDzrFwnA3EvYyR\naeMgaLD3hBjvxKrz10uox1X8q7YYhf2ViRtLRUMa2bEMYksE5hbhwpNf6mKAnLOC\nUuAT6cPPdUl/agKpJXviBPIR2LuzD17WsLJHp1HxUDssSkgfCaGcOGGNfLUhhIpF\n2JUctLmxiZoAZySlSjcwupSuDJ0aPm0XO8r9H8Qu5kF2Vkz5e5bFivLTmvzrQTe4\nv5V1UI6hThElCSeUmdNF3uG3wopxlvq4zXgLTnuLbrNf/Gc4mlpV+UDgTISj32Ep\nAB2vxKEbvQw4ti8YJnGXWjxLerhfrszFw+V8lpeduiDYA44ZFoVqvzxeIsVZNtcw\nIu7PvEPNAgMBAAECggEAVpyN9m7A1F631/aLheFpLgMbeKt4puV7zQtnaJ2XrZ9P\nPR7pmNDpTu4uF3k/D8qrIm+L+uhVa+hkquf3wDct6w1JVnfQ93riImbnoKdK13ic\nDcEZCwLjByfjFMNCxZ/gAZca55fbExlqhFy6EHmMjhB8s2LsXcTHRuGxNI/Vyi49\nsxECibe0U53aqdJbVWrphIS67cpwl4TUkN6mrHsNuDYNJ9dgkpapoqp4FTFQsBqC\nafOK5qgJ68dWZ47FBUng+AZjdCncqAIuJxxItGVQP6YPsFs+OXcivIVHJr363TpC\nl85FfdvqWV5OGBbwSKhNwiTNUVvfSQVmtURGWG/HbQKBgQD4gZ1z9+Lx19kT9WTz\nlw93lxso++uhAPDTKviyWSRoEe5aN3LCd4My+/Aj+sk4ON/s2BV3ska5Im93j+vC\nrCv3uPn1n2jUhWuJ3bDqipeTW4n/CQA2m/8vd26TMk22yOkkqw2MIA8sjJ//SD7g\ntdG7up6DgGMP4hgbO89uGU7DAwKBgQDJtkKd0grh3u52Foeh9YaiAgYRwc65IE16\nUyD1OJxIuX/dYQDLlo5KyyngFa1ZhWIs7qC7r3xXH+10kfJY+Q+5YMjmZjlL8SR1\nUjqd02R9F2//6OeswyReachJZbZdtiEw3lPa4jVFYfhSe0M2ZPxMwvoXb25eyCNI\n1lYjSKq87wKBgHnLTNghjeDp4UKe6rNYPgRm0rDrhziJtX5JeUov1mALKb6dnmkh\nGfRK9g8sQqKDfXwfC6Z2gaMK9YaryujGaWYoCpoPXtmJ6oLPXH4XHuLh4mhUiP46\nxn8FEfSimuQS4/FMxH8A128GHQSI7AhGFFzlwfrBWcvXC+mNDsTvMmLxAoGARc+4\nupppfccETQZ7JsitMgD1TMwA2f2eEwoWTAitvlXFNT9PYSbYVHaAJbga6PLLCbYF\nFzAjHpxEOKYSdEyu7n/ayDL0/Z2V+qzc8KarDsg/0RgwppBbU/nUgeKb/U79qcYo\ny4ai3UKNCS70Ei1dTMvmdpnwXwlxfNIBufB6dy0CgYBMYq9Lc31GkC6PcGEEbx6W\nvjImOadWZbuOVnvEQjb5XCdcOsWsMcg96PtoeuyyHmhnEF1GsMzcIdQv/PHrvYpK\nYp8D0aqsLEgwGrJQER26FPpKmyIwvcL+nm6q5W31PnU9AOC/WEkB6Zs58hsMzD2S\nkEJQcmfVew5mFXyxuEn3zA==\n-----END PRIVATE KEY-----", "project_id": "apisix", "token_uri": "http://127.0.0.1:1980/google/logging/token", - "scopes": [ + "scope": [ "https://apisix.apache.org/logs:admin" ], "entries_uri": "http://127.0.0.1:1980/google/logging/entries" diff --git a/t/secret/conf/success.json b/t/secret/conf/success.json index 294a4de0c7f6..d9cfbc3c816e 100644 --- a/t/secret/conf/success.json +++ b/t/secret/conf/success.json @@ -2,7 +2,7 @@ "private_key": "-----BEGIN PRIVATE KEY-----\nMIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDDzrFwnA3EvYyR\naeMgaLD3hBjvxKrz10uox1X8q7YYhf2ViRtLRUMa2bEMYksE5hbhwpNf6mKAnLOC\nUuAT6cPPdUl/agKpJXviBPIR2LuzD17WsLJHp1HxUDssSkgfCaGcOGGNfLUhhIpF\n2JUctLmxiZoAZySlSjcwupSuDJ0aPm0XO8r9H8Qu5kF2Vkz5e5bFivLTmvzrQTe4\nv5V1UI6hThElCSeUmdNF3uG3wopxlvq4zXgLTnuLbrNf/Gc4mlpV+UDgTISj32Ep\nAB2vxKEbvQw4ti8YJnGXWjxLerhfrszFw+V8lpeduiDYA44ZFoVqvzxeIsVZNtcw\nIu7PvEPNAgMBAAECggEAVpyN9m7A1F631/aLheFpLgMbeKt4puV7zQtnaJ2XrZ9P\nPR7pmNDpTu4uF3k/D8qrIm+L+uhVa+hkquf3wDct6w1JVnfQ93riImbnoKdK13ic\nDcEZCwLjByfjFMNCxZ/gAZca55fbExlqhFy6EHmMjhB8s2LsXcTHRuGxNI/Vyi49\nsxECibe0U53aqdJbVWrphIS67cpwl4TUkN6mrHsNuDYNJ9dgkpapoqp4FTFQsBqC\nafOK5qgJ68dWZ47FBUng+AZjdCncqAIuJxxItGVQP6YPsFs+OXcivIVHJr363TpC\nl85FfdvqWV5OGBbwSKhNwiTNUVvfSQVmtURGWG/HbQKBgQD4gZ1z9+Lx19kT9WTz\nlw93lxso++uhAPDTKviyWSRoEe5aN3LCd4My+/Aj+sk4ON/s2BV3ska5Im93j+vC\nrCv3uPn1n2jUhWuJ3bDqipeTW4n/CQA2m/8vd26TMk22yOkkqw2MIA8sjJ//SD7g\ntdG7up6DgGMP4hgbO89uGU7DAwKBgQDJtkKd0grh3u52Foeh9YaiAgYRwc65IE16\nUyD1OJxIuX/dYQDLlo5KyyngFa1ZhWIs7qC7r3xXH+10kfJY+Q+5YMjmZjlL8SR1\nUjqd02R9F2//6OeswyReachJZbZdtiEw3lPa4jVFYfhSe0M2ZPxMwvoXb25eyCNI\n1lYjSKq87wKBgHnLTNghjeDp4UKe6rNYPgRm0rDrhziJtX5JeUov1mALKb6dnmkh\nGfRK9g8sQqKDfXwfC6Z2gaMK9YaryujGaWYoCpoPXtmJ6oLPXH4XHuLh4mhUiP46\nxn8FEfSimuQS4/FMxH8A128GHQSI7AhGFFzlwfrBWcvXC+mNDsTvMmLxAoGARc+4\nupppfccETQZ7JsitMgD1TMwA2f2eEwoWTAitvlXFNT9PYSbYVHaAJbga6PLLCbYF\nFzAjHpxEOKYSdEyu7n/ayDL0/Z2V+qzc8KarDsg/0RgwppBbU/nUgeKb/U79qcYo\ny4ai3UKNCS70Ei1dTMvmdpnwXwlxfNIBufB6dy0CgYBMYq9Lc31GkC6PcGEEbx6W\nvjImOadWZbuOVnvEQjb5XCdcOsWsMcg96PtoeuyyHmhnEF1GsMzcIdQv/PHrvYpK\nYp8D0aqsLEgwGrJQER26FPpKmyIwvcL+nm6q5W31PnU9AOC/WEkB6Zs58hsMzD2S\nkEJQcmfVew5mFXyxuEn3zA==\n-----END PRIVATE KEY-----", "project_id": "apisix", "token_uri": "http://127.0.0.1:1980/google/secret/token", - "scopes": [ + "scope": [ "https://www.googleapis.com/auth/cloud" ], "entries_uri": "http://127.0.0.1:1984", diff --git a/t/secret/gcp.t b/t/secret/gcp.t index 65fa11ccee29..163bda317156 100644 --- a/t/secret/gcp.t +++ b/t/secret/gcp.t @@ -43,10 +43,10 @@ __DATA__ {auth_config = {client_email = "client", private_key = "private_key", project_id = 1234}}, {auth_config = {client_email = "client", private_key = "private_key", project_id = "project_id"}, ssl_verify = 1234}, {auth_config = {client_email = "client", private_key = "private_key", project_id = "project_id", token_uri = 1234}}, - {auth_config = {client_email = "client", private_key = "private_key", project_id = "project_id", scopes = 1234}}, + {auth_config = {client_email = "client", private_key = "private_key", project_id = "project_id", scope = 1234}}, {auth_config = {client_email = "client", private_key = "private_key", project_id = "project_id", entries_uri = 1234}}, {auth_config = {client_email = "client", private_key = "private_key", project_id = "project_id", token_uri = "token_uri", - scopes = {"scopes"}, entries_uri = "entries_uri"}, ssl_verify = true}, + scope = {"scope"}, entries_uri = "entries_uri"}, ssl_verify = true}, } local gcp = require("apisix.secret.gcp") local core = require("apisix.core") @@ -73,7 +73,7 @@ property "auth_config" validation failed: property "private_key" validation fail property "auth_config" validation failed: property "project_id" validation failed: wrong type: expected string, got number property "ssl_verify" validation failed: wrong type: expected boolean, got number property "auth_config" validation failed: property "token_uri" validation failed: wrong type: expected string, got number -property "auth_config" validation failed: property "scopes" validation failed: wrong type: expected array, got number +property "auth_config" validation failed: property "scope" validation failed: wrong type: expected array, got number property "auth_config" validation failed: property "entries_uri" validation failed: wrong type: expected string, got number done @@ -118,7 +118,7 @@ kEJQcmfVew5mFXyxuEn3zA== -----END PRIVATE KEY-----]], project_id = "apisix", token_uri = "http://127.0.0.1:1980/token", - scopes = { + scope = { "https://www.googleapis.com/auth/cloud-platform" }, }, @@ -178,7 +178,7 @@ kEJQcmfVew5mFXyxuEn3zA== -----END PRIVATE KEY-----]], project_id = "apisix", token_uri = "http://127.0.0.1:1980/google/secret/token", - scopes = { + scope = { "https://www.googleapis.com/auth/cloud-platform" }, entries_uri = "http://127.0.0.1:1984" @@ -477,7 +477,7 @@ kEJQcmfVew5mFXyxuEn3zA== -----END PRIVATE KEY-----]], project_id = "apisix", token_uri = "http://127.0.0.1:1980/google/secret/token", - scopes = { + scope = { "https://www.googleapis.com/auth/cloud-platform" }, entries_uri = "http://127.0.0.1:1984" @@ -536,7 +536,7 @@ kEJQcmfVew5mFXyxuEn3zA== -----END PRIVATE KEY-----]], project_id = "apisix", token_uri = "http://127.0.0.1:1980/google/secret/token", - scopes = { + scope = { "https://www.googleapis.com/auth/cloud-platform" }, entries_uri = "http://127.0.0.1:1984" @@ -595,7 +595,7 @@ kEJQcmfVew5mFXyxuEn3zA== -----END PRIVATE KEY-----]], project_id = "apisix", token_uri = "http://127.0.0.1:1980/google/secret/token", - scopes = { + scope = { "https://www.googleapis.com/auth/root/cloud-platform" }, entries_uri = "http://127.0.0.1:1984" @@ -616,7 +616,7 @@ failed to retrtive data from gcp secret manager: failed to get google oauth toke --- grep_error_log eval qr/\{\"error\"\:\"[\w+\s+]*\"\}/ --- grep_error_log_out -{"error":"no access to this scopes"} +{"error":"no access to this scope"} @@ -658,7 +658,7 @@ kEJQcmfVew5mFXyxuEn3zA== -----END PRIVATE KEY-----]], project_id = "apisix_error", token_uri = "http://127.0.0.1:1980/google/secret/token", - scopes = { + scope = { "https://www.googleapis.com/auth/cloud-platform" }, entries_uri = "http://127.0.0.1:1984" @@ -717,7 +717,7 @@ kEJQcmfVew5mFXyxuEn3zA== -----END PRIVATE KEY-----]], project_id = "apisix_error", token_uri = "http://127.0.0.1:1980/google/secret/token", - scopes = { + scope = { "https://www.googleapis.com/auth/cloud-platform" }, entries_uri = "http://127.0.0.1:1984" From 02a19104897e6136dba9b64134905c4ca02d6583 Mon Sep 17 00:00:00 2001 From: HuanXin-Chen <1056216208@qq.com> Date: Fri, 6 Sep 2024 10:16:22 -0700 Subject: [PATCH 19/24] docs(secret): fix the example --- docs/en/latest/terminology/secret.md | 4 ++-- docs/zh/latest/terminology/secret.md | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/en/latest/terminology/secret.md b/docs/en/latest/terminology/secret.md index 25eecf7898fe..94c1b8843b0a 100644 --- a/docs/en/latest/terminology/secret.md +++ b/docs/en/latest/terminology/secret.md @@ -322,7 +322,7 @@ The reference format is the same as before: | auth_config.project_id | True | | Project ID in the Google Cloud service account. | | auth_config.token_uri | False | https://oauth2.googleapis.com/token | Token URI of the Google Cloud service account. | | auth_config.entries_uri | False | https://secretmanager.googleapis.com/v1 | The API access endpoint for the Google Secrets Manager. | -| auth_config.scopes | False | https://www.googleapis.com/auth/cloud-platform | Access scopes of the Google Cloud service account. See [OAuth 2.0 Scopes for Google APIs](https://developers.google.com/identity/protocols/oauth2/scopes) | +| auth_config.scope | False | https://www.googleapis.com/auth/cloud-platform | Access scopes of the Google Cloud service account. See [OAuth 2.0 Scopes for Google APIs](https://developers.google.com/identity/protocols/oauth2/scopes) | | auth_file | True | | Path to the Google Cloud service account authentication JSON file. Either `auth_config` or `auth_file` must be provided. | | ssl_verify | False | true | When set to `true`, enables SSL verification as mentioned in [OpenResty docs](https://github.com/openresty/lua-nginx-module#tcpsocksslhandshake). | @@ -342,7 +342,7 @@ curl http://127.0.0.1:9180/apisix/admin/secrets/gcp/1 \ "project_id": "apisix-project", "token_uri": "https://oauth2.googleapis.com/token", "entries_uri": "https://secretmanager.googleapis.com/v1", - "scopes": "https://www.googleapis.com/auth/cloud-platform" + "scope": ["https://www.googleapis.com/auth/cloud-platform"] } }' diff --git a/docs/zh/latest/terminology/secret.md b/docs/zh/latest/terminology/secret.md index 4cb72c51536e..810abb7ddf6f 100644 --- a/docs/zh/latest/terminology/secret.md +++ b/docs/zh/latest/terminology/secret.md @@ -324,7 +324,7 @@ $secret://$manager/$id/$secret_name/$key | auth_config.project_id | 是 | | 谷歌服务帐号的项目 ID。 | | auth_config.token_uri | 否 | https://oauth2.googleapis.com/token | 请求谷歌服务帐户的令牌的 URI。 | | auth_config.entries_uri | 否 | https://secretmanager.googleapis.com/v1 | 谷歌密钥服务访问端点 API。 | -| auth_config.scopes | 否 | https://www.googleapis.com/auth/cloud-platform | 谷歌服务账号的访问范围,可参考 [OAuth 2.0 Scopes for Google APIs](https://developers.google.com/identity/protocols/oauth2/scopes)| +| auth_config.scope | 否 | https://www.googleapis.com/auth/cloud-platform | 谷歌服务账号的访问范围,可参考 [OAuth 2.0 Scopes for Google APIs](https://developers.google.com/identity/protocols/oauth2/scopes)| | auth_file | 是 | | `auth_config` 和 `auth_file` 必须配置一个。 | | ssl_verify | 否 | true | 当设置为 `true` 时,启用 `SSL` 验证。 | @@ -344,7 +344,7 @@ curl http://127.0.0.1:9180/apisix/admin/secrets/gcp/1 \ "project_id": "apisix-project", "token_uri": "https://oauth2.googleapis.com/token", "entries_uri": "https://secretmanager.googleapis.com/v1", - "scopes": "https://www.googleapis.com/auth/cloud-platform" + "scope": ["https://www.googleapis.com/auth/cloud-platform"] } }' From 5fdadbd5f5e6d1896b2b1639acd1836802565388 Mon Sep 17 00:00:00 2001 From: HuanXin-Chen <1056216208@qq.com> Date: Fri, 6 Sep 2024 10:24:25 -0700 Subject: [PATCH 20/24] style(secret): fix the lint problem --- apisix/secret/gcp.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apisix/secret/gcp.lua b/apisix/secret/gcp.lua index 36376fb689c8..b729216e7281 100644 --- a/apisix/secret/gcp.lua +++ b/apisix/secret/gcp.lua @@ -23,7 +23,7 @@ local google_oauth = require("apisix.utils.google-cloud-oauth") local str_sub = core.string.sub local str_find = core.string.find local decode_base64 = ngx.decode_base64 - + local lrucache = core.lrucache.new({ ttl = 300, count = 8 }) local schema = { From 0f53faa1ed7d0f5578f9c74beba4bd8cb4b694f2 Mon Sep 17 00:00:00 2001 From: HuanXin-Chen <1056216208@qq.com> Date: Fri, 6 Sep 2024 10:33:28 -0700 Subject: [PATCH 21/24] fix(utils): remove the default entries --- apisix/utils/google-cloud-oauth.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apisix/utils/google-cloud-oauth.lua b/apisix/utils/google-cloud-oauth.lua index 59f34f382466..6cb352848bad 100644 --- a/apisix/utils/google-cloud-oauth.lua +++ b/apisix/utils/google-cloud-oauth.lua @@ -105,7 +105,7 @@ function _M.new(config, ssl_verify) project_id = config.project_id, token_uri = config.token_uri or "https://oauth2.googleapis.com/token", auth_uri = config.auth_uri or "https://accounts.google.com/o/oauth2/auth", - entries_uri = config.entries_uri or "https://logging.googleapis.com/v2/entries:write", + entries_uri = config.entries_uri, access_token = nil, access_token_type = nil, access_token_expire_time = 0, From cdcd661e27672baebba8c1741c65d9c70e8872b5 Mon Sep 17 00:00:00 2001 From: HuanXin-Chen <1056216208@qq.com> Date: Wed, 11 Sep 2024 08:23:31 -0700 Subject: [PATCH 22/24] style(secret): remove some tips --- apisix/secret/gcp.lua | 8 ++++---- apisix/utils/google-cloud-oauth.lua | 1 + t/secret/gcp.t | 2 +- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/apisix/secret/gcp.lua b/apisix/secret/gcp.lua index b729216e7281..854503a1c4b6 100644 --- a/apisix/secret/gcp.lua +++ b/apisix/secret/gcp.lua @@ -142,7 +142,7 @@ local function get_secret(oauth, secrets_id) end -local function make_request_to_gcp(conf, secrets_id) +local function request_to_gcp(conf, secrets_id) local auth_config, err = fetch_oauth_conf(conf) if not auth_config then return nil, err @@ -174,11 +174,11 @@ function _M.get(conf, key) return nil, "can't find main key, key: " .. key end - local sub_key = idx and str_sub(key, idx + 1) or nil + local sub_key = idx and str_sub(key, idx + 1) core.log.info("main: ", main_key, sub_key and ", sub: " .. sub_key or "") - local res, err = make_request_to_gcp(conf, main_key) + local res, err = request_to_gcp(conf, main_key) if not res then return nil, "failed to retrtive data from gcp secret manager: " .. err end @@ -189,7 +189,7 @@ function _M.get(conf, key) local data, err = core.json.decode(res) if not data then - return nil, "failed to decode result, res: " .. res .. ", err: " .. err + return nil, "failed to decode result, err: " .. err end return data[sub_key] diff --git a/apisix/utils/google-cloud-oauth.lua b/apisix/utils/google-cloud-oauth.lua index 6cb352848bad..6b59ecc6453d 100644 --- a/apisix/utils/google-cloud-oauth.lua +++ b/apisix/utils/google-cloud-oauth.lua @@ -86,6 +86,7 @@ function _M.generate_jwt_token(self) aud = self.token_uri, scope = self.scope, iat = get_timestamp(), + --- access token lifetime : 60 * 60 exp = get_timestamp() + (60 * 60) }) diff --git a/t/secret/gcp.t b/t/secret/gcp.t index 163bda317156..b89ca1d9a9b6 100644 --- a/t/secret/gcp.t +++ b/t/secret/gcp.t @@ -26,7 +26,7 @@ run_tests; __DATA__ -=== TEST 1: sanity +=== TEST 1: sanity: validate different schema situation --- config location /t { content_by_lua_block { From a4cc43213e27b16358d1f4cfb77f4db53559a09e Mon Sep 17 00:00:00 2001 From: HuanXin-Chen <1056216208@qq.com> Date: Thu, 12 Sep 2024 10:44:40 -0700 Subject: [PATCH 23/24] style(secret): code style --- apisix/secret/gcp.lua | 11 +++++++---- apisix/utils/google-cloud-oauth.lua | 1 - 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/apisix/secret/gcp.lua b/apisix/secret/gcp.lua index 854503a1c4b6..6b6e661c4ff2 100644 --- a/apisix/secret/gcp.lua +++ b/apisix/secret/gcp.lua @@ -41,6 +41,9 @@ local schema = { }, scope = { type = "array", + items = { + type = "string" + }, default = { "https://www.googleapis.com/auth/cloud-platform" } @@ -101,7 +104,7 @@ end local function get_secret(oauth, secrets_id) - local http_new = http.new() + local httpc = http.new() local access_token = oauth:generate_access_token() if not access_token then @@ -111,7 +114,7 @@ local function get_secret(oauth, secrets_id) local entries_uri = oauth.entries_uri .. "/projects/" .. oauth.project_id .. "/secrets/" .. secrets_id .. "/versions/latest:access" - local res, err = http_new:request_uri(entries_uri, { + local res, err = httpc:request_uri(entries_uri, { ssl_verify = oauth.ssl_verify, method = "GET", headers = { @@ -142,7 +145,7 @@ local function get_secret(oauth, secrets_id) end -local function request_to_gcp(conf, secrets_id) +local function make_request_to_gcp(conf, secrets_id) local auth_config, err = fetch_oauth_conf(conf) if not auth_config then return nil, err @@ -178,7 +181,7 @@ function _M.get(conf, key) core.log.info("main: ", main_key, sub_key and ", sub: " .. sub_key or "") - local res, err = request_to_gcp(conf, main_key) + local res, err = make_request_to_gcp(conf, main_key) if not res then return nil, "failed to retrtive data from gcp secret manager: " .. err end diff --git a/apisix/utils/google-cloud-oauth.lua b/apisix/utils/google-cloud-oauth.lua index 6b59ecc6453d..6cb352848bad 100644 --- a/apisix/utils/google-cloud-oauth.lua +++ b/apisix/utils/google-cloud-oauth.lua @@ -86,7 +86,6 @@ function _M.generate_jwt_token(self) aud = self.token_uri, scope = self.scope, iat = get_timestamp(), - --- access token lifetime : 60 * 60 exp = get_timestamp() + (60 * 60) }) From ad3af950a671df0aab4f87048d57e4bee5900b6f Mon Sep 17 00:00:00 2001 From: HuanXin-Chen <1056216208@qq.com> Date: Wed, 18 Sep 2024 06:46:53 -0700 Subject: [PATCH 24/24] style(gcp): remove sanity --- t/secret/gcp.t | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/t/secret/gcp.t b/t/secret/gcp.t index b89ca1d9a9b6..b7fc5331cf37 100644 --- a/t/secret/gcp.t +++ b/t/secret/gcp.t @@ -26,7 +26,7 @@ run_tests; __DATA__ -=== TEST 1: sanity: validate different schema situation +=== TEST 1: validate different schema situation --- config location /t { content_by_lua_block {