capsh decodes the capability masks in /proc and lets you run a deliberately limited child shell to test them. These are observation and test tasks: none of the examples below change your account or the host.
You need a Linux system with the libcap2-bin package installed. The examples here were checked with libcap2-bin 1:2.66-5ubuntu2.4, which provides the installed capsh command on this machine. Allow about 15 minutes. You do not need root for the inspection examples, although some capability changes require the relevant privilege.
Check which executable your shell will run and read the installed option list. capsh's interface is a sequence of options processed in the order supplied, which matters later when you build a child environment:
$ command -v capsh
/usr/sbin/capsh
$ capsh --help | sed -n '1,12p'
usage: capsh [args ...]
--addamb=xxx add xxx,... capabilities to ambient set
--cap-uid=<n> use libcap cap_setuid() to change uid
--caps=xxx set caps as per cap_from_text()
--chroot=path chroot(2) to this path
--current show current caps and IAB vectors
--decode=xxx decode a hex string to a list of caps
--delamb=xxx remove xxx,... capabilities from ambient
--drop=xxx drop xxx,... caps from bounding set
--explain=xxx explain what capability xxx permits
--forkfor=<n> fork and make child sleep for <n> sec
Your path can differ. The installed help is worth trusting over the manual page here, since the local package may expose options added after the manual was written: on this machine the manual is dated 22 October 2021, while the package is newer.
Checkpoint: command -v capsh prints a path and capsh --help exits successfully.
Run --print before experimenting. It reports the effective state, bounding set, ambient set, inheritable and ambient information, securebits, user and group IDs, and libcap's guessed mode. The exact list depends on the process, kernel, container, and account that launched it:
$ capsh --print
Current: =
Bounding set =cap_chown,cap_dac_override,...
Ambient set =
Current IAB:
Securebits: 00/0x0/1'b0 (no-new-privs=0)
...
Guessed mode: HYBRID (4)
An empty Current: line is not proof that every privilege is unavailable. The bounding set is a separate ceiling, and permitted, effective, inheritable and ambient sets each answer a different question. For a shorter report, use --current:
$ capsh --current
Current: =
Current IAB:
To compare that against the kernel's raw view, read the capability fields for a process directly. Also read-only:
$ grep -E '^(Cap(Inh|Prm|Eff|Bnd|Amb)|NoNewPrivs):' /proc/self/status
CapInh: 0000000000000000
CapPrm: 0000000000000000
CapEff: 0000000000000000
CapBnd: 0000003fffffffff
CapAmb: 0000000000000000
NoNewPrivs: 0
Those values are only an example of the shape. A container, service manager, login shell or set of file capabilities can all produce different masks, so check the actual output on the process you are investigating.
When a status field holds a hexadecimal vector, pass it to --decode. It turns the low bits into capability names without changing the process:
$ capsh --decode=3
0x0000000000000003=cap_chown,cap_dac_override
3 here is only a small demonstration: copy the complete hex value from the field you actually care about, and decode each field separately, since CapEff answers a different question from CapBnd or CapAmb.
Checkpoint: capsh --decode=3 reports cap_chown and cap_dac_override, and makes no persistent change.
Use --supports for a script-friendly feature check. It exits zero when the named capability is known to the running kernel, and one when it is not. It does not grant the capability:
$ capsh --supports=cap_net_bind_service
$ printf 'exit=%s\n' "$?"
exit=0
$ capsh --explain=cap_net_bind_service
cap_net_bind_service (10) [/proc/self/status:CapXXX: 0x0000000000000400]
Allows a process to bind to privileged ports:
- TCP/UDP sockets below 1024
- ATM VCIs below 32
Support is not possession. A successful --supports check says the capability exists as a concept on this kernel, not that your process has it in its permitted or effective set. Use --has-p=cap_net_bind_service, --has-a=cap_net_bind_service, or the matching --print output to test a particular set. These use exit status, so a shell script needs to check $? or use them directly in an if.
-- ends capsh's own options and hands the rest to /bin/bash. Use -c to run one command and exit, so you never leave an experimental shell open by accident:
$ capsh -- -c 'printf "uid=%s\\n" "$UID"; id -u; capsh --current'
uid=1000
1000
Current: =
Current IAB:
The child prints its identity and capability state, then exits, using your current account with no sudo needed. For a different shell executable, the manual provides --shell=/full/path, but only use that with a path you have checked yourself.
Warning: Do not casually add --user, --uid, --gid, --caps, --inh, or --addamb to a copied command. These alter the child's identity or capability state, some need privileges, and a process can stay privileged in a permitted set after its effective capabilities are cleared. If the child is meant as a security boundary, validate the whole resulting state and the program it runs, not just one line of output.
--drop=capability removes capabilities from the process's bounding set. That reduction is not restored on exit, but normally affects only that process and its descendants. --noamb drops all ambient capabilities for the process. --chroot=/path changes the child's root and needs CAP_SYS_CHROOT: it is not a general container or privilege-isolation mechanism.
Warning: Options that change UID, GID, securebits, capability sets, ambient capabilities, the bounding set, or the root directory are security-sensitive. Test them in a disposable process or isolated environment first. Never paste a command containing a placeholder such as /path/to/root into a production shell, and never reach for sudo just to make an example succeed. There is no general undo for a dropped bounding-set capability inside the affected process: start a fresh process instead.
For a script, prefer a small assertion followed by a controlled exec. This checks the capability exists, then runs something harmless:
$ if capsh --supports=cap_net_bind_service; then
> printf '%s\n' 'capability name is supported'
> else
> printf '%s\n' 'capability name is not supported' >&2
> exit 1
> fi
capability name is supported
That example deliberately stops at discovery. Granting or arranging a capability belongs in the service or deployment configuration that owns the process, with its own review of why it is needed.
capsh --print gave you the actual state of the process you are investigating.--decode turned a real hexadecimal mask into names.--supports confirmed availability, nothing more.-- -c and exited without changing persistent host state.