COMP 3150Operating Systems

01 Introduction

The security's classical's model (Lessons 12.1–12.5) is the perimeter's defence: the system's boundary (the network's firewall, the user's account) protects the trusted's interior, and the interior's components (the users, the processes, the applications) trust one another. The model's assumption is the perimeter's impermeability, and the modern's threats (the remote's code's execution, the supply chain's compromise, the malicious's insider, the malware) invalidate the assumption: the attacker who breaches the perimeter (or the user who runs the untrusted's code) has the interior's trust, and the damage's scope is the whole system. The sandbox is the answer to the assumption's failure: the untrusted's code runs in the isolated's environment (the sandbox), and the sandbox's walls confine the code's capabilities to the minimum it needs. The compromise's damage is the sandbox's damage, not the system's.

The sandboxing is the security's least privilege's principle (Lesson 12.1's principle 6) applied at the process's (or the process group's) level: the process is granted the minimum resources it needs to do its work, and the kernel's mechanisms enforce the grant. The sandbox's walls are the mechanisms that the operating system provides:

  • The namespaces (the isolation's mechanisms: the process's view of the file system, the process's table, the network, the IPC, the users, the hostname, the cgroups, and the time).
  • The control groups (cgroups) (the resource's limits: the CPU's share, the memory's cap, the I/O's bandwidth, the process's count).
  • The seccomp's filters (the system calls' restrictions: the whitelist or the blacklist of the system calls the process may invoke).
  • The capabilities (the fine-grained's privileges: the process's capability's sets' restriction, the CapabilityBoundingSet's design).
  • The LSM's profiles (the SELinux's types, the AppArmor's profiles: the mandatory access control's confinement).
  • The chroot and the pivot_root (the file system's confinement: the process's root's directory's change).
  • The systemd's hardening's directives (the services' confinement: the ProtectSystem, the ProtectHome, the PrivateTmp, the NoNewPrivileges, the RestrictAddressFamilies).
  • The Landlock (the unprivileged's sandboxing: the file system's access's restriction for the unprivileged's processes).
  • The virtual machines (the strongest's isolation: the hardware's virtualization, the hypervisor's confinement). The VMs are the unit's next's subject (Unit 13), and they are the sandbox's extreme's case.

The sandboxing's design's central's principle is the defence in depth (Lesson 12.1's principle): the sandbox's walls are the multiple's mechanisms, and the compromise requires the multiple's failures. A well-designed's sandbox (the Chrome's, the modern's containers) combines the namespaces, the seccomp, the capabilities' minimization, the LSM's profiles, and the cgroups, so the attacker who escapes one layer is still confined by the others. The observation is the reason the sandboxing is the modern security's central's architecture: the untrusted's code's execution is the unavoidable (the web, the applications, the containers), and the sandbox is the design that makes the execution's risk acceptable.

This lesson presents the sandboxing. It defines the isolation's goals and the confinement's spectrum. It presents the Linux namespaces (the eight namespaces and their isolation's semantics). It presents the control groups (the v1 and the v2 and the resource's controllers). It presents the seccomp's BPF filters (the system calls' restrictions and their design). It presents the chroot and the pivot_root (the file system's confinement), the containers (the Docker, the Podman, the LXC, and the OCI's standards), and the browsers' sandboxes (the Chrome's and the Firefox's designs). It presents the systemd's hardening's directives and the Landlock's unprivileged's sandboxing. It presents the sandbox's escapes' attacks and the sandboxing's design's principles. It closes with the sandboxing's role in the modern systems and the lesson's synthesis.

Prerequisites

This lesson builds on Lessons 12.1 through 12.5. The security's goals (the CIA triad), the AAA model, the reference monitor, the Saltzer and Schroeder principles, and the defence in depth are assumed (Lesson 12.1). The authentication's mechanisms are assumed (Lesson 12.2). The authorization's concepts, the access control matrix, and the four models (the DAC, the MAC, the RBAC, the ABAC) are assumed (Lesson 12.3). The formal models (the Bell-LaPadula, the Biba, the Clark-Wilson) are assumed (Lesson 12.4). The Linux security's mechanisms (the identity's model, the permissions, the capabilities, the LSM's framework, the SELinux, the AppArmor) are assumed (Lesson 12.5). The virtual memory and the process's model (Units 3, 8, and 9) are the context for the namespace's and the cgroup's discussions. Familiarity with the Linux command line from Lesson 2.2 is essential, since the interactive activity uses the unshare, the nsenter, the ip netns, the systemd-cgls, the systemd-run, the docker (or the podman), the chroot, and the systemd-analyze security commands.

02 Learning objectives

By the end of this lesson you will be able to

  • Define the sandbox and explain the isolation's goals, and describe the confinement's spectrum from the chroot to the virtual machines.
  • Describe the Linux namespaces (the mount, the PID, the network, the user, the IPC, the UTS, the cgroup, and the time) and explain each namespace's isolation's semantics.
  • Describe the control groups (the v1 and the v2) and their resource's controllers, and explain how the cgroups enforce the resource's limits.
  • Describe the seccomp's BPF filters, the chroot and the pivot_root, and the systemd's hardening's directives, and explain how each contributes to the confinement.
  • Describe the Linux containers (the Docker, the Podman, the LXC, and the OCI's standards) and the browsers' sandboxes (the Chrome's and the Firefox's designs), and explain their isolation's designs.
  • Explain the sandbox's escapes' attacks and the sandboxing's design's principles, and evaluate a sandbox's strength against the lesson's criteria.

This lesson contributes to U12.6 and U12.7, and to course learning outcomes CLO1, CLO7, and CLO8.

03 Required reading

  • Operating System Concepts — Chapter 16, Security, the sections on the isolation's mechanisms, and Chapter 17, Protection. The standard treatment of the protection's mechanisms and the isolation's goals.
  • OSTEP — the Security chapters, and the Virtualization chapters (the discussion of the containers and the VMs). OSTEP's treatment of the virtualization's mechanisms is the clearest available account of the isolation's designs, and it is the basis for the worked example in this lesson.
  • Tanenbaum & Bos — Modern Operating Systems, Chapters 7 (the virtualization) and 9 (the security). A complementary treatment that develops the isolation's mechanisms from first principles.
  • Kerrisk, M. — The Linux Programming Interface. No Starch Press, 2010. Chapters 20 (the signals), 24 (the process creation), 28 (the process's credentials), 37 (the daemons), and 61 (the sockets). The definitive reference for the Linux process's isolation's mechanisms.
  • Kerrisk, M. — The Linux namespaces and the containers. The man 7 namespaces page and the LWN's articles ("The namespaces in the operating system"). The authoritative reference for the Linux namespaces.
  • Rosen, R. — Resource Management: The Linux and the SUSE Approaches. The Novell/SUSE's and the RHEL's resource-management's guides. The authoritative references for the cgroups.
  • Edge, J., & other LWN's authors. — The LWN's series on the seccomp's filters, the Landlock, and the container's security. The LWN's articles are the authoritative references for the modern's isolation's mechanisms.
  • Reis, C. — Docker and the OCI: The container's standards. The OCI's specifications (the Runtime's Specification, the Image's Specification, the Distribution's Specification). The authoritative references for the container's standards.
  • Rice, L. — Container Security. O'Reilly, 2020. The comprehensive treatment of the container's security's mechanisms and the escapes' attacks.
  • Linux man pages: man 7 namespaces, man 2 clone, man 2 unshare, man 1 unshare, man 1 nsenter, man 2 setns, man 5 proc (the /proc/PID/ns/ interface), man 7 cgroups, man 1 systemd-cgls, man 1 systemd-cgtop, man 1 systemd-run, man 5 systemd.resource-control, man 2 seccomp, man 3 seccomp_init (the libseccomp), man 1 systemd-analyze (the security verb), man 2 chroot, man 1 chroot, man 2 pivot_root, man 8 docker, man 1 podman, man 1 docker-run, man 1 docker-exec, man 1 firejail, man 1 bubblewrap, man 7 landlock (if available), man 2 seccomp_unotify, man 5 systemd.exec, man 5 systemd.resource-control.

Reading strategy

Read the man 7 namespaces page first. It is the authoritative reference for the Linux namespaces, and it explains the eight namespaces (the mount, the PID, the network, the user, the IPC, the UTS, the cgroup, and the time) and their isolation's semantics with the precision that the lesson's discussion requires. Then read the man 7 cgroups page for the control groups' design and the v1's and the v2's differences. The Kerrisk book is the deeper's reference, and its chapters on the process's creation and the credentials are the context for the clone's and the unshare's discussions. The Rice book is the comprehensive's container's security's reference, and it is worth reading for the escapes' attacks and the defence's designs. The OCI's specifications are the container's standards, and they are worth consulting for the container's runtime's design. Finally, the Linux man pages are the practical's tools: the unshare and the nsenter create and enter the namespaces, the ip netns manages the network's namespaces, the systemd-cgls and the systemd-cgtop display the cgroups' hierarchy, the systemd-run creates the transient's services with the resource's limits, and the systemd-analyze security evaluates a service's sandbox's strength. Pay particular attention to the distinction between the namespaces (the isolation's mechanisms: the process's view of the resources) and the cgroups (the resource's limits: the process's usage of the resources). The two are the complementary's mechanisms: the namespaces hide the resources (the process cannot see the other namespaces' processes, files, and networks), and the cgroups limit the resources (the process cannot use more than the cgroup's grant). The observation is the reason the modern's containers combine the both: the container's process sees the container's namespace's view, and the container's cgroup caps the process's resource's usage.

04 Core content

4.1 The sandbox and the confinement's spectrum

A sandbox is the isolated's environment in which the untrusted's code (or the untrusted's user's code) runs, with the walls that confine the code's capabilities to the minimum it needs. The sandbox's goals:

  • The damage's limitation. The compromise's damage is the sandbox's damage, not the system's: the attacker who compromises the sandboxed's code has only the sandbox's resources.
  • The least privilege's enforcement. The sandbox's code has only the resources it needs (the least privilege's principle), so the compromised's code's capabilities are the minimum.
  • The isolation's guarantee. The sandbox's code cannot affect the system's other's components (the other processes, the other users, the other applications), and the system's other's components cannot affect the sandbox's code (except through the sandbox's allowed's channels).
  • The multi-tenancy's support. The sandboxes can coexist on the same system (the server's containers, the browser's tabs, the mobile's applications), and each sandbox's code is confined to its own's resources.

The sandbox's confinement's spectrum is the range of the isolation's mechanisms, from the weakest (the chroot) to the strongest (the virtual machines):

The confinement's mechanisms' spectrum
Mechanism The isolation's scope The strength The typical use
chroot The file system's view (the root's directory's change) Weak (the escapes are the known, the privileged's user can bypass) The build's environments, the legacy's services
The capabilities The fine-grained's privileges Medium (the capability's model's breadth) The services' privilege's restriction
The LSM's profiles (the AppArmor, the SELinux) The mandatory access control's policy Medium to strong (the policy's coverage) The services' confinement, the browsers' profiles
seccomp The system calls' filter Strong (the attack's surface's reduction) The browsers, the containers, the systemd's services
The namespaces The process's view of the file system, the PIDs, the network, the IPC, the users, the hostname, the cgroups, the time Strong (the kernel's enforced) The containers, the browsers, the systemd's services
The cgroups The resource's usage's limits Medium (the limit's enforcement, not the isolation) The containers, the systemd's services, the user's sessions
The virtual machines The hardware's virtualization (the CPU, the memory, the devices) Strongest (the separate's kernel) The cloud's multi-tenancy, the browsers' isolated's processes, the untrusted's code's execution

The spectrum's central's observation: the mechanisms are the complementary, not the alternatives. The modern's sandboxes (the Chrome's, the containers) combine the mechanisms: the namespaces provide the isolation's view, the cgroups provide the resource's limits, the seccomp provides the system calls' filter, the capabilities provide the privilege's minimization, and the LSM's profiles provide the mandatory access control's policy. The combination's design is the defence in depth's principle (Lesson 12.1): the attacker must bypass the multiple's mechanisms, and the multiple's mechanisms' failures are the uncorrelated.

4.2 The Linux namespaces

The Linux namespaces are the kernel's mechanisms that give the processes the isolated's views of the system's resources. The namespace's design: the kernel's resources (the processes, the files, the networks, the IPC's objects, the users) are tagged with the namespace's identifier, and the processes see only the resources with their own's namespace's identifier. The namespaces' types (the eight):

The mount's namespace (CLONE_NEWNS)
The isolation of the file system's mount's points: the processes in the different mount's namespaces see the different file system's hierarchies. The mount's namespace's design: the processes' mount and umount operations affect only their own's namespace, so a container's file system's mounts are the container's, not the host's. The mount's namespace is the chroot's modern's successor: the container's file system's view is the namespace's mount's table, not the process's root's directory's change.
The PID's namespace (CLONE_NEWPID)
The isolation of the process's table: the processes in the different PID's namespaces have the different PID's numbers. The namespace's design: the first process in the new's namespace has the PID 1 (the init's process), and the processes' PIDs in the namespace are the namespace's internal's numbers; the host's kernel's view shows the processes with the host's PIDs (the namespace's PIDs' mapping). The design's consequence: the process in the namespace cannot see the other namespaces' processes (the /proc shows only the namespace's processes), and the PID 1's process's death kills the namespace's other processes.
The network's namespace (CLONE_NEWNET)
The isolation of the network's stack: the processes in the different network's namespaces have the different network's interfaces, the different routing's tables, the different firewall's rules, the different sockets. The namespace's design: the new's network's namespace starts with only the loopback's interface, and the administrator (or the container's runtime) configures the namespace's interfaces (the virtual's Ethernet pairs, the bridges, the IP's addresses). The network's namespace is the containers' network's isolation's mechanism: the container's processes see only the container's network, and the container's network is the virtual's Ethernet's pair, which connects to the host's bridge (or the overlay's network).
The user's namespace (CLONE_NEWUSER)
The isolation of the user's and the group's IDs: the processes in the different user's namespaces have the different UIDs and GIDs' mappings. The namespace's design: the namespace's process can be the root (the UID 0) inside the namespace while the host's kernel sees the process with the namespace's owner's UID (the unprivileged's user's UID), so the unprivileged's user can create the namespace and be the root inside it (the rootless's containers). The namespace's subtlety: the mapping is the explicit (the /proc/PID/uid_map and the /proc/PID/gid_map), and the mapping's design determines the namespace's capabilities (the mapped's UIDs have the namespace's privileges, the unmapped's UIDs have the none).
The IPC's namespace (CLONE_NEWIPC)
The isolation of the System V's IPC and the POSIX's message queues: the processes in the different IPC's namespaces have the different IPC's objects (the shared's memory's segments, the semaphores, the message's queues). The design's purpose: the processes' IPC's isolation, so the container's processes cannot see the host's IPC's objects.
The UTS's namespace (CLONE_NEWUTS)
The isolation of the hostname and the NIS's domain name: the processes in the different UTS's namespaces have the different hostnames. The design's name: the UTS is the Unix Time-sharing System (the historical's name), and the namespace's design's purpose is the containers' hostname's isolation.
The cgroup's namespace (CLONE_NEWCGROUP)
The isolation of the cgroup's hierarchy's view: the processes in the different cgroup's namespaces see the different cgroup's hierarchies (the /proc/PID/cgroup and the /sys/fs/cgroup's views). The design's purpose: the container's processes see only the container's cgroup's subtree, so the container's processes cannot see the host's cgroup's structure.
The time's namespace (CLONE_NEWTIME)
The isolation of the boot's time and the monotonic's clock's offsets: the processes in the different time's namespaces have the different offsets. The namespace's design is the newest (the Linux 5.6), and its purpose is the containers' time's isolation (the containers can have the different CLOCK_MONOTONIC and CLOCK_BOOTTIME's values, so the containers can be checkpointed and restored).

The namespaces' management's mechanisms:

  • The clone's system call (man 2 clone). The system call that creates the new's processes with the namespace's flags (the CLONE_NEWNS, the CLONE_NEWPID, the CLONE_NEWNET, the CLONE_NEWUSER, the CLONE_NEWIPC, the CLONE_NEWUTS, the CLONE_NEWCGROUP, the CLONE_NEWTIME). The new's process runs in the new's namespaces, and the process's children inherit the namespaces.
  • The unshare's system call and the unshare's command. The mechanism that moves the calling's process into the new's namespaces (without the clone's new's process). The unshare's command (the util-linux's tool) is the administrator's and the developer's tool for the namespace's experiments: unshare --mount --pid --fork --mount-proc /bin/bash creates a new's shell with the new's mount and PID's namespaces.
  • The setns's system call and the nsenter's command. The mechanism that moves the calling's process into the existing's namespaces (the /proc/PID/ns/'s file descriptors). The nsenter's command is the administrator's tool for the entering of a container's namespaces: nsenter --target <PID> --mount --pid --net /bin/bash enters the container's namespaces and runs the shell inside them.
  • The /proc/PID/ns/'s interface. The kernel exposes the process's namespaces as the symbolic links (/proc/PID/ns/mnt, the /proc/PID/ns/pid, the /proc/PID/ns/net, the /proc/PID/ns/user, the /proc/PID/ns/ipc, the /proc/PID/ns/uts, the /proc/PID/ns/cgroup, the /proc/PID/ns/time), and the process can open the links and pass the file descriptors to the setns's call. The /proc/PID/ns/'s links' values (the mnt:[4026531840]'s format) are the namespace's identifiers, and the processes with the same identifier share the namespace.
  • The ip netns's command. The iproute2's tool for the network's namespaces' management: the ip netns add creates a named's network's namespace (the /var/run/netns/'s bind mount), the ip netns list lists them, the ip netns exec runs a command in the namespace, and the ip netns del deletes it.

The namespaces' design's observations:

  • The namespaces are the views, not the limits. The namespaces isolate the processes' views of the resources, but they do not limit the processes' usage of the resources. The limits are the cgroups' role (section 4.3), and the two mechanisms are the complementary.
  • The namespaces' capabilities' checks are the privileged's operations. The namespace's creation requires the CAP_SYS_ADMIN's capability (the classic's requirement), except the user's namespace (the CLONE_NEWUSER is the unprivileged's since the Linux 3.8), and the user's namespace is the rootless's containers' foundation.
  • The namespace's nesting. The namespaces can be nested (the namespace's process can create the new's namespaces within it), and the nesting's design is the containers' the-inside's containers (the Docker's in the Docker's) and the rootless's containers' mechanism.
The Linux namespaces and the cgroups' isolation's design The diagram has two parts. The top part shows the Linux namespaces: the host's kernel with the four processes, and the two namespaces (the container's namespace and the VM's namespace) with the processes' isolated views of the mount, the PID, the network, the user, the IPC, the UTS, and the cgroup's namespaces. The bottom part shows the cgroups' hierarchy: the root's cgroup with the children's cgroups (the system.slice, the user.slice, the container's cgroup), and the resource's controllers (the cpu, the memory, the io, the pids) attached to the cgroups. THE LINUX NAMESPACES' ISOLATION The host's kernel the single kernel, the multiple namespaces The namespace A (the container's view) the mount's ns the A's file system's view the PID's ns the PID 1 is the A's init the net's ns the A's interfaces the user's ns the A's UIDs' mapping the IPC's ns the A's IPC's objects the UTS's ns the A's hostname the processes' PIDs in the A's namespace are the A's internal's the processes cannot see the B's resources the mount, the PID, the network, the user, the IPC, the UTS the cgroup, the time (the eight's namespaces) The namespace B (the other's container's view) the mount's ns the B's file system's view the PID's ns the PID 1 is the B's init the net's ns the B's interfaces the user's ns the B's UIDs' mapping the IPC's ns the B's IPC's objects the UTS's ns the B's hostname the processes' PIDs in the B's namespace are the B's internal's the processes cannot see the A's resources the isolation is the kernel's enforced the views are the separate, the kernel is the shared THE CGROUPS' HIERARCHY The root's cgroup system.slice user.slice container's cgroup The resource's controllers (the v2's unified's design) cpu the CPU's shares, the quota, the period memory the memory's max, the swap's max io the io's weight, the io's max pids the pids' max (the process's count's limit) the others the cpuset, the hugetlb, the freezer, the rdma The namespaces isolate the views; the cgroups limit the usage. The two are complementary.
Figure 12.6 — The Linux namespaces and the cgroups' isolation's design. The top diagram shows the Linux namespaces: the single host's kernel provides the multiple namespaces (the namespace A and the namespace B, each with the mount, the PID, the network, the user, the IPC, the UTS, the cgroup, and the time's namespaces), and the processes in each namespace see only their own namespace's resources. The bottom diagram shows the cgroups' hierarchy: the root's cgroup with its children (the system.slice, the user.slice, the container's cgroup), and the resource's controllers (the cpu, the memory, the io, the pids, and the others) that the cgroups use to limit the processes' resource's usage. The two mechanisms are the complementary: the namespaces isolate the views, and the cgroups limit the usage.

4.3 The control groups (cgroups)

The control groups (cgroups) are the kernel's mechanisms that limit and account the processes' resource's usage. The cgroups' design: the processes are organized into the hierarchy of the groups (the cgroups), and the resource's controllers (the subsystems) attach to the groups and enforce the resource's limits. The cgroups' two versions:

The cgroups v1 (the classical's design)
The v1's design: the multiple hierarchies, one per the resource's controller (the cpu's hierarchy, the memory's hierarchy, the blkio's hierarchy, the pids's hierarchy, and the others). The process can be in the different groups in the different hierarchies, and the controllers' limits are the separate. The v1's design's disadvantage: the complexity (the multiple hierarchies, the controllers' interactions, the inconsistent's semantics), and the observation is the reason the v2 replaced it.
The cgroups v2 (the unified's design)
The v2's design: the single's hierarchy (the /sys/fs/cgroup/'s unified's tree), with the controllers attached to the groups' nodes (the cgroup.controllers, the cgroup.subtree_control). The v2's design's advantages: the consistency (the single hierarchy, the controllers' coherent's semantics), the delegation (the unprivileged's users can manage the sub-hierarchies), the pressure's information (the cgroup.pressure's interfaces: the /proc/pressure/'s and the cgroup.pressure's metrics), and the nesting (the container's runtime can create the sub-cgroups). The modern's distributions (the Fedora's, the Ubuntu's recent, the Debian's recent) use the v2 by default, and the v1's controllers' migration is the ongoing.

The cgroups' resource's controllers:

  • The cpu's controller. The CPU's time's allocation: the cpu.weight (the relative's share: the v2's design, the 1–10000's range) and the cpu.max (the absolute's quota: the <quota> <period>'s format, for example the "200000 1000000" for the 20% of the one CPU).
  • The memory's controller. The memory's limits: the memory.max (the hard's limit: the cgroup's processes' total's memory's usage's cap, and the exceeding's allocation fails), the memory.high (the soft's limit: the exceeding's allocation is throttled), the memory.low and the memory.min (the protection's limits: the pages' reclaim's preference), and the memory.swap.max (the swap's limit).
  • The io's controller. The I/O's limits: the io.weight (the relative's share), the io.max (the per-device's absolute's limit: the the major:minor the rbps=... the wbps=... the riops=... the wiops=...'s format), and the io.latency (the latency's target: the cgroup's I/O's latency's guarantee).
  • The pids's controller. The process's count's limit: the pids.max (the maximum's number of the processes and the threads in the cgroup, and the exceeding's fork fails). The controller's purpose: the fork's bomb's prevention (the process's count's limit bounds the damage).
  • The other's controllers. The cpuset (the CPU's and the memory's nodes' binding), the hugetlb (the huge pages' limit), the freezer (the cgroup's processes' freezing), the rdma (the RDMA's resources' limit), the devices (the devices' access's control: the cgroup's allow or deny of the device's nodes), and the net_cls and the net_prio (the network's classification and the priority).

The cgroups' management's mechanisms:

  • The /sys/fs/cgroup/'s interface. The cgroups' file system: the administrator creates the cgroups (the directories), sets the controllers' limits (the files' writes), and moves the processes into the cgroups (the cgroup.procs's and the cgroup.threads's files' writes).
  • The systemd's integration. The systemd manages the cgroups on the modern's systems: the services are the cgroups (the system.slice, the user.slice, the machine.slice), the services' units' resource's directives (the CPUWeight, the CPUQuota, the MemoryMax, the MemoryHigh, the IOWeight, the TasksMax, and the others) configure the limits, and the tools (systemd-cgls, the systemd-cgtop, the systemctl status) display the cgroups' state.
  • The systemd-run's command. The tool that creates the transient's services (the systemd-run --unit=myjob --property=MemoryMax=1G --property=CPUQuota=50% command), and the tool is the administrator's and the developer's mechanism for the ad-hoc's resource's limits.
  • The cgroupfs's and the cgroup2's mounts. The v1's multiple's mounts (the /sys/fs/cgroup/cpu, the /sys/fs/cgroup/memory, the others) and the v2's single's mount (the /sys/fs/cgroup/), with the cgroup_no_v1='s and the systemd.unified_cgroup_hierarchy=1's kernel's parameters controlling the version.

The cgroups' design's observations:

  • The cgroups are the limits, not the isolation. The cgroups limit the processes' resource's usage, but they do not isolate the processes' views. The isolation is the namespaces' role, and the two are the complementary (the section 4.1's observation).
  • The cgroups' metrics. The cgroups expose the processes' resource's usage (the cpu.stat, the memory.current, the io.stat, the pids.current), so the administrator can monitor the usage and tune the limits. The pressure's stall's information (the cpu.pressure, the memory.pressure, the io.pressure) shows the resource's contention, and the information is the modern's monitoring's basis.
  • The cgroups' delegation. The v2's design supports the delegation: the unprivileged's user (or the container's runtime) can create the sub-cgroups within the delegated's cgroup (the systemd's Delegate=yes's directive), and the delegation's design is the rootless's containers' and the user's sessions' foundation.

4.4 The seccomp's filters

The seccomp (the secure computing's mode) is the kernel's mechanism that filters the process's system calls. The mechanism's design: the process installs the BPF's filter (the Berkeley Packet Filter's program) that the kernel evaluates on the every's system call, and the filter's action determines the system call's fate: the SECCOMP_RET_ALLOW (the system call's execution), the SECCOMP_RET_ERRNO (the system call returns the specified's error), the SECCOMP_RET_KILL_PROCESS (the process is killed), the SECCOMP_RET_KILL_THREAD (the thread is killed), the SECCOMP_RET_TRAP (the SIGSYS's signal's delivery), the SECCOMP_RET_TRACE (the tracer's notification), the SECCOMP_RET_LOG (the system call's logging), and the SECCOMP_RET_USER_NOTIF (the user-space's supervisor's notification).

The seccomp's modes:

  • The strict's mode (SECCOMP_MODE_STRICT). The original's mode: the process can invoke only the read, the write, the _exit, and the sigreturn's system calls. The mode's design's purpose: the minimal's filter for the untrusted's code, and its use is the rare (the modern's systems use the filter's mode).
  • The filter's mode (SECCOMP_MODE_FILTER). The modern's mode: the process installs the BPF's program that filters the arbitrary's system calls, and the program's action determines the fate. The mode's design's advantages: the flexibility (the arbitrary's filters), the performance (the BPF's compiled's program's evaluation's efficiency), and the composability (the multiple's filters' stacking, the seccomp's filters' addition's restriction: the additional's filters can only be the more restrictive).

The seccomp's design's observations:

  • The seccomp's filter is the attack's surface's reduction. The system calls are the kernel's interface, and the kernel's bugs (the vulnerabilities) are the system calls' exploits. The seccomp's filter reduces the exposed's system calls, so the kernel's attack's surface is the smaller. The observation is the reason the browsers (the Chrome's and the Firefox's sandboxes) and the containers (the Docker's default's profile) use the seccomp's filters: the untrusted's code's system calls are the restricted's set, so the kernel's vulnerabilities' exploitation is the harder.
  • The seccomp's filter's design's subtleties. The filter's whitelist (the allow's list: the system calls the process may invoke, and the every's other's system call is the denied) is the stronger's design; the filter's blacklist (the deny's list: the system calls the process may not invoke) is the weaker (the unknown's system calls are the allowed). The filter's granularity: the seccomp's filter can inspect the system call's number and its arguments (the scalar's values, the pointers' values, not the pointed's data), so the filter's design can be the fine-grained (the open's arguments' flags' inspection) or the coarse-grained (the system call's number's only).
  • The libseccomp's library. The user-space's library that generates the BPF's filters from the high-level's descriptions (the seccomp_rule_add's calls), and the library is the developers' tool for the seccomp's filters' design. The systemd's SystemCallFilter='s directive uses the library (or the pre-defined's groups: the @system-service, the @basic-io, the @network-io, the @process), and the containers' runtimes use the library for the default's profiles.

4.5 The chroot and the pivot_root

The chroot is the classical's file system's confinement's mechanism: the chroot's system call changes the process's root's directory (the /'s view) to the specified's directory, so the process and its children see the specified's directory as the root. The mechanism's design's purpose: the file system's view's restriction (the process cannot access the files outside the chroot's directory by the absolute's paths). The mechanism's design's weakness: the chroot is not the security's boundary. The process with the CAP_SYS_CHROOT's capability can escape (the chroot's nesting's tricks, the chdir's tricks), and the root's process can escape by the multiple's mechanisms (the /proc's access, the mknod's device's creation, the ptrace's debugging). The observation is the reason the chroot is the convenience's mechanism (the build's environments, the legacy's services), not the security's: the modern's sandboxes use the mount's namespace (the CLONE_NEWNS) and the pivot_root's mechanism instead.

The pivot_root is the modern's mechanism: the pivot_root's system call changes the process's root's and the current's directory to the new's file system, and it moves the old's root to the specified's directory (the put_old). The mechanism's design's difference from the chroot: the pivot_root changes the root's mount, not the directory, so the old's root is the separate's mount (the put_old's directory), and the process can unmount it (the umount's operation), so the escape's paths (the /proc's, the chroot's) are the closed. The pivot_root is the containers' runtimes' mechanism (the runc's, the crrun's, the LXC's): the container's file system is the mount's namespace's root, and the host's file system is the unmounted's (or the hidden's) mount.

4.6 The containers

The containers are the modern's sandboxing's architecture: the processes (the container's processes) run in the isolated's environment (the container), and the environment is the combination of the namespaces, the cgroups, the seccomp's filters, the capabilities' minimization, the LSM's profiles, and the container's file system (the image's layers). The containers' design's observation: the containers are not the virtual machines (the containers share the host's kernel, and the VMs have the separate's kernel), and the containers' isolation is the kernel's mechanisms' (the namespaces and the cgroups), while the VMs' isolation is the hardware's virtualization's (the hypervisor and the separate's kernel).

The containers' components:

  • The container's image. The file system's template: the layered's file system (the OCI's image's specification: the layers are the tar's archives, and the layers are the stacked's file systems via the overlayfs's union mount), the image's configuration (the entrypoint, the command, the environment's variables, the exposed's ports), and the image's metadata.
  • The container's runtime. The program that creates the container's environment: the low-level's runtimes (the runc, the crun) create the namespaces, the cgroups, the seccomp's filters, and the file system's mounts; the high-level's runtimes (the Docker's containerd, the Podman's conmon, the LXC's lxc-start) manage the container's lifecycle (the image's pulling, the container's creation, the container's start, the container's stop, the logs).
  • The container's engine. The user-facing's tool: the Docker's dockerd, the Podman's podman (the daemonless's design), the LXC's lxc-*, the Kubernetes's kubelet (the orchestrator's agent). The engine's design varies: the Docker's daemon's architecture (the client's and the daemon's separation), the Podman's daemonless's design (the rootless's containers' support), and the Kubernetes's declarative's design.
  • The container's standards. The Open Container Initiative (the OCI) defines the standards: the Runtime's Specification (the container's configuration and the runtime's interface), the Image's Specification (the image's format and the layers), and the Distribution's Specification (the registry's protocol). The standards' purpose: the interoperability (the same image runs on the different runtimes: the Docker, the Podman, the containerd, the CRI-O), and the standards' design is the container's ecosystem's foundation.

The containers' security's mechanisms:

  • The namespaces. The container's runtime creates the container's namespaces (the mount, the PID, the network, the user, the IPC, the UTS, the cgroup), so the container's processes see only the container's resources.
  • The cgroups. The container's runtime places the container's processes in the container's cgroup, and the cgroup's controllers (the cpu, the memory, the io, the pids) limit the processes' resource's usage.
  • The seccomp's filter. The container's runtime installs the default's seccomp's filter (the Docker's and the Podman's default's profiles: the ~44's blocked's system calls, including the mount, the reboot, the kexec_load, the init_module, the ptrace), so the container's processes' system calls are the restricted's set.
  • The capabilities' minimization. The container's runtime drops the unnecessary's capabilities (the Docker's default: the CAP_CHOWN, the CAP_DAC_OVERRIDE, the CAP_FOWNER, the CAP_KILL, the CAP_NET_BIND_SERVICE, the CAP_NET_RAW, the CAP_SETGID, the CAP_SETUID, the ~14), and the --cap-drop=ALL --cap-add=CAP_*'s options let the administrator's finer's control.
  • The LSM's profiles. The container's runtime applies the SELinux's or the AppArmor's profile (the Docker's default: the docker-default's AppArmor's profile, or the SELinux's container_t's type), so the container's processes' accesses are the mandatory access control's confined.
  • The user's namespace and the rootless's containers. The container's runtime can use the user's namespace (the --userns=host's default's change), so the container's root (the UID 0 inside) is the unprivileged's user (the UID 1000's mapping) on the host, and the container's root's privileges are the bounded (the rootless's containers: the Podman's default's design).
  • The read-only's file system and the tmpfs's mounts. The container's file system can be the read-only (the --read-only's option), and the writable's paths are the tmpfs's mounts, so the container's processes cannot modify the image's layers, and the persistence is the explicit (the volumes' mounts).

The containers' design's observations:

  • The containers' isolation's strength. The containers' isolation is the kernel's mechanisms' (the namespaces and the cgroups), and the containers' security depends on the kernel's security: a kernel's vulnerability (the Dirty COW, the Dirty Pipe, the OverlayFS's bugs) can be the container's escape's vector, and the containers' shared's kernel is the VMs' separate's kernel's security's difference. The observation is the reason the high-security's multi-tenancy uses the VMs (or the VM's and the container's combination: the Kata Containers, the gVisor, the Firecracker), and the containers' use is the trusted's or the single-tenant's workloads.
  • The containers' ecosystem's security's concerns. The containers' security includes the image's security (the base's image's vulnerabilities, the dependencies' vulnerabilities, the secrets' leakage), the registry's security (the images' signing, the trusted's registries), the runtime's security (the runtime's vulnerabilities, the configuration's mistakes), and the orchestration's security (the Kubernetes's RBAC, the network's policies, the secrets' management). The security's concerns are the modern's container's security's central's subjects.

4.7 The browsers' sandboxes

The browsers are the modern systems' most visible sandboxes, and their designs are the sandboxing's case studies. The browsers' threat's model: the web's pages (the JavaScript, the HTML, the CSS, the WebAssembly, the plugins) are the untrusted's code, and the browser must execute the code without the code compromising the user's system. The browsers' sandboxing's designs:

The Chrome's sandbox
The Chrome's design: the multi-process's architecture (the browser's process, the renderer's process per site, the GPU's process, the utility's processes) and the renderer's sandbox. The renderer's sandbox's mechanisms: the chroot (the file system's view's restriction to the minimal's set of the files), the seccomp's filter (the system calls' whitelist, the platform-specific's filters), the clone's namespaces (the Linux: the PID's, the network's, the user's, and the IPC's namespaces), the setuid's and the setgid's sandbox's user (the dedicated's unprivileged's user), the capabilities' dropping, and the pledge's and the unveil's mechanisms (the OpenBSD's), the sandbox_init's (the macOS's Seatbelt's profiles). The renderer's process has no direct access to the file system (the accesses are the broker's process's mediation: the IPC's requests), and the renderer's system calls are the whitelist's set.
The Firefox's sandbox
The Firefox's design: the multi-process's architecture (the parent's process, the content's processes, the GPU's process) and the content's processes' sandbox. The content's sandbox's mechanisms: the seccomp-bpf's filter (the Linux's, the default's enabled), the chroot's (the file system's view's restriction, the macOS's and the Linux's), the user's namespace's (the Linux's), the capabilities' dropping, the file's access's brokering (the parent's process's mediation), and the Windows's and the macOS's platform-specific's sandboxes. The Firefox's sandbox's levels (the content, the socket, the utility, the widevine) and the settings (security.sandbox.content.level) determine the confinement's strength.

The browsers' sandboxing's observations:

  • The browsers' sandbox is the multi-process's architecture's application. The renderer's process (the untrusted's code's executor) is the sandboxed's process, and the browser's process (the trusted's broker) is the privileged's process. The architecture's design: the untrusted's code's compromise (the renderer's process) gives the attacker only the renderer's process's resources, and the escape requires the sandbox's bypass (the kernel's vulnerability, the broker's IPC's vulnerability, the configuration's mistake).
  • The sandbox's trade-offs. The sandbox's design has the performance's cost (the IPC's brokering's overhead, the process's creation's cost) and the compatibility's cost (the sandbox's restrictions can break the legitimate's pages' features). The observation is the reason the browsers' sandboxes have the escape's hatches (the --no-sandbox's option, the plugin's processes' less restricted's sandboxes), and the hatches' designs are the security's and the usability's tension's examples.

4.8 The systemd's hardening's directives

The systemd is the modern's Linux's init's system and the services' manager, and its sandboxing's directives are the services' confinement's mechanisms. The directives:

  • The file system's directives. The ProtectSystem=strict (the /usr, the /boot, the /etc's read-only's mounts), the ProtectHome=yes (the /home, the /root, the /run/user's inaccessibility), the ReadWritePaths= and the ReadOnlyPaths= (the specific's paths' access's modification), the InaccessiblePaths= (the paths' hiding), the PrivateTmp=yes (the private's /tmp and the /var/tmp), the PrivateDevices=yes (the private's /dev with only the null, the zero, the full, the random, the urandom, the tty), and the ProtectKernelTunables=yes (the /proc/sys, the /sys's read-only's mounts).
  • The user's and the group's directives. The User= and the Group= (the service's user's and group's identities), the DynamicUser=yes (the transient's user's and group's allocation), the SupplementaryGroups= (the additional's groups' specification), and the RemoveIPC=yes (the IPC's objects' removal on the service's stop).
  • The capabilities' and the privilege's directives. The CapabilityBoundingSet= (the service's capabilities' limit), the AmbientCapabilities= (the service's ambient's capabilities), the NoNewPrivileges=yes (the setuid's and the capabilities' programs' privilege's escalation's prevention), and the SecureBits= (the kernel's securebits's flags).
  • The namespaces' directives. The PrivateMounts=yes (the private's mount's namespace), the PrivateNetwork=yes (the private's network's namespace with only the loopback), the PrivateUsers=yes (the private's user's namespace), the PrivateIPC=yes (the private's IPC's namespace), the ProtectHostname=yes (the private's UTS's namespace), and the ProtectClock=yes (the clock's modification's prevention).
  • The system calls' and the addressing's directives. The SystemCallFilter= (the seccomp's filter: the @system-service's group, the ~@privileged's denial, the specific's system calls), the SystemCallArchitectures= (the system calls' architectures' restriction), the RestrictAddressFamilies= (the socket's address's families' restriction: the AF_UNIX, the AF_INET, the AF_INET6), the RestrictNamespaces= (the namespace's creation's restriction), and the LockPersonality=yes (the personality's system call's restriction).
  • The memory's and the kernel's directives. The MemoryDenyWriteExecute=yes (the writable's and the executable's memory's mappings' prevention: the JIT's code's prevention), the RestrictRealtime=yes (the real-time's scheduling's restriction), the RestrictSUIDSGID=yes (the setuid's and the setgid's bits' creation's prevention), the ProtectKernelModules=yes (the kernel's modules' loading's prevention), the ProtectKernelLogs=yes (the kernel's log's reading's prevention), and the ProtectProc=invisible (the /proc's view's hiding).

The systemd's directives' management's tools: the systemd-analyze security's command evaluates the service's sandbox's strength and produces the score (the exposure's level: the OK, the EXPOSED, the MEDIUM, the UNSAFE), the systemd-run's command creates the transient's services with the directives, and the unit's files (/etc/systemd/system/*.service, the [Service]'s section) hold the persistent's configuration. The observation is the reason the modern's distributions ship the services' units with the sandboxing's directives (the ProtectSystem=strict, the NoNewPrivileges=yes, the SystemCallFilter=@system-service): the directives' defaults are the security's sensible's choices, and the observation is the psychological acceptability's principle's application (Lesson 12.1's principle 8).

4.9 The Landlock and the unprivileged's sandboxing

The Landlock is the Linux's mechanism for the unprivileged's sandboxing (the Linux 5.13). The mechanism's design: the process (the unprivileged's, without the CAP_SYS_ADMIN's capability) creates the Landlock's domain and attaches the rules that restrict the process's file system's accesses. The rules' design: the access's rights (the LANDLOCK_ACCESS_FS_EXECUTE, the LANDLOCK_ACCESS_FS_READ_FILE, the LANDLOCK_ACCESS_FS_WRITE_FILE, the LANDLOCK_ACCESS_FS_READ_DIR, the LANDLOCK_ACCESS_FS_REMOVE_DIR, the LANDLOCK_ACCESS_FS_REMOVE_FILE, the LANDLOCK_ACCESS_FS_MAKE_*) are the restricted's operations, and the rules' scope is the specified's paths (the file's or the directory's hierarchy). The mechanism's restriction: the Landlock's rules are the additional's restrictions (the rules can only remove the accesses, not grant them), so the sandbox's design is the least privilege's (the process's accesses are the subset of the accesses the process would otherwise have).

The Landlock's design's advantages: the unprivileged's use (the process does not need the CAP_SYS_ADMIN's capability, so the ordinary's applications can sandbox themselves), the composability (the Landlock's domains can be stacked, and the multiple's domains' rules are the intersection), and the file system's focus (the mechanism's design is the file system's accesses' restriction, which is the common's sandboxing's need). The Landlock's design's limitations: the file system's only's scope (the mechanism does not restrict the network, the IPC, or the other resources: the seccomp and the namespaces are the complementary's mechanisms), and the kernel's version's requirement (the Linux 5.13+, and the ABI's versions' extensions: the Linux 5.19's and the 6.x's additions).

The unprivileged's sandboxing's other's mechanisms:

  • The bubblewrap's tool. The container's runtime's lightweight's tool (the Flatpak's and the GNOME's foundation): the bwrap's command creates the sandboxed's environment (the namespaces, the bind mounts, the seccomp's filters, the capabilities' dropping), and the tool's design is the unprivileged's user's (the user's namespace's use).
  • The firejail's tool. The user-space's sandboxing's tool: the firejail's command runs the program with the sandbox (the namespaces, the seccomp's filters, the capabilities' dropping, the AppArmor's profiles, the firejail's profiles' library), and the tool's design is the administrator's and the developer's convenience's mechanism.
  • The systemd-run --user's mechanism. The systemd's user's services: the systemd-run --user creates the transient's user's services with the sandboxing's directives (the ProtectSystem, the PrivateTmp, the NoNewPrivileges, the SystemCallFilter), and the mechanism's design is the unprivileged's user's sandboxing's convenience.

4.10 The sandbox's escapes and the attacks

The sandbox's escape is the attacker's bypass of the sandbox's walls, and the escape's mechanisms are the sandbox's design's weaknesses:

  • The kernel's vulnerabilities. The sandbox's walls are the kernel's mechanisms (the namespaces, the seccomp, the LSM, the capabilities), and the kernel's bugs (the privilege's escalation's vulnerabilities, the use-after-free, the race conditions, the memory's corruptions) are the escape's vectors. The classical's examples: the Dirty COW (the CVE-2016-5195: the copy-on-write's race), the Dirty Pipe (the CVE-2022-0847: the pipe's buffers' overwrite), the OverlayFS's bugs (the CVE-2021-3493, the CVE-2023-0386), and the io_uring's bugs. The observation is the reason the containers' security depends on the kernel's security: the containers share the kernel, and the kernel's vulnerability compromises the every container.
  • The configuration's mistakes. The sandbox's strength depends on the configuration: the missing's seccomp's filter, the excessive's capabilities, the disabled's LSM's module, the --privileged's flag (the Docker's flag: the full's host's privileges), the --pid=host's or the --net=host's flags (the host's namespaces' sharing), the mounted's sensitive's paths (the -v /:/host's mount: the host's root's exposure), the docker.sock's mount (the container's control of the Docker's daemon: the full's host's compromise). The observation is the reason the containers' security's practice includes the configuration's review (the CIS's Docker's benchmark, the Kubernetes's hardening's guides).
  • The design's flaws. The sandbox's design can have the flaws: the incomplete's mediation (the seccomp's filter's missing's system calls), the incorrect's assumptions (the path's confinement's hard links' bypass: the AppArmor's profile's paths are the names, and the hard links can bypass the path's check), the IPC's channels (the sandbox's broker's interface: the broker's vulnerability is the escape's vector), and the shared's resources (the shared's memory, the device's nodes, the mounted's file systems).
  • The side channels. The sandbox's isolation does not cover the side channels: the timing's channels (the CPU's caches' timing), the speculative's execution (the Spectre's and the Meltdown's attacks: the CPU's microarchitectural's leaks), the power's channels, and the electromagnetic's channels. The side channels' defence requires the hardware's mitigations (the microcode's updates, the kernel's mitigations: the page-table's isolation, the branch's prediction's hardening), and the observation is the reason the VMs' or the separate's hardware's designs are the high-security's choices.

The escapes' defensive's practices:

  • The kernel's updates. The kernel's security's patches' application is the escapes' prevention's foundation: the vulnerabilities are the published, and the unpatched's kernel is the vulnerable's.
  • The configuration's minimization. The sandbox's configuration should have the minimum's privileges: the --cap-drop=ALL and the --cap-add='s specific's capabilities, the --security-opt's options (the no-new-privileges, the seccomp=, the apparmor=), the read-only's file system, the non-root's user, and the no-host's namespaces.
  • The defence in depth's layering. The sandbox's walls should be the multiple's: the namespaces, the cgroups, the seccomp, the capabilities, the LSM, the user's namespace, the read-only's file system. The multiple's walls' failures are the uncorrelated, and the escape requires the multiple's bypasses.
  • The monitoring and the detection. The sandbox's audit's logs (the SELinux's avc's denials, the seccomp's SECCOMP_RET_LOG's events, the container's runtime's logs) and the system's monitoring (the anomaly's detection: the unusual's system calls, the unexpected's network's connections) can detect the escape's attempts and the ongoing's compromises.

4.11 The sandboxing's design principles

The sandboxing's design applies the Lesson 12.1's principles, and the specific's applications:

  • The least privilege (the principle 6). The sandbox's code has the minimum's resources: the namespaces' minimal's views, the cgroups' tight's limits, the seccomp's whitelist's filters, the capabilities' dropped's sets, the LSM's profiles' confined's policies.
  • The fail-safe defaults (the principle 2). The sandbox's default is the denial: the container's default's capabilities are the dropped's set, the default's seccomp's filter is the restrictive's set, the default's user is the unprivileged's, and the default's file system is the read-only's.
  • The complete mediation (the principle 3). The sandbox's every access is mediated: the namespaces' every view's check, the cgroups' every resource's accounting, the seccomp's every system call's filter, the LSM's every access's policy's check. The incomplete's mediation (the missing's seccomp's rules, the unconfined's paths) is the escape's vector.
  • The economy of mechanism (the principle 1). The sandbox's mechanism is the simple and the small: the small reference's monitor (the kernel's mediation), the simple's seccomp's filters, the minimal's capabilities' sets. The complexity (the excessive's rules, the unnecessary's features) is the bugs' and the misconfigurations' source.
  • The separation of privilege (the principle 5). The sandbox's critical's operations require the multiple's conditions: the multi-factor's authentication for the privileged's operations, the separation of duties for the sensitive's changes.
  • The least common mechanism (the principle 7). The sandbox's shared's resources are the minimized: the per-sandbox's namespaces, the per-sandbox's file systems, the per-sandbox's networks. The shared's kernel is the containers' design's limitation (the VMs' separate's kernel is the alternative).
  • The psychological acceptability (the principle 8). The sandbox's mechanism is the usable: the sensible's defaults (the systemd's directives, the Docker's default's profiles), the escape's hatches for the legitimate's needs (the --privileged's flag, the --no-sandbox's option), and the understandable's configuration. The inusable's sandbox (the excessive's complexity, the constant's breakages) is the bypassed's sandbox.
  • The open design (the principle 4). The sandbox's mechanism is the public and the auditable: the open-source's implementations (the Linux's kernel, the containers' runtimes), the standardized's interfaces (the OCI's specifications), and the reviewable's configurations.

4.12 Common misconceptions

"The containers are the lightweight's virtual machines."

The containers are not the VMs: the containers share the host's kernel (the namespaces and the cgroups are the isolation's mechanisms), and the VMs have the separate's kernel (the hypervisor and the hardware's virtualization). The containers' isolation is the kernel's mechanisms', and the containers' security depends on the kernel's security: the kernel's vulnerability compromises the every container. The observation is the reason the high-security's multi-tenancy uses the VMs (or the VM's and the container's combination: the Kata Containers, the gVisor, the Firecracker).

"The chroot is the security's boundary."

The chroot is the file system's view's restriction, not the security's boundary: the process with the CAP_SYS_CHROOT's capability can escape (the chroot's nesting's tricks, the chdir's tricks), and the root's process can escape by the multiple's mechanisms (the /proc's access, the mknod's device's creation). The modern's sandboxes use the mount's namespace (the CLONE_NEWNS) and the pivot_root's mechanism, and the chroot is the convenience's mechanism (the build's environments, the legacy's services).

"The namespaces are the resource's limits."

The namespaces are the views (the isolation's mechanisms), not the limits (the resource's usage's mechanisms). The namespaces isolate the processes' views of the resources, and the cgroups limit the processes' usage of the resources. The two are the complementary: the container's process sees the container's namespace's view, and the container's cgroup caps the process's resource's usage. A sandbox without the cgroups can consume the system's resources (the fork's bomb, the memory's exhaustion), and a sandbox without the namespaces can see the system's resources (the host's processes, the host's files).

"The seccomp is the containers' optional's feature."

The seccomp's filter is the containers' security's central's mechanism: the Docker's and the Podman's default's profiles block the ~44's dangerous's system calls (the mount, the reboot, the kexec_load, the init_module, the ptrace), and the filter's absence is the escape's vector. The observation is the reason the containers' security's guides emphasize the seccomp's profile's configuration (the --security-opt seccomp='s option, the custom's profiles), and the containers' default's seccomp's filter is the security's sensible's default.

"The rootless's containers are the root's containers with the different's user."

The rootless's containers use the user's namespace (the CLONE_NEWUSER) to map the container's root (the UID 0 inside) to the unprivileged's user (the UID 1000's mapping) on the host. The design's consequence: the container's root's privileges are the bounded (the host's kernel sees the unprivileged's user), and the container's escape gives the attacker only the unprivileged's user's privileges, not the host's root's. The observation is the reason the rootless's containers (the Podman's default) are the security's recommended's design for the unprivileged's users' workloads.

"The sandbox is the absolute's isolation."

The sandbox is the isolation's mechanisms' combination, not the absolute's guarantee: the escapes are the known (the kernel's vulnerabilities, the configuration's mistakes, the design's flaws, the side channels), and the sandbox's strength depends on the kernel's security, the configuration's correctness, and the escape's hatches' control. The observation is the reason the sandboxing is the defence in depth's application: the multiple's walls, the multiple's failures' uncorrelation, and the monitoring's detection.

"The containers are the secure by default."

The containers' defaults are the security's sensible's choices (the dropped's capabilities, the default's seccomp's filter, the non-root's user, the unprivileged's ports), but the containers' security depends on the configuration: the --privileged's flag, the host's namespaces' sharing (--pid=host, --net=host), the sensitive's mounts (the docker.sock's mount, the host's root's mount), and the image's vulnerabilities are the common's weaknesses. The observation is the reason the containers' security's practice includes the configuration's review (the CIS's Docker's benchmark, the Kubernetes's hardening's guides).

"The systemd's sandboxing is the optional's hardening."

The systemd's sandboxing's directives are the services' confinement's central's mechanisms, and the modern's distributions ship the services' units with the directives (the ProtectSystem=strict, the NoNewPrivileges=yes, the SystemCallFilter=@system-service, the PrivateTmp=yes). The directives' defaults are the security's sensible's choices, and the systemd-analyze security's command evaluates the service's sandbox's strength and produces the score (the OK, the MEDIUM, the UNSAFE).

05 Worked example

The following example traces a sandbox's creation with the unshare's and the bubblewrap's tools, computes the sandbox's walls, and evaluates a service's sandbox's strength with the systemd-analyze security's command.

Creating a sandbox with the namespaces and the seccomp

The following commands create a minimal's sandbox with the unshare's command and observe its walls:

# The minimal's sandbox: the new's mount and PID and UTS's
# namespaces, with the new's mount's propagation's control
sudo unshare --mount --pid --fork --uts --mount-proc \
    /bin/bash -c '
        echo "The sandbox is running."
        echo "The PID: $$"
        echo "The hostname: $(hostname)"
        echo "The hostname's change:"
        hostname sandbox
        hostname
        echo "The processes:"
        ps aux
    '

The command's output shows the sandbox's walls:

  • The PID's namespace. The $$'s value is 1 (or a low's number), because the sandbox's first process is the PID 1 in the namespace. The ps aux's output shows only the sandbox's processes (the shell and the ps), not the host's processes.
  • The UTS's namespace. The hostname's output shows the host's hostname initially, and the hostname sandbox's command changes the namespace's hostname without affecting the host's (the host's hostname's command's output is the unchanged).
  • The mount's namespace. The --mount-proc's option mounts the new's /proc in the namespace (the mount -t proc proc /proc's equivalent), so the /proc's view shows only the namespace's processes.

The sandbox's walls and their checks

The sandbox's walls and the ways to verify them:

Wall The verification's command The expected's output
The PID's namespace echo $$, ps aux The low's PID, the sandbox's processes only
The mount's namespace mount | wc -l, ls / The sandbox's mounts, the host's file system's hierarchy (if the bind mounts are the not's applied)
The UTS's namespace hostname The host's hostname initially, the sandbox's after the change
The user's namespace id, cat /proc/self/uid_map The UID 0 inside (if the mapping), the mapping's view
The network's namespace ip link, ip addr The loopback's interface only
The IPC's namespace ipcs The empty's IPC's objects
The cgroup's namespace cat /proc/self/cgroup The namespace's cgroup's subtree

The seccomp's filter's effect

The seccomp's filter's effect:

# Install a seccomp's filter that denies the 'chmod' system call
# (the 'chmod' is the syscall's number 90 on x86_64)
# The following is the conceptual's demonstration;
# the practical's use is via the libseccomp or the systemd.

# The Python's example with the seccomp's library:
python3 -c '
import seccomp
filter = seccomp.SyscallFilter(defaction=seccomp.ALLOW)
filter.add_rule(seccomp.ERRNO(1), "chmod")
filter.add_rule(seccomp.ERRNO(1), "fchmod")
filter.load()
import os
os.chmod("/tmp/test", 0o777)
'

The filter's effect: the chmod's system call returns the EPERM's error (the errno 1), and the os.chmod's call fails. The observation: the seccomp's filter is the system calls' filter, and the filter's design determines the process's allowed's system calls. The modern's containers' runtimes use the libseccomp's filters with the default's profiles (the Docker's and the Podman's: the ~44's blocked's system calls), and the observation is the reason the seccomp's filter is the containers' security's central's mechanism.

The systemd's sandbox's evaluation

The systemd-analyze security's command evaluates a service's sandbox's strength:

# Evaluate the sshd's service's sandbox
systemd-analyze security sshd 2>/dev/null | head -40

# Evaluate the systemd's own's service
systemd-analyze security systemd-journald 2>/dev/null | head -40

# The output shows the service's exposure's score
# (the 0.0 is the perfect's sandbox, the 10.0 is the
# unsandboxed's service) and the per-directive's
# assessment (the OK, the MEDIUM, the EXPOSED).

# The example's output (the abbreviated):
#   NAME                                        DESCRIPTION                                       EXPOSURE
#   ✗ PrivateNetwork=                           Service has access to the host's network          0.5
#   ✗ User=/DynamicUser=                        Service runs as root user                         0.4
#   ✗ CapabilityBoundingSet=~CAP_SYS_ADMIN      Service has the CAP_SYS_ADMIN's capability       0.2
#   ✓ PrivateTmp=                               Service has a private's /tmp                       —
#   ...
#   Overall exposure level for sshd.service: 9.6 UNSAFE 😱

The command's output shows the service's sandbox's assessment: the per-directive's exposure (the ✗'s marks for the unmet's recommendations, the ✓'s for the met's) and the overall's score. The sshd's score is the high (the 9.6 UNSAFE), because the sshd's service needs the privileged's operations (the port 22's binding, the user's authentication, the session's setup), and the observation is the reason the systemd's hardening's directives are the services' trade-offs: the directives' application must account for the services' legitimate's needs, and the observation is the reason the hardening's process is the iterative (the directives' application, the testing, the adjustment).

A container's security's inspection

The following commands inspect a container's security's configuration (if the Docker or the Podman is installed):

# Run a container with the default's configuration
docker run -d --name test-container alpine sleep 3600 2>/dev/null

# Inspect the container's security's configuration
docker inspect test-container 2>/dev/null | \
    grep -A5 -E 'CapDrop|CapAdd|SecurityOpt|ReadonlyRootfs|Privileged|User|PidMode|NetworkMode'

# The output shows the container's dropped's capabilities
# (the CapDrop: the ALL's list, the CapAdd: the default's set),
# the SecurityOpt (the seccomp's filter, the AppArmor's profile,
# the no-new-privileges's flag), the ReadonlyRootfs's status,
# the Privileged's flag, the User, the PidMode, the NetworkMode.

# Show the container's namespaces
ls -la /proc/$(docker inspect -f '{{.State.Pid}}' test-container 2>/dev/null)/ns/ 2>/dev/null

# Show the container's cgroup's limits
cat /sys/fs/cgroup/system.slice/docker-*.scope/memory.max 2>/dev/null
cat /sys/fs/cgroup/system.slice/docker-*.scope/cpu.max 2>/dev/null

# Show the container's seccomp's status
grep Seccomp /proc/$(docker inspect -f '{{.State.Pid}}' test-container 2>/dev/null)/status 2>/dev/null

# Show the container's capabilities
capsh --print 2>/dev/null | head -5
sudo nsenter -t $(docker inspect -f '{{.State.Pid}}' test-container 2>/dev/null) -a capsh --print 2>/dev/null | head -10

# Clean up
docker rm -f test-container 2>/dev/null

The commands' outputs show the container's security's configuration: the dropped's and the added's capabilities, the security's options (the seccomp's filter, the AppArmor's profile), the read-only's file system's status, the privileged's flag, the user, the namespaces (the PID's, the network's, the mount's, the IPC's, the UTS's), the cgroup's limits (the memory's max, the CPU's max), and the seccomp's status (the mode 2: the filter's mode). The inspection is the container's security's assessment's first step, and the observation is the reason the containers' security's practice includes the configuration's review.

06 Interactive activity

Activity · explore the sandboxing on a live system

The goal of this activity is to use the Linux command line to create and inspect the sandboxes: the namespaces, the cgroups, the seccomp's filters, the chroot, the containers, and the systemd's hardening's directives. Work through the steps in your course VM.

  1. Inventory the namespaces. Use the /proc's interfaces to inspect the current process's namespaces.
    # Show the current process's namespaces
    ls -la /proc/self/ns/
    
    # Show the namespaces' identifiers
    readlink /proc/self/ns/mnt
    readlink /proc/self/ns/pid
    readlink /proc/self/ns/net
    readlink /proc/self/ns/user
    readlink /proc/self/ns/ipc
    readlink /proc/self/ns/uts
    readlink /proc/self/ns/cgroup
    readlink /proc/self/ns/time 2>/dev/null
    
    # Show the processes with the same namespace
    for pid in $$ 1; do
        echo "PID $pid:"
        ls -la /proc/$pid/ns/ 2>/dev/null
    done
    
    # List the network's namespaces
    sudo ip netns list 2>/dev/null
    
    # List the containers' namespaces (if Docker is installed)
    ls -la /var/run/docker/netns/ 2>/dev/null | head -5
    How many namespaces does the system have? What are the current process's namespace's identifiers? Do the current process and the PID 1 share the namespaces? Write down what you find.
  2. Create and inspect a namespace. Use the unshare's command to create the new's namespaces and inspect them.
    # Create a new's mount and PID and UTS's namespaces
    sudo unshare --mount --pid --fork --uts --mount-proc \
        /bin/bash -c '
            echo "=== The sandbox ==="
            echo "PID: $$"
            echo "Hostname: $(hostname)"
            hostname test-sandbox
            echo "After the hostname's change: $(hostname)"
            echo "The namespace's identifiers:"
            readlink /proc/self/ns/mnt
            readlink /proc/self/ns/pid
            readlink /proc/self/ns/uts
            echo "The processes in the namespace:"
            ps aux
        '
    
    # Verify the host's hostname is unchanged
    hostname
    What is the sandbox's PID? Did the hostname's change affect the host? How many processes are visible in the sandbox? Explain the isolation's design.
  3. Create a network's namespace. Use the ip netns's command to create and configure a network's namespace.
    # Create a network's namespace
    sudo ip netns add test-ns
    
    # Show the namespace's interfaces
    sudo ip netns exec test-ns ip link
    sudo ip netns exec test-ns ip addr
    
    # Create a virtual's Ethernet's pair
    sudo ip link add veth-host type veth peer name veth-ns
    
    # Move one end to the namespace
    sudo ip link set veth-ns netns test-ns
    
    # Configure the interfaces
    sudo ip addr add 10.99.0.1/24 dev veth-host
    sudo ip link set veth-host up
    sudo ip netns exec test-ns ip addr add 10.99.0.2/24 dev veth-ns
    sudo ip netns exec test-ns ip link set veth-ns up
    sudo ip netns exec test-ns ip link set lo up
    
    # Test the connectivity
    ping -c 2 10.99.0.2
    sudo ip netns exec test-ns ping -c 2 10.99.0.1
    
    # Clean up
    sudo ip netns delete test-ns
    sudo ip link delete veth-host 2>/dev/null
    Did the namespace's interfaces appear? Did the connectivity's test work? How does the network's namespace's isolation work? What are the containers' network's isolation's mechanisms?
  4. Inspect the cgroups. Use the cgroups' interfaces to inspect the hierarchy and the limits.
    # Show the cgroup's version
    stat -fc %T /sys/fs/cgroup/
    mount | grep cgroup
    
    # Show the cgroup's hierarchy
    systemd-cgls 2>/dev/null | head -30
    ls /sys/fs/cgroup/ 2>/dev/null
    
    # Show the current process's cgroup
    cat /proc/self/cgroup
    
    # Show the current cgroup's limits
    cat /sys/fs/cgroup/$(cat /proc/self/cgroup | cut -d: -f3)/memory.max 2>/dev/null
    cat /sys/fs/cgroup/$(cat /proc/self/cgroup | cut -d: -f3)/cpu.max 2>/dev/null
    
    # Show the resource's usage
    systemd-cgtop -b -n 1 2>/dev/null | head -10
    What is the cgroup's version (the v1, the v2)? What are the current process's cgroup's limits? What is the system's resource's usage's distribution? How do the cgroups complement the namespaces?
  5. Create a cgroup's limit. Use the systemd-run's command to create a transient's service with the resource's limits.
    # Create a transient's service with the memory's limit
    sudo systemd-run --unit=mem-limit-test --property=MemoryMax=50M \
        /bin/bash -c 'dd if=/dev/zero of=/dev/null bs=1M count=1000' 2>&1 | head -3
    
    # Watch the service's status
    systemctl status mem-limit-test 2>/dev/null | head -10
    
    # Show the service's cgroup's limits
    cat /sys/fs/cgroup/system.slice/mem-limit-test.service/memory.max 2>/dev/null
    
    # Clean up
    sudo systemctl stop mem-limit-test 2>/dev/null
    sudo systemctl reset-failed mem-limit-test 2>/dev/null
    
    # Create a transient's service with the CPU's limit
    sudo systemd-run --unit=cpu-limit-test --property=CPUQuota=20% \
        /bin/bash -c 'while true; do :; done' 2>&1 | head -3
    
    # Check the service's status
    systemctl status cpu-limit-test 2>/dev/null | head -10
    
    # Clean up
    sudo systemctl stop cpu-limit-test 2>/dev/null
    sudo systemctl reset-failed cpu-limit-test 2>/dev/null
    Did the memory's limit cause the service to fail? Did the CPU's quota limit the service's CPU's usage? How do the cgroups' limits enforce the resource's constraints?
  6. Evaluate a service's sandbox. Use the systemd-analyze security's command to evaluate the services' sandboxes.
    # Evaluate a few services
    for svc in sshd systemd-journald cron dbus; do
        echo "=== $svc ==="
        systemd-analyze security $svc 2>/dev/null | tail -5
    done
    
    # Show the detailed's assessment for one service
    systemd-analyze security sshd 2>/dev/null | head -40
    
    # Create a transient's service with the hardening's directives
    sudo systemd-run --unit=hardened-test \
        --property=ProtectSystem=strict \
        --property=ProtectHome=yes \
        --property=PrivateTmp=yes \
        --property=NoNewPrivileges=yes \
        --property=CapabilityBoundingSet= \
        --property=SystemCallFilter=@system-service \
        /bin/bash -c 'echo "The hardened service is running."'
    
    # Evaluate the hardened's service
    systemd-analyze security hardened-test 2>/dev/null | tail -5
    
    # Clean up
    sudo systemctl stop hardened-test 2>/dev/null
    sudo systemctl reset-failed hardened-test 2>/dev/null
    What are the services' exposure's scores? Which service has the most hardened's sandbox? What is the hardened's service's score, and how does it compare with the others? What are the directives' effects?
  7. Test the chroot and the pivot_root. Use the chroot's command to create a file system's confinement and observe its limitations.
    # Create a minimal's chroot's environment
    sudo mkdir -p /tmp/chroot-test/{bin,lib,lib64,etc}
    sudo cp /bin/bash /tmp/chroot-test/bin/
    sudo cp /bin/ls /tmp/chroot-test/bin/
    sudo ldd /bin/bash | grep -oP '/\S+' | while read lib; do
        sudo cp --parents "$lib" /tmp/chroot-test/ 2>/dev/null
    done
    sudo ldd /bin/ls | grep -oP '/\S+' | while read lib; do
        sudo cp --parents "$lib" /tmp/chroot-test/ 2>/dev/null
    done
    
    # Enter the chroot
    sudo chroot /tmp/chroot-test /bin/bash -c 'echo "The chroot is active."; ls /; echo "The /etc's files:"; ls /etc/'
    
    # Test the escape's attempt (as the root, the escape succeeds)
    sudo chroot /tmp/chroot-test /bin/bash -c '
        cd ../../../../.. 
        echo "The escape attempt:"
        ls / 2>&1 | head -5
    '
    
    # Clean up
    sudo rm -rf /tmp/chroot-test
    Did the chroot's environment work? How many files were in the chroot's /? Did the escape's attempt succeed? Explain the chroot's design's weakness and the modern's alternatives (the mount's namespace, the pivot_root).
  8. Read the man pages. Read the man pages that document the sandboxing's mechanisms.
    man 7 namespaces
    man 2 clone
    man 1 unshare
    man 1 nsenter
    man 2 setns
    man 1 ip-netns
    man 7 cgroups
    man 1 systemd-cgls
    man 1 systemd-cgtop
    man 1 systemd-run
    man 2 seccomp
    man 2 chroot
    man 2 pivot_root
    man 5 systemd.exec
    man 5 systemd.resource-control
    man 1 systemd-analyze
    man 1 firejail
    man 1 bubblewrap
    man 1 docker
    man 1 podman
    For each, write a sentence describing its main contribution to your understanding of the sandboxing. The man 7 namespaces page documents the eight namespaces; the man 7 cgroups page documents the control groups; the man 2 seccomp page documents the system calls' filter; the man 5 systemd.exec page documents the sandboxing's directives.

Reflect: the sandboxing is the modern security's central's architecture, and its mechanisms are the kernel's isolation's tools. The namespaces isolate the processes' views; the cgroups limit the processes' resource's usage; the seccomp's filters restrict the system calls; the chroot and the pivot_root confine the file system's view; the containers combine the mechanisms; the browsers' sandboxes apply the architecture at the web's scale; the systemd's directives configure the services' confinement; the Landlock enables the unprivileged's sandboxing. Which of the mechanisms from this lesson is the most important for the systems you use? What would you change about the sandboxing's design if you could? How would you explain the difference between the namespaces and the cgroups to someone who has never thought about it? What is the right balance between the sandbox's strength and the compatibility's needs?

07 Knowledge check

Check your understanding

Question 1

Define the sandbox and explain the isolation's goals. Describe the confinement's spectrum from the chroot to the virtual machines, and explain the mechanisms' complementarity.

Show answer
A sandbox is the isolated's environment in which the untrusted's code runs, with the walls that confine the code's capabilities to the minimum it needs. The sandbox's goals: the damage's limitation (the compromise's damage is the sandbox's damage, not the system's), the least privilege's enforcement (the sandbox's code has only the resources it needs), the isolation's guarantee (the sandbox's code cannot affect the system's other's components, and the system's other's components cannot affect the sandbox's code), and the multi-tenancy's support (the sandboxes coexist on the same system, and each is confined to its own's resources). The confinement's spectrum ranges from the weakest to the strongest: the chroot (the file system's view's restriction: weak, the escapes are the known), the capabilities (the fine-grained's privileges: medium), the LSM's profiles (the AppArmor's and the SELinux's mandatory access control: medium to strong), the seccomp (the system calls' filter: strong), the namespaces (the process's isolated views: strong), the cgroups (the resource's usage's limits: medium), and the virtual machines (the hardware's virtualization: the strongest, with the separate's kernel). The mechanisms' complementarity is the spectrum's central's observation: the mechanisms are the complementary, not the alternatives. The namespaces isolate the processes' views (the processes cannot see the other namespaces' resources), and the cgroups limit the processes' usage (the processes cannot use more than the cgroup's grant). The seccomp restricts the system calls (the attack's surface's reduction), the capabilities minimize the privileges (the least privilege), and the LSM's profiles apply the mandatory access control's policies. The modern's sandboxes (the Chrome's, the containers) combine the mechanisms, and the combination is the defence in depth's principle (Lesson 12.1): the attacker must bypass the multiple's mechanisms, and the multiple's mechanisms' failures are the uncorrelated.

Question 2

Describe the Linux namespaces (the mount, the PID, the network, the user, the IPC, the UTS, the cgroup, and the time). What is each namespace's isolation's semantics, and what are the namespaces' management's mechanisms?

Show answer
The Linux namespaces are the kernel's mechanisms that give the processes the isolated's views of the system's resources. The eight namespaces: the mount's namespace (CLONE_NEWNS: the isolation of the file system's mount's points, so the processes in the different namespaces see the different file system's hierarchies; the mount's namespace is the chroot's modern's successor); the PID's namespace (CLONE_NEWPID: the isolation of the process's table, so the processes in the different namespaces have the different PID's numbers; the namespace's first process has the PID 1, and the namespace's processes cannot see the other namespaces' processes); the network's namespace (CLONE_NEWNET: the isolation of the network's stack, so the processes in the different namespaces have the different interfaces, the routing's tables, the firewall's rules, the sockets; the new's namespace starts with the loopback only); the user's namespace (CLONE_NEWUSER: the isolation of the user's and the group's IDs, so the processes in the different namespaces have the different UIDs and GIDs' mappings; the namespace's process can be the root inside while the host sees the unprivileged's user: the rootless's containers' foundation); the IPC's namespace (CLONE_NEWIPC: the isolation of the System V's IPC and the POSIX's message queues); the UTS's namespace (CLONE_NEWUTS: the isolation of the hostname and the NIS's domain name); the cgroup's namespace (CLONE_NEWCGROUP: the isolation of the cgroup's hierarchy's view, so the namespace's processes see only the namespace's cgroup's subtree); and the time's namespace (CLONE_NEWTIME: the isolation of the boot's time and the monotonic's clock's offsets: the newest, the Linux 5.6, and the purpose is the containers' checkpoint's and restore's time's isolation). The namespaces' management's mechanisms: the clone's system call (the creation of the new's processes with the namespace's flags), the unshare's system call and the unshare's command (the move of the calling's process into the new's namespaces), the setns's system call and the nsenter's command (the move into the existing's namespaces), the /proc/PID/ns/'s interface (the namespace's symbolic links and their identifiers), and the ip netns's command (the network's namespaces' management). The namespaces' design's central's observation: the namespaces are the views, not the limits, so the cgroups are the complementary's mechanism.

Question 3

Describe the control groups (the cgroups). What are the v1's and the v2's designs, what are the resource's controllers, and how do the cgroups complement the namespaces?

Show answer
The control groups (cgroups) are the kernel's mechanisms that limit and account the processes' resource's usage. The cgroups' design: the processes are organized into the hierarchy of the groups (the cgroups), and the resource's controllers (the subsystems) attach to the groups and enforce the resource's limits. The cgroups v1's design: the multiple hierarchies, one per the resource's controller (the cpu's hierarchy, the memory's hierarchy, the blkio's hierarchy, the pids's hierarchy, and the others), so the process can be in the different groups in the different hierarchies and the controllers' limits are the separate. The v1's disadvantages: the complexity, the controllers' interactions, and the inconsistent's semantics. The cgroups v2's design: the single hierarchy (the /sys/fs/cgroup/'s unified's tree), with the controllers attached to the groups' nodes (the cgroup.controllers, the cgroup.subtree_control). The v2's advantages: the consistency (the single hierarchy, the coherent's semantics), the delegation (the unprivileged's users can manage the sub-hierarchies), the pressure's information (the cgroup.pressure's interfaces), and the nesting. The resource's controllers: the cpu (the cpu.weight and the cpu.max: the CPU's time's allocation), the memory (the memory.max, the memory.high, the memory.low, the memory.min, the memory.swap.max), the io (the io.weight, the io.max, the io.latency), the pids (the pids.max: the process's count's limit, the fork's bomb's prevention), and the others (the cpuset, the hugetlb, the freezer, the rdma, the devices, the net_cls, the net_prio). The cgroups' management's mechanisms: the /sys/fs/cgroup/'s interface, the systemd's integration (the services are the cgroups, the units' resource's directives, the systemd-cgls and the systemd-cgtop's tools), the systemd-run's command (the transient's services), and the cgroupfs's and the cgroup2's mounts. The cgroups complement the namespaces: the namespaces are the views (the isolation of the processes' view of the resources), and the cgroups are the limits (the processes' resource's usage's caps). The container's process sees the container's namespace's view, and the container's cgroup caps the process's resource's usage. A sandbox without the cgroups can consume the system's resources (the fork's bomb, the memory's exhaustion), and a sandbox without the namespaces can see the system's resources (the host's processes, the host's files). The two mechanisms' combination is the modern's containers' design.

Question 4

Describe the seccomp's filters, the chroot and the pivot_root, and the systemd's hardening's directives. What is each mechanism's design, and how does each contribute to the confinement?

Show answer
The seccomp is the kernel's mechanism that filters the process's system calls: the process installs the BPF's filter that the kernel evaluates on the every's system call, and the filter's action determines the system call's fate (the SECCOMP_RET_ALLOW, the SECCOMP_RET_ERRNO, the SECCOMP_RET_KILL_PROCESS, the SECCOMP_RET_TRAP, the SECCOMP_RET_TRACE, the SECCOMP_RET_LOG, the SECCOMP_RET_USER_NOTIF). The seccomp's two modes: the strict's mode (the original: the read, the write, the _exit, and the sigreturn only) and the filter's mode (the modern: the BPF's filters, the flexible's rules). The seccomp's contribution to the confinement: the attack's surface's reduction (the system calls are the kernel's interface, and the kernel's bugs are the system calls' exploits; the filter reduces the exposed's system calls, so the kernel's vulnerabilities' exploitation is the harder). The chroot is the classical's file system's confinement: the chroot's system call changes the process's root's directory, so the process sees the specified's directory as the root. The chroot's design's weakness: the chroot is not the security's boundary (the process with the CAP_SYS_CHROOT's capability can escape, and the root's process can escape by the /proc's and the mknod's mechanisms). The pivot_root is the modern's mechanism: the pivot_root's system call changes the process's root's and the current's directory to the new's file system, and it moves the old's root to the specified's directory (the put_old), so the old's root is the separate's mount and the escape's paths are the closed. The pivot_root is the containers' runtimes' mechanism. The systemd's hardening's directives are the services' confinement's mechanisms: the file system's directives (the ProtectSystem, the ProtectHome, the ReadWritePaths, the InaccessiblePaths, the PrivateTmp, the PrivateDevices, the ProtectKernelTunables), the user's and the group's directives (the User, the Group, the DynamicUser, the SupplementaryGroups), the capabilities' and the privilege's directives (the CapabilityBoundingSet, the AmbientCapabilities, the NoNewPrivileges, the SecureBits), the namespaces' directives (the PrivateMounts, the PrivateNetwork, the PrivateUsers, the PrivateIPC, the ProtectHostname, the ProtectClock), the system calls' and the addressing's directives (the SystemCallFilter, the SystemCallArchitectures, the RestrictAddressFamilies, the RestrictNamespaces, the LockPersonality), and the memory's and the kernel's directives (the MemoryDenyWriteExecute, the RestrictRealtime, the RestrictSUIDSGID, the ProtectKernelModules, the ProtectKernelLogs, the ProtectProc). The directives' contribution to the confinement: the services' resource's and privilege's restriction, so the compromised's service's damage is the bounded. The systemd-analyze security's command evaluates the service's sandbox's strength, and the observation is the reason the modern's distributions ship the services' units with the hardening's directives (the sensible's defaults).

Question 5

Describe the Linux containers and the browsers' sandboxes. What are the containers' security's mechanisms, what are the browsers' sandboxing's designs, and what are the sandbox's escapes' attacks and the sandboxing's design principles?

Show answer
The Linux containers are the sandboxing's architecture: the processes run in the isolated's environment (the container), and the environment is the combination of the namespaces, the cgroups, the seccomp's filters, the capabilities' minimization, the LSM's profiles, and the container's file system (the image's layers). The containers' components: the image (the layered's file system: the OCI's image's specification, the overlayfs's union mount), the runtime (the low-level's runtimes: the runc, the crun; the high-level's runtimes: the containerd, the conmon), the engine (the Docker's dockerd, the Podman's podman, the Kubernetes's kubelet), and the standards (the OCI's specifications: the Runtime's, the Image's, the Distribution's). The containers' security's mechanisms: the namespaces (the isolated views), the cgroups (the resource's limits), the seccomp's filter (the default's profiles: the Docker's and the Podman's ~44's blocked's system calls), the capabilities' minimization (the dropped's set: the ~14), the LSM's profiles (the SELinux's container_t, the AppArmor's docker-default), the user's namespace (the rootless's containers), and the read-only's file system (the --read-only's option). The containers' central's observation: the containers are not the VMs (the containers share the host's kernel, and the VMs have the separate's kernel), so the containers' security depends on the kernel's security. The browsers' sandboxes are the modern systems' most visible sandboxes. The Chrome's sandbox: the multi-process's architecture (the browser's process, the renderer's process per site, the GPU's process, the utility's processes) and the renderer's sandbox (the chroot, the seccomp's filter, the clone's namespaces, the setuid's and the setgid's sandbox's user, the capabilities' dropping, the OpenBSD's pledge and unveil, the macOS's sandbox_init). The renderer's process has no direct access to the file system (the broker's process's mediation), and its system calls are the whitelist's set. The Firefox's sandbox: the multi-process's architecture and the content's processes' sandbox (the seccomp-bpf's filter, the chroot, the user's namespace, the capabilities' dropping, the file's access's brokering), with the levels (the content, the socket, the utility, the widevine). The sandbox's escapes' attacks: the kernel's vulnerabilities (the Dirty COW, the Dirty Pipe, the OverlayFS's bugs: the containers share the kernel, so the kernel's vulnerability compromises the every container), the configuration's mistakes (the --privileged's flag, the host's namespaces' sharing, the sensitive's mounts: the docker.sock's mount), the design's flaws (the incomplete's mediation, the incorrect's assumptions, the IPC's channels, the shared's resources), and the side channels (the timing's channels, the speculative's execution: the Spectre's and the Meltdown's attacks). The sandboxing's design's principles apply the Lesson 12.1's principles: the least privilege (the minimum's resources), the fail-safe defaults (the denial's default), the complete mediation (the every access's check), the economy of mechanism (the simple and the small), the separation of privilege (the multiple's conditions), the least common mechanism (the minimized's sharing), the psychological acceptability (the usable), and the open design (the auditable). The sandboxing's central's observation: the sandbox is the defence in depth's application (the multiple's walls, the multiple's failures' uncorrelation), and the sandbox's strength depends on the kernel's security, the configuration's correctness, and the monitoring's detection.

08 Unit discussion

Discussion prompt

The sandboxing is the modern security's central's architecture, and its history is a history of the isolation's mechanisms' accumulation. The classical's Unix provided the chroot (the file system's view's restriction) and the users' separation (the processes' isolation), and the design was the sufficient for the time's single-tenant's systems. The ptrace's and the seccomp's mechanisms (the system calls' filtering), the namespaces (the views' isolation), and the cgroups (the resources' limits) were the 2000s' additions, and the containers (the Docker's, the Podman's, the Kubernetes's) were the 2010s' architecture. The browsers' sandboxes (the Chrome's, the Firefox's) applied the architecture at the web's scale, and the systemd's hardening's directives (the 2010s and the 2020s) brought the confinement to the services. Each mechanism was the response to the previous mechanisms' limitations, and each was shaped by the threats of its time.

The trend is toward the layered's isolation and the least privilege. The classical's Unix was the single's mechanism (the chroot) and the coarse's isolation (the user's separation); the modern's Linux is the multiple's mechanisms (the namespaces, the cgroups, the seccomp, the capabilities, the LSM, the Landlock) and the fine-grained's isolation (the per-container's namespaces, the per-service's systemd's directives, the per-process's seccomp's filters). The trend's driver is the workloads' change: the modern's systems run the untrusted's code (the web's pages, the containers' images, the third-party's applications) at the scale, and the traditional's perimeter's defence cannot protect the interior's components. The sandboxing is the recognition that the perimeter is the permeable, and the zero trust's architecture is the trend's logical's end: the every component's isolation, the every access's verification, and the every resource's confinement.

The trend also raises a question about the isolation's strength and the compatibility's tension. The stronger's isolation (the seccomp's whitelist's filters, the read-only's file systems, the minimal's capabilities, the VMs' separate's kernels) is the more secure and the less compatible, and the users' (and the developers') response to the inconvenient's isolation is the bypasses (the --privileged's flag, the --no-sandbox's option, the iptables's rules' disabling, the SELinux's permissive's mode). The observation is the reason the psychological acceptability's principle (Lesson 12.1's principle 8) is the sandboxing's design's central's concern: the sandbox's mechanism must be the usable, or the users will find the ways around it. The modern's approach (the sensible's defaults: the containers' default's profiles, the browsers' sandbox's defaults, the systemd's hardening's directives' defaults) is the principle's practical's application.

Which of the sandboxing's mechanisms is the most important for the modern systems, and why? Should the sandboxing's design prioritize the isolation's strength (the seccomp's whitelist's filters, the VMs' separate's kernels) or the compatibility (the escape's hatches, the sensible's defaults)? What is the right balance between the sandbox's confinement and the legitimate's functionality? How should the sandboxing's design evolve for the AI's systems, the edge's computing, and the serverless's platforms? Post your response to the Unit 12 discussion forum and reply to at least two classmates. Supports CLO1, CLO7, and CLO8.

09 Practice exercise

This ungraded exercise consolidates the lesson and prepares you for Lesson 12.7. Aim to spend 60–90 minutes on it.

  1. Explore the namespaces. On your VM, create and inspect the namespaces:
    # Show the current process's namespaces
    ls -la /proc/self/ns/
    
    # Create a new's mount and PID and UTS's namespaces
    sudo unshare --mount --pid --fork --uts --mount-proc \
        /bin/bash -c '
            echo "PID: $$"
            hostname sandbox-test
            echo "Hostname: $(hostname)"
            ls /proc/ | grep -E "^[0-9]+$" | wc -l
            ps aux
        '
    
    # Show the network's namespaces
    sudo ip netns list
    
    # Create a network's namespace and configure it
    sudo ip netns add test-ns
    sudo ip netns exec test-ns ip link
    sudo ip netns exec test-ns ip addr
    
    # Clean up
    sudo ip netns delete test-ns
    Which namespaces did the sandbox create? How many processes were visible in the sandbox's PID's namespace? Did the hostname's change affect the host? Write down your observations.
  2. Create and test a cgroup's limit. Use the systemd-run's command to create the transient's services with the resource's limits, and observe the limits' effects:
    # Create a memory-limited service
    sudo systemd-run --unit=mem-test --property=MemoryMax=100M \
        /bin/bash -c 'dd if=/dev/zero of=/dev/null bs=1M count=500' 2>&1 | head -3
    
    # Monitor the service
    systemctl status mem-test 2>/dev/null | head -10
    cat /sys/fs/cgroup/system.slice/mem-test.service/memory.max 2>/dev/null
    
    # Create a CPU-limited service
    sudo systemd-run --unit=cpu-test --property=CPUQuota=30% \
        /bin/bash -c 'timeout 10 bash -c "while true; do :; done"' 2>&1 | head -3
    
    # Check the CPU's usage
    systemd-cgtop -b -n 1 2>/dev/null | head -10
    
    # Clean up
    sudo systemctl stop mem-test cpu-test 2>/dev/null
    sudo systemctl reset-failed mem-test cpu-test 2>/dev/null
    Did the limits' effects appear? What was the memory's max's value? What was the CPU's quota's effect? How do the cgroups' limits enforce the resource's constraints?
  3. Evaluate the services' sandboxes. Use the systemd-analyze security's command to evaluate the system's services' sandboxes:
    # List the services
    systemctl list-units --type=service --state=running 2>/dev/null | head -20
    
    # Evaluate each service's sandbox
    for svc in $(systemctl list-units --type=service --state=running --no-legend 2>/dev/null | awk '{print $1}'); do
        score=$(systemd-analyze security $svc 2>/dev/null | tail -1 | grep -oP '[\d.]+' | head -1)
        echo "$svc: $score"
    done | sort -t: -k2 -rn | head -10
    Which services have the highest exposure? Which have the lowest? What are the most common's unmet's directives? What would you harden on the system?
  4. Inspect a container's security. If the Docker or the Podman is installed, inspect a container's security's configuration:
    # Run a container
    docker run -d --name sec-test alpine sleep 3600 2>/dev/null
    
    # Inspect the container's security
    docker inspect sec-test 2>/dev/null | \
        grep -A10 -E 'CapDrop|CapAdd|SecurityOpt|ReadonlyRootfs|Privileged|User'
    
    # Show the container's namespaces
    CONTAINER_PID=$(docker inspect -f '{{.State.Pid}}' sec-test 2>/dev/null)
    sudo ls -la /proc/$CONTAINER_PID/ns/
    
    # Show the container's capabilities
    sudo nsenter -t $CONTAINER_PID -a capsh --print 2>/dev/null | head -10
    
    # Show the container's seccomp's status
    sudo grep Seccomp /proc/$CONTAINER_PID/status 2>/dev/null
    
    # Show the container's cgroup's limits
    cat /sys/fs/cgroup/system.slice/docker-*.scope/memory.max 2>/dev/null
    
    # Clean up
    docker rm -f sec-test 2>/dev/null
    What are the container's dropped's and added's capabilities? What are the security's options? What are the container's namespaces? Is the seccomp's filter's mode the filter's (the mode 2)? What are the cgroup's limits?
  5. Design a container's security's policy. A web application is being containerized. The application needs to bind to the port 8080 (unprivileged), to read the files in the /app's directory, and to connect to the database on the network. Design the container's security's policy:
    • Which namespaces would you enable?
    • Which capabilities would you drop and which would you keep?
    • Which seccomp's profile would you use (the default, the custom)?
    • Which LSM's profile would you use?
    • What would be the cgroup's limits?
    • What would be the file system's configuration (the read-only's root, the volumes)?
    • What would be the user's configuration (the non-root's user, the rootless's containers)?
    Justify your design through the lesson's principles.
  6. Analyze a sandbox's escape. Choose a known sandbox's escape (the Dirty COW in the Docker, the CVE-2019-5736 in the runc, the CVE-2020-15257 in the containerd, the CVE-2021-25741 in the Kubernetes's subpath, or the browser's escape) and analyze it:
    • The vulnerability's description.
    • The escape's mechanism (which wall was bypassed?).
    • The affected's versions and the fixes.
    • The lessons for the sandboxing's design.
    Write a short report (500–750 words).
  7. Compare the sandboxing's mechanisms. Write a short essay (400–500 words) comparing the sandboxing's mechanisms (the chroot, the capabilities, the seccomp, the namespaces, the cgroups, the LSM's profiles, the VMs). Your essay should address:
    • Each mechanism's isolation's scope.
    • Each mechanism's strength.
    • Each mechanism's cost (the performance, the compatibility).
    • Each mechanism's appropriate use's cases.
    • The mechanisms' combinations in the modern's sandboxes (the containers, the browsers).
    Conclude with a recommendation for a multi-tenant's server and for a desktop's browser.
  8. Design a service's hardening. A web server (the nginx) is running on the system. Design the service's systemd's sandboxing's configuration:
    • Which file system's directives would you apply?
    • Which namespaces' directives would you apply?
    • Which capabilities' directives would you apply?
    • Which system calls' directives would you apply?
    • What would be the resource's limits (the CPU, the memory, the I/O)?
    • How would you test the configuration and verify the service's operation?
    Write the unit's file's [Service]'s section and justify the directives.
  9. Reflect. In your own words, explain the sandboxing's goals and the confinement's mechanisms (the namespaces, the cgroups, the seccomp, the chroot, the containers, the browsers' sandboxes, the systemd's directives). Then describe one situation in your own experience — a container you have run, a browser you have used, or a service you have administered — where the sandboxing affected the security or the usability. What was the situation, and how would a different configuration have changed the outcome?

10 Summary

  • A sandbox is the isolated's environment in which the untrusted's code runs, with the walls that confine the code's capabilities. The sandbox's goals: the damage's limitation, the least privilege's enforcement, the isolation's guarantee, and the multi-tenancy's support. The confinement's spectrum ranges from the chroot to the virtual machines, and the mechanisms are the complementary (the defence in depth).
  • The Linux namespaces give the processes the isolated's views: the mount, the PID, the network, the user, the IPC, the UTS, the cgroup, and the time. The management's mechanisms: the clone, the unshare, the setns, the /proc/PID/ns/'s interface, and the ip netns's command.
  • The control groups (cgroups) limit and account the processes' resource's usage. The v1's design is the multiple hierarchies (one per the controller); the v2's design is the single hierarchy. The controllers: the cpu, the memory, the io, the pids, and the others.
  • The seccomp filters the process's system calls: the BPF's filters with the actions (SECCOMP_RET_ALLOW, SECCOMP_RET_ERRNO, SECCOMP_RET_KILL_PROCESS, the others). The seccomp is the attack's surface's reduction, and it is the containers' and the browsers' central's mechanism.
  • The chroot changes the process's root's directory (the file system's view's restriction), and the pivot_root changes the root's mount (the modern's mechanism, the containers' runtimes' design).
  • The containers (the Docker, the Podman, the LXC, the OCI's standards) combine the namespaces, the cgroups, the seccomp's filters, the capabilities' minimization, the LSM's profiles, and the image's layers. The containers are the sandboxing's modern's architecture, and their isolation is the kernel's mechanisms' (the shared's kernel is the VMs' security's difference).
  • The browsers' sandboxes (the Chrome's, the Firefox's) apply the multi-process's architecture and the renderer's sandbox (the chroot, the seccomp, the namespaces, the sandbox's user, the capabilities' dropping, the broker's IPC's mediation).
  • The systemd's hardening's directives configure the services' sandboxing: the file system's directives (ProtectSystem, ProtectHome, PrivateTmp, the others), the namespaces' directives (PrivateMounts, PrivateNetwork, the others), the capabilities' directives (CapabilityBoundingSet, NoNewPrivileges), the system calls' directives (SystemCallFilter, the others). The systemd-analyze security's command evaluates the service's sandbox's strength.
  • The Landlock is the unprivileged's sandboxing's mechanism (the file system's access's restriction without the CAP_SYS_ADMIN's capability), and the bubblewrap and the firejail are the user-space's tools.
  • The sandbox's escapes are the kernel's vulnerabilities, the configuration's mistakes, the design's flaws, and the side channels. The sandboxing's design's principles apply the Lesson 12.1's principles: the least privilege, the fail-safe defaults, the complete mediation, the economy of mechanism, the separation of privilege, the least common mechanism, the psychological acceptability, and the open design.

Looking ahead

This lesson presented the sandboxing: the isolation's goals, the confinement's spectrum, the Linux namespaces, the cgroups, the seccomp's filters, the chroot and the pivot_root, the containers, the browsers' sandboxes, the systemd's hardening's directives, the Landlock, the escapes' attacks, and the design's principles. It referred several times to the virtual machines — the sandboxing's strongest's mechanism, with the separate's kernel and the hardware's virtualization — and to the hypervisors. Lesson 12.7 presents the operating system's hardening in detail: the hardening's methodology (the CIS's benchmarks, the NIST's guides, the STIG's guides), the configuration's hardening (the services, the kernel, the network, the file systems), the monitoring's and the detection's mechanisms (the HIDS, the file's integrity's monitoring, the logs' analysis), the patch's management, and the security's operations. The lesson is the unit's final's synthesis, and it is the bridge to the virtualization and the cloud that Unit 13 presents.

11 References

  • Silberschatz, A., Galvin, P. B., & Gagne, G. Operating System Concepts, 10th ed., Chapters 16 and 17.
  • Arpaci-Dusseau, R. H., & Arpaci-Dusseau, A. C. Operating Systems: Three Easy Pieces, the Security and the Virtualization chapters. Available online at ostep.org.
  • Tanenbaum, A. S., & Bos, H. Modern Operating Systems, 4th ed., Chapters 7 and 9.
  • Kerrisk, M. The Linux Programming Interface. No Starch Press, 2010. Chapters 20, 24, 28, 37, and 61.
  • Kerrisk, M. The Linux namespaces. The man 7 namespaces page and the LWN's articles.
  • Rosen, R. Resource Management: The Linux and the SUSE Approaches. The Novell/SUSE's and the RHEL's resource-management's guides.
  • Edge, J., & other LWN's authors. The LWN's series on the seccomp's filters, the Landlock, and the container's security.
  • Rice, L. Container Security. O'Reilly, 2020.
  • The Open Container Initiative (the OCI). The Runtime's Specification, the Image's Specification, and the Distribution's Specification.
  • Linux man-pages: man 7 namespaces, man 2 clone, man 1 unshare, man 1 nsenter, man 2 setns, man 1 ip-netns, man 7 cgroups, man 1 systemd-cgls, man 1 systemd-cgtop, man 1 systemd-run, man 2 seccomp, man 2 chroot, man 2 pivot_root, man 5 systemd.exec, man 5 systemd.resource-control, man 1 systemd-analyze, man 1 firejail, man 1 bubblewrap, man 8 docker, man 1 podman.
👤