Loading resty.saml replaces the host's random seed with time + pid
@shreemaan-abhishek is already working on this.
Since Sep 17, 2026.
- Dominant language
- Perl
- Stars
- 2
- Forks
- 3
- Avg merge
- 2d 20h
- Merged PRs (30d)
- 3
Description
What
resty.saml calls uuid.seed() when it is loaded (lua/resty/saml.lua:3). With no argument, resty.jit-uuid seeds from ngx.time() + ngx.worker.pid() and passes that to math.randomseed.
LuaJIT keeps one math.random state per process, so this replaces whatever the host seeded, for every math.random user in that worker, not only this library.
APISIX shows the effect. http_init_worker seeds each worker from /dev/urandom (apisix/init.lua, core.utils.get_seed_from_urandom), then loads plugins (plugin.init_worker()). Requiring saml-auth loads resty.saml, which re-seeds the worker with time + pid for the rest of its life. Any gateway with saml-auth enabled ends up with a weaker seed than it started with.
Why it matters
- Workers seeded in the same second with the same pid share a sequence. Container replicas usually have identical worker pids, so replicas started together (a rolling deploy) produce the same values: identical
AuthnRequestIDs andRelayStatevalues here, and identical UUIDs from every otherjit-uuiduser in the host (APISIX'srequest-id, for one). - Seeds also collide across pids: pid
pat secondtequals pidp + 1at secondt - 1. - The seed space is the start second times the pid range, so the values are guessable from a deploy time and a small pid range.
This has been the behaviour since before 0.2.6.
What to do
Keep seeding at load, so hosts that never seed stay covered, but seed from a strong source, e.g. read a number from /dev/urandom and pass it to uuid.seed(seed), with the current time + pid value only as the fallback when that read fails.
Two alternatives, both worse:
- Drop the load-time seed and leave it to the host. A host that never seeds is then left on LuaJIT's fixed default seed, so every worker produces the same sequence.
- Re-seed in each host after plugins load. It works around the library in every embedder and misses plugins loaded later, for example through a plugin syncer.
A regression test can start two workers from a forced identical time + pid and assert their first generate_v4() values differ.
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Assessment
This issue has not been assessed yet.