veilor-os/docs/HARDENING.md
2026-05-02 04:39:39 +01:00

7 KiB

Hardening Reference

What veilor-os locks down and why. Each item is applied by either the kickstart %post or the overlay tree shipped in /etc.

Boot chain

Item State Source
Secure Boot Required (bootloader signed) bootloader kickstart line
Kernel lockdown lockdown=integrity bootloader kernel args
Slab hardening slab_nomerge, init_on_alloc=1, init_on_free=1 bootloader
Stack offset randomize_kstack_offset=on bootloader
vsyscall vsyscall=none bootloader
LUKS2 aes-xts-plain64 / argon2id, mem=1GB, time=9 part pv.veilor
Module loading Locked 30s after graphical boot veilor-modules-lock.service

Kernel sysctl

/etc/sysctl.d/99-veilor-hardening.conf:

Key Value Why
kernel.kptr_restrict 2 hide kernel pointers from /proc
kernel.dmesg_restrict 1 dmesg root-only
kernel.yama.ptrace_scope 2 ptrace = parent only
kernel.perf_event_paranoid 3 unprivileged perf disabled
net.core.bpf_jit_harden 2 BPF JIT constant blinding
kernel.randomize_va_space 2 full ASLR
fs.suid_dumpable 0 no SUID core dumps
dev.tty.ldisc_autoload 0 block tty LPE vector
net.ipv4.tcp_syncookies 1 SYN flood mitigation
net.ipv4.conf.all.rp_filter 1 reverse-path filter
accept_source_route 0 (v4+v6) ignore source routing
accept_redirects 0 (v4+v6) ignore ICMP redirects

SELinux

  • Enforcing, targeted policy.
  • Custom module veilor-systemd grants systemd_modules_load_t the sys_admin and perfmon capabilities required by the modules-lock service. Source: scripts/selinux/veilor-systemd.te.

veilor-firstboot SELinux confinement

The first-boot password service is privileged (it has to write /etc/shadow) but small. Module veilor-firstboot carves a tight domain:

  • Allowed: read /etc/passwd, exec passwd(1), write /var/lib/veilor-firstboot.done, write /etc/sddm.conf.d/, start sddm.service.
  • neverallow rules block: network sockets (no phone-home), home_root_t / user_home_t access, sys_module, sys_ptrace, sys_rawio.

Source: scripts/selinux/veilor-firstboot.te. Build & load with scripts/selinux/build-policy.sh (loads all modules in one pass).

Network surface

  • firewalld default zone = drop.
  • Inbound: ssh only.
  • systemd-resolved: LLMNR off, DNSSEC allow-downgrade, DNS-over-TLS opportunistic. Resolvers: Cloudflare (1.1.1.1, 1.0.0.1), fallback Quad9 (9.9.9.9, 149.112.112.112).
  • chrony: NTS-authenticated time from time.cloudflare.com and nts.sth1/2.ntp.se. Pool fallback only.

SSH

/etc/ssh/sshd_config.d/10-veilor-hardening.conf:

  • PasswordAuthentication no
  • PermitRootLogin no
  • AllowUsers admin
  • X11Forwarding no
  • MaxAuthTries 3
  • ClientAliveInterval 300
  • LogLevel VERBOSE

Auth / accounts

  • Root account locked (passwd -l root). No interactive root login.
  • Single admin user, wheel group, full sudo.
  • pwquality.conf: minlen=14, 4 character classes required, dictcheck.
  • First-boot password flow: chage -d 0 admin expires the empty password immediately. veilor-firstboot.service runs on TTY1 before SDDM, prompts for new password, then starts the graphical session.

Audit

/etc/audit/rules.d/99-veilor-hardening.rules watches:

  • /etc/passwd, /etc/shadow, /etc/group, /etc/gshadow
  • /etc/sudoers, /etc/sudoers.d/
  • /etc/ssh/sshd_config*, /etc/selinux/, /etc/firewalld/
  • /etc/cron.*, /var/spool/cron/
  • /etc/sysctl.*, /etc/systemd/system/, /usr/lib/systemd/system/
  • All privileged binaries (sudo, su, passwd, mount, pkexec, etc.)
  • Kernel module load/unload syscalls
  • Permission/ownership changes by uid≥1000

Intrusion detection

fail2ban jails:

  • sshd — aggressive mode, 3 retries, 24h ban
  • pam-generic — 5 retries, 1h ban (catches XDM, su, sudo failures)

Backend: systemd journal. Action: firewalld rich rules.

USB

USBGuard daemon, ImplicitPolicyTarget=block.

Ships with empty allowlist. On first boot, admin runs:

sudo usbguard generate-policy > /etc/usbguard/rules.conf
sudo systemctl restart usbguard

This snapshots all currently-connected devices into the allowlist. Anything plugged in afterward is blocked unless explicitly allowed:

sudo usbguard list-devices
sudo usbguard allow-device <id>

Disabled services

abrt*, cups, cups-browsed, geoclue, avahi-daemon, bluetooth, ModemManager, gssproxy, atd, pcscd.socket, pcscd.service, kdeconnectd (removed at package level).

AppArmor (v0.5)

Fedora 43 ships AppArmor alongside SELinux. veilor-os keeps SELinux as the primary MAC layer (enforcing, targeted) but ships AppArmor profile skeletons for high-risk userland binaries that benefit from a second, binary-scoped policy on top of SELinux's role-based one.

Profiles live in scripts/apparmor/:

Profile Target Default mode
usr.bin.thorium Thorium browser complain
usr.local.bin.lm-studio LM Studio LLM runner complain
usr.bin.veilor-power Power profile switcher enforce

Profiles are not loaded automatically — they are opt-in until v0.5. Enable a profile post-install with:

sudo dnf install apparmor-utils apparmor-parser
sudo install -m 0644 scripts/apparmor/usr.bin.thorium /etc/apparmor.d/
sudo apparmor_parser -r /etc/apparmor.d/usr.bin.thorium
sudo aa-complain /etc/apparmor.d/usr.bin.thorium      # log only
sudo aa-enforce  /etc/apparmor.d/usr.bin.thorium      # block

Refine complain-mode profiles with aa-logprof after exercising the app through normal use; it converts logged denials into rule additions interactively.

Audit log shipping (optional)

Local journald is the default audit sink. For off-device shipping to a trusted log collector (Loki / Wazuh / Splunk), veilor-os ships a disabled-by-default plugin template:

  • /etc/audit/plugins.d/veilor-remote.conf — auditd plugin shim (set active = yes to enable).
  • /etc/audisp/audisp-remote.conf.disabled — audisp-remote target config template (rename to audisp-remote.conf and edit remote_server to enable).

Warning: enabling remote audit shipping leaks every privileged syscall, file-watch hit, and auth event off-device. Treat the collector as a host with the same trust level as root. Only enable if the collector itself is hardened and the transport is TLS or kerberized.

Reference integration paths in the template: Loki via promtail/vector syslog source, Wazuh via local wazuh-agent (no network shipping needed), Splunk via HEC bridge.

What's not enabled by default

  • Disk swap — replaced by zram (RAM-only, no key leak risk).
  • Bluetooth — disabled. Enable with systemctl enable --now bluetooth.
  • Printing — CUPS removed. Reinstall if needed: dnf install cups.
  • Snapd, Flatpak — not installed (Flatpak optional add-on).
  • PackageKit — removed; updates manual via dnf.