Files
finit/doc/config/capabilities.md
T
Joachim Wiberg ddda905487 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>
2026-07-30 15:23:35 +02:00

202 lines
6.8 KiB
Markdown

Linux Capabilities
==================
Finit supports Linux capabilities, allowing services to run with minimal
required privileges instead of running as root. This significantly improves
system security by following the principle of least privilege.
## Overview
Linux capabilities divide the traditional root privileges into distinct units
that can be independently granted to processes. For example, a web server only
needs the capability to bind to privileged ports (< 1024), not full root access.
Finit uses the modern IAB (Inheritable, Ambient, Bounding) API from libcap,
which is the same approach used by other modern service managers like dinit.
## Basic Usage
Capabilities are specified with the `capabilities` key, alias `caps`:
```conf
service nginx {
description = "Web server"
runlevel = "2345"
user = "www-data"
group = "www-data"
capabilities = { "^cap_net_bind_service" }
command = "/usr/sbin/nginx -g 'daemon off;'"
}
```
This example allows nginx to bind to privileged ports (like 80 and 443) while
running as the unprivileged `www-data` user.
## IAB Format
The capability string uses the IAB (Inheritable, Ambient, Bounding) format
with the following prefixes:
- `^` **Ambient** (and Inheritable) - **Recommended for most use cases**
- Capabilities survive across `exec()` calls
- Automatically raised to effective after exec
- Example: `^cap_net_bind_service`
- `%` **Inheritable** only
- Requires the executed binary to have matching file capabilities
- Less common, more complex setup
- Example: `%cap_net_admin`
- `!` **Bounding** - Block capability from bounding set
- Prevents the service from ever acquiring this capability
- Useful for security hardening
- Example: `!cap_sys_admin`
Multiple capabilities can be specified as a comma-separated list:
```conf
capabilities = { "^cap_net_raw", "^cap_net_admin", "^cap_net_bind_service" }
```
## Common Use Cases
### Web Server (Privileged Ports)
Allow a web server to bind to ports 80 and 443 without running as root:
```conf
service webserver {
runlevel = "2345"
user = "www-data"
group = "www-data"
capabilities = { "^cap_net_bind_service" }
command = "/usr/sbin/nginx -g 'daemon off;'"
}
```
### Network Monitoring (Raw Sockets)
Allow packet capture without root privileges:
```conf
service tcpdump {
runlevel = "2345"
user = "tcpdump"
capabilities = { "^cap_net_raw", "^cap_net_admin" }
command = "/usr/sbin/tcpdump -i eth0 -w /var/log/capture.pcap"
}
```
### NTP Daemon (System Time)
Allow time synchronization without full root:
```conf
service ntpd {
runlevel = "2345"
user = "ntp"
capabilities = { "^cap_sys_time", "^cap_sys_nice" }
command = "/usr/sbin/ntpd -n"
}
```
## Available Capabilities
Common capabilities include (see `man 7 capabilities` for the complete list):
- `cap_chown` - Make arbitrary changes to file UIDs and GIDs
- `cap_dac_override` - Bypass file read, write, and execute permission checks
- `cap_dac_read_search` - Bypass file read permission checks
- `cap_fowner` - Bypass permission checks on operations that normally require filesystem UID
- `cap_kill` - Bypass permission checks for sending signals
- `cap_net_admin` - Perform various network-related operations
- `cap_net_bind_service` - Bind to privileged ports (< 1024)
- `cap_net_raw` - Use RAW and PACKET sockets
- `cap_setgid` - Make arbitrary manipulations of process GIDs
- `cap_setuid` - Make arbitrary manipulations of process UIDs
- `cap_sys_admin` - Perform system administration operations (very powerful!)
- `cap_sys_module` - Load and unload kernel modules
- `cap_sys_nice` - Raise process nice value and change scheduling
- `cap_sys_time` - Set system clock
## Security Best Practices
1. **Use the minimum required capabilities**
- Only grant what the service actually needs
- Don't grant `cap_sys_admin` unless absolutely necessary
2. **Specify a user (preferably non-root)**
- The `user` setting is **required** for `capabilities` to take effect
- For ambient capabilities (`^`), use a non-root user (not `"root"`)
- Example: `user = "www-data"`, `user = "nginx"`, `user = "tcpdump"`
3. **Use ambient capabilities (`^`)**
- The `^` prefix ensures capabilities survive exec()
- Simpler than setting file capabilities on binaries
4. **Block dangerous capabilities**
- Use `!` to explicitly block capabilities you don't want
- Example: `!cap_sys_admin,!cap_sys_module`
5. **Test with `getpcaps`**
- After starting a service, verify its capabilities:
```bash
getpcaps $(pidof nginx)
```
- Should show only the capabilities you granted
## Verification
After configuring a service with capabilities, verify it works correctly:
```bash
# Start the service
initctl start webserver
# Check the process capabilities
getpcaps $(pidof nginx)
# Should show something like:
# 12345: cap_net_bind_service=eip
# Verify the user
ps -o user,pid,cmd -p $(pidof nginx)
# Should show the service running as the specified user
```
## Requirements
- Linux kernel 4.3+ (for ambient capabilities support)
- libcap library installed
- Finit built with `--enable-libcap`, otherwise a `capabilities` list is
ignored, with a warning
## Limitations
- `capabilities` requires `user` to be set for it to take effect
- Without `user`, the service runs as root with full capabilities and
the `capabilities` list is silently ignored
- You can use `user = "root"`, but see below about ambient capabilities
- For ambient capabilities (`^`, recommended), the user **must be non-root**
- 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
- Some very old binaries may not work correctly with ambient capabilities
- File system capabilities are not managed by Finit (use `setcap` for that)
## See Also
- [capabilities(7)](https://man7.org/linux/man-pages/man7/capabilities.7.html) - Linux capabilities overview
- [cap_iab(3)](https://man7.org/linux/man-pages/man3/cap_iab.3.html) - IAB capability API documentation
- [setcap(8)](https://man7.org/linux/man-pages/man8/setcap.8.html) - Set file capabilities
- [getcap(8)](https://man7.org/linux/man-pages/man8/getcap.8.html) - Query file capabilities
- [capsh(1)](https://man7.org/linux/man-pages/man1/capsh.1.html) - Capability shell wrapper