Skip to content

feat(core): add keys and key-sets entity #9737

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Nov 16, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions kong-3.1.0-0.rockspec
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,10 @@ build = {
["kong.db.declarative"] = "kong/db/declarative/init.lua",
["kong.db.declarative.marshaller"] = "kong/db/declarative/marshaller.lua",
["kong.db.schema"] = "kong/db/schema/init.lua",
["kong.db.dao.keys"] = "kong/db/dao/keys.lua",
["kong.db.dao.key_sets"] = "kong/db/dao/key_sets.lua",
["kong.db.schema.entities.keys"] = "kong/db/schema/entities/keys.lua",
["kong.db.schema.entities.key_sets"] = "kong/db/schema/entities/key_sets.lua",
["kong.db.schema.entities.consumers"] = "kong/db/schema/entities/consumers.lua",
["kong.db.schema.entities.routes"] = "kong/db/schema/entities/routes.lua",
["kong.db.schema.entities.routes_subschemas"] = "kong/db/schema/entities/routes_subschemas.lua",
Expand Down
16 changes: 16 additions & 0 deletions kong/constants.lua
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,15 @@ for p,_ in pairs(protocols_with_subsystem) do
end
table.sort(protocols)

local key_formats_map = {
["jwk"] = true,
["pem"] = true,
}
local key_formats = {}
for k in pairs(key_formats_map) do
key_formats[#key_formats + 1] = k
end

local constants = {
BUNDLED_PLUGINS = plugin_map,
DEPRECATED_PLUGINS = deprecated_plugin_map,
Expand Down Expand Up @@ -130,6 +139,8 @@ local constants = {
"clustering_data_planes",
"parameters",
"vaults",
"key_sets",
"keys",
},
ENTITY_CACHE_STORE = setmetatable({
consumers = "cache",
Expand All @@ -143,6 +154,8 @@ local constants = {
tags = "cache",
ca_certificates = "core_cache",
vaults = "core_cache",
key_sets = "core_cache",
keys = "core_cache",
}, {
__index = function()
return "cache"
Expand Down Expand Up @@ -202,6 +215,9 @@ local constants = {
CLUSTERING_OCSP_TIMEOUT = 5000, -- 5 seconds

CLEAR_HEALTH_STATUS_DELAY = 300, -- 300 seconds

KEY_FORMATS_MAP = key_formats_map,
KEY_FORMATS = key_formats
}

for _, v in ipairs(constants.CLUSTERING_SYNC_STATUS) do
Expand Down
76 changes: 76 additions & 0 deletions kong/db/dao/key_sets.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
-- This software is copyright Kong Inc. and its licensors.
-- Use of the software is subject to the agreement between your organization
-- and Kong Inc. If there is no such agreement, use is governed by and
-- subject to the terms of the Kong Master Software License Agreement found
-- at https://konghq.com/enterprisesoftwarelicense/.
-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]

local key_sets = {}


function key_sets:truncate()
return self.super.truncate(self)
end


function key_sets:select(primary_key, options)
return self.super.select(self, primary_key, options)
end


function key_sets:page(size, offset, options)
return self.super.page(self, size, offset, options)
end


function key_sets:each(size, options)
return self.super.each(self, size, options)
end


function key_sets:insert(entity, options)
return self.super.insert(self, entity, options)
end


function key_sets:update(primary_key, entity, options)
return self.super.update(self, primary_key, entity, options)
end


function key_sets:upsert(primary_key, entity, options)
return self.super.upsert(self, primary_key, entity, options)
end


function key_sets:delete(primary_key, options)
return self.super.delete(self, primary_key, options)
end


function key_sets:select_by_cache_key(cache_key, options)
return self.super.select_by_cache_key(self, cache_key, options)
end


function key_sets:select_by_name(unique_value, options)
return self.super.select_by_name(self, unique_value, options)
end


function key_sets:update_by_name(unique_value, entity, options)
return self.super.update_by_name(self, unique_value, entity, options)
end


function key_sets:upsert_by_name(unique_value, entity, options)
return self.super.upsert_by_name(self, unique_value, entity, options)
end


function key_sets:delete_by_name(unique_value, options)
return self.super.delete_by_name(self, unique_value, options)
end


return key_sets
133 changes: 133 additions & 0 deletions kong/db/dao/keys.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
-- This software is copyright Kong Inc. and its licensors.
-- Use of the software is subject to the agreement between your organization
-- and Kong Inc. If there is no such agreement, use is governed by and
-- subject to the terms of the Kong Master Software License Agreement found
-- at https://konghq.com/enterprisesoftwarelicense/.
-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]

local pkey = require("resty.openssl.pkey")
local fmt = string.format
local type = type

local keys = {}


function keys:truncate()
return self.super.truncate(self)
end

function keys:select(primary_key, options)
return self.super.select(self, primary_key, options)
end

function keys:page(size, offset, options)
return self.super.page(self, size, offset, options)
end

function keys:each(size, options)
return self.super.each(self, size, options)
end

function keys:insert(entity, options)
return self.super.insert(self, entity, options)
end

function keys:update(primary_key, entity, options)
return self.super.update(self, primary_key, entity, options)
end

function keys:upsert(primary_key, entity, options)
return self.super.upsert(self, primary_key, entity, options)
end

function keys:delete(primary_key, options)
return self.super.delete(self, primary_key, options)
end

function keys:select_by_cache_key(cache_key, options)
return self.super.select_by_cache_key(self, cache_key, options)
end

function keys:page_for_set(foreign_key, size, offset, options)
return self.super.page_for_set(self, foreign_key, size, offset, options)
end

function keys:each_for_set(foreign_key, size, options)
return self.super.each_for_set(self, foreign_key, size, options)
end

function keys:cache_key(kid, set_name)
if not kid then
return nil, "kid must exist"
end
if type(kid) == "table" then
kid = kid.kid
end
if not set_name then
set_name = ""
end
if type(set_name) == "table" then
set_name = set_name.name
end
-- ignore ws_id, kid+set is unique
return fmt("keys:%s:%s", tostring(kid), set_name)
end

-- load to lua-resty-openssl pkey module
local function _load_pkey(key, part)
local pk, err
if part == "public" then part = "public_key" end
if part == "private" then part = "private_key" end

if key.jwk then
pk, err = pkey.new(key.jwk, { format = "JWK" })
end
if key.pem then
if not key.pem[part] then
return nil, fmt("%s key not found.", part)
end
pk, err = pkey.new(key.pem[part], { format = "PEM" })
end
if not pk then
return nil, "could not load pkey. " .. err
end
return pk
end

local function _key_format(key)
-- no nil checks needed. schema validation ensures on of these
-- entries to be present.
if key.jwk then
return "JWK"
end
if key.pem then
return "PEM"
end
end

local function _get_key(key, part)
if part ~= "public" and part ~= "private" then
return nil, "part needs to be public or private"
end
-- pkey expects uppercase formats
local k_fmt = _key_format(key)

local pk, err = _load_pkey(key, part)
if not pk or err then
return nil, err
end
return pk:tostring(part, k_fmt)
end

-- getter for public key
function keys:get_pubkey(key)
return _get_key(key, "public")
end

-- getter for private key
function keys:get_privkey(key)
return _get_key(key, "private")
end


return keys
107 changes: 105 additions & 2 deletions kong/db/migrations/core/017_300_to_310.lua
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,116 @@ return {
-- Do nothing, accept existing state
END;
$$;

CREATE TABLE IF NOT EXISTS "key_sets" (
"id" UUID PRIMARY KEY,
"name" TEXT UNIQUE,
"tags" TEXT[],
"ws_id" UUID REFERENCES "workspaces" ("id"),
"created_at" TIMESTAMP WITH TIME ZONE,
"updated_at" TIMESTAMP WITH TIME ZONE
);

DO $$
BEGIN
CREATE INDEX IF NOT EXISTS "key_sets_tags_idx" ON "key_sets" USING GIN ("tags");
EXCEPTION WHEN UNDEFINED_COLUMN then
-- do nothing, accept existing state
END$$;

DROP TRIGGER IF EXISTS "key_sets_sync_tags_trigger" ON "key_sets";

DO $$
BEGIN
CREATE TRIGGER "key_sets_sync_tags_trigger"
AFTER INSERT OR UPDATE OF "tags"
OR DELETE ON "key_sets"
FOR EACH ROW
EXECUTE PROCEDURE "sync_tags" ();
EXCEPTION WHEN undefined_column OR undefined_table THEN
-- do nothing, accept existing state
END$$;

CREATE TABLE IF NOT EXISTS "keys" (
"id" UUID PRIMARY KEY,
"set_id" UUID REFERENCES "key_sets" ("id") on delete cascade,
"name" TEXT UNIQUE,
"cache_key" TEXT UNIQUE,
"ws_id" UUID REFERENCES "workspaces" ("id"),
"kid" TEXT,
"jwk" TEXT,
"pem" JSONB,
"tags" TEXT[],
"created_at" TIMESTAMP WITH TIME ZONE,
"updated_at" TIMESTAMP WITH TIME ZONE,
UNIQUE ("kid", "set_id")
);

DO $$
BEGIN
CREATE INDEX IF NOT EXISTS "keys_fkey_key_sets" ON "keys" ("set_id");
EXCEPTION WHEN undefined_column THEN
-- do nothing, accept existing state
END$$;

DO $$
BEGIN
CREATE INDEX IF NOT EXISTS "keys_tags_idx" ON "keys" USING GIN ("tags");
EXCEPTION WHEN undefined_column THEN
-- do nothing, accept existing state
END$$;

DROP TRIGGER IF EXISTS "keys_sync_tags_trigger" ON "keys";

DO $$
BEGIN
CREATE TRIGGER "keys_sync_tags_trigger"
AFTER INSERT OR UPDATE OF "tags"
OR DELETE ON "keys"
FOR EACH ROW
EXECUTE PROCEDURE "sync_tags" ();
EXCEPTION WHEN undefined_column or UNDEFINED_TABLE then
-- do nothing, accept existing state
END$$;
]]
},

cassandra = {
up = [[
ALTER TABLE upstreams ADD use_srv_name boolean;
create table if not exists keys (
id uuid,
name text,
cache_key text,
ws_id uuid,
kid text,
jwk text,
pem text,
tags set<text>,
set_id uuid,
created_at timestamp,
updated_at timestamp,
PRIMARY KEY (id)
);
-- creating indexes for all queryable fields
-- to avoid ALLOW_FILTERING requirements.
create index if not exists keys_ws_id_idx on keys (ws_id);
create index if not exists keys_set_id_idx on keys (set_id);
create index if not exists keys_kid_idx on keys (kid);
create index if not exists keys_name_idx on keys (name);
create index if not exists keys_cache_key_idx on keys (cache_key);

create table if not exists key_sets (
id uuid,
name text,
ws_id uuid,
tags set<text>,
created_at timestamp,
updated_at timestamp,
PRIMARY KEY (id)
);
create index if not exists key_sets_ws_id_idx on key_sets (ws_id);
create index if not exists key_sets_name_idx on key_sets (name);
]]
},
}

Loading