// Single source of truth for limits and layout. Mirrors SPEC.md; change both together. #pragma once #include #include #include #include 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"; // 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 kSizeCheckMinIntervalMs = 2000; // floor between usage scans, never faster constexpr int kSizeScanDutyFactor = 10; // next scan waits at least 10x the last scan's duration 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 // Resource limits per container (--memory, --pids, --cpus). Enforced by cgroup v2; cpus 0 means "all cores". constexpr std::uint64_t kDefaultMemoryBytes = 8ULL << 30; constexpr std::uint64_t kMinMemoryBytes = 64ULL << 20; constexpr std::uint64_t kMaxMemoryBytes = 1ULL << 40; constexpr std::uint32_t kDefaultPids = 4096; constexpr std::uint32_t kMinPids = 16; constexpr std::uint32_t kMaxPids = 4194304; constexpr std::uint32_t kMaxMilliCpus = 1024000; // 1024 cpus constexpr std::uint32_t kMinMilliCpus = 100; // 0.1 cpu constexpr int kCpuPeriodUs = 100000; // 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"; constexpr std::string_view kDebianBase = "https://raw.githubusercontent.com/debuerreotype/docker-debian-artifacts/"; constexpr std::string_view kDebianDefaultRelease = "trixie"; // Release signing keys, pinned by fingerprint. Keys are fetched from the publisher and rejected unless the // fingerprint matches. Sources of the fingerprints: alpinelinux.org/downloads, ubuntu.com verify tutorial. constexpr std::string_view kAlpineKeyUrl = "https://alpinelinux.org/keys/ncopa.asc"; constexpr std::string_view kAlpineKeyFpr = "0482D84022F52DF1C4E7CD43293ACD0907D9495A"; constexpr std::string_view kUbuntuKeyUrl = "https://keyserver.ubuntu.com/pks/lookup?op=get&search=0x843938DF228D22F7B3742BC0D94AA3F0EFE21092"; constexpr std::string_view kUbuntuKeyFpr = "843938DF228D22F7B3742BC0D94AA3F0EFE21092"; // 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. constexpr std::array 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 kCopiedDirs = {"/etc"}; // Host mode /etc under --hardening default: only these entries are copied (relaxed copies everything readable). // Entries that may hold credentials are skipped even inside allowed directories. constexpr std::array kEtcAllow = { "passwd", "group", "nsswitch.conf", "hosts", "hostname", "host.conf", "localtime", "timezone", "machine-id", "ld.so.conf", "ld.so.conf.d", "ld.so.cache", "ssl", "ca-certificates", "ca-certificates.conf", "pki", "alternatives", "apt", "dpkg", "bash.bashrc", "bashrc", "profile", "profile.d", "environment", "inputrc", "os-release", "lsb-release", "issue", "issue.net", "debian_version", "shells", "protocols", "services", "mime.types", "magic", "gai.conf", "locale.gen", "locale.conf", "default", "gitconfig", "bash_completion", "bash_completion.d", "vim", "emacs", "xdg", "fonts", "mailcap", "wgetrc", "ssh/ssh_config", "ssh/ssh_config.d", "perl", "python3", "terminfo"}; constexpr std::array kEtcDenyNames = {"private", "shadow", "gshadow", "sudoers", "sudoers.d", "auth.conf", "auth.conf.d", "ssh_host_*"}; // Capabilities kept in the bounding set under --hardening default (the Docker default set). constexpr std::array kKeptCaps = {0 /*CHOWN*/, 1 /*DAC_OVERRIDE*/, 3 /*FOWNER*/, 4 /*FSETID*/, 5 /*KILL*/, 6 /*SETGID*/, 7 /*SETUID*/, 8 /*SETPCAP*/, 10 /*NET_BIND_SERVICE*/, 13 /*NET_RAW*/, 18 /*SYS_CHROOT*/, 27 /*MKNOD*/, 29 /*AUDIT_WRITE*/, 31 /*SETFCAP*/}; // Extra overlay mounted inside the private /var, only when the host has the directory (dpkg database). constexpr std::array 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 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 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 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}}}; // 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 kCompatDirs = {"/bin", "/sbin", "/lib", "/lib32", "/lib64"}; // Persistent per-container private directories (no host lower layer). constexpr std::array kPrivateDirs = {"/root", "/var", "/etc"}; // Empty mountpoints / tmpfs constexpr std::array kEmptyDirs = {"/mnt", "/srv", "/tmp", "/run", "/dev", "/proc"}; // Host device nodes bound into the container /dev (no mknod possible in a userns). constexpr std::array kDevices = {"null", "zero", "full", "random", "urandom", "tty"}; // /proc paths remounted read-only, or hidden behind /dev/null. constexpr std::array kProcReadOnly = {"/proc/sys", "/proc/sysrq-trigger", "/proc/irq", "/proc/bus", "/proc/fs"}; constexpr std::array kProcMasked = {"/proc/kcore", "/proc/keys", "/proc/timer_list"}; } // namespace spec