#!/usr/bin/env bash # veilor-os — kernel + service hardening (sysctl, USBGuard, NTS chrony, pwquality, service prune) # Idempotent. Run as root. set -uo pipefail GREEN='\033[0;32m'; YELLOW='\033[1;33m'; RED='\033[0;31m'; NC='\033[0m' ok() { echo -e "${GREEN}[OK]${NC} $*"; } info() { echo -e "${YELLOW}[INFO]${NC} $*"; } err() { echo -e "${RED}[ERR]${NC} $*"; } [[ $EUID -eq 0 ]] || { err "Must run as root"; exit 1; } echo "════════════════════════════════════════════════════════" echo " veilor-os :: 20-harden-kernel" echo "════════════════════════════════════════════════════════" # ── sysctl hardening ── info "Writing /etc/sysctl.d/99-veilor-hardening.conf" cat > /etc/sysctl.d/99-veilor-hardening.conf << 'EOF' # kernel pointer hiding kernel.kptr_restrict = 2 kernel.dmesg_restrict = 1 # BPF JIT constant blinding net.core.bpf_jit_harden = 2 # unprivileged perf events disabled kernel.perf_event_paranoid = 3 # ptrace restricted to parent kernel.yama.ptrace_scope = 2 # full ASLR kernel.randomize_va_space = 2 # modules unlocked at boot; veilor-modules-lock locks 30s after graphical kernel.modules_disabled = 0 # reverse path filter net.ipv4.conf.all.rp_filter = 1 net.ipv4.conf.default.rp_filter = 1 # log martians net.ipv4.conf.all.log_martians = 1 net.ipv4.conf.default.log_martians = 1 # no SUID core dumps fs.suid_dumpable = 0 # block unprivileged tty line discipline loading (LPE vector) dev.tty.ldisc_autoload = 0 # /proc/sched_debug hardened kernel.sched_schedstats = 0 # TCP SYN cookies net.ipv4.tcp_syncookies = 1 # ignore ICMP broadcast net.ipv4.icmp_echo_ignore_broadcasts = 1 # no source routing net.ipv4.conf.all.accept_source_route = 0 net.ipv6.conf.all.accept_source_route = 0 # no ICMP redirects net.ipv4.conf.all.accept_redirects = 0 net.ipv4.conf.all.send_redirects = 0 net.ipv6.conf.all.accept_redirects = 0 EOF sysctl --system >/dev/null 2>&1 || true ok "sysctl hardening written" # ── kernel module lock service ── info "Enabling veilor-modules-lock.service" systemctl enable veilor-modules-lock.service 2>/dev/null || \ info "veilor-modules-lock.service not yet installed (overlay step)" # ── chrony with NTS ── info "Configuring chrony with NTS" [[ -f /etc/chrony.conf ]] && cp /etc/chrony.conf /etc/chrony.conf.bak.veilor 2>/dev/null cat > /etc/chrony.conf << 'EOF' # NTS-authenticated time sources server time.cloudflare.com iburst nts server nts.sth1.ntp.se iburst nts server nts.sth2.ntp.se iburst nts # unauthenticated pool fallback pool 2.fedora.pool.ntp.org iburst sourcedir /run/chrony-dhcp ntsdumpdir /var/lib/chrony driftfile /var/lib/chrony/drift makestep 1.0 3 rtcsync leapseclist /usr/share/zoneinfo/leap-seconds.list logdir /var/log/chrony EOF systemctl enable chronyd ok "chrony NTS configured (Cloudflare + NETNOD)" # ── password complexity ── info "Writing /etc/security/pwquality.conf" cat > /etc/security/pwquality.conf << 'EOF' minlen = 14 dcredit = -1 ucredit = -1 lcredit = -1 ocredit = -1 minclass = 3 maxrepeat = 3 maxclassrepeat = 4 difok = 3 dictcheck = 1 usercheck = 1 enforce_for_root retry = 3 EOF ok "pwquality: minlen=14, 4 classes required" # ── disable unneeded services ── for svc in gssproxy atd pcscd.socket pcscd.service cups cups-browsed abrtd \ abrt-journal-core abrt-xorg abrt-oops abrt-ccpp geoclue avahi-daemon \ bluetooth ModemManager; do systemctl disable --now "$svc" 2>/dev/null && ok "disabled $svc" || true done # ── USBGuard ── info "Setting up USBGuard" rpm -q usbguard &>/dev/null || dnf install -y usbguard usbguard-tools # At install time no devices are connected — ship empty allowlist. # First boot, admin runs: usbguard generate-policy > /etc/usbguard/rules.conf mkdir -p /etc/usbguard [[ -f /etc/usbguard/rules.conf ]] || : > /etc/usbguard/rules.conf chmod 600 /etc/usbguard/rules.conf chown root:root /etc/usbguard/rules.conf cat > /etc/usbguard/usbguard-daemon.conf << 'EOF' ImplicitPolicyTarget=block AuditBackend=LinuxAudit IPCAllowedUsers=root IPCAllowedGroups=wheel RuleFile=/etc/usbguard/rules.conf EOF systemctl enable usbguard ok "USBGuard configured (generate-policy on first boot to allowlist your devices)" # ── firewalld drop zone ── info "Setting firewalld default zone to drop" systemctl enable firewalld firewall-offline-cmd --set-default-zone=drop 2>/dev/null || true firewall-offline-cmd --zone=drop --add-service=ssh 2>/dev/null || true ok "firewalld: default drop, ssh allowed" echo "════════════════════════════════════════════════════════" echo " 20-harden-kernel complete" echo "════════════════════════════════════════════════════════"