From 2cd52a88bee19c8948df8a71e9494d2474f05e5b Mon Sep 17 00:00:00 2001 From: Valentine Krasnobaeva Date: Fri, 21 Jun 2024 18:11:46 +0200 Subject: [PATCH] MINOR: cli/debug: show dev: show capabilities If haproxy compiled with Linux capabilities support, let's show process capabilities before applying the configuration and at runtime in 'show dev' command output. This maybe useful for debugging purposes. Especially in cases, when process changes its UID and GID to non-priviledged or it has started and run under non-priviledged UID and needed capabilities are set by admin on the haproxy binary. --- include/haproxy/linuxcap.h | 2 ++ src/debug.c | 53 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+) diff --git a/include/haproxy/linuxcap.h b/include/haproxy/linuxcap.h index 34d4d9a6b..7f9fe97cc 100644 --- a/include/haproxy/linuxcap.h +++ b/include/haproxy/linuxcap.h @@ -3,6 +3,8 @@ #include #include +#define CAPS_TO_ULLONG(low, high) (((ullong)high << 32) | (ullong)low) + /* for haproxy process itself, allocate this 8 byte-size struct only once in * .data and makes it accessible from other compile-units, because we always * fill it with the same values and because we could use it to collect diff --git a/src/debug.c b/src/debug.c index a7626b18f..522c78af5 100644 --- a/src/debug.c +++ b/src/debug.c @@ -38,6 +38,9 @@ #include #include #include +#if defined(USE_LINUX_CAP) +#include +#endif #include #include #include @@ -113,6 +116,13 @@ struct post_mortem { pid_t pid; uid_t boot_uid; gid_t boot_gid; +#if defined(USE_LINUX_CAP) + struct { + // initial process capabilities + struct __user_cap_data_struct boot[_LINUX_CAPABILITY_U32S_3]; + int err; // errno, if capget() syscall fails + } caps; +#endif struct rlimit limit_fd; // RLIMIT_NOFILE struct rlimit limit_ram; // RLIMIT_DATA char **argv; @@ -500,6 +510,10 @@ static int debug_parse_cli_show_libs(char **args, char *payload, struct appctx * /* parse a "show dev" command. It returns 1 if it emits anything otherwise zero. */ static int debug_parse_cli_show_dev(char **args, char *payload, struct appctx *appctx, void *private) { +#if defined(USE_LINUX_CAP) + /* to dump runtime process capabilities */ + struct __user_cap_data_struct runtime_caps[_LINUX_CAPABILITY_U32S_3] = { }; +#endif const char **build_opt; int i; @@ -559,6 +573,41 @@ static int debug_parse_cli_show_dev(char **args, char *payload, struct appctx *a chunk_appendf(&trash, " boot gid: %d\n", post_mortem.process.boot_gid); chunk_appendf(&trash, " runtime gid: %d\n", getegid()); +#if defined(USE_LINUX_CAP) + /* let's dump saved in feed_post_mortem() initial capabilities sets */ + if(!post_mortem.process.caps.err) { + chunk_appendf(&trash, " boot capabilities:\n"); + chunk_appendf(&trash, " \tCapEff: 0x%016llx\n", + CAPS_TO_ULLONG(post_mortem.process.caps.boot[0].effective, + post_mortem.process.caps.boot[1].effective)); + chunk_appendf(&trash, " \tCapPrm: 0x%016llx\n", + CAPS_TO_ULLONG(post_mortem.process.caps.boot[0].permitted, + post_mortem.process.caps.boot[1].permitted)); + chunk_appendf(&trash, " \tCapInh: 0x%016llx\n", + CAPS_TO_ULLONG(post_mortem.process.caps.boot[0].inheritable, + post_mortem.process.caps.boot[1].inheritable)); + } else + chunk_appendf(&trash, " capget() failed with: %s.\n", + strerror(post_mortem.process.caps.err)); + + + /* let's print actual capabilities sets, could be useful in order to compare */ + if (capget(&cap_hdr_haproxy, runtime_caps) == 0) { + chunk_appendf(&trash, " runtime capabilities:\n"); + chunk_appendf(&trash, " \tCapEff: 0x%016llx\n", + CAPS_TO_ULLONG(runtime_caps[0].effective, + runtime_caps[1].effective)); + chunk_appendf(&trash, " \tCapPrm: 0x%016llx\n", + CAPS_TO_ULLONG(runtime_caps[0].permitted, + runtime_caps[1].permitted)); + chunk_appendf(&trash, " \tCapInh: 0x%016llx\n", + CAPS_TO_ULLONG(runtime_caps[0].inheritable, + runtime_caps[1].inheritable)); + } else + chunk_appendf(&trash, " capget() failed with: %s.\n", + strerror(errno)); + +#endif if ((ulong)post_mortem.process.limit_fd.rlim_cur != RLIM_INFINITY) chunk_appendf(&trash, " fd limit (soft): %lu\n", (ulong)post_mortem.process.limit_fd.rlim_cur); if ((ulong)post_mortem.process.limit_fd.rlim_max != RLIM_INFINITY) @@ -2285,6 +2334,10 @@ static int feed_post_mortem() post_mortem.process.argc = global.argc; post_mortem.process.argv = global.argv; +#if defined(USE_LINUX_CAP) + if (capget(&cap_hdr_haproxy, post_mortem.process.caps.boot) == -1) + post_mortem.process.caps.err = errno; +#endif getrlimit(RLIMIT_NOFILE, &post_mortem.process.limit_fd); getrlimit(RLIMIT_DATA, &post_mortem.process.limit_ram);