3. Privileges and Trust
Hint
Abstract
This chapter explains which features of Flake Pilot need root
privileges and how they get them: through one program, the
Flake Pilot helper, which is called through sudo. It describes
what the helper checks, what a sudo rule for it grants and
introduces the reference sudoers policy which comes with the
flake-pilot package.
Note
A sudo rule for the helper is not a root shell, but it is trust.
The helper provides a fixed set of commands and checks every
argument and the authority of the calling user. A user with a
sudo rule for it cannot run arbitrary commands as root. The
rules do let the user change the network setup of the host for
the firecracker VMs, and run every podman flake registered system
wide, as root, with what its registration grants on the host.
3.1. How Flake Pilot Gets Its Privileges
Flake Pilot does not install any setuid binary. Every action which needs more privileges than the calling user has is performed by the helper:
/usr/libexec/flake-pilot/flake-pilot-helper
flake-ctl, podman-pilot and bubblewrap-pilot call it
through sudo. It is the only program Flake Pilot calls through
sudo. A caller who is root already runs it directly. Every
other program, podman included, is called with the privileges of
the process that calls it.
The helper belongs to root and refuses to run as a setuid program,
it has to be called through sudo or by root. It takes the
calling user from sudo and clears its environment before it does
anything else. Every command checks its arguments against a strict
pattern before they become part of a path, a device name or a
program argument, and it checks the authority of the caller against
data which belongs to root. man 8 flake-pilot-helper lists the
commands.
3.2. What the Helper Checks
- The network setup of the firecracker VMs
The private network of the VMs has to be one of the networks Flake Pilot selects from and must not collide with a network of the host. The outgoing interface has to exist. Both are recorded in the network state,
/var/lib/firecracker/network-state.yaml, which belongs to root. The address of a VM, the route to it and its firewall rules are only ever created within the recorded network. The TAP device of a VM is created for the caller, only root can create it for another user. The flake configuration which identifies the VM has to belong to the caller or to the system wide registry. See Firecracker Networking.- NFS exports
A directory can only be exported by its owner, or by root for a user other than root. It is exported to exactly the VM the network state records for the flake instance. Every access of the VM is mapped to the owner of the directory. Only that owner and root can release the export. See Firecracker Volumes.
- The root filesystem of a bubblewrap sandbox
All paths are derived from the caller and the instance name. The directories of the setup have to belong to the caller and are opened without following a symbolic link. The tmpfs and the overlay are mounted nosuid and nodev onto these opened directories. The tree of the sandbox and the layers a flake stacks on top of it are the read only layers of the overlay, access to their content through the overlay is checked against their own permissions. The writable layer is a tmpfs of the caller which is kept until the caller discards it or the host is rebooted, it uses memory of the host meanwhile. Its size is limited by
sandbox_tmpfs_sizein/etc/flakes.yml, a caller can only ask for less. See Applications From a Sandbox.- System wide podman flakes
The application has to be a link to
podman-pilotwhich belongs to root, and its registration has to belong to root. The helper startspodman-pilotas root for the caller with a fresh environment. The container and its runtime options are taken from the registration. A%VARreference in the runtime options, e.g%HOME, resolves to the value of the user database for the caller, not to a value the caller set. The meta data of the instances is kept in a directory which belongs to root. See Applications From a Container.
3.3. What Each Feature Needs
Feature |
Privileges |
Grants |
|---|---|---|
podman flake from the user registry, |
none, rootless podman of the calling user |
nothing |
podman flake from the system registry, |
helper, |
what the registration grants, see below |
|
must be called by root |
n/a |
firecracker flake, |
none |
nothing |
|
none for the user registry, root for the system registry |
nothing |
|
helper, |
the network setup of the host |
|
helper, |
the VMs of the caller |
|
helper, |
the directories of the caller |
bubblewrap flake, |
helper, |
the sandboxes of the caller |
|
helper, |
nothing |
flake-ctl firecracker volume add and remove only change the
flake configuration and need no privileges.
3.4. What This Means in Practice
- Flakes of the calling user and firecracker flakes need no rule.
Podman flakes from the user registry run in the rootless podman setup of the user, firecracker flakes run the VM as the calling user. Neither of them calls the helper.
- A system wide podman flake is granted to every user with the rule.
The flake runs as root in the podman setup of root, with the container and the runtime options of its registration. Whatever the flake can do on the host, e.g through the host directories its registration mounts into the container, every user with the rule can do through it. Register a podman flake system wide only if all of these users may use it this way.
- The content of a system wide flake is trusted as well.
A delta container may ship a
/systemfilesscript which collects the files to provision from the host.podman-pilotruns it on the host in a bash process of the user who calls the flake, with the privileges of that user only, the files it lists are copied into the container as root. The script runs with the privileges of every user who calls the flake and decides which host files end up in a container of root. Register only containers and images whose authors you trust with that.- The network setup is host wide.
A user with the rule for it picks the private network of the VMs, the interface their traffic leaves the host through and the services of the host they may reach. Deleting the setup disconnects the VMs of all users. Consider creating the host setup as root and granting only the rules for the per flake commands.
- The metadata directory is provided by root, not by the pilots.
All pilots keep the state of their instances in a private directory of the calling user below a metadata directory shared by all users,
/tmp/flakesby default. The owner of that directory can delete and replace the private directories of all other users. It therefore has to belong to root and to have the permissions1777, like/tmp. No pilot creates it. A pilot refuses to run if it does not exist or is set up differently, and tells how to fix it. Theflake-pilotpackage letssystemd-tmpfilescreate it at boot and when the package is installed, see/usr/lib/tmpfiles.d/flake-pilot.conf. On a host withoutsystemd, or with a metadata directory other than/tmp/flakesconfigured in/etc/flakes.yml, callflake-ctl initonce after every boot instead. It creates the directory through the helper.- A user can block the flakes of another user.
The private directory of a user is named after the ID of that user, e.g
/tmp/flakes/1000. Another user can create it first, or a symbolic link or file in its place. The pilots of the user it is named after then refuse to run and name the user who owns the entry along with the call to remove it, which only root can do.flake-ctl initcalled as root reports all such entries.- A password limits who uses the rule.
sudoasks for the password of the calling user and caches it for a few minutes.NOPASSWDgives the user no further privilege, but it allows every process running under their account, e.g a malicious build script, to use the helper without any interaction. The reference policy does not use it.sudologs every call.The calls of the helper show up in the system journal, e.g with
journalctl _COMM=sudo. The helper logs the user it acts for with every change of the host network setup.
3.5. The Reference Policy
The flake-pilot package ships a reference policy as
documentation, e.g in
/usr/share/doc/packages/flake-pilot/flake-pilot.sudoers. In the
source tree it is located in doc/sudoers/flake-pilot.sudoers.
The policy grants the members of the flake-pilot group the use
of the helper, one rule per feature, and states what each rule
grants. The first argument of the helper is the command to perform,
each rule is limited to the commands of its feature.
sudo ignores files in /etc/sudoers.d whose name contains a
dot. Copied there under its original name, the policy therefore
stays inactive. To activate it:
Edit a copy of the file and delete the rules of the features you do not want to grant.
Create the group:
groupadd --system flake-pilot
Check the edited copy and install it without a dot in its name:
visudo --check --file ./flake-pilot.sudoers install -m 0440 -o root -g root ./flake-pilot.sudoers \ /etc/sudoers.d/flake-pilot
Add the users to the group who may use the features listed above:
usermod --append --groups flake-pilot USER
The complete policy:
#
# Reference sudoers policy for flake-pilot
# ========================================
#
# flake-pilot performs every action which needs root privileges
# through one program, the flake-pilot helper:
#
# /usr/libexec/flake-pilot/flake-pilot-helper
#
# It is the only program flake-pilot runs through sudo. The helper
# belongs to root and provides a fixed set of commands. It checks
# every argument and the authority of the calling user, which it
# takes from sudo, against data which belongs to root. It never
# passes an argument of the user on to a program unchecked.
#
# What the rule grants
# --------------------
# Unlike a rule for general purpose programs like mount, iptables or
# podman, the rules for the helper do not let a user run arbitrary
# commands as root. A user with the rules can:
#
# * Prepare and delete the network setup of the firecracker VMs:
# IP forwarding, the firewall chains of the setup and the TAP
# devices of the VMs. This is host wide: the user picks the
# private network of the VMs out of the networks flake-pilot
# selects from, the interface their traffic leaves the host
# through and the services of the host they may use. Deleting
# the setup disconnects the VMs of all users.
#
# * Connect and disconnect the VMs of its own flakes and of the
# system wide flakes. Only root can do this for another user.
#
# * Export a directory it owns through NFS to such a VM, and
# release the exports of its own directories. Every access of
# the VM is mapped to the owner of the directory.
#
# * Mount and unmount the root filesystem of its own bubblewrap
# sandboxes: an overlay of directory trees, mounted nosuid and
# nodev below its own directory in /var/tmp. The trees are only
# read, the overlay grants no access the user does not have on
# the trees themselves. The writable layer is a tmpfs, kept
# until the user discards it or the host is rebooted, which uses
# memory of the host meanwhile. Set sandbox_tmpfs_size in
# /etc/flakes.yml to limit it, users can only ask for less.
#
# * Run the podman flakes registered system wide, as root, in the
# podman setup of root. The container and its runtime options
# are taken from the registration, which only root can write.
# Whatever such a flake can do on the host, e.g through the host
# directories mounted into its container, every user with this
# rule can do through it. A delta container runs its
# /systemfiles script on the host, as the user who calls the
# flake, and the host files the script lists are copied into the
# container as root. Registering a flake system wide therefore
# means granting it to every user with this rule, and trusting
# the author of its container with the accounts of these users
# and with the host files the script lists.
#
# * Create the meta data directories of the pilots configured in
# /etc/flakes.yml.
#
# Flakes registered by the calling user and run in its rootless
# podman setup, as well as firecracker flakes, need no rule at all.
#
# The rules below are grouped by feature. The first argument of the
# helper is the command to perform, a rule can therefore be limited
# to the commands of a feature. Delete the groups of the features you
# do not want to grant, e.g the host network setup if root creates it.
#
# Not active as shipped
# ---------------------
# sudo ignores files in /etc/sudoers.d whose name contains a dot.
# This file therefore stays inactive even when copied there under its
# original name. To activate it:
#
# 1. Edit a copy and delete the groups of the features you do
# not want to grant
# 2. groupadd --system flake-pilot
# 3. visudo --check --file ./flake-pilot.sudoers
# 4. install -m 0440 -o root -g root ./flake-pilot.sudoers \
# /etc/sudoers.d/flake-pilot
# 5. usermod --append --groups flake-pilot USER
#
# Passwords
# ---------
# The rules ask for the password of the calling user, which is the
# sudo default. sudo caches it for a few minutes, as configured by
# timestamp_timeout. A user who runs system wide podman flakes or
# bubblewrap sandboxes is asked for it when the cache expires.
# NOPASSWD does not give the user any additional privilege, but it
# allows every process running under the account of the user, e.g a
# malicious build script, to use the helper without any interaction.
# Weigh this against the system wide flakes the helper runs before
# using it.
#
# The log messages of the helper are only shown if PILOT_DEBUG is
# set for the caller, which passes --debug after the command then.
# The rules for commands without other arguments list that form.
#
# The helper is given by its absolute path, the way flake-pilot
# passes it to sudo. The helper refuses to run setuid, it has to be
# called through sudo or by root.
#
#
# podman flakes registered system wide, /usr/share/flakes
# -------------------------------------------------------------------
# Used by: podman-pilot
# GRANTS: every system wide podman flake as root, see above
#
Cmnd_Alias FLAKE_HELPER_PODMAN = \
/usr/libexec/flake-pilot/flake-pilot-helper podman-pilot *
%flake-pilot ALL = (root) FLAKE_HELPER_PODMAN
#
# bubblewrap flakes
# -------------------------------------------------------------------
# Used by: bubblewrap-pilot, for system wide flakes as well as for
# flakes registered by the calling user
# GRANTS: the root filesystem mounts of the sandboxes of the caller
#
Cmnd_Alias FLAKE_HELPER_SANDBOX = \
/usr/libexec/flake-pilot/flake-pilot-helper sandbox-mount *, \
/usr/libexec/flake-pilot/flake-pilot-helper sandbox-umount *
%flake-pilot ALL = (root) FLAKE_HELPER_SANDBOX
#
# flake-ctl firecracker network add, remove, volume export, release
# -------------------------------------------------------------------
# GRANTS: the network connection of the VMs of the caller and the
# NFS exports of the directories of the caller
#
Cmnd_Alias FLAKE_HELPER_VM = \
/usr/libexec/flake-pilot/flake-pilot-helper network-connect *, \
/usr/libexec/flake-pilot/flake-pilot-helper network-disconnect *, \
/usr/libexec/flake-pilot/flake-pilot-helper volume-export *, \
/usr/libexec/flake-pilot/flake-pilot-helper volume-release *
%flake-pilot ALL = (root) FLAKE_HELPER_VM
#
# flake-ctl firecracker network init, cleanup and flake-ctl init
# -------------------------------------------------------------------
# GRANTS: the network setup of the host for the VMs of all users,
# the meta data directories of the pilots
#
# The network setup changes the host, not a single flake. Consider
# creating it as root instead of granting this rule.
#
Cmnd_Alias FLAKE_HELPER_HOST = \
/usr/libexec/flake-pilot/flake-pilot-helper network-init *, \
/usr/libexec/flake-pilot/flake-pilot-helper network-cleanup, \
/usr/libexec/flake-pilot/flake-pilot-helper network-cleanup --debug, \
/usr/libexec/flake-pilot/flake-pilot-helper meta-dirs-init, \
/usr/libexec/flake-pilot/flake-pilot-helper meta-dirs-init --debug
%flake-pilot ALL = (root) FLAKE_HELPER_HOST