Files
kong--kong/kong/cmd/utils/process_secrets.lua
T
2026-07-13 12:32:21 +08:00

197 lines
4.3 KiB
Lua

local b64 = require "ngx.base64"
local cjson = require "cjson.safe"
local file = require "pl.file"
local path = require "pl.path"
local cipher = require "resty.openssl.cipher"
local digest = require "resty.openssl.digest"
local rand = require "resty.openssl.rand"
local fmt = string.format
local sub = string.sub
local type = type
local pairs = pairs
local shallow_copy = require("kong.tools.table").shallow_copy
local CIPHER_ALG = "aes-256-gcm"
local DIGEST_ALG = "sha256"
local IV_SIZE = 12
local TAG_SIZE = 16
local AAD = fmt("%s|%s", CIPHER_ALG, DIGEST_ALG)
local function read_key_data(key_data_path)
if not path.exists(key_data_path) then
return nil, fmt("failed to read key data (%s): file not found", key_data_path)
end
local key_data, err = file.read(key_data_path, true)
if not key_data then
return nil, fmt("failed to read key data file: %s", err)
end
return key_data
end
local function hash_key_data(key_data)
local hash, err = digest.new(DIGEST_ALG)
if not hash then
return nil, fmt("unable to initialize digest (%s)", err)
end
local ok
ok, err = hash:update(key_data)
if not ok then
return nil, fmt("unable to update digest (%s)", err)
end
local key
key, err = hash:final()
if not key then
return nil, fmt("unable to create digest (%s)", err)
end
return key
end
local function extract(conf)
local refs = conf["$refs"]
if not refs or type(refs) ~= "table" then
return
end
local secrets = {}
for k in pairs(refs) do
secrets[k] = shallow_copy(conf[k])
end
return secrets
end
local function encrypt(plaintext, key_data)
local key, err = hash_key_data(key_data)
if not key then
return nil, err
end
local iv
iv, err = rand.bytes(IV_SIZE)
if not iv then
return nil, fmt("unable to generate initialization vector (%s)", err)
end
local cip, err = cipher.new(CIPHER_ALG)
if not cip then
return nil, fmt("unable to initialize cipher (%s)", err)
end
local ciphertext
ciphertext, err = cip:encrypt(key, iv, plaintext, false, AAD)
if not ciphertext then
return nil, fmt("unable to encrypt (%s)", err)
end
local tag
tag, err = cip:get_aead_tag(TAG_SIZE)
if not tag then
return nil, fmt("unable to get authentication tag (%s)", err)
end
return iv .. tag .. ciphertext
end
local function decrypt(ciphertext, key_data)
local key, err = hash_key_data(key_data)
if not key then
return nil, err
end
local iv = sub(ciphertext, 1, IV_SIZE)
local tag = sub(ciphertext, IV_SIZE + 1, IV_SIZE + TAG_SIZE)
ciphertext = sub(ciphertext, IV_SIZE + TAG_SIZE + 1)
local cip, err = cipher.new(CIPHER_ALG)
if not cip then
return nil, fmt("unable to initialize cipher (%s)", err)
end
local plaintext
plaintext, err = cip:decrypt(key, iv, ciphertext, false, AAD, tag)
if not plaintext then
return nil, fmt("unable to decrypt (%s)", err)
end
return plaintext
end
local function serialize(input, key_data_path)
local output, err = cjson.encode(input)
if not output then
return nil, fmt("failed to json encode process secrets: %s", err)
end
if key_data_path then
local key_data
key_data, err = read_key_data(key_data_path)
if not key_data then
return nil, err
end
output, err = encrypt(output, key_data)
if not output then
return nil, fmt("failed to encrypt process secrets: %s", err)
end
end
output, err = b64.encode_base64url(output)
if not output then
return nil, fmt("failed to base64 encode process secrets: %s", err)
end
return output
end
local function deserialize(input, key_data_path)
local output, err = b64.decode_base64url(input)
if not output then
return nil, fmt("failed to base64 decode process secrets: %s", err)
end
if key_data_path then
local key_data
key_data, err = read_key_data(key_data_path)
if not key_data then
return nil, err
end
output, err = decrypt(output, key_data)
if not output then
return nil, fmt("failed to decrypt process secrets: %s", err)
end
end
output, err = cjson.decode(output)
if not output then
return nil, fmt("failed to json decode process secrets: %s", err)
end
return output
end
return {
extract = extract,
encrypt = encrypt,
decrypt = decrypt,
serialize = serialize,
deserialize = deserialize,
}