BUG/MINOR: init: use more than ha_random64() for the cluster secret

When not set, the cluster secret is randomly generated by two
consecutive calls to ha_random64(). However, the random64 PRNG may be
partially observed on a fully idle machine (QUIC retry tokens, UUID,
WS key), and it could be rolled back to the initial call that produced
the secret. This is purely theoretical as a normally loaded system
wouldn't reveal meaningful sequences, but better address this while
it's still easy.

The first here consists in isolating the cluster_secret from the PRNG
sequence. When RAND_bytes() is available and works, it's used. Otherwise
ha_random64() is mixed with uncorrelated bits from random().

This could be backported to stable releases.
This commit is contained in:
Willy Tarreau 2026-05-24 11:06:31 +02:00
parent c0e302fe79
commit 7d182a2ed5

View file

@ -1926,20 +1926,37 @@ static void dump_registered_keywords(void)
/* Generate a random cluster-secret in case the setting is not provided in the
* configuration. This allows to use features which rely on it albeit with some
* limitations.
* limitations. The function doesn't (solely) use ha_random64() because this
* secret is permanent, and ha_random64() can easily be leaked at various
* places.
*/
static void generate_random_cluster_secret()
{
/* used as a default random cluster-secret if none defined. */
uint64_t rand;
union {
uint64_t by64[2];
uint32_t by32[4];
uchar by8[16];
} rand;
/* The caller must not overwrite an already defined secret. */
BUG_ON(cluster_secret_isset);
BUG_ON(sizeof(global.cluster_secret) != sizeof(rand));
#ifdef USE_OPENSSL
if (RAND_bytes(rand.by8, sizeof(rand.by8)) != 1)
#endif
{
/* no SSL or not working, fall back to other sources */
rand.by64[0] = ha_random64();
rand.by64[1] = ha_random64();
rand.by32[0] ^= ((random() & 0x00ffff00) << 8) | ((random() & 0x00ffff00) >> 8);
rand.by32[1] ^= ((random() & 0x00ffff00) << 8) | ((random() & 0x00ffff00) >> 8);
rand.by32[2] ^= ((random() & 0x00ffff00) << 8) | ((random() & 0x00ffff00) >> 8);
rand.by32[3] ^= ((random() & 0x00ffff00) << 8) | ((random() & 0x00ffff00) >> 8);
}
rand = ha_random64();
memcpy(global.cluster_secret, &rand, sizeof(rand));
rand = ha_random64();
memcpy(global.cluster_secret + sizeof(rand), &rand, sizeof(rand));
cluster_secret_isset = 1;
}