Skip to content
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

feat(ca_certificates): make ca_certificates cert referenceable #13834

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
message: The `cert` field of CA certificates entity is now referenceable.
type: feature
scope: Core
32 changes: 26 additions & 6 deletions kong/db/schema/entities/ca_certificates.lua
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
local typedefs = require "kong.db.schema.typedefs"
local openssl_x509 = require "resty.openssl.x509"
local is_reference = require "kong.pdk.vault".is_reference

local find = string.find
local ngx_time = ngx.time
local null = ngx.null
local to_hex = require("resty.string").to_hex

local CERT_TAG = "-----BEGIN CERTIFICATE-----"
Expand All @@ -17,7 +19,7 @@ return {
{ id = typedefs.uuid, },
{ created_at = typedefs.auto_timestamp_s },
{ updated_at = typedefs.auto_timestamp_s },
{ cert = typedefs.certificate { required = true }, },
{ cert = typedefs.certificate { required = true, referenceable = true }, },
{ cert_digest = { type = "string", unique = true }, },
{ tags = typedefs.tags },
},
Expand All @@ -26,11 +28,15 @@ return {
{
input = { "cert" },
on_write = function(cert)
local digest = openssl_x509.new(cert):digest("sha256")
if not digest then
return nil, "cannot create digest value of certificate"
if not is_reference(cert) then
local digest = openssl_x509.new(cert):digest("sha256")
if not digest then
return nil, "cannot create digest value of certificate"
end
return { cert_digest = to_hex(digest) }
else
return {}
end
return { cert_digest = to_hex(digest) }
end,
},
},
Expand Down Expand Up @@ -62,6 +68,20 @@ return {

return true
end,
} }
} }, {
custom_entity_check = {
field_sources = { "cert", "cert_digest" },
run_with_vault_reference = true,
fn = function(entity)
local cert = entity.cert
local digest = entity.cert_digest
if is_reference(cert) and (digest == nil or digest == null) then
return nil, "the cert_digest of a vault referenced CA certificate must be provided manually"
end

return true
end,
}
}
}
}
6 changes: 5 additions & 1 deletion kong/db/schema/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -770,6 +770,7 @@ Schema.entity_checkers = {
custom_entity_check = {
run_with_missing_fields = false,
run_with_invalid_fields = false,
run_with_vault_reference = false,
field_sources = { "field_sources" },
required_fields = { ["field_sources"] = true },
fn = function(entity, arg)
Expand Down Expand Up @@ -1272,8 +1273,11 @@ local function run_entity_check(self, name, input, arg, full_check, errors)
all_nil = false

-- Don't run if any of the values is a reference in a referenceable field
-- and the check is not allowed to run with vault references
local field = get_schema_field(self, fname)
if field.type == "string" and field.referenceable and is_reference(value) then
if field.type == "string" and field.referenceable
and is_reference(value) and (not checker.run_with_vault_reference)
and (not arg.run_with_vault_reference) then
return
end
end
Expand Down
1 change: 1 addition & 0 deletions kong/db/schema/metaschema.lua
Original file line number Diff line number Diff line change
Expand Up @@ -358,6 +358,7 @@ local entity_checkers = {
{ fn = { type = "function" } },
{ run_with_missing_fields = { type = "boolean" } },
{ run_with_invalid_fields = { type = "boolean" } },
{ run_with_vault_reference = { type = "boolean" } },
}
}
},
Expand Down
83 changes: 83 additions & 0 deletions spec/01-unit/01-db/01-schema/01-schema_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ local Schema = require "kong.db.schema"
local cjson = require "cjson"
local helpers = require "spec.helpers"
local table_copy = require "kong.tools.table".deep_copy
local is_reference = require "kong.pdk.vault".is_reference


local SchemaKind = {
Expand Down Expand Up @@ -2764,6 +2765,88 @@ describe("schema", function()
assert.falsy(err)
end)

it("run an #referenceable entity check with flag 'run_with_vault_reference'", function ()
local Test = Schema.new({
fields = {
{ aaa = { type = "string", len_min = 4, referenceable = true } },
{ bbb = { type = "string", len_min = 8 } },
{ ccc = { type = "number", between = { 0, 10 } } },
},
entity_checks = {
{ custom_entity_check = {
run_with_missing_fields = true,
run_with_vault_reference = true,
field_sources = { "aaa", "bbb", "ccc" },
fn = function(entity)
if is_reference(entity.aaa) and (entity.bbb == nil or entity.bbb == ngx.null) then
return nil, "bbb cannot be nil when aaa is a reference"
end

return true
end,
} }
}
})

-- missing field 'aaa'
local ok, err = Test:validate_update({
bbb = "foo",
ccc = 42
})
assert.falsy(ok)
assert.is_nil(err["aaa"])
assert.match("length must be at least 8", err["bbb"])
assert.match("value should be between 0 and 10", err["ccc"])
assert.falsy(err["@entity"])

-- missing field 'aaa', others are right
local ok, err = Test:validate_update({
bbb = "12345678",
ccc = 2
})
assert.is_nil(err)
assert.truthy(ok)
assert.falsy(err)

-- has field 'aaa' but not a reference
local ok, err = Test:validate_update({
aaa = "xxx",
bbb = "foo",
ccc = 42
})
assert.falsy(ok)
assert.match("length must be at least 4", err["aaa"])
assert.match("length must be at least 8", err["bbb"])
assert.match("value should be between 0 and 10", err["ccc"])
assert.falsy(err["@entity"])

-- has field 'aaa' with correct value
local ok, err = Test:validate_update({
aaa = "xxxx",
bbb = "foobarfoobar",
ccc = 5
})
assert.truthy(ok)
assert.falsy(err)

-- has field 'aaa' as a reference but missing 'bbb'
local ok, err = Test:validate_update({
aaa = "{vault://vault_prefix/test}",
ccc = 5
})
assert.falsy(ok)
assert.match("bbb cannot be nil when aaa is a reference", err["@entity"][1])

-- all fields are right
local ok, err = Test:validate_update({
aaa = "{vault://another_vault_prefix/test2}",
bbb = "12345678",
ccc = 2
})
assert.truthy(ok)
assert.falsy(err)
end)

it("supports entity checks on nested fields", function()
local Test = Schema.new({
fields = {
Expand Down
6 changes: 3 additions & 3 deletions spec/02-integration/03-db/22-ca_certificates_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -121,21 +121,21 @@ for _, strategy in helpers.each_strategy() do

describe("ca_certificates:delete()", function()
it("can delete ca certificate that is not being referenced", function()
local ok, err, err_t = db.ca_certificates:delete({ id = other_ca.id })
local ok, err, err_t = db.ca_certificates:delete({ id = other_ca.id })
assert.is_nil(err)
assert.is_nil(err_t)
assert(ok)
end)

it("can't delete ca certificate that is referenced by services", function()
local ok, err = db.ca_certificates:delete({ id = ca1.id })
local ok, err = db.ca_certificates:delete({ id = ca1.id })
assert.matches(fmt("ca certificate %s is still referenced by services (id = %s)", ca1.id, service.id),
err, nil, true)
assert.is_nil(ok)
end)

it("can't delete ca certificate that is referenced by plugins", function()
local ok, err = db.ca_certificates:delete({ id = ca2.id })
local ok, err = db.ca_certificates:delete({ id = ca2.id })
assert.matches(fmt("ca certificate %s is still referenced by plugins (id = %s)", ca2.id, plugin.id),
err, nil, true)
assert.is_nil(ok)
Expand Down
24 changes: 19 additions & 5 deletions spec/02-integration/13-vaults/01-vault_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,25 @@ local cjson = require "cjson"


for _, strategy in helpers.each_strategy() do
describe("vaults: #" .. strategy, function ()
local db
lazy_setup(function ()
local _
_, db = helpers.get_db_utils(strategy, {
"vaults",
},
nil, {
"env",
"mock",
})
end)

it("generate correct cache key", function ()
local cache_key = db.vaults:cache_key("test")
assert.equal("vaults:test:::::", cache_key)
end)
end)

describe("/certificates with DB: #" .. strategy, function()
local client
local db
Expand Down Expand Up @@ -175,10 +194,5 @@ for _, strategy in helpers.each_strategy() do
assert.is_equal("{vault://unknown/missing-key}", certificate.key_alt)
assert.is_nil(certificate["$refs"])
end)

it("generate correct cache key", function ()
local cache_key = db.vaults:cache_key("test")
assert.equal("vaults:test:::::", cache_key)
end)
end)
end
Loading
Loading