Skip to content

Commit

Permalink
fix(conf) ngx.socket.tcp not available on init
Browse files Browse the repository at this point in the history
### Summary

Kong's initialization parses Kong configuration twice:

1. first the config is parsed in timer context by kong start (cli)
2. then the config is parsed again in init context when kong server is started

Many Vault implementations will require the availability of `ngx.socket.tcp`
to be able to fetch secrets. Unfortunately the `ngx.socket.tcp` is not available
on init or init worker phases, but it works on timer context.

Alternative approach would be to fetch secrets using `LuaSocket` on init phase,
but that would mean that each secret is fetched twice (added latency + possible
costs of accessing Vaults). Also, it is impossible to make `LuaSocket` and
`ngx.socket.tcp` fully compatible, but there is one project that at least tried it:
https://github.com/thibaultcha/lua-resty-socket

This commit takes yet another approach:
1. it fetches secret on CLI
2. it passes secrets to server via environment variable
3. except on `kong reload` it passes it to server via file `.kong_process_secrets`

The error on current master branch looks like this:
```
Error: ./kong/cmd/start.lua:64: nginx: [error] init_by_lua error: ./kong/globalpatches.lua:396: no request found
```

This commit fixes it.
  • Loading branch information
bungle committed May 4, 2022
1 parent 79ad5fc commit d55d33b
Show file tree
Hide file tree
Showing 12 changed files with 681 additions and 38 deletions.
1 change: 1 addition & 0 deletions kong-2.8.0-0.rockspec
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ build = {
["kong.cmd.utils.tty"] = "kong/cmd/utils/tty.lua",
["kong.cmd.utils.nginx_signals"] = "kong/cmd/utils/nginx_signals.lua",
["kong.cmd.utils.prefix_handler"] = "kong/cmd/utils/prefix_handler.lua",
["kong.cmd.utils.process_secrets"] = "kong/cmd/utils/process_secrets.lua",

["kong.api"] = "kong/api/init.lua",
["kong.api.api_helpers"] = "kong/api/api_helpers.lua",
Expand Down
2 changes: 1 addition & 1 deletion kong/cmd/reload.lua
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ local function execute(args)
conf.declarative_config = nil
end

assert(prefix_handler.prepare_prefix(conf, args.nginx_conf))
assert(prefix_handler.prepare_prefix(conf, args.nginx_conf, nil, true))
assert(nginx_signals.reload(conf))

log("Kong reloaded")
Expand Down
73 changes: 66 additions & 7 deletions kong/cmd/utils/nginx_signals.lua
Original file line number Diff line number Diff line change
@@ -1,11 +1,28 @@
local ffi = require "ffi"
local log = require "kong.cmd.utils.log"
local kill = require "kong.cmd.utils.kill"
local meta = require "kong.meta"
local pl_path = require "pl.path"
local version = require "version"
local pl_utils = require "pl.utils"
local pl_stringx = require "pl.stringx"
local process_secrets = require "kong.cmd.utils.process_secrets"


local fmt = string.format
local ipairs = ipairs
local unpack = unpack
local tostring = tostring


local C = ffi.C


ffi.cdef([[
int setenv(const char *name, const char *value, int overwrite);
int unsetenv(const char *name);
]])


local nginx_bin_name = "nginx"
local nginx_search_paths = {
Expand All @@ -14,9 +31,11 @@ local nginx_search_paths = {
""
}


local nginx_version_pattern = "^nginx.-openresty.-([%d%.]+)"
local nginx_compatible = version.set(unpack(meta._DEPENDENCIES.nginx))


local function is_openresty(bin_path)
local cmd = fmt("%s -v", bin_path)
local ok, _, _, stderr = pl_utils.executeex(cmd)
Expand All @@ -34,9 +53,10 @@ local function is_openresty(bin_path)
log.debug("OpenResty 'nginx' executable not found at %s", bin_path)
end


local function send_signal(kong_conf, signal)
if not kill.is_running(kong_conf.nginx_pid) then
return nil, "nginx not running in prefix: " .. kong_conf.prefix
return nil, fmt("nginx not running in prefix: %s", kong_conf.prefix)
end

log.verbose("sending %s signal to nginx running at %s", signal, kong_conf.nginx_pid)
Expand All @@ -49,8 +69,33 @@ local function send_signal(kong_conf, signal)
return true
end


local function set_process_secrets_env(kong_conf)
local secrets = process_secrets.extract(kong_conf)
if not secrets then
return false
end

local err
secrets, err = process_secrets.serialize(secrets, kong_conf.kong_env)
if not secrets then
return nil, err
end

return C.setenv("KONG_PROCESS_SECRETS", secrets, 1) == 0
end


local function unset_process_secrets_env(has_process_secrets)
if has_process_secrets then
C.unsetenv("KONG_PROCESS_SECRETS")
end
end


local _M = {}


function _M.find_nginx_bin(kong_conf)
log.debug("searching for OpenResty 'nginx' executable")

Expand Down Expand Up @@ -84,13 +129,14 @@ function _M.find_nginx_bin(kong_conf)
end

if not found then
return nil, ("could not find OpenResty 'nginx' executable. Kong requires" ..
" version %s"):format(tostring(nginx_compatible))
return nil, fmt("could not find OpenResty 'nginx' executable. Kong requires version %s",
tostring(nginx_compatible))
end

return found
end


function _M.start(kong_conf)
local nginx_bin, err = _M.find_nginx_bin(kong_conf)
if not nginx_bin then
Expand All @@ -101,6 +147,11 @@ function _M.start(kong_conf)
return nil, "nginx is already running in " .. kong_conf.prefix
end

local has_process_secrets, err = set_process_secrets_env(kong_conf)
if err then
return nil, err
end

local cmd = fmt("%s -p %s -c %s", nginx_bin, kong_conf.prefix, "nginx.conf")

log.debug("starting nginx: %s", cmd)
Expand All @@ -110,6 +161,7 @@ function _M.start(kong_conf)
-- "executeex" method
local ok, _, _, stderr = pl_utils.executeex(cmd)
if not ok then
unset_process_secrets_env(has_process_secrets)
return nil, stderr
end

Expand All @@ -121,13 +173,16 @@ function _M.start(kong_conf)
-- redirection instead.
local ok, retcode = pl_utils.execute(cmd)
if not ok then
return nil, ("failed to start nginx (exit code: %s)"):format(retcode)
unset_process_secrets_env(has_process_secrets)
return nil, fmt("failed to start nginx (exit code: %s)", retcode)
end
end

unset_process_secrets_env(has_process_secrets)
return true
end


function _M.check_conf(kong_conf)
local nginx_bin, err = _M.find_nginx_bin(kong_conf)
if not nginx_bin then
Expand All @@ -141,24 +196,27 @@ function _M.check_conf(kong_conf)

local ok, retcode, _, stderr = pl_utils.executeex(cmd)
if not ok then
return nil, ("nginx configuration is invalid " ..
"(exit code %d):\n%s"):format(retcode, stderr)
return nil, fmt("nginx configuration is invalid (exit code %d):\n%s",
retcode, stderr)
end

return true
end


function _M.stop(kong_conf)
return send_signal(kong_conf, "TERM")
end


function _M.quit(kong_conf)
return send_signal(kong_conf, "QUIT")
end


function _M.reload(kong_conf)
if not kill.is_running(kong_conf.nginx_pid) then
return nil, "nginx not running in prefix: " .. kong_conf.prefix
return nil, fmt("nginx not running in prefix: %s", kong_conf.prefix)
end

local nginx_bin, err = _M.find_nginx_bin(kong_conf)
Expand All @@ -179,4 +237,5 @@ function _M.reload(kong_conf)
return true
end


return _M
76 changes: 68 additions & 8 deletions kong/cmd/utils/prefix_handler.lua
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ local default_nginx_template = require "kong.templates.nginx"
local kong_nginx_template = require "kong.templates.nginx_kong"
local kong_nginx_stream_template = require "kong.templates.nginx_kong_stream"
local system_constants = require "lua_system_constants"
local process_secrets = require "kong.cmd.utils.process_secrets"
local openssl_bignum = require "resty.openssl.bn"
local openssl_rand = require "resty.openssl.rand"
local openssl_pkey = require "resty.openssl.pkey"
Expand All @@ -21,6 +22,7 @@ local bit = require "bit"
local nginx_signals = require "kong.cmd.utils.nginx_signals"


local getmetatable = getmetatable
local tonumber = tonumber
local tostring = tostring
local assert = assert
Expand Down Expand Up @@ -283,10 +285,11 @@ end
local function write_env_file(path, data)
os.remove(path)

local c = require "lua_system_constants"

local flags = bit.bor(c.O_CREAT(), c.O_WRONLY())
local mode = ffi.new("int", bit.bor(c.S_IRUSR(), c.S_IWUSR(), c.S_IRGRP()))
local flags = bit.bor(system_constants.O_CREAT(),
system_constants.O_WRONLY())
local mode = ffi.new("int", bit.bor(system_constants.S_IRUSR(),
system_constants.S_IWUSR(),
system_constants.S_IRGRP()))

local fd = ffi.C.open(path, flags, mode)
if fd < 0 then
Expand Down Expand Up @@ -318,6 +321,45 @@ local function write_env_file(path, data)
return true
end

local function write_process_secrets_file(path, data)
os.remove(path)

local flags = bit.bor(system_constants.O_RDONLY(),
system_constants.O_CREAT())

local mode = ffi.new("int", bit.bor(system_constants.S_IRUSR(),
system_constants.S_IWUSR()))

local fd = ffi.C.open(path, flags, mode)
if fd < 0 then
local errno = ffi.errno()
return nil, "unable to open process secrets path " .. path .. " (" ..
ffi.string(ffi.C.strerror(errno)) .. ")"
end

local ok = ffi.C.close(fd)
if ok ~= 0 then
local errno = ffi.errno()
return nil, "failed to close fd (" ..
ffi.string(ffi.C.strerror(errno)) .. ")"
end

local file, err = io.open(path, "w+b")
if not file then
return nil, "unable to open process secrets path " .. path .. " (" .. err .. ")"
end

local ok, err = file:write(data)

file:close()

if not ok then
return nil, "unable to write process secrets path " .. path .. " (" .. err .. ")"
end

return true
end

local function compile_kong_conf(kong_config)
return compile_conf(kong_config, kong_nginx_template)
end
Expand All @@ -331,7 +373,7 @@ local function compile_nginx_conf(kong_config, template)
return compile_conf(kong_config, template)
end

local function prepare_prefix(kong_config, nginx_custom_template_path, skip_write)
local function prepare_prefix(kong_config, nginx_custom_template_path, skip_write, write_process_secrets)
log.verbose("preparing nginx prefix directory at %s", kong_config.prefix)

if not pl_path.exists(kong_config.prefix) then
Expand Down Expand Up @@ -486,9 +528,15 @@ local function prepare_prefix(kong_config, nginx_custom_template_path, skip_writ
}

local refs = kong_config["$refs"]
local has_refs = refs and type(refs) == "table"

local secrets
if write_process_secrets and has_refs then
secrets = process_secrets.extract(kong_config)
end

for k, v in pairs(kong_config) do
if refs and refs[k] then
if has_refs and refs[k] then
v = refs[k]
end

Expand All @@ -505,12 +553,24 @@ local function prepare_prefix(kong_config, nginx_custom_template_path, skip_writ
end
end

local ok, err = write_env_file(kong_config.kong_env,
table.concat(buf, "\n") .. "\n")
local env = table.concat(buf, "\n") .. "\n"
local ok, err = write_env_file(kong_config.kong_env, env)
if not ok then
return nil, err
end

if secrets then
secrets, err = process_secrets.serialize(secrets, kong_config.kong_env)
if not secrets then
return nil, err
end

ok, err = write_process_secrets_file(kong_config.kong_process_secrets, secrets)
if not ok then
return nil, err
end
end

return true
end

Expand Down
Loading

0 comments on commit d55d33b

Please sign in to comment.