Skip to content

Commit

Permalink
feat(ca_certificates): make ca_certificates cert referenceable
Browse files Browse the repository at this point in the history
  • Loading branch information
windmgc committed Nov 5, 2024
1 parent 0aaa4e0 commit 0ccad69
Show file tree
Hide file tree
Showing 8 changed files with 294 additions and 15 deletions.
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

0 comments on commit 0ccad69

Please sign in to comment.