# Hardening tier 2 — concrete plan **Agent 4 of 9-agent wave, 2026-05-05.** ## Repo state already in tree - `scripts/apparmor/` ships **3 profiles** (`thorium`, `veilor-power`, `lm-studio`) — complain-mode, **not auto-loaded**. No browser/mail /Element profile. - `scripts/selinux/` ships custom `.te` modules — primary MAC. - `overlay/etc/audit/plugins.d/veilor-remote.conf` + `audisp-remote.conf.disabled` — **scaffold present, opt-in switch missing**. - `kickstart/veilor-os.ks` — single live-ks. Real LUKS install lives in `overlay/usr/local/bin/veilor-installer` (generates ks at runtime). - No nftables overlay. No homed scaffold. No `veilor-audit-shipping` CLI. ## Item-by-item plan ### 1. AppArmor stack with SELinux — M Fedora 43 ships `apparmor-parser`/`libapparmor`. Kernel has both LSMs. Stacking works since 5.1; SELinux stays primary, AppArmor confines specific binaries by path. **No conflict** — they layer. Risk: AA profiles based on Debian/Ubuntu paths fail on Fedora. **Files:** - `kickstart/veilor-os.ks` `%packages` add `apparmor-parser apparmor-utils apparmor-profiles` - `overlay/etc/apparmor.d/veilor.d/` (new) — vendor profiles `firefox`, `thunderbird`, `element-desktop`, `signal-desktop` - `scripts/40-apparmor.sh` (new) — parses + sets all veilor profiles to **complain** on first install (logs only, no break) - `overlay/usr/local/bin/veilor-doctor` — adds AA status check **Test:** `aa-status | grep complain` shows >=4 loaded; firefox writes outside policy → audit.log denial. ### 2. systemd-homed opt-in — L Default LUKS storage `homectl` drops key on suspend; resume needs PAM unlock again — **breaks "lid open, keep working"**. Use `--storage=fscrypt` on top of existing btrfs `/home` subvol — suspend transparent, encrypts at rest with per-user key. **Files:** - `overlay/usr/local/bin/veilor-homed-enable` (new) — confirms warning, runs `homectl create admin --storage=fscrypt --real-name="veilor admin"` after migrating files - `overlay/etc/pam.d/sddm` drop-in for `pam_systemd_home.so` - doc in `docs/HARDENING.md`. **Not auto-run** — only via post-install. ### 3. nftables alongside firewalld — S firewalld speaks nftables backend on F43 — they don't conflict; firewalld owns `inet firewalld` table. veilor-os preset = separate `inet veilor` table loaded by its own service. **Files:** - `overlay/etc/nftables/veilor.nft` (new) — table `inet veilor`: ssh per-IP rate limit (5/min), ICMP rate limit, optional `ip6 daddr ::/0 drop` toggled by sysctl-style `/etc/veilor/ipv6.disabled`, anti-port-scan via `meter` set - `overlay/etc/systemd/system/veilor-nftables.service` (new) — `After=firewalld.service` - `kickstart/veilor-os.ks` `%packages` add `nftables`, services-enabled add `veilor-nftables` **Test:** `nft list ruleset` shows both `firewalld` AND `veilor`; `hping3 -S -p 22 --flood` from second VM gets rate-limited. ### 4. Audit log shipping — S Plumbing **already in tree** (`audisp-remote.conf.disabled`, `veilor-remote.conf` with `active=no`). What's missing: CLI to flip the switch with cert pinning. **Files:** - `overlay/usr/local/bin/veilor-audit-shipping` (new) - `enable HOST PORT FINGERPRINT` writes `/etc/veilor/audit-pin.sha256`, copies `audisp-remote.conf.disabled` → `audisp-remote.conf` with substituted host/port, enables plugin (`active=yes`), restarts auditd - `disable` reverses - audisp-remote speaks TLS directly; cert pinning via `verify_peer=yes` + `peer_cert_fingerprint` - Use **self-signed pinned**, not LE — collectors are LAN/VPN **Test:** stand up `rsyslog` listener on nullstone with self-signed cert; run helper; trigger `sudo -i`; tail nullstone for AUTHPRIV event; revoke cert → events stop with logged TLS error. ### 5. Installer kickstart split — needs re-scope, S Roadmap item is **stale**. As of v0.5.30 we already do real LUKS+btrfs in `veilor-installer` which generates ks at runtime. **Re-scope:** extract that generated ks template into static `kickstart/veilor-os-install.ks` (parameterised via `%include /tmp/answers.ks`), so reviewable in repo and reusable headlessly. **Files:** - split `overlay/usr/local/bin/veilor-installer` heredoc into `kickstart/veilor-os-install.ks` - installer just writes answers + `cp` the ks - CI lints both with `ksvalidator` ### 6. Audit baseline re-run — S Mechanical: `cp security/audit-template.md security/veilor-os-distro/2026-05-DD.md`, run on VM, target lower findings count than v0.2's baseline. ## Order, dependencies, ship plan Dependencies: (5) blocks (6) — audit a stable installer, not a moving heredoc. Else parallel. **Total effort:** 2S + 1S(rescope) + 1S + 1M + 1L ≈ **5–7 dev-days**. - **v0.5.32 (small wins):** (4) audit shipping CLI + (3) nftables preset. Both S, scaffold completion, pure overlay (no kickstart risk). - **v0.5.33:** (5) ks split + (6) audit baseline re-run. - **v0.6 (medium):** (1) AppArmor stack — package install + 4 profiles + doctor integration; complain-mode keeps blast radius zero. - **v0.7 (big lift):** (2) systemd-homed — UX-disruptive, needs migration helper + doc page + suspend/lock/swap testing.