Under the hood
Security
Firewall, hardening, daily update checks, a first-use malware check and disk encryption.
What it is
WolOS turns on a firewall, kernel hardening and update checks from the first boot, checks new programs the first time they open, and can encrypt the disk with the TPM chip and your password.
Why
Protection people have to switch on rarely gets switched on. Each layer here says plainly what it covers and what it does not, so nobody mistakes it for more than it is.
How
wol security shows the state. Settings > Privacy & Security has the malware list, the optional full ClamAV signatures and app permissions. The disk opens with the TPM, then your password, then the recovery key.
Reference
On from the first boot (ADR 0015). Code: wol_core::security (defaults, status), wol_core::update::check, wol_core::home_crypt.
wol security [--json] shows the state; wol firewall on|off|status changes the firewall through wol-admind.
Defaults
- Firewall: nftables,
/etc/wolos/nftables.conf(loaded by anftables.servicedrop-in),nftables.serviceenabled.- Inbound dropped except replies, ping, DHCP, IPv6 neighbour discovery and mDNS. Outbound allowed.
- No remote login:
sshd.servicestays disabled until Sharing turns it on. - The log stays local: journald only, no network syslog daemon.
- Kernel hardening (
/etc/sysctl.d/90-wolos.conf, one reason per line):kernel.kptr_restrict=2,dmesg_restrict,yama.ptrace_scope=1(own children only),unprivileged_bpf_disabled,fs.protected_hardlinks/symlinks,net.ipv4.tcp_syncookies.wol securityreads them from /proc/sys.
Updates (ADR 0016, 0017)
- A systemd timer runs
/usr/lib/wolos/update-checkhourly; it checks at most every 20 h:pacman -Sy(pinned snapshot),pacman -Qu,arch-audit -q,pacman -Qq(kernels), and the signed release manifest (release.json+.sig, verified with the package keys when read). - Findings go to
/var/lib/wolos/updates, readable by every account. Never installs. - Release manifest: version, Arch snapshot date,
[wolos]revision, linux-wolos version, image URL and sha256. - Update Now (wol-admind
pkg-upgrade) =pacman -Syuinside the release: only[wolos]moves. - A new release =
wol update apply(root): btrfs snapshot of the running root into the other slot (@wolos-a/@wolos-b, previous restore point deleted first), mirrorlist moved to the new date,arch-chroot ... pacman -Syu(wolos-base moves as one unit),mkinitcpio -P, kernel to the ESP, boot entry<slot>+3.conf(boot counting, always linux-wolos),bootctl set-default. Any failure deletes the new slot. The running slot stays the restore point and the fallback. - Packages the person installed with pacman come along (the slot is a copy, not an image).
Other kernels are allowed, reported by
wol update statusandwol games doctor, never removed. pacman -S/-R/-Uon the sealed system (/usr/local/bin/pacman, runs/usr/bin/pacman): the change goes into a layer (systemd-sysext, merged over/usrat once;/etcand the package database stay live), live with no restart; the nextwol update applyfolds the layers into the new slot. Kernel, initramfs and systemd packages and-Syugo to the pending slot instead (taking effect at the next restart; a second change stacks on it); queries pass through. A failed change leaves nothing.- Developer mode:
wol seal off(root) makes the running system writable and stops the boot check until the next update. wol base verifychecks every file in/usr/share/wolos/base.manifest(sha256) and lists changed or missing ones.
Malware check (ADR 0024, 0027)
A layer of protection against known malware. Code: crates/xguard, wol_core::malware. Runs on this computer only: no file, hash or verdict leaves it, and sample submission does not exist.
Threat model:
- Covers: a known bad file arriving as a download, on a removable disk, as an AppImage,
.wol, Windows program, or a program marked executable under/home,/tmpand removable disks, and whatever wopkg is about to install. - Does not cover: new or altered malware with no signature, and an attacker already running on the computer, who can stop nothing here without root but can also do harm without ever opening a new file.
- Ways past the first-use check, known and accepted: code run by an interpreter (
python x.py,wine x.exefrom a terminal; double-click goes through the check),dlopenandmmapof a file that was never executed, code inmemfd, and packed Windows programs (no unpacker runs). - The signature database is data from public lists. Formats read in part: hashes, section hashes and plain patterns are read; bytecode, targets that need normalised text, HTML or mail, alternatives, negations and regular expressions are left out and counted at build time. See the ADR for the numbers.
How it works:
wolos-xguard.service: holdsFAN_OPEN_EXEC_PERMon the mounts in scope andFAN_OPEN_PERMon tagged files. Files outside scope, and the sealed root, cost nothing.- Tag:
user.wolos.quarantine, set when a file lands in a Downloads folder, oruser.xdg.origin.url, set by browsers, and by curl and wget through the shipped.curlrcand.wgetrc. - Verdict: a clean file gets an evictable kernel ignore mark, removed by the kernel when the file changes. The verdict store (
/var/lib/wolos/xguard/verdicts) keeps file identity and sha256, or the fs-verity digest when there is one, so a restart reads nothing again. - Fails open: the guard dying, or any check it cannot finish, lets the file run. A stopped file needs a signature match; nothing else stops anything.
- Engines: the mapped database in the guard; rule sets (YARA-X) only in a short-lived worker with no privileges, for tagged files, removable disks, Windows programs, AppImages and
.wol; and, after the person opts in, ClamAV for the same files. - Full signatures, off by default: a switch in Privacy & Security enables
wolos-clam-update.timer. freshclam fetches the data to this computer; it is never in the image.wol-xguard-clamis the only program that links libclamav; it starts on demand fromwolos-clam.socket, takes 1 GB while loaded, and exits after 120 idle seconds. Off deletes the data. wol-analyze, from Files' right-click (desktop/nautilus/wolos-analyze.c), runswol-xguard scan --clamav: without an answer from ClamAV the result is Incomplete, never clean. With the signatures off it checks nothing and offers Settings; only the switch there turns them on.- The guard's unit must keep the host's mount namespace: no
ProtectSystem,PrivateTmp,PrivateNetworkor the like, or its kernel marks land on copies and it stops nothing. - Background pass:
wolos-xguard-sweep.timer, on mains power and while everyone is away and no game runs, at idle priority. It stops at the first sign of activity. - Updates:
wolos-xguard-update.timerdownloads a signed bundle;wol-xguard rules installrefuses anything unsigned, older or altered, then the guard restarts so files are looked at again. - People see nothing unless a file is stopped: then one notification, and a row in Settings > Privacy & Security with Move to Trash and a deliberate Open Anyway. Each account's list is readable by that account only.
wol security,wol security scan <path>,wol security allow <path>.
For installers (wopkg and the like): libxguard.so.1 (include/xguard.h) or wol-xguard scan --json [--rules DIR] PATH, exit 0 clean, 1 found, 2 incomplete, 3 could not run. Archives, AppImages and .wol are read in memory, never unpacked to disk. The report gives verdict, findings, skipped and signatures_age_days, so a caller can treat old signatures as it likes.
Error reports (off by default)
/usr/lib/wolos/report(wolos-report.timer, hourly) sends only when/etc/wolos/reports.confhassend=1.- Each new failed unit or coredump goes once to
https://wolos.squareweb.app/report:kind, WolOS version, kernel,detail(unit name, or program and signal). No logs, user or host names. - Turn on:
echo send=1 | sudo tee /etc/wolos/reports.conf. A Settings > Privacy switch comes next.
Disk unlock (TPM disks)
- Order at start: the security chip (TPM2), then the account's own password, then the 64-character recovery key. systemd-cryptsetup asks once; the password and the recovery key both answer it.
- The password slot is the installer account's password (
install-run,wol encrypt enroll ... PASSFILE), never a shared one; the account is named in/var/lib/wolos/disk-unlock-user. - After a start the chip refused (Secure Boot turned off, new firmware: PCR 7 changed),
wolos-tpm-reseal.service(wol encrypt reseal) binds the chip again to the PCRs measured now. The secret comes from systemd-cryptsetup's password cache: what was typed stays ~150 s in root's user keyring as theuserkeycryptsetup(password-cache=yes, the default). It goes to systemd-cryptenroll as$PASSWORD, then the key is invalidated. Nothing typed: nothing happens. - Changing the password in Settings (wol-admind
user-password, the current password every time) also replaces the disk slot (wol encrypt password USER, unlocked by the chip).passwdin a terminal does not: the disk keeps the previous password (the chip and the recovery key still work).
Encrypted homes (off by default)
home_crypt: a systemd-homed LUKS home (homectl create --identity=-, record and password on stdin).- wol-login activates it with the password it has just checked (
PASSWORDin homectl's environment) and deactivates it after logout unless something still uses it. - Off until proven on a real install. A new password needs
home_crypt::change_key(homectl passwd).
App permissions
| Permission | Enforced by | Why |
|---|---|---|
| Screen capture, cursor position, input capture | Hyprland (hypr/permissions.conf, first match wins) | Only the portal and WolOS's own tools copy the screen directly. Apps ask through the portal. |
| Compositor plugins | Hyprland | Loaded only from /usr/local/lib/hyprland/plugins. |
| Camera | advisory | Every account needs group video for the GPU, and /dev/video* belongs to it. |
| Microphone | advisory | PipeWire gives the microphone to every app of the account. |
| Location | advisory | WolOS ships no location service. |
| Files outside home, virtual keyboards, clipboard history, window list | advisory | Apps run as the account, with no per-app sandbox. |
Advisory means a grant is recorded and shown to helpers that ask. An app that ignores it is not stopped. Settings says which kind each permission is (security::enforcement).
What runs as root
| Program | Does | Takes input from |
|---|---|---|
| wold | starts and restarts system jobs | its job files |
| wol-admind | runs a fixed allow-list of actions (AdminAction), each validated, audited, with a password unless it is an everyday change by the account at the computer | a Unix socket, peer uid from the kernel |
| wol-login | checks passwords (PAM), starts sessions, opens encrypted homes | the login window's socket |
wol-xguard-clam (runs as _xguard, not root) | ClamAV, on demand, no network | open files over a Unix socket |
| wol-xguard guard | answers first-use checks, reads the files it is asked about | kernel events, file content, a Unix socket with file descriptors, peer uid from the kernel |
| systemd units and timers | wol power apply, update-check | none |
No Capsicum or jails yet. Each daemon runs argv lists it builds itself (never a shell with user text). Each parses only its own small wire format.
Reference synced from wonderlandos/wolos docs/modules/security.md.