mirror of
https://github.com/openssh/openssh-portable.git
synced 2026-11-06 05:16:43 +00:00
174 lines
8.0 KiB
Plaintext
174 lines
8.0 KiB
Plaintext
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.
|
|
|