From 7d182a2ed5c6489314da339e832534b28281bbd9 Mon Sep 17 00:00:00 2001 From: Willy Tarreau Date: Sun, 24 May 2026 11:06:31 +0200 Subject: [PATCH] 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. --- src/haproxy.c | 27 ++++++++++++++++++++++----- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/src/haproxy.c b/src/haproxy.c index 560bc1e07..51d1fc0de 100644 --- a/src/haproxy.c +++ b/src/haproxy.c @@ -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; }