From 580926a5e5ecd4adac370861858027f620949a7d Mon Sep 17 00:00:00 2001 From: Thibault Charbonnier Date: Wed, 14 Sep 2016 17:14:59 -0700 Subject: [PATCH] hotfix(cli) seed random number generator in CLI If spawning multiple nodes at once (making use of the CLI from different host machines), we need to make sure none of them are using the same seed. To enforce this, we make use of the patched 'math.randomseed()' function, which should greatly reduce the probability of seed collision. To allow for this change, we need a special flag indicating our scripts if we are running inside of our CLI, so that out 'math.randomseed()' does not complain about being called in resty-cli's 'timer' context. * add `ngx.RESTY_CLI` flag in `bin/kong` * add an edge case in our patched `math.randomseed()` * apply `kong.core.globalpatches` to our CLI environment Fix #1592 --- bin/kong | 4 ++++ kong/cmd/init.lua | 7 +++++-- kong/core/globalpatches.lua | 24 ++++++++++++++++-------- 3 files changed, 25 insertions(+), 10 deletions(-) diff --git a/bin/kong b/bin/kong index 39f8628cdc8e..51b27c60512b 100755 --- a/bin/kong +++ b/bin/kong @@ -1,5 +1,9 @@ #!/usr/bin/env resty +-- a flag to detect whether our modules (especially kong.core.globalpatches) +-- are running under resty-cli or in a real ngx_lua, runtime environment. +ngx.RESTY_CLI = true + -- force LuaSocket usage to resolve `/etc/hosts` until -- supported by resty-cli. -- See https://github.com/Mashape/kong/issues/1523 diff --git a/kong/cmd/init.lua b/kong/cmd/init.lua index 6d7ad5b07616..327c244110ce 100644 --- a/kong/cmd/init.lua +++ b/kong/cmd/init.lua @@ -1,6 +1,9 @@ +require "kong.core.globalpatches" + +math.randomseed() + local pl_app = require "pl.lapp" local log = require "kong.cmd.utils.log" -local meta = require "kong.meta" local options = [[ --v verbose @@ -77,7 +80,7 @@ return function(args) log.set_lvl(log.levels.debug) end - log.verbose("Kong: %s", meta._VERSION) + log.verbose("Kong: %s", _KONG._VERSION) log.debug("ngx_lua: %s", ngx.config.ngx_lua_version) log.debug("nginx: %s", ngx.config.nginx_version) log.debug("Lua: %s", jit and jit.version or _VERSION) diff --git a/kong/core/globalpatches.lua b/kong/core/globalpatches.lua index c69d3aae3d60..901c5abc72cf 100644 --- a/kong/core/globalpatches.lua +++ b/kong/core/globalpatches.lua @@ -9,21 +9,29 @@ _G._KONG = { 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). +-- Once - properly - seeded, this method is replaced with a stub +-- one. This is to enforce best-practises for seeding in ngx_lua, +-- and prevents 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). +-- +-- This patched method will create a unique seed per worker process, +-- using a combination of both time and the worker's pid. -- luacheck: globals math _G.math.randomseed = function() if not seed then - if ngx.get_phase() ~= "init_worker" then + -- If we're in runtime nginx, we have multiple workers so we _only_ + -- accept seeding when in the 'init_worker' phase. + -- That is because that phase is the earliest one before the + -- workers have a chance to process business logic, and because + -- if we'd do that in the 'init' phase, the Lua VM is not forked + -- yet and all workers would end-up using the same seed. + if not ngx.RESTY_CLI and ngx.get_phase() ~= "init_worker" then error("math.randomseed() must be called in init_worker", 2) end seed = ngx.time() + ngx.worker.pid() - ngx.log(ngx.DEBUG, "random seed: ", seed, " for worker n", ngx.worker.id(), + ngx.log(ngx.DEBUG, "random seed: ", seed, " for worker nb ", ngx.worker.id(), " (pid: ", ngx.worker.pid(), ")") randomseed(seed) else