From: William Lallemand Date: Thu, 6 Aug 2026 07:46:19 +0000 (+0200) Subject: BUG/MINOR: acme: restrict the permissions of the generated account key X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=19d02b81f0952b65ebdbb8919501d2e5d6956765;p=thirdparty%2Fhaproxy.git BUG/MINOR: acme: restrict the permissions of the generated account key When no ACME account key exists yet, haproxy generates one and writes it through a plain BIO_new_file(), so the file is created with the process' umask applied. With the common 022 umask the unencrypted private key ends up on disk as 0644, readable by every local user. Whoever reads that key can authenticate to the CA as this haproxy ACME account, and from there deactivate it or manipulate the orders and revocations for the domains it has validated. Let's restrict the file to 0600 right after creating it and before writing anything into it. This was introduced in 3.2 by commit 856b6042d ("MEDIUM: acme: generate the account file when not found"). It must be backported to 3.2. Reported-by: Claude (ANT-2026-2TZ0NDHX) --- diff --git a/src/acme.c b/src/acme.c index 367fa12b1..d536b2be9 100644 --- a/src/acme.c +++ b/src/acme.c @@ -864,6 +864,7 @@ static int cfg_postsection_acme() char *path; char store_path[PATH_MAX]; /* complete path with crt_base */ struct stat st; + int fd; /* if dns-persist-01 is set, add an extra INITIAL_DNS check */ if (strcasecmp(cur_acme->challenge, "dns-persist-01") == 0) @@ -1012,6 +1013,16 @@ static int cfg_postsection_acme() goto out; } + /* the file is created with the process' umask applied, which + * commonly leaves it world-readable; this is an unencrypted + * private key, restrict it before writing anything into it. + */ + if (BIO_get_fd(bio, &fd) > 0 && fchmod(fd, S_IRUSR | S_IWUSR) == -1) { + ha_alert("acme: cannot set the permissions of the file '%s'.\n", cur_acme->account.file); + err_code |= ERR_ALERT | ERR_FATAL | ERR_ABORT; + goto out; + } + if ((PEM_write_bio_PrivateKey(bio, key, NULL, NULL, 0, NULL, NULL)) == 0) { ha_alert("acme: cannot write account key '%s'.\n", cur_acme->account.file); err_code |= ERR_ALERT | ERR_FATAL | ERR_ABORT;