]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
BUG/MINOR: acme: restrict the permissions of the generated account key
authorWilliam Lallemand <wlallemand@haproxy.com>
Thu, 6 Aug 2026 07:46:19 +0000 (09:46 +0200)
committerWilliam Lallemand <wlallemand@haproxy.com>
Fri, 7 Aug 2026 10:03:01 +0000 (10:03 +0000)
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)
src/acme.c

index 367fa12b1c674d40853a695ca1ebc1a17154d2cf..d536b2be97184607c7d29be5251d1bf24e6b1bdd 100644 (file)
@@ -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;