Skip to content

Commit

Permalink
feat(admin): add kms admin api (#8394)
Browse files Browse the repository at this point in the history
  • Loading branch information
kingluo authored Nov 29, 2022
1 parent a71f410 commit de069aa
Show file tree
Hide file tree
Showing 6 changed files with 496 additions and 0 deletions.
1 change: 1 addition & 0 deletions apisix/admin/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ local resources = {
plugin_metadata = require("apisix.admin.plugin_metadata"),
plugin_configs = require("apisix.admin.plugin_config"),
consumer_groups = require("apisix.admin.consumer_group"),
kms = require("apisix.admin.kms"),
}


Expand Down
203 changes: 203 additions & 0 deletions apisix/admin/kms.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,203 @@
--
-- 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 utils = require("apisix.admin.utils")
local type = type
local tostring = tostring


local _M = {
need_v3_filter = true,
}


local function check_conf(id, conf, need_id, typ)
if not conf then
return nil, {error_msg = "missing configurations"}
end

id = id or conf.id
if need_id and not id then
return nil, {error_msg = "missing id"}
end

if not need_id and id then
return nil, {error_msg = "wrong id, do not need it"}
end

if need_id and conf.id and tostring(conf.id) ~= tostring(id) then
return nil, {error_msg = "wrong id"}
end

conf.id = id

core.log.info("conf: ", core.json.delay_encode(conf))
local ok, err = core.schema.check(core.schema["kms_" .. typ], conf)
if not ok then
return nil, {error_msg = "invalid configuration: " .. err}
end

return true
end


local function split_typ_and_id(id, sub_path)
local uri_segs = core.utils.split_uri(sub_path)
local typ = id
local id = nil
if #uri_segs > 0 then
id = uri_segs[1]
end
return typ, id
end


function _M.put(id, conf, sub_path)
local typ, id = split_typ_and_id(id, sub_path)
if not id then
return 400, {error_msg = "no kms id in uri"}
end

local ok, err = check_conf(typ .. "/" .. id, conf, true, typ)
if not ok then
return 400, err
end

local key = "/kms/" .. typ .. "/" .. id

local ok, err = utils.inject_conf_with_prev_conf("kms", key, conf)
if not ok then
return 503, {error_msg = err}
end

local res, err = core.etcd.set(key, conf)
if not res then
core.log.error("failed to put kms [", key, "]: ", err)
return 503, {error_msg = err}
end

return res.status, res.body
end


function _M.get(id, conf, sub_path)
local typ, id = split_typ_and_id(id, sub_path)

local key = "/kms/"
if id then
key = key .. typ
key = key .. "/" .. id
end

local res, err = core.etcd.get(key, not id)
if not res then
core.log.error("failed to get kms [", key, "]: ", err)
return 503, {error_msg = err}
end

utils.fix_count(res.body, id)
return res.status, res.body
end


function _M.delete(id, conf, sub_path)
local typ, id = split_typ_and_id(id, sub_path)
if not id then
return 400, {error_msg = "no kms id in uri"}
end

local key = "/kms/" .. typ .. "/" .. id

local res, err = core.etcd.delete(key)
if not res then
core.log.error("failed to delete kms [", key, "]: ", err)
return 503, {error_msg = err}
end

return res.status, res.body
end


function _M.patch(id, conf, sub_path)
local uri_segs = core.utils.split_uri(sub_path)
if #uri_segs < 2 then
return 400, {error_msg = "no kms id and/or sub path in uri"}
end
local typ = id
id = uri_segs[1]
sub_path = core.table.concat(uri_segs, "/", 2)

if not id then
return 400, {error_msg = "missing kms id"}
end

if not conf then
return 400, {error_msg = "missing new configuration"}
end

if not sub_path or sub_path == "" then
if type(conf) ~= "table" then
return 400, {error_msg = "invalid configuration"}
end
end

local key = "/kms/" .. typ .. "/" .. id
local res_old, err = core.etcd.get(key)
if not res_old then
core.log.error("failed to get kms [", key, "]: ", err)
return 503, {error_msg = err}
end

if res_old.status ~= 200 then
return res_old.status, res_old.body
end
core.log.info("key: ", key, " old value: ",
core.json.delay_encode(res_old, true))

local node_value = res_old.body.node.value
local modified_index = res_old.body.node.modifiedIndex

if sub_path and sub_path ~= "" then
local code, err, node_val = core.table.patch(node_value, sub_path, conf)
node_value = node_val
if code then
return code, err
end
utils.inject_timestamp(node_value, nil, true)
else
node_value = core.table.merge(node_value, conf)
utils.inject_timestamp(node_value, nil, conf)
end

core.log.info("new conf: ", core.json.delay_encode(node_value, true))

local ok, err = check_conf(typ .. "/" .. id, node_value, true, typ)
if not ok then
return 400, {error_msg = err}
end

local res, err = core.etcd.atomic_set(key, node_value, nil, modified_index)
if not res then
core.log.error("failed to set new kms[", key, "]: ", err)
return 503, {error_msg = err}
end

return res.status, res.body
end


return _M
1 change: 1 addition & 0 deletions apisix/constants.lua
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ return {
["/protos"] = true,
["/plugin_configs"] = true,
["/consumer_groups"] = true,
["/kms"] = true,
},
STREAM_ETCD_DIRECTORY = {
["/upstreams"] = true,
Expand Down
15 changes: 15 additions & 0 deletions apisix/schema_def.lua
Original file line number Diff line number Diff line change
Expand Up @@ -692,6 +692,21 @@ _M.service = {
}


_M.kms_vault = {
type = "object",
properties = {
uri = _M.uri_def,
prefix = {
type = "string",
},
token = {
type = "string",
},
},
required = {"uri", "prefix", "token"},
}


_M.consumer = {
type = "object",
properties = {
Expand Down
59 changes: 59 additions & 0 deletions docs/en/latest/admin-api.md
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ Resources that support paging queries:
- SSL
- Stream Route
- Upstream
- kms

### Support filtering query

Expand Down Expand Up @@ -1119,3 +1120,61 @@ Route used in the [Stream Proxy](./stream-proxy.md).
To learn more about filtering in stream proxies, check [this](./stream-proxy.md#more-route-match-options) document.

[Back to TOC](#table-of-contents)

## kms

**API**: /apisix/admin/kms/{secretmanager}/{id}

kms means `Secrets Management`, which could use any secret manager supported, e.g. `vault`.

### Request Methods

| Method | Request URI | Request Body | Description |
| ------ | ---------------------------------- | ------------ | ------------------------------------------------- |
| GET | /apisix/admin/kms | NULL | Fetches a list of all kms. |
| GET | /apisix/admin/kms/{secretmanager}/{id} | NULL | Fetches specified kms by id. |
| PUT | /apisix/admin/kms/{secretmanager} | {...} | Create new kms configuration. |
| DELETE | /apisix/admin/kms/{secretmanager}/{id} | NULL | Removes the kms with the specified id. |
| PATCH | /apisix/admin/kms/{secretmanager}/{id} | {...} | Updates the selected attributes of the specified, existing kms. To delete an attribute, set value of attribute set to null. |
| PATCH | /apisix/admin/kms/{secretmanager}/{id}/{path} | {...} | Updates the attribute specified in the path. The values of other attributes remain unchanged. |

### Request Body Parameters

When `{secretmanager}` is `vault`:

| Parameter | Required | Type | Description | Example |
| ----------- | -------- | ----------- | ------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------ |
| uri | True | URI | URI of the vault server. | |
| prefix | True | string | key prefix
| token | True | string | vault token. | |

Example Configuration:

```shell
{
"uri": "https://localhost/vault",
"prefix": "/apisix/kv",
"token": "343effad"
}
```

Example API usage:

```shell
$ curl -i http://127.0.0.1:9180/apisix/admin/kms/vault/test2 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '
{
"uri": "http://xxx/get",
"prefix" : "apisix",
"token" : "apisix"
}'
HTTP/1.1 200 OK
...

{"key":"\/apisix\/kms\/vault\/test2","value":{"id":"vault\/test2","token":"apisix","prefix":"apisix","update_time":1669625828,"create_time":1669625828,"uri":"http:\/\/xxx\/get"}}
```

### Response Parameters

Currently, the response is returned from etcd.

[Back to TOC](#table-of-contents)
Loading

0 comments on commit de069aa

Please sign in to comment.