diff --git a/docs/THREAT-MODEL.md b/docs/THREAT-MODEL.md index 92e3e86..ea86d5d 100644 --- a/docs/THREAT-MODEL.md +++ b/docs/THREAT-MODEL.md @@ -1,6 +1,6 @@ # Threat Model -> **Status:** Draft for v0.7 public flex. Honest scope. +> **Status:** Final for v0.7 public launch. Honest scope. veilor-os is a hardened daily-driver desktop. Not a paranoia OS, not an anonymity OS, not an isolation OS. This document exists so that @@ -14,36 +14,39 @@ tool**. veilor-os will not save you, and we will not pretend otherwise. ## In scope — what veilor-os defends against +Every row cites the file or setting that implements the mitigation, so the +claim is auditable from a clean checkout. + | Adversary / scenario | veilor-os mitigation | |---|---| -| Lost or stolen laptop, powered off | LUKS2 (aes-xts-plain64, argon2id, mem=1 GB) on root + swap-as-zram. Disk yields ciphertext. | -| Generic browser / email malware (drive-by RCE, malicious attachment) | SELinux enforcing + `veilor-systemd` policy + sysctl hardening (kptr_restrict, ptrace=2, perf=3, BPF JIT harden, full ASLR, no SUID core dumps). AppArmor stack lands in v0.5. | -| Console-side USB attack (BadUSB, rubber ducky, juice-jack) | USBGuard daemon, default-block, empty allowlist on first boot. New device = explicit operator allow. | -| SSH brute-force / credential-stuffing | sshd password-auth off, root login off, MaxAuthTries=3, fail2ban with sshd + pam-generic jails wired to firewalld rich-rule. | -| Post-incident forensics ("what happened?") | auditd rules covering passwd/shadow/sudoers/ssh/cron/sysctl/kernel modules and all privileged binaries. Logs survive reboot. | -| Supply-chain on the OS image itself | Fedora's signed shim → GRUB → kernel chain (Secure Boot enforced). v0.4 adds GPG-signed ISO + sha256 + own MOK. | -| Unprivileged local user attempting LPE | root account locked (`passwd -S root` → `L`), single sudo user with pwquality minlen=14 / 4 classes, kernel module loading frozen 30 s after graphical boot. | -| Network-listening services as attack surface | firewalld default zone = `drop`; only sshd answers. abrt/cups/avahi/bluetooth/ModemManager/kdeconnectd/PackageKit are masked. | -| Time-based MITM (back-dated certs, replay) | NTS-authenticated chrony, DNS-over-TLS via systemd-resolved, LLMNR off. | +| Lost or stolen laptop, powered off | LUKS2 `aes-xts-plain64` + `argon2id` (`mem=1 GiB`, `time=9`) on root LV; swap is `zram` only — no persistent key material on disk. Defined in `kickstart/veilor-os.ks` `part pv.veilor` block. | +| Generic browser / email malware (drive-by RCE, malicious attachment) | SELinux `enforcing` + targeted policy + custom `veilor-systemd.te` module (`scripts/selinux/`); sysctl knobs in `/etc/sysctl.d/99-veilor-hardening.conf`: `kernel.kptr_restrict=2`, `kernel.yama.ptrace_scope=2`, `kernel.perf_event_paranoid=3`, `net.core.bpf_jit_harden=2`, `kernel.randomize_va_space=2`, `fs.suid_dumpable=0`, `dev.tty.ldisc_autoload=0`. AppArmor profile skeletons in `scripts/apparmor/` for Trivalent/Thorium/lm-studio (opt-in, complain mode, hardens to enforce per profile). | +| Console-side USB attack (BadUSB, rubber ducky, juice-jack) | USBGuard daemon, `ImplicitPolicyTarget=block`, **id-based** rules in `/etc/usbguard/rules.conf` (vendor:product, not hash — survives dock replug). Empty allowlist on first boot; operator runs `usbguard generate-policy` after plugging trusted devices. | +| SSH brute-force / credential-stuffing | `/etc/ssh/sshd_config.d/10-veilor-hardening.conf`: `PasswordAuthentication no`, `PermitRootLogin no`, `AllowUsers admin`, `MaxAuthTries 3`, `X11Forwarding no`, `LogLevel VERBOSE`. `fail2ban` `sshd` + `pam-generic` jails (journald backend) ban via firewalld `rich-rule` action. | +| Post-incident forensics ("what happened?") | `auditd` rules in `/etc/audit/rules.d/99-veilor-hardening.rules` watch `/etc/{passwd,shadow,group,sudoers,sudoers.d,ssh/sshd_config*,selinux,firewalld,cron.*,sysctl.*,systemd/system}`, every privileged binary (`sudo`, `su`, `passwd`, `mount`, `pkexec`, …), `init_module`/`finit_module`/`delete_module` syscalls, and uid≥1000 perm/owner changes. Logs persist across reboot. | +| Supply-chain on the OS image itself | Secure Boot enforced (Fedora signed shim → GRUB → kernel). v0.7 adds cosign-signed OCI image at `ghcr.io/veilor/veilor-os:43`, GPG-signed ISO + sha256 + .asc, plus our own MOK for out-of-tree module signing. | +| Unprivileged local user attempting LPE | Root account locked (`passwd -l root`; `passwd -S root` → `L`); single `admin` user in `wheel`; `pwquality.conf` `minlen=14`, `minclass=4`, dictcheck on. Kernel `lockdown=integrity`, `slab_nomerge`, `init_on_alloc=1`, `init_on_free=1`, `randomize_kstack_offset=on`, `vsyscall=none` set in bootloader args. Module loading frozen 30 s after graphical boot via `veilor-modules-lock.service`. | +| Network-listening services as attack surface | `firewalld` default zone = `drop`; only `sshd` answers. `abrt*`, `cups`, `cups-browsed`, `geoclue`, `avahi-daemon`, `bluetooth`, `ModemManager`, `gssproxy`, `atd`, `pcscd.{socket,service}` are masked; `kdeconnectd` and `PackageKit` are removed at the package level. | +| Time-based MITM (back-dated certs, replay) | `chrony` with NTS authentication against `time.cloudflare.com` and `nts.sth1/2.ntp.se` (pool fallback only). `systemd-resolved` with DNS-over-TLS opportunistic, DNSSEC `allow-downgrade`, LLMNR off; resolvers Cloudflare 1.1.1.1 / 1.0.0.1, fallback Quad9 9.9.9.9 / 149.112.112.112. | --- ## Out of scope — what veilor-os does NOT defend against -We are honest about this list because pretending otherwise is how people get -hurt. **If your adversary is here, pick a different tool.** +These adversaries are unambiguously outside our scope. Pretending otherwise +gets people hurt. **If your adversary is on this list, pick a different tool.** | Adversary / scenario | Why veilor-os doesn't help | Use instead | |---|---|---| -| Nation-state firmware-level implant (UEFI, ME, BMC) | Secure Boot validates the OS, not the firmware below it. We do not flash custom firmware. | Heads / coreboot on supported hardware. | -| Evil-maid attack on a running, unlocked system | LUKS keys live in RAM while the system is up. A physically present attacker can dump RAM (cold boot, DMA via Thunderbolt, debug header). | Power off when unattended. Disable Thunderbolt DMA in firmware. Qubes-in-a-Faraday-bag if you're that target. | -| Hardware keylogger / hardware mod between keyboard and machine | We're software. Software cannot detect a passive hardware tap. | Physical custody of the device. Tamper-evident seals. | -| Targeted RCE on the user session (browser 0-day, signal-app exploit) | KDE Plasma is not sandboxed. A logged-in compromise has the user's full data and tokens. SELinux confines daemons, not the desktop. | Qubes (per-app VM isolation). | -| Side-channel attacks on AES (timing, cache, power analysis) | We use stock kernel crypto. No constant-time guarantees beyond what the kernel/CPU provide. | Threat-specific HSM. | -| Physical attack on a TPM2 chip (probe, glitch, decap) | We don't ship TPM2 binding yet. Even when v1.0 lands, TPM2 is not anti-tamper hardware. | Off-device key custody. | -| Network-level traffic correlation / traffic analysis | All packets leave the box on the local IP. We don't onion-route. | Tails, Whonix, Tor. | -| Trust-on-first-use attacks (user clicks "accept bad cert") | We can't override the user's decisions. Bad SSL/SSH key acceptance by the operator is out of scope. | Enrolment policy, MDM. | -| Adversary with sustained physical access and time | Given enough physical time and tools, any laptop falls. | Operational security, not OS choice. | +| Firmware-level implant (UEFI, Intel ME, BMC, EC) | veilor-os does not protect against firmware implants. Secure Boot validates the OS chain only; we do not flash, audit, or sign firmware below GRUB. | Heads / coreboot on supported hardware. | +| Evil-maid attack on a running, unlocked system | LUKS master keys live in RAM while the system is up. A physically present attacker can dump RAM (cold-boot, Thunderbolt DMA, debug header) and recover them. | Power off when unattended. Disable Thunderbolt DMA in firmware. Qubes-in-a-Faraday-bag if you are that target. | +| Hardware keylogger / interposer between keyboard and machine | veilor-os is software. Software cannot detect a passive hardware tap. | Physical custody of the device. Tamper-evident seals. | +| Targeted RCE on the user session (browser 0-day, messenger exploit) | KDE Plasma is not sandboxed. A logged-in compromise owns the user's data and tokens. SELinux confines daemons; it does not confine the desktop session. | Qubes OS (per-app Xen VM isolation). | +| Side-channel attacks on AES (timing, cache, power, EM) | veilor-os ships stock kernel crypto. We provide no constant-time or power-analysis guarantees beyond what the kernel and CPU deliver. | Threat-specific HSM, air-gap. | +| Physical attack on a TPM2 chip (bus probe, glitch, decap) | veilor-os does not bind keys to TPM2 in v0.7. Even when binding lands post-v1.0, TPM2 is not anti-tamper hardware. | Off-device key custody (smartcard / YubiKey / OnlyKey). | +| Network-level traffic correlation / traffic analysis | All packets leave the box on the local IP. veilor-os does not onion-route. | Tails, Whonix, Tor. | +| Trust-on-first-use attacks (operator accepts a bad cert) | veilor-os cannot override the operator's explicit decisions. Bad SSL or SSH host-key acceptance is out of scope. | Enrolment policy, MDM, certificate pinning. | +| Adversary with sustained physical access and time | Given unlimited physical time and tools, any laptop falls. | Operational security, not OS choice. | --- @@ -92,19 +95,21 @@ Hardening that breaks ordinary work gets called out, not hidden. Scoring legend: `✓` shipped & on by default, `~` partial / opt-in, `✗` not provided, `n/a` not applicable to that distro's model. +Project metrics are GitHub / Codeberg figures as of 2026-05. -| Axis | veilor-os | Stock Fedora KDE | Kicksecure | Tails | Qubes OS | secureblue | -|---|:---:|:---:|:---:|:---:|:---:|:---:| -| **Encrypted at rest by default** | ✓ (LUKS2 argon2id) | ~ (optional) | ✓ | n/a (amnesic) | ✓ | ✓ | -| **MAC enforcing OOTB** | ✓ (SELinux + AppArmor v0.5) | ✓ (SELinux) | ✓ (AppArmor) | ✓ (AppArmor) | ✓ (per-VM) | ✓ (SELinux) | -| **Default-deny firewall** | ✓ | ✗ | ✓ | ✓ (Tor-only) | ✓ | ✓ | -| **USB default-block** | ✓ (USBGuard) | ✗ | ✓ | ✓ | ✓ (sys-usb) | ✓ | -| **Per-app isolation (VM/sandbox)** | ✗ | ✗ | ✗ | ~ (AppArmor) | ✓ (Xen VMs) | ~ (Flatpak/bwrap) | -| **Anonymity / Tor by default** | ✗ | ✗ | ✗ | ✓ | ~ (Whonix VMs) | ✗ | -| **Daily driver target (persistent)** | ✓ | ✓ | ✓ | ✗ | ✓ (heavy) | ✓ | -| **Signed releases (publisher key)** | ✓ (v0.4) | ✓ | ✓ | ✓ | ✓ | ✓ | -| **Threat model published** | ✓ (this doc) | ✗ | ✓ | ✓ | ✓ | ✓ | -| **Hardware compatibility (laptops)** | ✓ (Fedora kernel) | ✓ | ~ | ~ (live USB) | ~ (Xen-pinned) | ✓ | +| Axis | veilor-os | Stock Fedora KDE | Kicksecure | Tails | Qubes OS | secureblue | Athena OS | +|---|:---:|:---:|:---:|:---:|:---:|:---:|:---:| +| **Encrypted at rest by default** | ✓ (LUKS2 argon2id, mem=1 GiB) | ~ (optional in Anaconda) | ✓ | n/a (amnesic, session-only) | ✓ | ✓ | ~ (optional) | +| **MAC enforcing OOTB** | ✓ (SELinux + opt-in AppArmor) | ✓ (SELinux) | ✓ (AppArmor) | ✓ (AppArmor) | ✓ (per-VM) | ✓ (SELinux) | ✓ (AppArmor) | +| **Default-deny firewall** | ✓ (firewalld zone=drop) | ✗ | ✓ | ✓ (Tor-only) | ✓ | ✓ | ✓ | +| **USB default-block** | ✓ (USBGuard, id-rules) | ✗ | ✓ | ✓ | ✓ (sys-usb) | ✓ (USBGuard) | ✗ | +| **Per-app isolation (VM/sandbox)** | ✗ | ✗ | ✗ | ~ (AppArmor) | ✓ (Xen VMs) | ~ (Flatpak/bwrap) | ✗ | +| **Anonymity / Tor by default** | ✗ | ✗ | ✗ | ✓ | ~ (Whonix VMs) | ✗ | ✗ | +| **Daily driver target (persistent)** | ✓ | ✓ | ✓ | ✗ (amnesic) | ✓ (heavy, hardware-partitioning) | ✓ | ✓ | +| **Signed releases (cosign + GPG)** | ✓ (v0.7) | ✓ | ✓ | ✓ | ✓ | ✓ (cosign on OCI) | ~ (sha256 only) | +| **Threat model published** | ✓ (this doc) | ✗ | ✓ | ✓ | ✓ | ✗ | ✓ | +| **Hardware compatibility (laptops)** | ✓ (Fedora kernel) | ✓ | ~ | ~ (live USB) | ~ (Xen-pinned HCL) | ✓ | ✓ (Arch kernel) | +| **Project size (contributors / stars, 2026-05)** | solo / pre-public | n/a (Fedora-wide) | small team / ~600 | ~30 / ~3k | large / ~5k | ~30 / ~940, active monthly cadence | ~8 / ~1.4k | ---