diff --git a/src/haproxy.c b/src/haproxy.c index 74b2e8ff1..753ef2b16 100644 --- a/src/haproxy.c +++ b/src/haproxy.c @@ -3597,21 +3597,6 @@ int main(int argc, char **argv) } } - if ((global.last_checks & LSTCHK_NETADM) && global.uid) { - ha_alert("[%s.main()] Some configuration options require full privileges, so global.uid cannot be changed.\n" - "", argv[0]); - protocol_unbind_all(); - exit(1); - } - - /* If the user is not root, we'll still let them try the configuration - * but we inform them that unexpected behaviour may occur. - */ - if ((global.last_checks & LSTCHK_NETADM) && getuid()) - ha_warning("[%s.main()] Some options which require full privileges" - " might not work well.\n" - "", argv[0]); - if ((global.mode & (MODE_MWORKER|MODE_DAEMON)) == 0) { /* chroot if needed */ @@ -3640,6 +3625,34 @@ int main(int argc, char **argv) if ((global.mode & (MODE_MWORKER | MODE_DAEMON)) == 0) set_identity(argv[0]); + /* set_identity() above might have dropped LSTCHK_NETADM if + * it changed to a new UID while preserving enough permissions + * to honnor LSTCHK_NETADM. + */ + if ((global.last_checks & LSTCHK_NETADM) && getuid()) { + /* If global.uid is present in config, it is already set as euid + * and ruid by set_identity() call just above, so it's better to + * remind the user to fix uncoherent settings. + */ + if (global.uid) { + ha_alert("[%s.main()] Some configuration options require full " + "privileges, so global.uid cannot be changed.\n", argv[0]); +#if defined(USE_LINUX_CAP) + ha_alert("[%s.main()] Alternately, if your system supports " + "Linux capabilities, you may also consider using " + "'setcap cap_net_raw' or 'setcap cap_net_admin' in the " + "'global' section.\n", argv[0]); +#endif + protocol_unbind_all(); + exit(1); + } + /* If the user is not root, we'll still let them try the configuration + * but we inform them that unexpected behaviour may occur. + */ + ha_warning("[%s.main()] Some options which require full privileges" + " might not work well.\n", argv[0]); + } + /* check ulimits */ limit.rlim_cur = limit.rlim_max = 0; getrlimit(RLIMIT_NOFILE, &limit); diff --git a/src/linuxcap.c b/src/linuxcap.c index 919086c0a..4a2a3ab04 100644 --- a/src/linuxcap.c +++ b/src/linuxcap.c @@ -23,6 +23,7 @@ #include #include #include +#include #include /* supported names, zero-terminated */ @@ -59,9 +60,10 @@ static uint32_t caplist; * - switch euid to non-zero * - set the effective and permitted caps again * - then the caller can safely call setuid() + * On success LSTCHK_NETADM is unset from global.last_checks, if CAP_NET_ADMIN + * or CAP_NET_RAW was found in the caplist from config. * We don't do this if the current euid is not zero or if the target uid - * is zero. Returns >=0 on success, negative on failure. Alerts or warnings - * may be emitted. + * is zero. Returns 0 on success, negative on failure. Alerts may be emitted. */ int prepare_caps_for_setuid(int from_uid, int to_uid) { @@ -101,6 +103,10 @@ int prepare_caps_for_setuid(int from_uid, int to_uid) ha_alert("Failed to set the final capabilities using capset(): %s\n", strerror(errno)); return -1; } + + if (caplist & ((1 << CAP_NET_ADMIN)|(1 << CAP_NET_RAW))) + global.last_checks &= ~LSTCHK_NETADM; + /* all's good */ return 0; }