From: Maxime Henrion Date: Mon, 1 Jun 2026 14:49:02 +0000 (-0400) Subject: BUG/MINOR: startup: unbreak chroot with CAP_SYS_CHROOT X-Git-Tag: v3.4.0~16 X-Git-Url: http://git.ipfire.org/gitweb/index.cgi?a=commitdiff_plain;h=c24db7c76a2c930a78eabde10f3889b2fab574a1;p=thirdparty%2Fhaproxy.git BUG/MINOR: startup: unbreak chroot with CAP_SYS_CHROOT The use of the unshare() mechanism to get the ability to chroot as an unprivileged user produced a warning on some configurations where the haproxy process has the CAP_SYS_CHROOT capability. We now only attempt to use it when a previous chroot() call failed because of insufficient privileges. This should fix GitHub issue #3395. No backport needed. --- diff --git a/src/haproxy.c b/src/haproxy.c index 62c3262ea..6af7b031e 100644 --- a/src/haproxy.c +++ b/src/haproxy.c @@ -3361,16 +3361,18 @@ static void setup_user_ns(uid_t euid, gid_t egid) static int do_chroot(const char *prog, const char *path) { - const char *chroot_dir = path; - int error = 0; + const char *dir, *chroot_dir; + int error, chroot_error; + error = chroot_error = 0; + dir = chroot_dir = path; if (strcmp(path, "auto") == 0) { /* When "chroot auto" is used, we attempt to chroot to an * anonymous and read-only directory. */ char tmpdir[] = "/tmp/haproxy.XXXXXX"; - chroot_dir = mkdtemp(tmpdir); - if (chroot_dir == NULL) { + dir = mkdtemp(tmpdir); + if (dir == NULL) { ha_alert("[%s.main()] Cannot create(%s) for chroot auto.\n", prog, tmpdir); return -1; @@ -3381,16 +3383,32 @@ static int do_chroot(const char *prog, const char *path) * want to remove the directory). */ DISGUISE(rmdir(tmpdir)); + chroot_dir = "."; if (!error) - error = chroot("."); + chroot_error = chroot("."); } else if (strcmp(path, "/") != 0) { - error = chroot(path); + chroot_error = chroot(path); } - if (!error) +#ifdef CLONE_NEWUSER + /* If the chroot failed because of insufficient privileges and + * unshare(CLONE_NEWUSER) is available, we attempt it to gain the + * abilty to chroot as an unprivileged user. If that worked, we + * try the chroot again. + */ + if (chroot_error && errno == EPERM) { + uid_t euid = geteuid(); + gid_t egid = getegid(); + if (unshare(CLONE_NEWUSER) == 0) { + setup_user_ns(euid, egid); + chroot_error = chroot(chroot_dir); + } + } +#endif + if (!error && !chroot_error) error = chdir("/"); - if (error) { - ha_alert("[%s.main()] Cannot chroot(%s).\n", prog, chroot_dir); + if (error || chroot_error) { + ha_alert("[%s.main()] Cannot chroot(%s).\n", prog, dir); return -1; } @@ -3734,20 +3752,6 @@ int main(int argc, char **argv) } } -#ifdef CLONE_NEWUSER - /* When we aren't root and intend to chroot, we try the Linux-only - * unshare(CLONE_NEWUSER) mechanism if available to allow chroot as an - * unprivileged user. If that doesn't work, we just let the subsequent - * chroot() fail as it would have previously. - */ - if (geteuid() != 0 && global.chroot != NULL) { - uid_t euid = geteuid(); - gid_t egid = getegid(); - if (unshare(CLONE_NEWUSER) == 0) - setup_user_ns(euid, egid); - } -#endif - /* Must chroot and setgid/setuid in the children */ /* chroot if needed */ if (global.chroot != NULL && do_chroot(argv[0], global.chroot) != 0) {