Skip to content

Commit

Permalink
feat(vaults) store dao references in $refs (needed for rotation)
Browse files Browse the repository at this point in the history
### Summary

When there are references used in dao fields with `referenceable=true`,
Kong replaces the references with values when the data is read
(excluding admin api and control planes). When Kong replaces the reference,
it is basically lost, and thus the automatic secret rotation cannot be
implemented. This commit stores the references on returned entities to
`"$refs"` property:

```
local certificate = kong.db.certificates:select(...)
-- the possible reference can be found here:
print(certificate["$refs"].key)
```

There will be helper functions so `"$refs"` property is not intended to
end users.
  • Loading branch information
bungle committed Apr 19, 2022
1 parent ac69743 commit 5156596
Show file tree
Hide file tree
Showing 2 changed files with 29 additions and 9 deletions.
13 changes: 13 additions & 0 deletions kong/db/schema/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -1682,6 +1682,8 @@ function Schema:process_auto_fields(data, context, nulls, opts)
end
end

local refs

for key, field in self:each_field(data) do
local ftype = field.type
local value = data[key]
Expand Down Expand Up @@ -1736,9 +1738,16 @@ function Schema:process_auto_fields(data, context, nulls, opts)
if resolve_references then
if ftype == "string" and field.referenceable then
if is_reference(value) then
if refs then
refs[key] = value
else
refs = { [key] = value }
end

local deref, err = dereference(value)
if deref then
value = deref

else
if err then
kong.log.warn("unable to resolve reference ", value, " (", err, ")")
Expand All @@ -1755,8 +1764,10 @@ function Schema:process_auto_fields(data, context, nulls, opts)
if subfield.type == "string" and subfield.referenceable then
local count = #value
if count > 0 then
refs[key] = new_tab(count, 0)
for i = 1, count do
if is_reference(value[i]) then
refs[key][i] = value[i]
local deref, err = dereference(value[i])
if deref then
value[i] = deref
Expand Down Expand Up @@ -1809,6 +1820,8 @@ function Schema:process_auto_fields(data, context, nulls, opts)
end
end

data["$refs"] = refs

return data
end

Expand Down
25 changes: 16 additions & 9 deletions spec/02-integration/13-vaults/01-vault_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ for _, strategy in helpers.each_strategy() do
end)

it("create certificates with cert and key as secret", function()
local res, err = client:post("/certificates", {
local res, err = client:post("/certificates", {
headers = { ["Content-Type"] = "application/json" },
body = {
cert = "{vault://test-vault/cert}",
Expand All @@ -64,11 +64,16 @@ for _, strategy in helpers.each_strategy() do
assert.equal("{vault://test-vault/key}", certificate.key)
assert.equal("{vault://unknown/cert}", certificate.cert_alt)
assert.equal("{vault://unknown/missing-key}", certificate.key_alt)
assert.is_nil(certificate["$refs"])

certificate, err = db.certificates:select({ id = certificate.id })
assert.is_nil(err)
assert.equal(ssl_fixtures.cert, certificate.cert)
assert.equal(ssl_fixtures.key, certificate.key)
assert.equal("{vault://test-vault/cert}", certificate["$refs"].cert)
assert.equal("{vault://test-vault/key}", certificate["$refs"].key)
assert.equal("{vault://unknown/cert}", certificate["$refs"].cert_alt)
assert.equal("{vault://unknown/missing-key}", certificate["$refs"].key_alt)
assert.is_nil(certificate.cert_alt)
assert.is_nil(certificate.key_alt)

Expand All @@ -81,16 +86,18 @@ for _, strategy in helpers.each_strategy() do
assert.equal("{vault://test-vault/key}", certificate.key)
assert.equal("{vault://unknown/cert}", certificate.cert_alt)
assert.equal("{vault://unknown/missing-key}", certificate.key_alt)
assert.is_nil(certificate["$refs"])

-- verify that certificate attributes are of type reference when querying
local gres = client:get("/certificates/"..certificate.id)
local gbody = assert.res_status(200, gres)
local gcertificate = cjson.decode(gbody)
assert.is_equal("{vault://test-vault/cert}", gcertificate.cert)
assert.is_equal("{vault://test-vault/key}", gcertificate.key)
assert.is_equal("{vault://unknown/cert}", gcertificate.cert_alt)
assert.is_equal("{vault://unknown/missing-key}", gcertificate.key_alt)
res, err = client:get("/certificates/"..certificate.id)
assert.is_nil(err)
body = assert.res_status(200, res)
certificate = cjson.decode(body)
assert.is_equal("{vault://test-vault/cert}", certificate.cert)
assert.is_equal("{vault://test-vault/key}", certificate.key)
assert.is_equal("{vault://unknown/cert}", certificate.cert_alt)
assert.is_equal("{vault://unknown/missing-key}", certificate.key_alt)
assert.is_nil(certificate["$refs"])
end)

end)
end

0 comments on commit 5156596

Please sign in to comment.