Privilege separation, or privsep, is a method in OpenSSH by which operations that require root privilege are performed by a separate privileged monitor process. Its purpose is to prevent privilege escalation and mitigate attacks by: - reducing the privileged attack surface by performing most operations involving untrusted data in unprivileged code, - facilitating OS-level sandboxing of the components that are most exposed to attack, - reducing information-sharing between privileged and unprivileged code, and - containing attacks as much as possible to the unprivileged process, should they occur. To achieve privilege separation in OpenSSH's sshd, the functions it performs are split across three discrete binaries: sshd is the main entry-point binary for the server. This binary retains privilege but performs a very limited set of tasks: loading and checking the configuration, listening for incoming connections and monitoring the status of connections through the pre-authentication phase of their lifecycle to implement the MaxStartups and PerSourcePenalties features. Apart from listening for and accepting connections, the sshd binary does not handle untrusted, network-derived data; instead it forks and executes the sshd-session binary for each new connection. +---------------------------------------------------------------+ | | | listening socket +-------------------+ | | \--------------+ sshd (listener) | | | +--------++---------+ | | || | | || fork+exec | | || | | +--------------++------------------+ | | | sshd-session (preauth monitor) | | | +--------------++------------------+ | | || | | || fork+exec | | || | | network socket +------------++--------------+ | | \-----------+ sshd-auth (unprivileged) | | | +------------++--------------+ | | | +---------------------------------------------------------------+ pre-authentication process structure sshd-session initially acts as the privileged monitor process for a connection before it completes authentication. During this phase, it does not deal directly with network-derived data, but forks and executes a third binary (sshd-auth, described next) to handle the SSH protocol communication over the network socket. sshd-session receives RPC messages from the sshd-auth subprocess when it needs to perform privileged operations, such as verifying an account's existence, signing with a host key, or checking a user's password. The monitor process implements a loose state machine that enforces the required structure and order of operations that sshd-auth may request. sshd-auth is the network-facing process that implements the initial key agreement and authentication phase of the SSH protocol. It is started with privilege, but will sandbox and/or chroot(2) itself and drop privilege to an unprivileged account before processing any network traffic. All operations that require privilege, such as looking up user information, private key signatures, checking passwords, etc are performed by RPC to the parent sshd-session process. After authentication completes, sshd-auth serialises its SSH protocol and connection state, exports this to the parent sshd-session process and exits. Communication between parent and child processes occurs over shared file descriptors. Parent processes also monitor their children for abnormal exit conditions, such as crashes or signalling via exit(3) status. These conditions are used in sshd to implement the PerSourcePenalties controls. +---------------------------------------------------------------+ | | | +---------------++------------------+ | | | sshd-session (postauth monitor) | | | +---------------++------------------+ | | || | | || fork | | || | | network socket +--------------++---------------+ | | \-----------+ sshd-session (unprivileged) | | | +--------------++---------------+ | | | +---------------------------------------------------------------+ post-authentication process structure After authentication, sshd-session disconnects from its parent listener sshd (which doesn't track connections after authentication has succeeded) and forks again. The forked sshd-session child process will drop privilege to that of the authenticated user, import the previously-serialised connection state from sshd-auth and continue to perform network and SSH protocol operations for the remainder of the session. The parent sshd-session process continues to act as a privileged monitor process, performing actions that require privilege when requested via RPC. These operations include allocating PTYs and signing key re-exchange messages. In portable OpenSSH several compile-time options affect privilege separation: --with-sandbox=style Controls the sandboxing implementation used by sshd-auth. OS level sandboxing is supported on a number of platforms. A fallback rlimit(2)-based sandbox provides some basic control on a wider set of platforms. Most supported sandboxes are detected and enabled automatically, so this option is mostly used to disable the sandbox (this is handy when debugging or running under tools like AddressSanitizer). --with-privsep-user=user Specifies the unprivileged user that sshd-auth will switch to before it starts handling untrusted data. This user must not be shared with any other system service, should own no files, should have a locked password, a shell that denies access (such as /bin/nologin or /bin/false) and the home directory set to the privilege separation path. For most platforms the default user is "sshd". --with-privsep-path=/path Controls the directory that sshd-auth will chroot(2) to before dropping privileges. This directory should be empty, not shared with other system accounts and not readable or writable by the sandbox user. The default privsep path is "/var/empty". You should do something like the following to prepare the privsep preauth environment: # mkdir /var/empty # chown root:sys /var/empty # chmod 755 /var/empty # groupadd sshd # useradd -g sshd -c 'sshd privsep' -d /var/empty -s /bin/false sshd On some platforms, only the pre-authentication part of privsep is supported, and the post-authentication part is disabled due to lack of support from the operating system. This is an example process list for a connection in the pre-authentication phase: PID PPID UID CMD 1436 1 0 sshd: /usr/sbin/sshd -D [listener] 1 of 10-100 startups 47077 1436 0 sshd-session: djm [priv] 47078 47077 107 sshd-auth: djm [net] Where process 1436 is the listener, 47077 is the privileged sshd-session monitor process and 47078 is the unprivileged, network-facing sshd-auth process. And in the post-authentication phase: PID PPID UID CMD 47077 1436 0 sshd-session: djm [priv] 47091 47077 1000 sshd-session: djm@pts/1 47092 47091 1000 -bash Where process 47077 continues its role as a privileged monitor, 47091 is the user-privileged network-facing post-authentication process and 47092 is the user shell started for the session.