-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
hotfix(globalpatches) use OpenSSL to seed PRNG
This changes the seeding technique for LuaJIT's PRNG from using a combination of `time (s precision) + worker PID` to using OpenSSL's `RAND_bytes()`. Reasoning: in modern deployment setups such as application containers and AWS AMIs (etc...), it is a common practise to deploy from a forked VM, resulting in high chances of collision for PIDs at a seconds precision. This could result in duplicated PRNG seeds, which is ultimately the PRNG used to generate UUIDs in Kong, by the use of [lua-resty-jit-uuid](https://github.com/thibaultcha/lua-resty-jit-uuid). Solution: in order to have a higher entropy when seeding LuaJIT's PRNG, a proposed fix was to use `/dev/urandom`. This implementation however uses OpenSSL's `RAND_bytes()`, which has the advantage of returning an error if the entropy is estimated to be too low. However, this won't cover use cases where the VM has been forked, resulting in multiple VM clones with a high entropy, but equal to that of the other clones. We suggest that such deployment environment increase their cloned VMs entropy before starting Kong. Full changelog: * use OpenSSL's `RAND_bytes()` to read random bytes * truncate the final seed to 12 digits to prevent integer overflows * update fallback seeding technique (time + worker PID) to use ms precision, just in case * introduce a new `kong` lua shared dict. This dictionary's purpose is to hold essential data through Kong's lifecycle, and should eventually only be used through `safe_set()` (an abstraction for this could be envisaged later on, but is not the purpose of this patch) * chosen seeds for each worker are stored in the kong shm, and can be consulted via the `/` endpoint. There is currently no way to re-seed all the workers at once unless by sending `SIGHUP`, because only 1 worker would be receiving such a request through the Kong Admin API. * update `debug.traceback()` calls to use lvl 2 of the call stack, to show the actual caller of our patched `math.randomseed()` * update log messages to be more explicit Fix Kong#1751 Kong#1739 Kong#1623
- Loading branch information
1 parent
a0198c1
commit 06de2da
Showing
7 changed files
with
164 additions
and
46 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,35 +1,73 @@ | ||
local meta = require "kong.meta" | ||
local randomseed = math.randomseed | ||
|
||
_G._KONG = { | ||
_NAME = meta._NAME, | ||
_VERSION = meta._VERSION | ||
} | ||
|
||
local seed | ||
|
||
--- Seeds the random generator, use with care. | ||
-- The uuid.seed() method will create a unique seed per worker | ||
-- process, using a combination of both time and the worker's pid. | ||
-- We only allow it to be called once to prevent third-party modules | ||
-- from overriding our correct seed (many modules make a wrong usage | ||
-- of `math.randomseed()` by calling it multiple times or do not use | ||
-- unique seed for Nginx workers). | ||
-- luacheck: globals math | ||
_G.math.randomseed = function() | ||
if not seed then | ||
if ngx.get_phase() ~= "init_worker" then | ||
error("math.randomseed() must be called in init_worker", 2) | ||
end | ||
return function(opts) | ||
opts = opts or {} | ||
|
||
do | ||
local meta = require "kong.meta" | ||
|
||
seed = ngx.time() + ngx.worker.pid() | ||
ngx.log(ngx.DEBUG, "random seed: ", seed, " for worker n", ngx.worker.id(), | ||
" (pid: ", ngx.worker.pid(), ")") | ||
randomseed(seed) | ||
else | ||
ngx.log(ngx.DEBUG, "attempt to seed random number generator, but ", | ||
"already seeded with ", seed) | ||
_G._KONG = { | ||
_NAME = meta._NAME, | ||
_VERSION = meta._VERSION | ||
} | ||
end | ||
|
||
return seed | ||
do | ||
local util = require "kong.tools.utils" | ||
local seed | ||
local randomseed = math.randomseed | ||
|
||
_G.math.randomseed = function() | ||
if not seed then | ||
if not opts.cli and ngx.get_phase() ~= "init_worker" then | ||
ngx.log(ngx.WARN, "math.randomseed() must be called in init_worker ", | ||
"context\n", debug.traceback('', 2)) -- nil [message] arg doesn't work with level | ||
end | ||
|
||
local bytes, err = util.get_rand_bytes(8) | ||
if bytes then | ||
ngx.log(ngx.DEBUG, "seeding PRNG from OpenSSL RAND_bytes()") | ||
|
||
local t = {} | ||
for i = 1, #bytes do | ||
local byte = string.byte(bytes, i) | ||
t[#t+1] = byte | ||
end | ||
local str = table.concat(t) | ||
if #str > 12 then | ||
-- truncate the final number to prevent integer overflow, | ||
-- since math.randomseed() could get cast to a platform-specific | ||
-- integer with a different size and get truncated, hence, lose | ||
-- randomness. | ||
-- double-precision floating point should be able to represent numbers | ||
-- without rounding with up to 15/16 digits but let's use 12 of them. | ||
str = string.sub(str, 1, 12) | ||
end | ||
seed = tonumber(str) | ||
else | ||
ngx.log(ngx.ERR, "could not seed from OpenSSL RAND_bytes, seeding ", | ||
"PRNG with time and worker pid instead (this can ", | ||
"result to duplicated seeds): ", err) | ||
|
||
seed = ngx.now()*1000 + ngx.worker.pid() | ||
end | ||
|
||
ngx.log(ngx.DEBUG, "random seed: ", seed, " for worker nb ", | ||
ngx.worker.id()) | ||
|
||
if not opts.cli then | ||
local ok, err = ngx.shared.kong:safe_set("pid: " .. ngx.worker.pid(), seed) | ||
if not ok then | ||
ngx.log(ngx.WARN, "could not store PRNG seed in kong shm: ", err) | ||
end | ||
end | ||
|
||
randomseed(seed) | ||
else | ||
ngx.log(ngx.DEBUG, "attempt to seed random number generator, but ", | ||
"already seeded with: ", seed, "\n", | ||
debug.traceback('', 2)) -- nil [message] arg doesn't work with level | ||
end | ||
|
||
return seed | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters