table of contents
| xchpst(8) | System Manager's Manual | xchpst(8) |
NAME¶
xchpst — eXtended
CHange Process STate
SYNOPSIS¶
xchpst |
--help |
xchpst |
--version |
xchpst |
--exit[=retcode] |
xchpst |
[OPTIONS] [--]
command ... |
DESCRIPTION¶
The xchpst utility changes process state
according to the supplied options and then calls
exec()
on a named executable with the positional arguments.
xchpst is a backwards-compatible extension
to the chpst(8) tool which is supplied with runit.
xchpst enables runit service scripts to take
advantage of hardening capabilities available with recent Linux kernels such
as namespaces and capabilities. xchpst can set up
shadow subtrees within the filesystem hierarchy to isolate long-running
services from parts of the system to which they ought to need no access,
e.g. with private /tmp areas and read-only
/usr.
Extended xchpst options¶
The extra options provided by xchpst are
as follows:
--help- Show help text and usage.
--filefile- Read options from file. See Options file for the file format.
--exit[=retcode]- Exit immediately with exit status 0 if the given options are supported. retcode if specified.
--mount-ns- Create new mount namespace. Various other options also implicitly enable mount namespaces as this is important to their operation; this option is rarely likely to be needed to be specified explicitly.
--net-ns- Create new network namespace. This will more or less isolate the process from the networking subsystem.
--uts-ns- Create new UTS namespace.
--pid-ns- Create a PID namespace. This implies
--fork-joinbecause a new process is needed to act as PID 1 and in order to be able to mount a new procfs for the namespace. --fork-join- Fork a new process and wait for it to finish, passing on to the child
process any signals received by the
xchpstprocess. This option is necessary to take advantage of PID namespaces. The exit status is that of the child process. --user-ns- Create a user namespace.
--adopt-netpath- Adopt the network namespace bound to path. Per
convention among other network-namespace aware applications, e.g.
ip(8), it is interpreted relative to
/var/run/netns. The binding will be deleted from
the filesystem meaning that the namespace will disappear when the process
exits, if there is no other reference to it. This allows the calling
script to set up a suitable networking environment for the process and
hand it over.
A later version of this tool may replace the asymmetric
--adopt-netoption with a general mechanism to enter an existing namespace without then unbinding it from the filesystem. --new-root- Create a new root filesystem (will implicitly enable the creation of a new
mount namespace). The new root filesystem is created as a tmpfs and all
the top-level directories in the original root filesystem are bind mounted
and any symlinks are replicated.
This mode of operation stops a command that retains the capability to unmount filesystems from unmounting any read-only or private bind mounts (from
--ro-sys,--ro-home,--ro-etc,--private-tmpor--private-run) to reveal the stacked read-write mounts. When used in combination with--ro-home, which bind mounts /run/user as read only, or any other case where the read-only bind mount is not on an existing mountpoint, there may still end up stacked read-write mounts underneath. --private-run- Mount an isolated /run directory for the process.
Unless
--new-rootis also specified, the old shared /run directory will still be accessible if the stacked mount is removed. --private-tmp- Mount an isolated /tmp directory for the process.
Unless
--new-rootis also specified, the old shared /tmp directory will still be accessible if the stacked mount is removed. --protect-home- Mount isolated /home, /root
and /run/user directories for the process.
Unless
--new-rootis also specified, the old shared host directories will still be accessible if the stacked mounts are removed. --ro-sys- Convert /usr and /boot
into read-only mounts. Note that if the hardened process has the rights to
unmount filesystems, it can reveal the original writable filesystems. The
--new-rootoption is designed to prevent this. Alternatively, use--cap-bs-dropto remove the ‘CAP_SYS_ADMIN’ capability, preventing the bind mount from being unmounted. --ro-home- Convert /home, /root and
and /run/usr into read-only mounts. This option
has the same limitations as
--ro-sys. --ro-etc- Convert /etc into a read-only mount. This option
has the same limitations as
--ro-sys. --caps-bs-keepcapability[,capability...]- Keeps only the listed capabilities in the bounding set.
--caps-bs-dropcapability[,capability...]- Drops the listed capabilities from the bounding set. Use only one of the two options governing the bounding set.
--caps-keepcapability[,capability...]- Retain the listed capabilities when dropping to a non-root user.
--caps-dropcapability[,capability...]- Drop the listed capabilities when dropping to a non-root user, but retain all others.
--no-new-privs- Prevent the target application from obtaining any new privileges. See PR_SET_NO_NEW_PRIVS(2const).
--cpu-schedulerother|batch|idle- Set the scheduler policy, as per sched_setscheduler(2).
--io-schedulerrt|best-effort|idle[:priority]- Set the I/O scheduler policy and priority, as per ionice(1).
--cpusstart[-end[:stride]][,...]- Set CPU affinity in the same format as taskset(1).
--umaskmode- Set umask to the octal value mode.
--appname- Override program name used for pre-creating system directories.
--run-dir- Create a directory for the program under /run, owned by the appropriate user.
--state-dir- Create a directory for the program under /var/lib, owned by the appropriate user.
--log-dir- Create a directory for the program under /var/log, owned by the appropriate user.
--cache-dir- Create a directory for the program under /var/cache, owned by the appropriate user.
--login- Create a login environment, using the user specified by -u, -U or the current user, in order of preference. If this option is specified and no command is specified to be executed, then the shell defined for the given user is launched, instead of an error being returned.
--oomadjustment- Set the out-of-memory (OOM) score adjustment to adjustment.
-a,--limit-asbytes- Set soft limit for address space size. The short version of this option
matches
softlimitemulation but is deprecated in case a future version ofchpst, which does not provide it, uses this option character for a different purpose and my be removed. -r,--limit-rssbytes- Set soft limit for resident set size. The short version of this option
matches
softlimitemulation but is deprecated in case a future version ofchpst, which does not provide it, uses this option character for a different purpose and may be removed. This option is basically useless on any system new enough to run xchpst (see setrlimit(2)). -s,--limit-stackbytes- Set soft limit for stack segment size. The short version of this option
matches
softlimitemulation but is deprecated in case a future version ofchpst, which does not provide it, uses this option character for a different purpose and may be removed. --limit-memlockbytes- Set soft limit for amount of locked memory.
--limit-msgqueuebytes- Set soft limit for message queue space for this user.
--limit-niceniceness- Set 20 minus the minimum niceness possible for this process.
--limit-rtptioprio- Set soft limit for real time priority of the process.
--limit-rttimems- Set soft limit for amount of real time processing between blocking system calls.
--limit-sigpendingnumber- Set soft limit for the number of pending signals permitted for the process.
--limit-locksnumber- Set soft limit for the number of file locks that this process may hold.
--hardlimit- Also set the hard limit for any soft limit option that follows.
-@- Switch to chpst-compatible option handling only for the remaining options.
This is to support scripts that can convert an
xchpstinvocation into a command line forchpstifxchpstis not present on the system.
chpst-compatible options¶
The options compatible with classic chpst
are as follows:
-u[:]user[:group]...- Set uid, gid and supplementary groups. Prepend the argument with a colon
for numerical inputs rather than names to be looked up. If no group is
specified then the specified user's group is used and as an extension to
chpst, supplementary groups are also looked up. -U[:]user[:group]...- Like
-ubut the environment variablesUIDandGIDare set instead of changing the user. As an extension tochpst, supplementary groups, if present, are joined in a comma-separated list in theGIDLISTenvironment variable. -bargv0- Set argv[0] to argv0 instead of the target executable path when launching the program.
-edir- Populate environment. For every file within dir, the filename represents an environment variable that will be set or unset. The first line of the corresponding files is the content to be set, with NUL characters replaced by LF and trailing whitespace removed. If the file is 0 bytes long then the variable is unset. (So a file with just a newline results in the variable being set with an empty value.)
-/dir- Run in a chroot. Change to the dir directory and make it the new root.
-Cdir- Change directory. Change to the dir directory (after any chroot setting is applied).
-ninc- Increase niceness by inc, which can be negative, resulting in the process taking a higher priority.
-lfile- Wait for lock. Take a lock out on file and wait to
obtain it before proceeding to
exec(). -Lfile- Try to obtain lock; bail out if it can't be obtained.
-mbytes- Set soft limit for data and stack segments and virtual memory size and locked memory.
-dbytes- Set soft limit for data segment size.
-ofiles- Set soft limit for the number of open files.
-pprocs- Set soft limit for the number of processes for this user.
-fbytes- Set soft limit for the size of file that this process may create.
-cbytes- Set soft limit for the size of core this process may dump.
-tseconds- Set soft limit for the amount of CPU time this process may consume.
-v- Be verbose. This option may be repeated for increased verbosity to support debugging.
-V- Show
xchpstversion number. -P- Make this process the process group leader, allocating a new session idea.
-0- Close stdin.
-1- Close stout.
-2- Close stderr.
s6-inspired options¶
The following options to xchpst correspond
to options from the s6-applyuidgid command and their
implementation supports the emulation mode thereof:
--ugids-from-env- Set uid, gid and comma-separated supplementary groups from the
UID,GIDandGIDLISTenvironment variables. The values must be numeric and are not looked up in the password database, by design. --ugids-clear-env- Clear the
UID,GIDandGIDLISTenvironment variables before starting the next process.
Resource limit options¶
The resource limit options above take a parameter in one of the following forms:
- soft
- Set only the soft limit, in the style of
chpstandsoftlimit, unless--hardlimithas previously been specified, in which case both soft and hard limits are defined, in the style ofprlimit. - soft:
- Set only the soft limit, in the style of prlimit(1).
- soft:hard
- Set soft and hard limits.
- :hard
- Set only the hard limit.
- +both
- Set both soft and hard limits.
An unlimited limit may be selected by any value of
‘-1’,
‘unlimited’ or
‘infinity’.
Emulating daemontools family tools¶
xchpst follows
chpst in emulating chain-loading utilities from the
daemontools suite and other suites in the daemontools 'family', which tend
to follow similar principles and have partially-compatible options and
behaviour. These emulated modes take effect when the
xchpst executable is invoked under the name of the
respective tool such as via a symbolic link. The matching of
argv[0] against emulated tool names ignores file
extensions (*.ext) and known suite prefixes
(s6-*).
Note that there can be variations between the options and behaviour of equivalent tools from the different suites so documentation should be checked before relying on emulation modes.
The following table describes the emulated tools, their home
suite, the equivalent xchpst option and notable
differences in behaviour.
| Tool | Home suite | Equivalent xchpst option | Differences |
| chpst | runit | supplemental group lookup | |
| envdir | daemontools | -e | |
| envuidgid | daemontools | -U | |
| pgrphack | daemontools | -P | |
| setlock | daemontools | -l | |
| setuidgid | daemontools | -u | |
| softlimit | daemontools | ||
| applyuidgid | s6 | --ugids-from-env | |
| setuidgid-fromenv | nosh | --ugids-from-env |
As an additional feature, all these tools when so invoked, accept
the -v option to increase verbosity.
Options file¶
An options file specifies additional options to apply, one option
per line. Each line begins with an option name. Options that take an
argument have horizontal white space and the option value following the
option name. Comments begin with a ‘#’
character and may only be preceded by whitespace, otherwise they will be
interpreted as part of an option name or value.
Example options file:
# Comment line private-tmp app my-service run-dir pid-ns hardlimit o 2048
EXIT STATUS¶
- 0
- The default exit status when
--exitis specified is 0. This can be used for a quick test thatxchpstis available on the system in shell scripts and that the given options are supported. - 100
- The return code when an invalid option or option argument is specified, including if a username cannot be resolved, for example.
- 111
- When the requested process state cannot be changed.
- other
- The
--exitoption takes an optional argument with a return code to use.
If there is no error and the intended application is
exec()'d, the exit status will be that of the
application, not xchpst.
Behaviour on failure to apply¶
The table below divides the process change options between those that abort on failure to effect the requested change (with error code 111) and those that continue execution. Nonsense configuration values always fail with error code 100.
| Option group | Abort on error | Continue on error |
| chpst | u U e / C n L l m d o p f c r t | b 0 1 2 |
| rlimit | s a limit-memlock limit-msgqueue limit-nice limit-rtprio limit-rttime limit-sigpending limit-locks | |
| namespaces | fork-join new-root mount-ns net-ns user-ns pid-ns uts-ns net-adopt | |
| capabilities 1 | cap-bs-keep cap-bs-drop caps-keep caps-drop | |
| filesystem | private-run private-tmp protect-home ro-sys ro-home ro-etc run-dir state-dir cache-dir log-dir | |
| other | cpus cpu-scheduler io-scheduler no-new-privs umask oom login ugids-from-env ugids-clear-env |
- [1]
- If capabilities are not available in the kernel, errors are ignored. Otherwise any failure causes an abort. See BUGS
NOTES¶
systemd option mapping¶
The table below shows how some systemd service directives map onto
xchpst options. See
systemd.exec(5) This is not an exhaustive list.
| systemd | xchpst | Differences |
| ProtectSystem=yes | ro-sys | |
| ProtectSystem=full | ro-sys ro-etc | |
| ProtectHome=read-only | ro-home | |
| ProtectHome=tmpfs | protect-home | |
| PrivateTmp=yes | private-tmp | |
| CapabilityBoundingSet= | cap-bs-keep | |
| CapabilityBoundingSet=~ | cap-bs-drop | |
| AmbientCapabilities= | caps-keep | |
| AmbientCapabilities=~ | caps-drop | |
| NoNewPrivileges=yes | no-new-privs | |
| CPUAffinity= | cpus | use taskset 1 format |
| CPUSchedulingPolicy= | cpu-scheduler | |
| IOSchedulingClass= IOSchedulingPriority= | io-scheduler | |
| UMask= | umask | |
| RuntimeDirectory= | run-dir | Leaf name governed by target command or app option |
| StateDirectory= | state-dir | Leaf name governed by target command or app option |
| CacheDirectory= | cache-dir | Leaf name governed by target command or app option |
| LogsDirectory= | log-dir | Leaf name governed by target command or app option |
| OOMScoreAdjust= | oom | |
| User= Group= | u | Check syntax. In particular, numeric ids begin with a colon. |
| User= with ExecStart=+ or ExecStart=! | U | In these modes, systemd does not drop the user, expecting the application to do that, but instead uses the specified username for other options. In this case the environment variable setting by is not useful but other options like run-dir will use the right user. |
Resource limit option mapping¶
The table below shows how xchpst resource
limit options correspond to those of other tools.
| xchpst | chpst | softlimit | prlimit | ulimit | systemd |
| d | d | d | d | d | LimitDATA= |
| o | o | o | n | n | LimitNOFILE= |
| p | p | p | u | u | LimitNPROC= |
| f | f | f | f | f | LimitFSIZE= |
| c | c | c | c | c | LimitCORE= |
| t | t | t | t | t | LimitCPU= |
| m | m | m | d s l v | d s l v | LimitDATA= LimitSTACK= LimitAS= LimitMEMLOCK= |
| limit-as (or a) | a | v | v | LimitAS= | |
| limit-rss (or r) | r | m | m | LimitRSS= | |
| limit-stack (or s) | s | s | s | LimitSTACK= | |
| limit-memlock | l | l | l | LimitMEMLOCK= | |
| limit-msgqueue | q | q | LimitMSGQUEUE= | ||
| limit-nice | e | e | LimitNICE= | ||
| limit-rtprio | r | r | LimitRTPRIO= | ||
| limit-rttime | y | R | LimitRTTIME= | ||
| limit-sigpending | i | i | LimitSIGPENDING= | ||
| limit-locks | x | x | LimitLOCKS= |
The following options from the bash
ulimit command are not available: b
k p P T.
BUGS¶
The --cpu-scheduler option should accept
more scheduling policies and should accept additional parameters to qualify
those policies. Currently unknown policy names are treated as the default
Linux scheduling policy.
When the kernel supports capabilities but not specific
capabilities that have been requested to be dropped or kept,
xchpst should continue rather than aborting.
EXAMPLES¶
Testing the emulation of ‘envdir’:
xchpst -b envdir --
xchpstLaunch with read-only filesystem if xchpst
is available, else use chpst:
xchpst --exit && exec xchpst
--ro-sys -l /var/lock/ntpsec-ntpdate ntpd; exec chpst -l
/var/log/ntpsec-ntpdate ntpdDrop a capability from the bounding set:
xchpst --cap-bs-drop CAP_SYS_ADMIN --
acmedDrop user while retaining some capabilities:
xchpst -u :500:500 --caps-keep
CAP_DAC_OVERRIDE fakeroot /usr/sbin/gpm -D -m /dev/input/mice -t
exps2Diagnostics¶
To see what is going on, including options enabled implicitly due
to other options, add the ‘--verbose’
option.
Use ‘--login’ without a
command name to explore the hardened environment from a shell.
You can enter the created namespaces (but not other aspects of hardening), including any synthesised root filesystem, by identifying the process id of the hardened application and running:
nsenter -a -t PIDSEE ALSO¶
chpst(8), runit(8), unshare(1), capsh(1), taskset(1), chrt(1), choom(1), proc_pid_oom_score_adj(5), prlimit(1), prlimit(2), namespaces(7), capabilities(7), envdir(8), envuidgid(8), pgrphack(8), setlock(8), setuidgid(8), softlimit(8)
AUTHORS¶
Andrew Bower <andrew@bower.uk>
| September 9, 2025 | Debian |