2026-10-23 20:54:55 +02:00
|
|
|
// Single source of truth for limits and layout. Mirrors SPEC.md; change both together.
|
|
|
|
|
#pragma once
|
|
|
|
|
#include <array>
|
|
|
|
|
#include <cstddef>
|
2026-10-23 21:41:28 +02:00
|
|
|
#include <cstdint>
|
2026-10-23 20:54:55 +02:00
|
|
|
#include <string_view>
|
|
|
|
|
|
|
|
|
|
namespace spec {
|
|
|
|
|
|
|
|
|
|
// Hard caps
|
|
|
|
|
constexpr std::size_t kMaxContainers = 64;
|
|
|
|
|
constexpr std::size_t kMaxNameLen = 32;
|
|
|
|
|
constexpr std::size_t kMaxCommandArgs = 256;
|
|
|
|
|
constexpr std::size_t kMaxTreeDepth = 256; // removal recursion cap
|
|
|
|
|
constexpr unsigned long kMaxOpenFiles = 4096; // RLIMIT_NOFILE ceiling inside
|
|
|
|
|
constexpr int kNetReadyTimeoutMs = 5000; // slirp4netns must be ready by then
|
|
|
|
|
constexpr int kLockTimeoutMs = 5000; // per-container lock wait
|
|
|
|
|
constexpr std::string_view kTmpSize = "256m";
|
|
|
|
|
constexpr std::string_view kRunSize = "64m";
|
|
|
|
|
constexpr std::string_view kShmSize = "64m";
|
|
|
|
|
|
2026-10-23 21:41:28 +02:00
|
|
|
// Writable-layer size cap per container (--size). Enforced by a watchdog that freezes the container.
|
|
|
|
|
constexpr std::uint64_t kDefaultSizeBytes = 4ULL << 30;
|
|
|
|
|
constexpr std::uint64_t kMinSizeBytes = 64ULL << 20;
|
|
|
|
|
constexpr std::uint64_t kMaxSizeBytes = 1ULL << 40;
|
|
|
|
|
constexpr int kSizeWarnPercent = 80;
|
|
|
|
|
constexpr int kSizeThawPercent = 95; // thaw again below this share of the cap
|
|
|
|
|
constexpr int kSizeCheckIntervalMs = 10000; // floor between usage scans, never faster
|
|
|
|
|
constexpr std::size_t kMaxScanEntries = 5000000; // usage scan stops (and reports) beyond this
|
|
|
|
|
constexpr std::string_view kCgroupDir = "doos"; // under the delegated user@UID.service cgroup
|
|
|
|
|
|
|
|
|
|
// Seeding limits
|
|
|
|
|
constexpr std::size_t kMaxSkeletonDirs = 200000;
|
|
|
|
|
constexpr std::size_t kMaxEtcCopyFiles = 50000;
|
|
|
|
|
constexpr std::uint64_t kMaxEtcCopyBytes = 256ULL << 20;
|
|
|
|
|
|
|
|
|
|
// Id mapping: container ids 0..kMaxIdRange. 0 maps to the invoking user, the rest to the subuid range.
|
|
|
|
|
constexpr std::uint32_t kMaxIdRange = 65536;
|
|
|
|
|
constexpr std::string_view kNewUidMap = "newuidmap";
|
|
|
|
|
constexpr std::string_view kNewGidMap = "newgidmap";
|
|
|
|
|
|
|
|
|
|
// Images
|
|
|
|
|
constexpr std::size_t kMaxImages = 16;
|
|
|
|
|
constexpr std::uint64_t kMaxImageDownloadBytes = 2ULL << 30;
|
|
|
|
|
constexpr int kDownloadTimeoutSec = 900;
|
|
|
|
|
constexpr std::size_t kMaxCaptureBytes = 1 << 20; // listing / checksum fetches
|
|
|
|
|
constexpr std::string_view kAlpineBase = "https://dl-cdn.alpinelinux.org/alpine/latest-stable/releases/";
|
|
|
|
|
constexpr std::string_view kUbuntuBase = "https://cdimage.ubuntu.com/ubuntu-base/releases/";
|
|
|
|
|
constexpr std::string_view kUbuntuDefaultVersion = "26.04";
|
|
|
|
|
|
2026-10-23 20:54:55 +02:00
|
|
|
// Networking
|
|
|
|
|
constexpr std::string_view kSlirpBinary = "slirp4netns";
|
|
|
|
|
constexpr std::string_view kSlirpDns = "10.0.2.3";
|
|
|
|
|
constexpr std::string_view kSlirpTapName = "tap0";
|
|
|
|
|
|
|
|
|
|
// Container identity
|
|
|
|
|
constexpr std::string_view kContainerPath =
|
|
|
|
|
"/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin";
|
|
|
|
|
constexpr std::string_view kHome = "/root";
|
|
|
|
|
|
|
|
|
|
// Host directories exposed as overlay lowerdirs. Writes land in the per-container upperdir.
|
2026-10-23 21:41:28 +02:00
|
|
|
constexpr std::array<std::string_view, 2> kOverlayDirs = {"/usr", "/opt"};
|
|
|
|
|
|
|
|
|
|
// Host directories copied once into a private per-container dir (owned by the container root, so
|
|
|
|
|
// chown --reference and in-place edits work). Unreadable entries are skipped and counted.
|
|
|
|
|
constexpr std::array<std::string_view, 1> kCopiedDirs = {"/etc"};
|
|
|
|
|
|
|
|
|
|
// Extra overlay mounted inside the private /var, only when the host has the directory (dpkg database).
|
|
|
|
|
constexpr std::array<std::string_view, 1> kVarOverlayDirs = {"/var/lib/dpkg"};
|
|
|
|
|
|
|
|
|
|
// Lock files that exist on the host owned by an unmapped uid: an empty copy is created in the upper layer so
|
|
|
|
|
// the container can open them for writing. Each must live under one of the overlay dirs above.
|
|
|
|
|
constexpr std::array<std::string_view, 3> kUpperLockFiles = {"/var/lib/dpkg/lock", "/var/lib/dpkg/lock-frontend",
|
|
|
|
|
"/var/lib/dpkg/triggers/Lock"};
|
|
|
|
|
|
|
|
|
|
// Directories whose regular files (not subdirs) are copied into the upper layer at creation, so the container
|
|
|
|
|
// owns them: dpkg hard-links `status` to `status-old`, which the kernel refuses for files owned by nobody.
|
|
|
|
|
constexpr std::array<std::string_view, 3> kUpperCopyDirs = {"/var/lib/dpkg", "/var/lib/dpkg/triggers",
|
|
|
|
|
"/var/lib/dpkg/alternatives"};
|
|
|
|
|
|
|
|
|
|
struct DirSpec {
|
|
|
|
|
std::string_view path;
|
|
|
|
|
unsigned mode;
|
|
|
|
|
};
|
|
|
|
|
// Directories created in the private /var (package managers expect them).
|
|
|
|
|
constexpr std::array<DirSpec, 8> kVarDirs = {{{"lib/apt/lists/partial", 0755},
|
|
|
|
|
{"lib/dpkg", 0755},
|
|
|
|
|
{"cache/apt/archives/partial", 0755},
|
|
|
|
|
{"log/apt", 0755},
|
|
|
|
|
{"tmp", 01777},
|
|
|
|
|
{"lib/apt", 0755},
|
|
|
|
|
{"cache", 0755},
|
|
|
|
|
{"log", 0755}}};
|
2026-10-23 20:54:55 +02:00
|
|
|
|
|
|
|
|
// Host top-level entries: recreated as symlinks when the host has them as symlinks (usr-merge),
|
|
|
|
|
// overlaid like kOverlayDirs when they are real directories, skipped when absent.
|
|
|
|
|
constexpr std::array<std::string_view, 5> kCompatDirs = {"/bin", "/sbin", "/lib", "/lib32", "/lib64"};
|
|
|
|
|
|
|
|
|
|
// Persistent per-container private directories (no host lower layer).
|
2026-10-23 21:41:28 +02:00
|
|
|
constexpr std::array<std::string_view, 3> kPrivateDirs = {"/root", "/var", "/etc"};
|
2026-10-23 20:54:55 +02:00
|
|
|
|
|
|
|
|
// Empty mountpoints / tmpfs
|
|
|
|
|
constexpr std::array<std::string_view, 6> kEmptyDirs = {"/mnt", "/srv", "/tmp", "/run", "/dev", "/proc"};
|
|
|
|
|
|
|
|
|
|
// Host device nodes bound into the container /dev (no mknod possible in a userns).
|
|
|
|
|
constexpr std::array<std::string_view, 6> kDevices = {"null", "zero", "full", "random", "urandom", "tty"};
|
|
|
|
|
|
|
|
|
|
// /proc paths remounted read-only, or hidden behind /dev/null.
|
|
|
|
|
constexpr std::array<std::string_view, 5> kProcReadOnly = {"/proc/sys", "/proc/sysrq-trigger", "/proc/irq",
|
|
|
|
|
"/proc/bus", "/proc/fs"};
|
|
|
|
|
constexpr std::array<std::string_view, 3> kProcMasked = {"/proc/kcore", "/proc/keys", "/proc/timer_list"};
|
|
|
|
|
|
|
|
|
|
} // namespace spec
|