service: warn when capabilities cannot take effect

An ambient capability only reaches the effective set when euid is
non-zero, so a service that pairs `capabilities = { "^cap_..." }` with a
root user gets none of the restriction it asks for, and keeps the full
root set instead.  Finit read the list, applied it, and said nothing.  A
build without libcap dropped the list on the floor just as quietly.

Both now warn, naming the service:

    nginx: ambient capabilities ('^') have no effect as root, use a
    non-root user, or '%' and '!' entries

The ambient entries are read back from the parsed IAB value rather than
matched in the text, so inheritable ('%') and bounding ('!') entries stay
silent -- those work fine as root.

The warning repeats when the .conf files are re-read on runlevel change,
as parse warnings here already do.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
This commit is contained in:
Joachim Wiberg
2026-07-30 15:23:35 +02:00
parent 00794d41bc
commit ddda905487
2 changed files with 23 additions and 3 deletions
+6 -1
View File
@@ -169,7 +169,8 @@ ps -o user,pid,cmd -p $(pidof nginx)
- Linux kernel 4.3+ (for ambient capabilities support)
- libcap library installed
- Finit built with `--enable-libcap`
- Finit built with `--enable-libcap`, otherwise a `capabilities` list is
ignored, with a warning
## Limitations
@@ -181,6 +182,10 @@ ps -o user,pid,cmd -p $(pidof nginx)
- Using `user = "root"` with `^` capabilities will not work effectively, as ambient
capabilities are only added to the effective set when euid ≠ 0
- Use inheritable (`%`) or bounding (`!`) capabilities with `user = "root"` if needed
- Finit warns about this when reading the .conf file:
nginx: ambient capabilities ('^') have no effect as root, use a
non-root user, or '%' and '!' entries
- Services without `capabilities` use standard privilege dropping:
- Services with a non-root `user` have no special capabilities
- Services without `user` run as root with full capabilities
+17 -2
View File
@@ -1537,11 +1537,26 @@ static void parse_caps(svc_t *svc, char *caps)
return;
}
if (!strcmp(svc->username, "root")) {
cap_value_t cap;
for (cap = 0; cap <= CAP_LAST_CAP; cap++) {
if (!cap_iab_get_vector(cap_iab, CAP_IAB_AMB, cap))
continue;
/* the ambient set only reaches effective when euid != 0 */
logit(LOG_WARNING, "%s: ambient capabilities ('^') have no effect"
" as root, use a non-root user, or '%%' and '!' entries",
svc_ident(svc, NULL, 0));
break;
}
}
cap_free(cap_iab);
strlcpy(svc->capabilities, caps, sizeof(svc->capabilities));
#else
(void)svc;
(void)caps;
logit(LOG_WARNING, "%s: capabilities require Finit built with --enable-libcap,"
" ignoring '%s'", svc_ident(svc, NULL, 0), caps);
#endif
}