veilor-os v0.1 scaffold — kickstart + hardening + 3-mode power + DuckSans-ready KDE black theme
This commit is contained in:
commit
1822005df1
37 changed files with 1733 additions and 0 deletions
13
.gitignore
vendored
Normal file
13
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
build/out/
|
||||||
|
build/cache/
|
||||||
|
*.iso
|
||||||
|
*.img
|
||||||
|
*.log
|
||||||
|
*.pp
|
||||||
|
*.mod
|
||||||
|
.DS_Store
|
||||||
|
.idea/
|
||||||
|
.vscode/
|
||||||
|
secrets/
|
||||||
|
*.key
|
||||||
|
*.pem
|
||||||
21
LICENSE
Normal file
21
LICENSE
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2026 veilor-os contributors
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
||||||
49
README.md
Normal file
49
README.md
Normal file
|
|
@ -0,0 +1,49 @@
|
||||||
|
# veilor-os
|
||||||
|
|
||||||
|
> Hardened minimal Fedora KDE remix. Black-on-black. Locked down by default.
|
||||||
|
|
||||||
|
veilor-os is a Fedora 43 KDE spin built for operators who want a clean, fast,
|
||||||
|
opinionated desktop with serious hardening already in place. No prompts at
|
||||||
|
install beyond the LUKS passphrase. Boot, set admin password, work.
|
||||||
|
|
||||||
|
## Highlights
|
||||||
|
|
||||||
|
- **Single-prompt install** — only LUKS passphrase. No account wizard, no
|
||||||
|
initial-setup screen. `admin` account is created automatically; password
|
||||||
|
is set on first boot.
|
||||||
|
- **Hardened by default** — SELinux enforcing, USBGuard, fail2ban, firewalld
|
||||||
|
drop zone, kernel sysctl lockdown, NTS-authenticated NTP, DNS-over-TLS.
|
||||||
|
- **3-mode power management** — `veilor-power save | mid | perf`, with
|
||||||
|
AC/battery auto-switching via udev. Backed by tuned profiles.
|
||||||
|
- **DuckSans system font** — variable font, single binary, low cache
|
||||||
|
footprint.
|
||||||
|
- **Pure-black KDE color scheme** — `veilor-black` theme system-wide.
|
||||||
|
- **LUKS2 + Secure Boot** — argon2id, aes-xts, btrfs subvolumes, zram swap
|
||||||
|
(no disk swap, no cold-boot leak).
|
||||||
|
- **Reproducible build** — kickstart + podman + livemedia-creator. ISO
|
||||||
|
output is deterministic given pinned base.
|
||||||
|
|
||||||
|
## Repo layout
|
||||||
|
|
||||||
|
```
|
||||||
|
kickstart/ veilor-os.ks full kickstart definition
|
||||||
|
build/ Containerfile + build-iso.sh reproducible ISO builder
|
||||||
|
overlay/ files dropped into installed root via %post
|
||||||
|
scripts/ hardening, SELinux policy, theme apply, firstboot
|
||||||
|
assets/ fonts, KDE color scheme, branding, plymouth theme
|
||||||
|
docs/ HARDENING / POWER / BUILD / INSTALL
|
||||||
|
test/ boot-checklist + findings log
|
||||||
|
```
|
||||||
|
|
||||||
|
See `docs/BUILD.md` for build instructions, `docs/INSTALL.md` for install,
|
||||||
|
`docs/HARDENING.md` for what's locked down and why.
|
||||||
|
|
||||||
|
## Status
|
||||||
|
|
||||||
|
Pre-release. v0.x. Repo private until first green ISO boots clean on test
|
||||||
|
hardware.
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
MIT — see [LICENSE](LICENSE). DuckSans font ships under its own license; see
|
||||||
|
`assets/fonts/ducksans/README.md`.
|
||||||
40
assets/fonts/ducksans/README.md
Normal file
40
assets/fonts/ducksans/README.md
Normal file
|
|
@ -0,0 +1,40 @@
|
||||||
|
# DuckSans
|
||||||
|
|
||||||
|
DuckSans is a variable font commissioned by DuckDuckGo from Fontwerk
|
||||||
|
(designer: Christoph Koeberlin, based on the Pangea typeface, 2026).
|
||||||
|
|
||||||
|
## Why this font
|
||||||
|
|
||||||
|
- **Variable font** — single binary covers full weight + width axis.
|
||||||
|
Smaller font cache, less I/O, fewer files for fontconfig to scan.
|
||||||
|
- **Designed for text-heavy UIs** — high readability, good hinting.
|
||||||
|
- **Recognizable but unbranded look** — distinctive without being kitsch.
|
||||||
|
|
||||||
|
## Vendor instructions
|
||||||
|
|
||||||
|
Drop the font files here:
|
||||||
|
|
||||||
|
```
|
||||||
|
assets/fonts/ducksans/DuckSans-VF.ttf
|
||||||
|
```
|
||||||
|
|
||||||
|
(plus optional italic axis if shipped separately)
|
||||||
|
|
||||||
|
The build pipeline copies this directory to
|
||||||
|
`/usr/share/fonts/ducksans/` in the installed system and runs
|
||||||
|
`fc-cache -f`.
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
DuckSans license terms TBD (Fontwerk commercial license vs SIL OFL).
|
||||||
|
**Do not commit the .ttf to a public repo until license is verified.**
|
||||||
|
|
||||||
|
If license forbids redistribution, the kickstart `%post` should fetch
|
||||||
|
the font from an authenticated source at build time. See
|
||||||
|
`build/build-iso.sh` for the pull point.
|
||||||
|
|
||||||
|
## Fallback
|
||||||
|
|
||||||
|
If DuckSans is not present, fontconfig falls through to the system
|
||||||
|
default sans-serif. veilor-os will still install and run; the system
|
||||||
|
font will just not be DuckSans.
|
||||||
105
assets/kde/veilor-black.colors
Normal file
105
assets/kde/veilor-black.colors
Normal file
|
|
@ -0,0 +1,105 @@
|
||||||
|
[ColorEffects:Disabled]
|
||||||
|
Color=0,0,0
|
||||||
|
ColorAmount=0
|
||||||
|
ColorEffect=0
|
||||||
|
ContrastAmount=0.65
|
||||||
|
ContrastEffect=1
|
||||||
|
IntensityAmount=0.1
|
||||||
|
IntensityEffect=2
|
||||||
|
|
||||||
|
[ColorEffects:Inactive]
|
||||||
|
ChangeSelectionColor=true
|
||||||
|
Color=112,111,110
|
||||||
|
ColorAmount=0.025
|
||||||
|
ColorEffect=2
|
||||||
|
ContrastAmount=0.1
|
||||||
|
ContrastEffect=2
|
||||||
|
Enable=false
|
||||||
|
IntensityAmount=0
|
||||||
|
IntensityEffect=0
|
||||||
|
|
||||||
|
[Colors:Button]
|
||||||
|
BackgroundAlternate=20,20,20
|
||||||
|
BackgroundNormal=10,10,10
|
||||||
|
DecorationFocus=255,255,255
|
||||||
|
DecorationHover=200,200,200
|
||||||
|
ForegroundActive=255,255,255
|
||||||
|
ForegroundInactive=140,140,140
|
||||||
|
ForegroundLink=200,200,255
|
||||||
|
ForegroundNegative=237,21,21
|
||||||
|
ForegroundNeutral=176,128,0
|
||||||
|
ForegroundNormal=232,232,232
|
||||||
|
ForegroundPositive=128,210,128
|
||||||
|
ForegroundVisited=180,180,220
|
||||||
|
|
||||||
|
[Colors:Selection]
|
||||||
|
BackgroundAlternate=40,40,40
|
||||||
|
BackgroundNormal=60,60,60
|
||||||
|
DecorationFocus=255,255,255
|
||||||
|
DecorationHover=200,200,200
|
||||||
|
ForegroundActive=255,255,255
|
||||||
|
ForegroundInactive=180,180,180
|
||||||
|
ForegroundLink=200,200,255
|
||||||
|
ForegroundNegative=237,21,21
|
||||||
|
ForegroundNeutral=176,128,0
|
||||||
|
ForegroundNormal=255,255,255
|
||||||
|
ForegroundPositive=128,210,128
|
||||||
|
ForegroundVisited=180,180,220
|
||||||
|
|
||||||
|
[Colors:Tooltip]
|
||||||
|
BackgroundAlternate=20,20,20
|
||||||
|
BackgroundNormal=8,8,8
|
||||||
|
DecorationFocus=255,255,255
|
||||||
|
DecorationHover=200,200,200
|
||||||
|
ForegroundActive=255,255,255
|
||||||
|
ForegroundInactive=140,140,140
|
||||||
|
ForegroundLink=200,200,255
|
||||||
|
ForegroundNegative=237,21,21
|
||||||
|
ForegroundNeutral=176,128,0
|
||||||
|
ForegroundNormal=232,232,232
|
||||||
|
ForegroundPositive=128,210,128
|
||||||
|
ForegroundVisited=180,180,220
|
||||||
|
|
||||||
|
[Colors:View]
|
||||||
|
BackgroundAlternate=10,10,10
|
||||||
|
BackgroundNormal=0,0,0
|
||||||
|
DecorationFocus=255,255,255
|
||||||
|
DecorationHover=200,200,200
|
||||||
|
ForegroundActive=255,255,255
|
||||||
|
ForegroundInactive=140,140,140
|
||||||
|
ForegroundLink=200,200,255
|
||||||
|
ForegroundNegative=237,21,21
|
||||||
|
ForegroundNeutral=176,128,0
|
||||||
|
ForegroundNormal=232,232,232
|
||||||
|
ForegroundPositive=128,210,128
|
||||||
|
ForegroundVisited=180,180,220
|
||||||
|
|
||||||
|
[Colors:Window]
|
||||||
|
BackgroundAlternate=8,8,8
|
||||||
|
BackgroundNormal=0,0,0
|
||||||
|
DecorationFocus=255,255,255
|
||||||
|
DecorationHover=200,200,200
|
||||||
|
ForegroundActive=255,255,255
|
||||||
|
ForegroundInactive=140,140,140
|
||||||
|
ForegroundLink=200,200,255
|
||||||
|
ForegroundNegative=237,21,21
|
||||||
|
ForegroundNeutral=176,128,0
|
||||||
|
ForegroundNormal=232,232,232
|
||||||
|
ForegroundPositive=128,210,128
|
||||||
|
ForegroundVisited=180,180,220
|
||||||
|
|
||||||
|
[General]
|
||||||
|
ColorScheme=veilor-black
|
||||||
|
Name=veilor-black
|
||||||
|
shadeSortColumn=true
|
||||||
|
|
||||||
|
[KDE]
|
||||||
|
contrast=4
|
||||||
|
|
||||||
|
[WM]
|
||||||
|
activeBackground=0,0,0
|
||||||
|
activeBlend=255,255,255
|
||||||
|
activeForeground=255,255,255
|
||||||
|
inactiveBackground=10,10,10
|
||||||
|
inactiveBlend=180,180,180
|
||||||
|
inactiveForeground=140,140,140
|
||||||
16
assets/kde/veilor-default.kdeglobals
Normal file
16
assets/kde/veilor-default.kdeglobals
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
[General]
|
||||||
|
ColorScheme=veilor-black
|
||||||
|
Name=veilor-black
|
||||||
|
font=DuckSans,11,-1,5,400,0,0,0,0,0,0,0,0,0,0,1
|
||||||
|
fixed=DuckSans Mono,10,-1,5,400,0,0,0,0,0,0,0,0,0,0,1
|
||||||
|
menuFont=DuckSans,11,-1,5,400,0,0,0,0,0,0,0,0,0,0,1
|
||||||
|
smallestReadableFont=DuckSans,9,-1,5,400,0,0,0,0,0,0,0,0,0,0,1
|
||||||
|
toolBarFont=DuckSans,10,-1,5,400,0,0,0,0,0,0,0,0,0,0,1
|
||||||
|
|
||||||
|
[Icons]
|
||||||
|
Theme=breeze-dark
|
||||||
|
|
||||||
|
[KDE]
|
||||||
|
LookAndFeelPackage=org.kde.breezedark.desktop
|
||||||
|
SingleClick=false
|
||||||
|
contrast=4
|
||||||
22
build/Containerfile
Normal file
22
build/Containerfile
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
FROM registry.fedoraproject.org/fedora:43
|
||||||
|
|
||||||
|
LABEL org.opencontainers.image.title="veilor-os build env"
|
||||||
|
LABEL org.opencontainers.image.source="https://github.com/veilor-uk/veilor-os"
|
||||||
|
|
||||||
|
RUN dnf install -y \
|
||||||
|
lorax \
|
||||||
|
livecd-tools \
|
||||||
|
pykickstart \
|
||||||
|
anaconda-tui \
|
||||||
|
squashfs-tools \
|
||||||
|
xorriso \
|
||||||
|
genisoimage \
|
||||||
|
syslinux \
|
||||||
|
rsync \
|
||||||
|
git \
|
||||||
|
which \
|
||||||
|
&& dnf clean all
|
||||||
|
|
||||||
|
WORKDIR /work
|
||||||
|
|
||||||
|
ENTRYPOINT ["/bin/bash"]
|
||||||
54
build/build-iso.sh
Executable file
54
build/build-iso.sh
Executable file
|
|
@ -0,0 +1,54 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
# veilor-os — ISO builder
|
||||||
|
# Wraps livemedia-creator inside a podman container for reproducibility.
|
||||||
|
# Run from repo root.
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
REPO_ROOT="$(cd "$(dirname "$0")/.." && pwd)"
|
||||||
|
OUT_DIR="$REPO_ROOT/build/out"
|
||||||
|
KS="$REPO_ROOT/kickstart/veilor-os.ks"
|
||||||
|
RELEASEVER="${RELEASEVER:-43}"
|
||||||
|
DATE="$(date +%Y%m%d)"
|
||||||
|
ISO_NAME="veilor-os-${RELEASEVER}-${DATE}.iso"
|
||||||
|
|
||||||
|
mkdir -p "$OUT_DIR"
|
||||||
|
|
||||||
|
# ── Validate kickstart ──
|
||||||
|
if command -v ksvalidator &>/dev/null; then
|
||||||
|
ksvalidator "$KS"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ── Build container ──
|
||||||
|
podman build -t veilor-build:latest "$REPO_ROOT/build"
|
||||||
|
|
||||||
|
# ── Build ISO ──
|
||||||
|
# --make-iso requires --privileged (loop devices, mount).
|
||||||
|
podman run --rm --privileged \
|
||||||
|
-v "$REPO_ROOT:/work:Z" \
|
||||||
|
-v "$OUT_DIR:/out:Z" \
|
||||||
|
veilor-build:latest -c "
|
||||||
|
set -e
|
||||||
|
livemedia-creator \
|
||||||
|
--make-iso \
|
||||||
|
--no-virt \
|
||||||
|
--ks /work/kickstart/veilor-os.ks \
|
||||||
|
--resultdir /out/build-${DATE} \
|
||||||
|
--project veilor-os \
|
||||||
|
--releasever ${RELEASEVER} \
|
||||||
|
--title 'veilor-os' \
|
||||||
|
--tmp /tmp/lmc \
|
||||||
|
--logfile /out/build-${DATE}.log
|
||||||
|
cp /out/build-${DATE}/*.iso /out/${ISO_NAME}
|
||||||
|
sha256sum /out/${ISO_NAME} > /out/${ISO_NAME}.sha256
|
||||||
|
"
|
||||||
|
|
||||||
|
echo
|
||||||
|
echo "════════════════════════════════════════════════════════"
|
||||||
|
echo " ISO ready: $OUT_DIR/$ISO_NAME"
|
||||||
|
echo " Checksum: $OUT_DIR/$ISO_NAME.sha256"
|
||||||
|
echo " Build log: $OUT_DIR/build-${DATE}.log"
|
||||||
|
echo "════════════════════════════════════════════════════════"
|
||||||
|
echo
|
||||||
|
echo " Write to USB: sudo dd if=$OUT_DIR/$ISO_NAME of=/dev/sdX bs=4M status=progress conv=fsync"
|
||||||
|
echo " (replace /dev/sdX with your USB device — use lsblk to identify)"
|
||||||
76
docs/BUILD.md
Normal file
76
docs/BUILD.md
Normal file
|
|
@ -0,0 +1,76 @@
|
||||||
|
# Building veilor-os
|
||||||
|
|
||||||
|
## Requirements
|
||||||
|
|
||||||
|
- **Host:** Fedora 43+ or RHEL/CentOS 9+ (anything with podman + KVM bits)
|
||||||
|
- **podman** with rootless or rootful — privileged mode required
|
||||||
|
- **Disk:** ~15GB free for build cache + ISO
|
||||||
|
- **Network:** internet (pulls Fedora repos, base container)
|
||||||
|
|
||||||
|
## One-shot build
|
||||||
|
|
||||||
|
From repo root:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./build/build-iso.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
Output: `build/out/veilor-os-43-YYYYMMDD.iso` and `.sha256`.
|
||||||
|
|
||||||
|
## What the build does
|
||||||
|
|
||||||
|
1. `ksvalidator` checks `kickstart/veilor-os.ks` syntax.
|
||||||
|
2. Builds `veilor-build:latest` container from `build/Containerfile`
|
||||||
|
(Fedora 43 base + lorax + livemedia-creator + pykickstart).
|
||||||
|
3. Runs `livemedia-creator --make-iso --no-virt` inside the container
|
||||||
|
with `--privileged` (loop devices and chroot mounts required).
|
||||||
|
4. Anaconda runs the kickstart in a tmpfs root, packages are pulled,
|
||||||
|
`%post` executes (hardening + theme + branding), root is squashed
|
||||||
|
into a Live ISO.
|
||||||
|
5. ISO + sha256 + build log dropped in `build/out/`.
|
||||||
|
|
||||||
|
## Custom builds
|
||||||
|
|
||||||
|
Environment variables:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
RELEASEVER=43 ./build/build-iso.sh # default
|
||||||
|
RELEASEVER=44 ./build/build-iso.sh # rebase to Fedora 44 when released
|
||||||
|
```
|
||||||
|
|
||||||
|
Edit `kickstart/veilor-os.ks` to:
|
||||||
|
|
||||||
|
- Change locale / timezone (`lang`, `keyboard`, `timezone` lines)
|
||||||
|
- Add/remove packages (`%packages` section)
|
||||||
|
- Adjust LUKS parameters (`part pv.veilor` line)
|
||||||
|
|
||||||
|
## Writing to USB
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo dd if=build/out/veilor-os-43-YYYYMMDD.iso of=/dev/sdX bs=4M status=progress conv=fsync
|
||||||
|
sync
|
||||||
|
```
|
||||||
|
|
||||||
|
Replace `/dev/sdX` with your USB device. **Triple-check** with `lsblk`
|
||||||
|
before running — `dd` will overwrite without warning.
|
||||||
|
|
||||||
|
Ventoy is **not** supported for hardened-install ISOs because Anaconda
|
||||||
|
expects to find the kickstart at the ISO root. Use `dd` directly.
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
- **`livemedia-creator` fails inside container:** ensure `--privileged`
|
||||||
|
is set (the script already passes it). On hosts with strict SELinux,
|
||||||
|
set `setsebool -P container_manage_cgroup on` once.
|
||||||
|
- **Packages not found:** the Fedora mirror may have moved. Update
|
||||||
|
`url --mirrorlist=` in the kickstart.
|
||||||
|
- **Kickstart syntax errors:** run `ksvalidator kickstart/veilor-os.ks`
|
||||||
|
directly. Errors point to a line number in the .ks file.
|
||||||
|
- **Build hangs at "Setting up Install Process":** Fedora mirror
|
||||||
|
timeouts. Pin a specific mirror with `url --url=https://...`.
|
||||||
|
|
||||||
|
## Reproducibility
|
||||||
|
|
||||||
|
The same kickstart + same Fedora release version + same overlay tree
|
||||||
|
should produce ISOs with identical package sets. Bit-for-bit identical
|
||||||
|
ISOs require pinning Fedora compose IDs (planned for v1).
|
||||||
128
docs/HARDENING.md
Normal file
128
docs/HARDENING.md
Normal file
|
|
@ -0,0 +1,128 @@
|
||||||
|
# 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`.
|
||||||
|
|
||||||
|
## 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:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
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:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
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).
|
||||||
|
|
||||||
|
## 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`.
|
||||||
106
docs/INSTALL.md
Normal file
106
docs/INSTALL.md
Normal file
|
|
@ -0,0 +1,106 @@
|
||||||
|
# Installing veilor-os
|
||||||
|
|
||||||
|
## What you need
|
||||||
|
|
||||||
|
- USB drive (8GB+) flashed with the veilor-os ISO
|
||||||
|
- Target machine with UEFI (BIOS legacy works but Secure Boot is the
|
||||||
|
whole point — use UEFI)
|
||||||
|
- ~30GB free disk
|
||||||
|
|
||||||
|
## Install flow
|
||||||
|
|
||||||
|
The installer is **fully scripted**. The only thing it asks you for
|
||||||
|
is the **LUKS passphrase**.
|
||||||
|
|
||||||
|
1. Boot from USB.
|
||||||
|
2. Pick "Install veilor-os" from the boot menu.
|
||||||
|
3. Anaconda runs the kickstart automatically.
|
||||||
|
4. When prompted, **set a strong LUKS passphrase**. This is the only
|
||||||
|
prompt. Choose well — losing it = losing the disk.
|
||||||
|
5. Wait. Install + `%post` hardening takes ~10–15 min depending on
|
||||||
|
network speed.
|
||||||
|
6. Reboot. Pull out the USB.
|
||||||
|
|
||||||
|
## First boot
|
||||||
|
|
||||||
|
1. **LUKS prompt** — enter your passphrase to unlock the disk.
|
||||||
|
2. **TTY1 banner appears:**
|
||||||
|
|
||||||
|
```
|
||||||
|
┌──────────────────────────────────────────────────────────┐
|
||||||
|
│ veilor-os │
|
||||||
|
│ first boot — admin password │
|
||||||
|
└──────────────────────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
3. Type a password for the local admin account. Must meet:
|
||||||
|
- ≥ 14 characters
|
||||||
|
- 1 digit, 1 upper, 1 lower, 1 special
|
||||||
|
4. Once accepted, SDDM starts.
|
||||||
|
5. Log in as `admin` with the password you just set.
|
||||||
|
6. Shell prompt: `admin@veilor-os`.
|
||||||
|
|
||||||
|
## Post-install hygiene
|
||||||
|
|
||||||
|
### Set USBGuard allowlist
|
||||||
|
|
||||||
|
USBGuard ships with an empty allowlist — every USB device you plug in
|
||||||
|
will be blocked until you whitelist your trusted set.
|
||||||
|
|
||||||
|
Plug in everything you trust (keyboard, mouse, dock, yubikey, etc.),
|
||||||
|
then run:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo usbguard generate-policy > /etc/usbguard/rules.conf
|
||||||
|
sudo systemctl restart usbguard
|
||||||
|
```
|
||||||
|
|
||||||
|
To allow a new device after that:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo usbguard list-devices
|
||||||
|
sudo usbguard allow-device <id>
|
||||||
|
```
|
||||||
|
|
||||||
|
### Verify hardening
|
||||||
|
|
||||||
|
```bash
|
||||||
|
getenforce # Enforcing
|
||||||
|
mokutil --sb-state # SecureBoot enabled
|
||||||
|
sysctl kernel.yama.ptrace_scope # = 2
|
||||||
|
sysctl fs.suid_dumpable # = 0
|
||||||
|
firewall-cmd --get-default-zone # drop
|
||||||
|
fail2ban-client status sshd # active, jail loaded
|
||||||
|
veilor-power status # current profile + governor
|
||||||
|
```
|
||||||
|
|
||||||
|
### Check `/etc/os-release`
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cat /etc/os-release
|
||||||
|
# NAME="veilor-os"
|
||||||
|
# PRETTY_NAME="veilor-os 0.1 (Fedora 43 base)"
|
||||||
|
# ID=veilor
|
||||||
|
# ID_LIKE=fedora
|
||||||
|
```
|
||||||
|
|
||||||
|
### Add additional users
|
||||||
|
|
||||||
|
The kickstart only creates `admin`. Add more users from there:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo useradd -m -s /bin/bash <name>
|
||||||
|
sudo passwd <name>
|
||||||
|
```
|
||||||
|
|
||||||
|
Don't add anyone to `wheel` unless they need root.
|
||||||
|
|
||||||
|
## Known caveats
|
||||||
|
|
||||||
|
- **Bluetooth disabled by default** — `sudo systemctl enable --now bluetooth`
|
||||||
|
if you need it.
|
||||||
|
- **Printing disabled** — CUPS removed; `sudo dnf install cups cups-browsed`
|
||||||
|
if you need a printer.
|
||||||
|
- **No PackageKit** — updates manual via `sudo dnf upgrade`. Run weekly.
|
||||||
|
- **Battery cap at 80%** — udev rule. Edit
|
||||||
|
`/etc/udev/rules.d/91-veilor-battery-threshold.rules` to change.
|
||||||
71
docs/POWER.md
Normal file
71
docs/POWER.md
Normal file
|
|
@ -0,0 +1,71 @@
|
||||||
|
# Power Management
|
||||||
|
|
||||||
|
veilor-os ships a 3-mode power profile system backed by `tuned`.
|
||||||
|
|
||||||
|
## Profiles
|
||||||
|
|
||||||
|
| Profile | Governor | EPP | Boost | ASUS TTP | Use |
|
||||||
|
|---------|----------|-----|-------|----------|-----|
|
||||||
|
| `veilor-powersave` | powersave | power | off | 2 (silent) | max battery |
|
||||||
|
| `veilor-balanced` | powersave | balance_performance | on | 1 (mid) | on the go |
|
||||||
|
| `veilor-performance` | performance | performance | on | 0 (full) | plugged in |
|
||||||
|
|
||||||
|
`ASUS TTP` (throttle_thermal_policy) only applies to ASUS laptops with
|
||||||
|
`asus-nb-wmi`. On other hardware those writes are silently skipped.
|
||||||
|
|
||||||
|
## Switching
|
||||||
|
|
||||||
|
```bash
|
||||||
|
veilor-power save # max battery (aliases: powersave, s)
|
||||||
|
veilor-power mid # balanced (aliases: balanced, b)
|
||||||
|
veilor-power perf # performance (aliases: performance, p)
|
||||||
|
veilor-power # status: profile, governor, EPP, boost, freq
|
||||||
|
```
|
||||||
|
|
||||||
|
`veilor-power` calls `tuned-adm` via a NOPASSWD sudoers drop-in
|
||||||
|
locked to `veilor-*` profiles only (`/etc/sudoers.d/veilor-power`).
|
||||||
|
|
||||||
|
## Auto-switch on AC plug/unplug
|
||||||
|
|
||||||
|
`/etc/udev/rules.d/90-veilor-ac-switch.rules`:
|
||||||
|
|
||||||
|
```
|
||||||
|
SUBSYSTEM=="power_supply", ATTR{online}=="0", RUN+="/usr/bin/tuned-adm profile veilor-powersave"
|
||||||
|
SUBSYSTEM=="power_supply", ATTR{online}=="1", RUN+="/usr/bin/tuned-adm profile veilor-performance"
|
||||||
|
```
|
||||||
|
|
||||||
|
Override anytime with `veilor-power mid`.
|
||||||
|
|
||||||
|
## Battery longevity
|
||||||
|
|
||||||
|
`/etc/udev/rules.d/91-veilor-battery-threshold.rules` caps charge at
|
||||||
|
80% on supported hardware. Adjust by editing the rule or:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
echo 100 | sudo tee /sys/class/power_supply/BAT0/charge_control_end_threshold
|
||||||
|
```
|
||||||
|
|
||||||
|
## What each profile actually does
|
||||||
|
|
||||||
|
`/etc/tuned/profiles/veilor-<profile>/script.sh` writes:
|
||||||
|
|
||||||
|
- `/sys/devices/system/cpu/cpufreq/boost`
|
||||||
|
- `/sys/devices/platform/asus-nb-wmi/throttle_thermal_policy` (ASUS only)
|
||||||
|
- `/sys/bus/pci/devices/*/power/control` (NVMe autosuspend)
|
||||||
|
- `/sys/class/drm/card*/device/power_dpm_force_performance_level` (AMD iGPU)
|
||||||
|
- `usb_autosuspend` enable/disable
|
||||||
|
|
||||||
|
All writes are guarded with `[ -w ... ]` so non-applicable hardware
|
||||||
|
silently no-ops.
|
||||||
|
|
||||||
|
## Persistence
|
||||||
|
|
||||||
|
`tuned.service` starts at boot and loads the last active profile from
|
||||||
|
`/var/lib/tuned/save.conf`. No GRUB params needed.
|
||||||
|
|
||||||
|
## Caveat: `platform_profile` vs `throttle_thermal_policy`
|
||||||
|
|
||||||
|
On some ASUS laptops the `platform_profile` sysfs key maps to TTP in
|
||||||
|
non-obvious order (e.g. `quiet`→TTP2, `balanced`→TTP0,
|
||||||
|
`performance`→TTP1). veilor profiles write TTP directly and never
|
||||||
|
touch `platform_profile` to avoid the second-write override race.
|
||||||
163
kickstart/veilor-os.ks
Normal file
163
kickstart/veilor-os.ks
Normal file
|
|
@ -0,0 +1,163 @@
|
||||||
|
#version=DEVEL
|
||||||
|
# veilor-os kickstart — Fedora 43 KDE base, hardened, minimal.
|
||||||
|
# Build with livemedia-creator inside build/Containerfile.
|
||||||
|
|
||||||
|
# ── Install source ──
|
||||||
|
url --mirrorlist="https://mirrors.fedoraproject.org/mirrorlist?repo=fedora-$releasever&arch=$basearch"
|
||||||
|
repo --name=updates --mirrorlist="https://mirrors.fedoraproject.org/mirrorlist?repo=updates-released-f$releasever&arch=$basearch"
|
||||||
|
|
||||||
|
# ── Locale / keyboard / time (template — adjust per build) ──
|
||||||
|
keyboard --xlayouts='us'
|
||||||
|
lang en_GB.UTF-8
|
||||||
|
timezone Europe/London --utc
|
||||||
|
|
||||||
|
# ── Install mode ──
|
||||||
|
text
|
||||||
|
firstboot --disable
|
||||||
|
eula --agreed
|
||||||
|
selinux --enforcing
|
||||||
|
services --enabled=sshd,fail2ban,usbguard,tuned,auditd,firewalld,chronyd,sddm,veilor-firstboot,veilor-modules-lock
|
||||||
|
|
||||||
|
# ── Network / hostname ──
|
||||||
|
network --bootproto=dhcp --device=link --activate --hostname=veilor-os
|
||||||
|
firewall --enabled --service=ssh
|
||||||
|
|
||||||
|
# ── Identity (zero-prompt; only LUKS passphrase asked at install) ──
|
||||||
|
rootpw --lock
|
||||||
|
user --name=admin --groups=wheel --gecos="veilor admin" --password="" --plaintext
|
||||||
|
auth --useshadow --passalgo=sha512
|
||||||
|
|
||||||
|
# ── Bootloader: kernel hardening flags ──
|
||||||
|
bootloader --location=mbr --append="lockdown=integrity slab_nomerge init_on_alloc=1 init_on_free=1 randomize_kstack_offset=on vsyscall=none"
|
||||||
|
|
||||||
|
# ── Disk: BIOS+UEFI, LUKS2, btrfs subvols, zram swap (no disk swap) ──
|
||||||
|
zerombr
|
||||||
|
clearpart --all --initlabel
|
||||||
|
reqpart --add-boot
|
||||||
|
part /boot --fstype=ext4 --size=1024 --asprimary
|
||||||
|
part pv.veilor --size=1 --grow --encrypted --luks-version=luks2 \
|
||||||
|
--pbkdf=argon2id --pbkdf-memory=1048576 --pbkdf-iterations=9 \
|
||||||
|
--cipher=aes-xts-plain64 --hash=sha512
|
||||||
|
volgroup veilor pv.veilor
|
||||||
|
logvol / --vgname=veilor --name=root --fstype=btrfs --size=1 --grow \
|
||||||
|
--mkfsoptions="--mixed"
|
||||||
|
|
||||||
|
# ── Packages ──
|
||||||
|
%packages --excludedocs
|
||||||
|
@^kde-desktop-environment
|
||||||
|
@kde-apps
|
||||||
|
@core
|
||||||
|
@hardware-support
|
||||||
|
@standard
|
||||||
|
|
||||||
|
# core hardening tools
|
||||||
|
fail2ban
|
||||||
|
fail2ban-firewalld
|
||||||
|
usbguard
|
||||||
|
usbguard-tools
|
||||||
|
audit
|
||||||
|
policycoreutils-python-utils
|
||||||
|
tuned
|
||||||
|
chrony
|
||||||
|
firewalld
|
||||||
|
plymouth
|
||||||
|
|
||||||
|
# admin essentials
|
||||||
|
git
|
||||||
|
vim-enhanced
|
||||||
|
tmux
|
||||||
|
htop
|
||||||
|
podman
|
||||||
|
skopeo
|
||||||
|
NetworkManager
|
||||||
|
NetworkManager-wifi
|
||||||
|
|
||||||
|
# fonts
|
||||||
|
fontconfig
|
||||||
|
freetype
|
||||||
|
|
||||||
|
# remove fluff
|
||||||
|
-cups
|
||||||
|
-cups-browsed
|
||||||
|
-abrt*
|
||||||
|
-snapd
|
||||||
|
-geoclue2
|
||||||
|
-avahi
|
||||||
|
-avahi-libs
|
||||||
|
-kde-connect
|
||||||
|
-open-vm-tools-desktop
|
||||||
|
-PackageKit
|
||||||
|
-PackageKit-command-not-found
|
||||||
|
-mlocate
|
||||||
|
-ModemManager
|
||||||
|
-pcsc-lite
|
||||||
|
-rsync-daemon
|
||||||
|
|
||||||
|
%end
|
||||||
|
|
||||||
|
# ── Post-install (nochroot): copy overlay tree into installed root ──
|
||||||
|
%post --nochroot
|
||||||
|
set -eu
|
||||||
|
SRC=/run/install/repo/veilor
|
||||||
|
DEST=/mnt/sysimage
|
||||||
|
if [[ -d $SRC/overlay ]]; then
|
||||||
|
cp -a $SRC/overlay/. $DEST/
|
||||||
|
fi
|
||||||
|
mkdir -p $DEST/usr/share/veilor-os
|
||||||
|
cp -a $SRC/assets $DEST/usr/share/veilor-os/
|
||||||
|
cp -a $SRC/scripts $DEST/usr/share/veilor-os/
|
||||||
|
%end
|
||||||
|
|
||||||
|
# ── Post-install (chroot): apply hardening, theme, branding ──
|
||||||
|
%post
|
||||||
|
set -uo pipefail
|
||||||
|
exec > >(tee -a /var/log/veilor-install.log) 2>&1
|
||||||
|
|
||||||
|
echo "════════════════════════════════════════════════════════"
|
||||||
|
echo " veilor-os install — %post"
|
||||||
|
echo "════════════════════════════════════════════════════════"
|
||||||
|
|
||||||
|
REPO=/usr/share/veilor-os
|
||||||
|
chmod +x $REPO/scripts/*.sh $REPO/scripts/selinux/*.sh /usr/local/bin/veilor-power /usr/local/sbin/veilor-firstboot
|
||||||
|
|
||||||
|
# Apply hardening
|
||||||
|
bash $REPO/scripts/10-harden-base.sh
|
||||||
|
bash $REPO/scripts/20-harden-kernel.sh
|
||||||
|
|
||||||
|
# Build SELinux module
|
||||||
|
bash $REPO/scripts/selinux/build-policy.sh || echo "[WARN] SELinux build failed; load on first boot"
|
||||||
|
|
||||||
|
# Apply KDE theme + DuckSans + os-release branding
|
||||||
|
bash $REPO/scripts/kde-theme-apply.sh
|
||||||
|
|
||||||
|
# Force admin password set on first boot (chage expires immediately)
|
||||||
|
chage -d 0 admin
|
||||||
|
|
||||||
|
# zram swap (no disk swap; keys never leak to platter)
|
||||||
|
dnf install -y zram-generator || true
|
||||||
|
cat > /etc/systemd/zram-generator.conf << 'EOF'
|
||||||
|
[zram0]
|
||||||
|
zram-size = min(ram, 8192)
|
||||||
|
compression-algorithm = zstd
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# Enable services
|
||||||
|
systemctl enable veilor-firstboot.service
|
||||||
|
systemctl enable veilor-modules-lock.service
|
||||||
|
systemctl enable sshd fail2ban usbguard tuned auditd firewalld chronyd
|
||||||
|
|
||||||
|
# Default tuned profile = balanced (AC/battery udev rule will override)
|
||||||
|
tuned-adm profile veilor-balanced 2>/dev/null || true
|
||||||
|
|
||||||
|
# Lock root explicitly (kickstart --lock should already do this)
|
||||||
|
passwd -l root
|
||||||
|
|
||||||
|
# Sanity: zero references to onyx / personal IPs in installed system
|
||||||
|
if grep -rqi 'onyx\|192\.168\.0\.\|fedora\.local' /etc/veilor* /etc/tuned/profiles/veilor-* 2>/dev/null; then
|
||||||
|
echo "[ERR] brand leak detected in /etc — investigate"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "════════════════════════════════════════════════════════"
|
||||||
|
echo " veilor-os install complete"
|
||||||
|
echo "════════════════════════════════════════════════════════"
|
||||||
|
%end
|
||||||
11
overlay/etc/os-release.d/veilor
Normal file
11
overlay/etc/os-release.d/veilor
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
NAME="veilor-os"
|
||||||
|
PRETTY_NAME="veilor-os 0.1 (Fedora 43 base)"
|
||||||
|
ID=veilor
|
||||||
|
ID_LIKE=fedora
|
||||||
|
VERSION="0.1"
|
||||||
|
VERSION_ID="0.1"
|
||||||
|
HOME_URL="https://github.com/veilor-uk/veilor-os"
|
||||||
|
DOCUMENTATION_URL="https://github.com/veilor-uk/veilor-os/tree/main/docs"
|
||||||
|
BUG_REPORT_URL="https://github.com/veilor-uk/veilor-os/issues"
|
||||||
|
ANSI_COLOR="0;30;47"
|
||||||
|
LOGO=veilor-logo
|
||||||
15
overlay/etc/sddm.conf.d/veilor.conf
Normal file
15
overlay/etc/sddm.conf.d/veilor.conf
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
[Theme]
|
||||||
|
Current=breeze
|
||||||
|
CursorTheme=breeze_cursors
|
||||||
|
|
||||||
|
[Users]
|
||||||
|
HideUsers=root
|
||||||
|
HideShells=/sbin/nologin,/bin/false
|
||||||
|
MaximumUid=60000
|
||||||
|
MinimumUid=1000
|
||||||
|
|
||||||
|
[General]
|
||||||
|
Numlock=on
|
||||||
|
|
||||||
|
[Autologin]
|
||||||
|
Relogin=false
|
||||||
16
overlay/etc/ssh/sshd_config.d/10-veilor-hardening.conf
Normal file
16
overlay/etc/ssh/sshd_config.d/10-veilor-hardening.conf
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
# veilor-os — sshd hardening drop-in
|
||||||
|
# Loaded last (10-* prefix sorts after distro 50-*)
|
||||||
|
X11Forwarding no
|
||||||
|
AllowUsers admin
|
||||||
|
PasswordAuthentication no
|
||||||
|
PermitRootLogin no
|
||||||
|
PermitEmptyPasswords no
|
||||||
|
ChallengeResponseAuthentication no
|
||||||
|
KbdInteractiveAuthentication no
|
||||||
|
UsePAM yes
|
||||||
|
ClientAliveInterval 300
|
||||||
|
ClientAliveCountMax 2
|
||||||
|
LoginGraceTime 30
|
||||||
|
MaxAuthTries 3
|
||||||
|
MaxSessions 4
|
||||||
|
LogLevel VERBOSE
|
||||||
3
overlay/etc/sudoers.d/veilor-power
Normal file
3
overlay/etc/sudoers.d/veilor-power
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
# veilor-power — allow wheel to switch tuned profiles without password
|
||||||
|
# Locked to veilor-* profiles only.
|
||||||
|
%wheel ALL=(root) NOPASSWD: /usr/bin/tuned-adm profile veilor-powersave, /usr/bin/tuned-adm profile veilor-balanced, /usr/bin/tuned-adm profile veilor-performance
|
||||||
25
overlay/etc/sysctl.d/99-veilor-hardening.conf
Normal file
25
overlay/etc/sysctl.d/99-veilor-hardening.conf
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
# veilor-os — kernel sysctl hardening
|
||||||
|
# Mirrors scripts/20-harden-kernel.sh; ships in overlay so values present
|
||||||
|
# at first boot before scripts run.
|
||||||
|
|
||||||
|
kernel.kptr_restrict = 2
|
||||||
|
kernel.dmesg_restrict = 1
|
||||||
|
net.core.bpf_jit_harden = 2
|
||||||
|
kernel.perf_event_paranoid = 3
|
||||||
|
kernel.yama.ptrace_scope = 2
|
||||||
|
kernel.randomize_va_space = 2
|
||||||
|
kernel.modules_disabled = 0
|
||||||
|
net.ipv4.conf.all.rp_filter = 1
|
||||||
|
net.ipv4.conf.default.rp_filter = 1
|
||||||
|
net.ipv4.conf.all.log_martians = 1
|
||||||
|
net.ipv4.conf.default.log_martians = 1
|
||||||
|
fs.suid_dumpable = 0
|
||||||
|
dev.tty.ldisc_autoload = 0
|
||||||
|
kernel.sched_schedstats = 0
|
||||||
|
net.ipv4.tcp_syncookies = 1
|
||||||
|
net.ipv4.icmp_echo_ignore_broadcasts = 1
|
||||||
|
net.ipv4.conf.all.accept_source_route = 0
|
||||||
|
net.ipv6.conf.all.accept_source_route = 0
|
||||||
|
net.ipv4.conf.all.accept_redirects = 0
|
||||||
|
net.ipv4.conf.all.send_redirects = 0
|
||||||
|
net.ipv6.conf.all.accept_redirects = 0
|
||||||
21
overlay/etc/systemd/system/veilor-firstboot.service
Normal file
21
overlay/etc/systemd/system/veilor-firstboot.service
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
[Unit]
|
||||||
|
Description=veilor-os first-boot admin password setup
|
||||||
|
Documentation=https://github.com/veilor-uk/veilor-os
|
||||||
|
ConditionPathExists=!/var/lib/veilor-firstboot.done
|
||||||
|
Before=sddm.service display-manager.service
|
||||||
|
After=systemd-user-sessions.service plymouth-quit-wait.service
|
||||||
|
Conflicts=sddm.service
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=oneshot
|
||||||
|
RemainAfterExit=no
|
||||||
|
ExecStart=/usr/local/sbin/veilor-firstboot
|
||||||
|
StandardInput=tty
|
||||||
|
StandardOutput=tty
|
||||||
|
StandardError=tty
|
||||||
|
TTYPath=/dev/tty1
|
||||||
|
TTYReset=yes
|
||||||
|
TTYVHangup=yes
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
16
overlay/etc/systemd/system/veilor-modules-lock.service
Normal file
16
overlay/etc/systemd/system/veilor-modules-lock.service
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
[Unit]
|
||||||
|
Description=Lock kernel module loading after graphical boot (veilor-os)
|
||||||
|
Documentation=https://www.kernel.org/doc/html/latest/admin-guide/sysctl/kernel.html
|
||||||
|
After=graphical.target network.target local-fs.target
|
||||||
|
ConditionKernelCommandLine=!module.sig_enforce=1
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=oneshot
|
||||||
|
RemainAfterExit=yes
|
||||||
|
ExecStartPre=/bin/sleep 30
|
||||||
|
ExecStart=/usr/bin/sysctl -w kernel.modules_disabled=1
|
||||||
|
StandardOutput=journal
|
||||||
|
StandardError=journal
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=graphical.target
|
||||||
32
overlay/etc/tuned/profiles/veilor-balanced/script.sh
Executable file
32
overlay/etc/tuned/profiles/veilor-balanced/script.sh
Executable file
|
|
@ -0,0 +1,32 @@
|
||||||
|
#!/usr/bin/bash
|
||||||
|
# veilor-balanced — on-the-go
|
||||||
|
. /usr/lib/tuned/functions
|
||||||
|
|
||||||
|
start() {
|
||||||
|
[ -w /sys/devices/system/cpu/cpufreq/boost ] && echo 1 > /sys/devices/system/cpu/cpufreq/boost
|
||||||
|
[ -w /sys/devices/platform/asus-nb-wmi/throttle_thermal_policy ] && \
|
||||||
|
echo 1 > /sys/devices/platform/asus-nb-wmi/throttle_thermal_policy
|
||||||
|
for nvme in /sys/bus/pci/devices/*/nvme; do
|
||||||
|
ctl="${nvme%/nvme}/power/control"
|
||||||
|
[ -w "$ctl" ] && echo auto > "$ctl"
|
||||||
|
done
|
||||||
|
for card in /sys/class/drm/card*/device/power_dpm_force_performance_level; do
|
||||||
|
[ -w "$card" ] && echo auto > "$card"
|
||||||
|
done
|
||||||
|
enable_usb_autosuspend
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
stop() {
|
||||||
|
for nvme in /sys/bus/pci/devices/*/nvme; do
|
||||||
|
ctl="${nvme%/nvme}/power/control"
|
||||||
|
[ -w "$ctl" ] && echo on > "$ctl"
|
||||||
|
done
|
||||||
|
for card in /sys/class/drm/card*/device/power_dpm_force_performance_level; do
|
||||||
|
[ -w "$card" ] && echo auto > "$card"
|
||||||
|
done
|
||||||
|
disable_usb_autosuspend
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
process $@
|
||||||
10
overlay/etc/tuned/profiles/veilor-balanced/tuned.conf
Normal file
10
overlay/etc/tuned/profiles/veilor-balanced/tuned.conf
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
[main]
|
||||||
|
summary=veilor balanced — on-the-go, boost enabled, mid PPT
|
||||||
|
|
||||||
|
[cpu]
|
||||||
|
governor=powersave
|
||||||
|
energy_performance_preference=balance_performance
|
||||||
|
force_latency=cstate.id_no_zero:1
|
||||||
|
|
||||||
|
[script]
|
||||||
|
script=${i:PROFILE_DIR}/script.sh
|
||||||
31
overlay/etc/tuned/profiles/veilor-performance/script.sh
Executable file
31
overlay/etc/tuned/profiles/veilor-performance/script.sh
Executable file
|
|
@ -0,0 +1,31 @@
|
||||||
|
#!/usr/bin/bash
|
||||||
|
# veilor-performance — full power
|
||||||
|
. /usr/lib/tuned/functions
|
||||||
|
|
||||||
|
start() {
|
||||||
|
[ -w /sys/devices/system/cpu/cpufreq/boost ] && echo 1 > /sys/devices/system/cpu/cpufreq/boost
|
||||||
|
[ -w /sys/devices/platform/asus-nb-wmi/throttle_thermal_policy ] && \
|
||||||
|
echo 0 > /sys/devices/platform/asus-nb-wmi/throttle_thermal_policy
|
||||||
|
for nvme in /sys/bus/pci/devices/*/nvme; do
|
||||||
|
ctl="${nvme%/nvme}/power/control"
|
||||||
|
[ -w "$ctl" ] && echo on > "$ctl"
|
||||||
|
done
|
||||||
|
for card in /sys/class/drm/card*/device/power_dpm_force_performance_level; do
|
||||||
|
[ -w "$card" ] && echo auto > "$card"
|
||||||
|
done
|
||||||
|
disable_usb_autosuspend
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
stop() {
|
||||||
|
for nvme in /sys/bus/pci/devices/*/nvme; do
|
||||||
|
ctl="${nvme%/nvme}/power/control"
|
||||||
|
[ -w "$ctl" ] && echo on > "$ctl"
|
||||||
|
done
|
||||||
|
for card in /sys/class/drm/card*/device/power_dpm_force_performance_level; do
|
||||||
|
[ -w "$card" ] && echo auto > "$card"
|
||||||
|
done
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
process $@
|
||||||
10
overlay/etc/tuned/profiles/veilor-performance/tuned.conf
Normal file
10
overlay/etc/tuned/profiles/veilor-performance/tuned.conf
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
[main]
|
||||||
|
summary=veilor performance — full PPT, max boost, no artificial cap
|
||||||
|
|
||||||
|
[cpu]
|
||||||
|
governor=performance
|
||||||
|
energy_performance_preference=performance
|
||||||
|
force_latency=1
|
||||||
|
|
||||||
|
[script]
|
||||||
|
script=${i:PROFILE_DIR}/script.sh
|
||||||
43
overlay/etc/tuned/profiles/veilor-powersave/script.sh
Executable file
43
overlay/etc/tuned/profiles/veilor-powersave/script.sh
Executable file
|
|
@ -0,0 +1,43 @@
|
||||||
|
#!/usr/bin/bash
|
||||||
|
# veilor-powersave — max battery
|
||||||
|
. /usr/lib/tuned/functions
|
||||||
|
|
||||||
|
start() {
|
||||||
|
# CPU boost off
|
||||||
|
[ -w /sys/devices/system/cpu/cpufreq/boost ] && echo 0 > /sys/devices/system/cpu/cpufreq/boost
|
||||||
|
|
||||||
|
# ASUS throttle_thermal_policy: 2 = silent (~5W PPT). No-op on non-ASUS.
|
||||||
|
[ -w /sys/devices/platform/asus-nb-wmi/throttle_thermal_policy ] && \
|
||||||
|
echo 2 > /sys/devices/platform/asus-nb-wmi/throttle_thermal_policy
|
||||||
|
|
||||||
|
# NVMe PCI autosuspend (first NVMe device)
|
||||||
|
for nvme in /sys/bus/pci/devices/*/nvme; do
|
||||||
|
ctl="${nvme%/nvme}/power/control"
|
||||||
|
[ -w "$ctl" ] && echo auto > "$ctl"
|
||||||
|
done
|
||||||
|
|
||||||
|
# AMD iGPU — minimum clocks (no-op on non-AMD)
|
||||||
|
for card in /sys/class/drm/card*/device/power_dpm_force_performance_level; do
|
||||||
|
[ -w "$card" ] && echo low > "$card"
|
||||||
|
done
|
||||||
|
|
||||||
|
# USB autosuspend
|
||||||
|
enable_usb_autosuspend
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
stop() {
|
||||||
|
[ -w /sys/devices/system/cpu/cpufreq/boost ] && echo 1 > /sys/devices/system/cpu/cpufreq/boost
|
||||||
|
for nvme in /sys/bus/pci/devices/*/nvme; do
|
||||||
|
ctl="${nvme%/nvme}/power/control"
|
||||||
|
[ -w "$ctl" ] && echo on > "$ctl"
|
||||||
|
done
|
||||||
|
for card in /sys/class/drm/card*/device/power_dpm_force_performance_level; do
|
||||||
|
[ -w "$card" ] && echo auto > "$card"
|
||||||
|
done
|
||||||
|
disable_usb_autosuspend
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
process $@
|
||||||
12
overlay/etc/tuned/profiles/veilor-powersave/tuned.conf
Normal file
12
overlay/etc/tuned/profiles/veilor-powersave/tuned.conf
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
[main]
|
||||||
|
summary=veilor powersave — max battery, CPU capped, boost off
|
||||||
|
|
||||||
|
[cpu]
|
||||||
|
governor=powersave
|
||||||
|
energy_performance_preference=power
|
||||||
|
force_latency=cstate.id_no_zero:1
|
||||||
|
min_perf_pct=0
|
||||||
|
max_perf_pct=30
|
||||||
|
|
||||||
|
[script]
|
||||||
|
script=${i:PROFILE_DIR}/script.sh
|
||||||
3
overlay/etc/udev/rules.d/90-veilor-ac-switch.rules
Normal file
3
overlay/etc/udev/rules.d/90-veilor-ac-switch.rules
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
# veilor-os — auto-switch tuned profile on AC plug/unplug
|
||||||
|
SUBSYSTEM=="power_supply", ATTR{online}=="0", RUN+="/usr/bin/tuned-adm profile veilor-powersave"
|
||||||
|
SUBSYSTEM=="power_supply", ATTR{online}=="1", RUN+="/usr/bin/tuned-adm profile veilor-performance"
|
||||||
|
|
@ -0,0 +1,2 @@
|
||||||
|
# veilor-os — cap battery charge at 80% for longevity
|
||||||
|
ACTION=="add", SUBSYSTEM=="power_supply", ATTR{type}=="Battery", ATTR{charge_control_end_threshold}=="*", ATTR{charge_control_end_threshold}="80"
|
||||||
35
overlay/usr/local/bin/veilor-power
Executable file
35
overlay/usr/local/bin/veilor-power
Executable file
|
|
@ -0,0 +1,35 @@
|
||||||
|
#!/usr/bin/bash
|
||||||
|
# veilor-power — power profile switcher
|
||||||
|
# Usage: veilor-power [save|mid|perf|status]
|
||||||
|
|
||||||
|
_switch() {
|
||||||
|
sudo /usr/bin/tuned-adm profile "$1"
|
||||||
|
}
|
||||||
|
|
||||||
|
_status() {
|
||||||
|
local profile gov epp boost asus freq
|
||||||
|
profile=$(tuned-adm active 2>/dev/null | awk '{print $NF}')
|
||||||
|
gov=$(cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor 2>/dev/null)
|
||||||
|
epp=$(cat /sys/devices/system/cpu/cpu0/cpufreq/energy_performance_preference 2>/dev/null)
|
||||||
|
boost=$(cat /sys/devices/system/cpu/cpufreq/boost 2>/dev/null)
|
||||||
|
asus=$(cat /sys/devices/platform/asus-nb-wmi/throttle_thermal_policy 2>/dev/null || echo "n/a")
|
||||||
|
freq=$(awk '{printf "%.0f MHz", $1/1000}' /sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq 2>/dev/null)
|
||||||
|
|
||||||
|
echo "Profile : $profile"
|
||||||
|
echo "Governor: $gov | EPP: $epp | Boost: $boost"
|
||||||
|
echo "ASUS TTP: $asus (0=perf 1=balanced 2=silent) | Cur freq: $freq"
|
||||||
|
}
|
||||||
|
|
||||||
|
case "$1" in
|
||||||
|
save|powersave|s) _switch veilor-powersave ;;
|
||||||
|
mid|balanced|b) _switch veilor-balanced ;;
|
||||||
|
perf|performance|p) _switch veilor-performance ;;
|
||||||
|
status|"") _status ;;
|
||||||
|
*)
|
||||||
|
echo "Usage: veilor-power [save|mid|perf|status]"
|
||||||
|
echo " save — max battery (boost off, lowest PPT)"
|
||||||
|
echo " mid — balanced (boost on, mid PPT)"
|
||||||
|
echo " perf — performance (boost on, full PPT)"
|
||||||
|
echo " status / no arg — show current state"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
47
overlay/usr/local/sbin/veilor-firstboot
Executable file
47
overlay/usr/local/sbin/veilor-firstboot
Executable file
|
|
@ -0,0 +1,47 @@
|
||||||
|
#!/usr/bin/bash
|
||||||
|
# veilor-firstboot — set admin password on first boot, then self-disable.
|
||||||
|
# Runs on TTY1 before SDDM. Only fires while admin password is empty/expired.
|
||||||
|
|
||||||
|
set -uo pipefail
|
||||||
|
|
||||||
|
STATE=/var/lib/veilor-firstboot.done
|
||||||
|
[[ -f $STATE ]] && exit 0
|
||||||
|
|
||||||
|
# Branded banner
|
||||||
|
clear
|
||||||
|
cat << 'EOF'
|
||||||
|
|
||||||
|
┌──────────────────────────────────────────────────────────┐
|
||||||
|
│ │
|
||||||
|
│ veilor-os │
|
||||||
|
│ first boot — admin password │
|
||||||
|
│ │
|
||||||
|
└──────────────────────────────────────────────────────────┘
|
||||||
|
|
||||||
|
Set a password for the local admin account.
|
||||||
|
|
||||||
|
Requirements: minimum 14 characters, at least one digit,
|
||||||
|
one uppercase, one lowercase, one special character.
|
||||||
|
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# Loop until passwd succeeds (pwquality enforces complexity)
|
||||||
|
until passwd admin; do
|
||||||
|
echo
|
||||||
|
echo " Password not accepted. Try again."
|
||||||
|
echo
|
||||||
|
sleep 1
|
||||||
|
done
|
||||||
|
|
||||||
|
# Mark done so service doesn't fire again
|
||||||
|
touch "$STATE"
|
||||||
|
|
||||||
|
# Disable self for next boots
|
||||||
|
systemctl disable veilor-firstboot.service >/dev/null 2>&1 || true
|
||||||
|
|
||||||
|
echo
|
||||||
|
echo " Password set. Starting graphical session..."
|
||||||
|
sleep 2
|
||||||
|
|
||||||
|
# Start SDDM (was held back by service ordering)
|
||||||
|
systemctl start sddm.service
|
||||||
175
scripts/10-harden-base.sh
Executable file
175
scripts/10-harden-base.sh
Executable file
|
|
@ -0,0 +1,175 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
# veilor-os — base hardening (services, DNS, fail2ban, auditd)
|
||||||
|
# Idempotent. Run as root during kickstart %post or post-install.
|
||||||
|
|
||||||
|
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 :: 10-harden-base"
|
||||||
|
echo "════════════════════════════════════════════════════════"
|
||||||
|
|
||||||
|
# ── Remove KDE Connect (network exposure, DBus surface) ──
|
||||||
|
info "Removing kdeconnectd"
|
||||||
|
dnf remove -y kdeconnectd 2>/dev/null && ok "kdeconnectd removed" || info "kdeconnectd absent"
|
||||||
|
|
||||||
|
# ── systemd-resolved: LLMNR off, DNSSEC, DNS-over-TLS ──
|
||||||
|
info "Hardening systemd-resolved"
|
||||||
|
mkdir -p /etc/systemd/resolved.conf.d
|
||||||
|
cat > /etc/systemd/resolved.conf.d/veilor-hardening.conf << 'EOF'
|
||||||
|
[Resolve]
|
||||||
|
LLMNR=no
|
||||||
|
DNSSEC=allow-downgrade
|
||||||
|
DNS=1.1.1.1#cloudflare-dns.com 1.0.0.1#cloudflare-dns.com 2606:4700:4700::1111#cloudflare-dns.com
|
||||||
|
FallbackDNS=9.9.9.9#dns.quad9.net 149.112.112.112#dns.quad9.net
|
||||||
|
DNSOverTLS=opportunistic
|
||||||
|
EOF
|
||||||
|
systemctl restart systemd-resolved 2>/dev/null || true
|
||||||
|
ok "systemd-resolved hardened (LLMNR off, DNSSEC, DoT)"
|
||||||
|
|
||||||
|
# ── fail2ban ──
|
||||||
|
info "Installing fail2ban"
|
||||||
|
rpm -q fail2ban &>/dev/null || dnf install -y fail2ban fail2ban-firewalld
|
||||||
|
cat > /etc/fail2ban/jail.local << 'EOF'
|
||||||
|
[DEFAULT]
|
||||||
|
backend = systemd
|
||||||
|
bantime = 3600
|
||||||
|
findtime = 600
|
||||||
|
maxretry = 5
|
||||||
|
banaction = firewallcmd-rich-rules
|
||||||
|
banaction_allports = firewallcmd-rich-rules
|
||||||
|
|
||||||
|
[sshd]
|
||||||
|
enabled = true
|
||||||
|
mode = aggressive
|
||||||
|
port = ssh
|
||||||
|
maxretry = 3
|
||||||
|
bantime = 86400
|
||||||
|
|
||||||
|
[pam-generic]
|
||||||
|
enabled = true
|
||||||
|
filter = pam-generic
|
||||||
|
bantime = 3600
|
||||||
|
maxretry = 5
|
||||||
|
EOF
|
||||||
|
systemctl enable fail2ban
|
||||||
|
ok "fail2ban configured + enabled"
|
||||||
|
|
||||||
|
# ── auditd rules ──
|
||||||
|
info "Deploying auditd rules"
|
||||||
|
mkdir -p /etc/audit/rules.d
|
||||||
|
cat > /etc/audit/rules.d/99-veilor-hardening.rules << 'EOF'
|
||||||
|
## veilor-os audit ruleset
|
||||||
|
-D
|
||||||
|
-b 8192
|
||||||
|
-f 1
|
||||||
|
|
||||||
|
## time & clock
|
||||||
|
-a always,exit -F arch=b64 -S adjtimex,settimeofday -k time-change
|
||||||
|
-a always,exit -F arch=b32 -S adjtimex,settimeofday,stime -k time-change
|
||||||
|
-a always,exit -F arch=b64 -S clock_settime -k time-change
|
||||||
|
-a always,exit -F arch=b32 -S clock_settime -k time-change
|
||||||
|
-w /etc/localtime -p wa -k time-change
|
||||||
|
|
||||||
|
## identity
|
||||||
|
-w /etc/group -p wa -k identity
|
||||||
|
-w /etc/passwd -p wa -k identity
|
||||||
|
-w /etc/gshadow -p wa -k identity
|
||||||
|
-w /etc/shadow -p wa -k identity
|
||||||
|
-w /etc/security/opasswd -p wa -k identity
|
||||||
|
|
||||||
|
## hostname / network
|
||||||
|
-a always,exit -F arch=b64 -S sethostname,setdomainname -k system-locale
|
||||||
|
-a always,exit -F arch=b32 -S sethostname,setdomainname -k system-locale
|
||||||
|
-w /etc/hosts -p wa -k system-locale
|
||||||
|
-w /etc/hostname -p wa -k system-locale
|
||||||
|
|
||||||
|
## SELinux
|
||||||
|
-w /etc/selinux/ -p wa -k MAC-policy
|
||||||
|
|
||||||
|
## logins
|
||||||
|
-w /var/log/lastlog -p wa -k logins
|
||||||
|
-w /var/run/faillock/ -p wa -k logins
|
||||||
|
-w /var/run/utmp -p wa -k session
|
||||||
|
-w /var/log/wtmp -p wa -k logins
|
||||||
|
-w /var/log/btmp -p wa -k logins
|
||||||
|
|
||||||
|
## perm changes
|
||||||
|
-a always,exit -F arch=b64 -S chmod,fchmod,fchmodat -F auid>=1000 -F auid!=unset -k perm_mod
|
||||||
|
-a always,exit -F arch=b32 -S chmod,fchmod,fchmodat -F auid>=1000 -F auid!=unset -k perm_mod
|
||||||
|
-a always,exit -F arch=b64 -S chown,fchown,fchownat,lchown -F auid>=1000 -F auid!=unset -k perm_mod
|
||||||
|
-a always,exit -F arch=b32 -S chown,fchown,fchownat,lchown -F auid>=1000 -F auid!=unset -k perm_mod
|
||||||
|
-a always,exit -F arch=b64 -S setxattr,lsetxattr,fsetxattr,removexattr,lremovexattr,fremovexattr -F auid>=1000 -F auid!=unset -k perm_mod
|
||||||
|
-a always,exit -F arch=b32 -S setxattr,lsetxattr,fsetxattr,removexattr,lremovexattr,fremovexattr -F auid>=1000 -F auid!=unset -k perm_mod
|
||||||
|
|
||||||
|
## access denials
|
||||||
|
-a always,exit -F arch=b64 -S creat,open,openat,truncate,ftruncate -F exit=-EACCES -F auid>=1000 -F auid!=unset -k access
|
||||||
|
-a always,exit -F arch=b32 -S creat,open,openat,truncate,ftruncate -F exit=-EACCES -F auid>=1000 -F auid!=unset -k access
|
||||||
|
-a always,exit -F arch=b64 -S creat,open,openat,truncate,ftruncate -F exit=-EPERM -F auid>=1000 -F auid!=unset -k access
|
||||||
|
-a always,exit -F arch=b32 -S creat,open,openat,truncate,ftruncate -F exit=-EPERM -F auid>=1000 -F auid!=unset -k access
|
||||||
|
|
||||||
|
## privileged commands
|
||||||
|
-a always,exit -F path=/usr/bin/sudo -F perm=x -F auid>=1000 -F auid!=unset -k privileged
|
||||||
|
-a always,exit -F path=/usr/bin/su -F perm=x -F auid>=1000 -F auid!=unset -k privileged
|
||||||
|
-a always,exit -F path=/usr/bin/newgrp -F perm=x -F auid>=1000 -F auid!=unset -k privileged
|
||||||
|
-a always,exit -F path=/usr/bin/chsh -F perm=x -F auid>=1000 -F auid!=unset -k privileged
|
||||||
|
-a always,exit -F path=/usr/bin/chfn -F perm=x -F auid>=1000 -F auid!=unset -k privileged
|
||||||
|
-a always,exit -F path=/usr/bin/gpasswd -F perm=x -F auid>=1000 -F auid!=unset -k privileged
|
||||||
|
-a always,exit -F path=/usr/bin/passwd -F perm=x -F auid>=1000 -F auid!=unset -k privileged
|
||||||
|
-a always,exit -F path=/usr/bin/chage -F perm=x -F auid>=1000 -F auid!=unset -k privileged
|
||||||
|
-a always,exit -F path=/usr/bin/pkexec -F perm=x -F auid>=1000 -F auid!=unset -k privileged
|
||||||
|
-a always,exit -F path=/usr/bin/mount -F perm=x -F auid>=1000 -F auid!=unset -k privileged
|
||||||
|
-a always,exit -F path=/usr/bin/umount -F perm=x -F auid>=1000 -F auid!=unset -k privileged
|
||||||
|
|
||||||
|
## kernel modules
|
||||||
|
-w /sbin/insmod -p x -k modules
|
||||||
|
-w /sbin/rmmod -p x -k modules
|
||||||
|
-w /sbin/modprobe -p x -k modules
|
||||||
|
-a always,exit -F arch=b64 -S init_module,delete_module,finit_module -k modules
|
||||||
|
-a always,exit -F arch=b32 -S init_module,delete_module,finit_module -k modules
|
||||||
|
|
||||||
|
## sudoers
|
||||||
|
-w /etc/sudoers -p wa -k scope
|
||||||
|
-w /etc/sudoers.d/ -p wa -k scope
|
||||||
|
|
||||||
|
## sshd
|
||||||
|
-w /etc/ssh/sshd_config -p wa -k sshd
|
||||||
|
-w /etc/ssh/sshd_config.d/ -p wa -k sshd
|
||||||
|
|
||||||
|
## cron
|
||||||
|
-w /etc/crontab -p wa -k cron
|
||||||
|
-w /etc/cron.d/ -p wa -k cron
|
||||||
|
-w /etc/cron.daily/ -p wa -k cron
|
||||||
|
-w /etc/cron.hourly/ -p wa -k cron
|
||||||
|
-w /etc/cron.monthly/ -p wa -k cron
|
||||||
|
-w /etc/cron.weekly/ -p wa -k cron
|
||||||
|
-w /var/spool/cron/ -p wa -k cron
|
||||||
|
|
||||||
|
## sysctl
|
||||||
|
-w /etc/sysctl.conf -p wa -k sysctl
|
||||||
|
-w /etc/sysctl.d/ -p wa -k sysctl
|
||||||
|
|
||||||
|
## firewall
|
||||||
|
-w /etc/firewalld/ -p wa -k firewall
|
||||||
|
|
||||||
|
## boot
|
||||||
|
-w /etc/default/grub -p wa -k grub
|
||||||
|
-w /etc/grub.d/ -p wa -k grub
|
||||||
|
|
||||||
|
## systemd
|
||||||
|
-w /etc/systemd/system/ -p wa -k systemd
|
||||||
|
-w /usr/lib/systemd/system/ -p wa -k systemd
|
||||||
|
EOF
|
||||||
|
augenrules --load 2>/dev/null || true
|
||||||
|
systemctl enable auditd
|
||||||
|
ok "auditd rules deployed"
|
||||||
|
|
||||||
|
echo "════════════════════════════════════════════════════════"
|
||||||
|
echo " 10-harden-base complete"
|
||||||
|
echo "════════════════════════════════════════════════════════"
|
||||||
145
scripts/20-harden-kernel.sh
Executable file
145
scripts/20-harden-kernel.sh
Executable file
|
|
@ -0,0 +1,145 @@
|
||||||
|
#!/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 "════════════════════════════════════════════════════════"
|
||||||
19
scripts/firstboot.sh
Executable file
19
scripts/firstboot.sh
Executable file
|
|
@ -0,0 +1,19 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
# veilor-os — first user-login touch-ups (runs once via XDG autostart)
|
||||||
|
# Currently a placeholder for per-user setup that can't be done at install time.
|
||||||
|
|
||||||
|
set -uo pipefail
|
||||||
|
|
||||||
|
DONE=$HOME/.config/veilor-firstboot.done
|
||||||
|
[[ -f $DONE ]] && exit 0
|
||||||
|
|
||||||
|
# ── Generate USBGuard allowlist for currently connected devices ──
|
||||||
|
# Requires sudo; admin runs this manually:
|
||||||
|
# sudo usbguard generate-policy > /etc/usbguard/rules.conf
|
||||||
|
# sudo systemctl restart usbguard
|
||||||
|
|
||||||
|
# ── Refresh font cache for user ──
|
||||||
|
fc-cache -f "$HOME/.local/share/fonts" 2>/dev/null || true
|
||||||
|
|
||||||
|
mkdir -p "$(dirname "$DONE")"
|
||||||
|
touch "$DONE"
|
||||||
62
scripts/kde-theme-apply.sh
Executable file
62
scripts/kde-theme-apply.sh
Executable file
|
|
@ -0,0 +1,62 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
# veilor-os — apply system-wide KDE theme + DuckSans font default
|
||||||
|
# Run during %post (chroot) or post-install. Idempotent.
|
||||||
|
|
||||||
|
set -uo pipefail
|
||||||
|
|
||||||
|
GREEN='\033[0;32m'; YELLOW='\033[1;33m'; NC='\033[0m'
|
||||||
|
ok() { echo -e "${GREEN}[OK]${NC} $*"; }
|
||||||
|
info() { echo -e "${YELLOW}[INFO]${NC} $*"; }
|
||||||
|
|
||||||
|
REPO="${VEILOR_REPO:-/usr/share/veilor-os}"
|
||||||
|
|
||||||
|
# ── Install color scheme system-wide ──
|
||||||
|
info "Installing veilor-black color scheme"
|
||||||
|
install -d -m 0755 /usr/share/color-schemes
|
||||||
|
install -m 0644 "$REPO/assets/kde/veilor-black.colors" /usr/share/color-schemes/veilor-black.colors
|
||||||
|
ok "color scheme installed"
|
||||||
|
|
||||||
|
# ── KDE system defaults ──
|
||||||
|
info "Setting system kdedefaults"
|
||||||
|
install -d -m 0755 /etc/xdg/kdedefaults
|
||||||
|
install -m 0644 "$REPO/assets/kde/veilor-default.kdeglobals" /etc/xdg/kdedefaults/kdeglobals
|
||||||
|
ok "kdedefaults written"
|
||||||
|
|
||||||
|
# ── DuckSans fontconfig default ──
|
||||||
|
info "Setting DuckSans as default sans-serif"
|
||||||
|
install -d -m 0755 /etc/fonts/conf.d
|
||||||
|
cat > /etc/fonts/conf.d/55-veilor-ducksans.conf << 'EOF'
|
||||||
|
<?xml version="1.0"?>
|
||||||
|
<!DOCTYPE fontconfig SYSTEM "fonts.dtd">
|
||||||
|
<fontconfig>
|
||||||
|
<alias>
|
||||||
|
<family>sans-serif</family>
|
||||||
|
<prefer><family>DuckSans</family></prefer>
|
||||||
|
</alias>
|
||||||
|
<alias>
|
||||||
|
<family>system-ui</family>
|
||||||
|
<prefer><family>DuckSans</family></prefer>
|
||||||
|
</alias>
|
||||||
|
</fontconfig>
|
||||||
|
EOF
|
||||||
|
fc-cache -f /usr/share/fonts/ducksans 2>/dev/null || true
|
||||||
|
ok "fontconfig: DuckSans = default sans-serif"
|
||||||
|
|
||||||
|
# ── /etc/os-release branding ──
|
||||||
|
info "Branding /etc/os-release"
|
||||||
|
if [[ -f "$REPO/overlay/etc/os-release.d/veilor" ]]; then
|
||||||
|
install -m 0644 "$REPO/overlay/etc/os-release.d/veilor" /etc/os-release
|
||||||
|
ln -sf /etc/os-release /usr/lib/os-release 2>/dev/null || true
|
||||||
|
ok "os-release set to veilor-os"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ── Plymouth theme (optional) ──
|
||||||
|
if [[ -d "$REPO/assets/plymouth/veilor" ]] && command -v plymouth-set-default-theme &>/dev/null; then
|
||||||
|
info "Installing plymouth theme"
|
||||||
|
install -d -m 0755 /usr/share/plymouth/themes/veilor
|
||||||
|
cp -r "$REPO/assets/plymouth/veilor/." /usr/share/plymouth/themes/veilor/
|
||||||
|
plymouth-set-default-theme -R veilor 2>/dev/null || true
|
||||||
|
ok "plymouth theme set to veilor"
|
||||||
|
fi
|
||||||
|
|
||||||
|
ok "kde-theme-apply complete"
|
||||||
10
scripts/selinux/build-policy.sh
Executable file
10
scripts/selinux/build-policy.sh
Executable file
|
|
@ -0,0 +1,10 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
# Build + load veilor-systemd SELinux policy module.
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
cd "$(dirname "$0")"
|
||||||
|
|
||||||
|
checkmodule -M -m -o veilor-systemd.mod veilor-systemd.te
|
||||||
|
semodule_package -o veilor-systemd.pp -m veilor-systemd.mod
|
||||||
|
semodule -i veilor-systemd.pp
|
||||||
|
echo "[OK] veilor-systemd SELinux module loaded"
|
||||||
11
scripts/selinux/veilor-systemd.te
Normal file
11
scripts/selinux/veilor-systemd.te
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
module veilor-systemd 1.0;
|
||||||
|
|
||||||
|
require {
|
||||||
|
type systemd_modules_load_t;
|
||||||
|
class capability2 perfmon;
|
||||||
|
class capability sys_admin;
|
||||||
|
}
|
||||||
|
|
||||||
|
#============= systemd_modules_load_t ==============
|
||||||
|
allow systemd_modules_load_t self:capability sys_admin;
|
||||||
|
allow systemd_modules_load_t self:capability2 perfmon;
|
||||||
115
test/boot-checklist.md
Normal file
115
test/boot-checklist.md
Normal file
|
|
@ -0,0 +1,115 @@
|
||||||
|
# Spare-laptop validation checklist
|
||||||
|
|
||||||
|
Run after installing a fresh veilor-os ISO. Each item should pass
|
||||||
|
before the build is considered green.
|
||||||
|
|
||||||
|
## Install flow
|
||||||
|
|
||||||
|
- [ ] Anaconda **only** prompts for LUKS passphrase — no account wizard,
|
||||||
|
no initial-setup screen
|
||||||
|
- [ ] Install completes without `%post` errors (check `/var/log/veilor-install.log`)
|
||||||
|
- [ ] Reboot succeeds, USB removed cleanly
|
||||||
|
|
||||||
|
## First boot
|
||||||
|
|
||||||
|
- [ ] LUKS prompt appears at boot
|
||||||
|
- [ ] TTY1 shows veilor-os banner + password prompt
|
||||||
|
- [ ] Password rejection on weak input (try `password123` — should fail)
|
||||||
|
- [ ] Password set succeeds with strong input
|
||||||
|
- [ ] SDDM starts after password set
|
||||||
|
- [ ] `admin@veilor-os` shell prompt visible after first login
|
||||||
|
- [ ] `veilor-firstboot.service` shows `inactive (dead)` and `disabled`
|
||||||
|
after first run
|
||||||
|
|
||||||
|
## Identity
|
||||||
|
|
||||||
|
- [ ] `passwd -S root` reports `L` (locked)
|
||||||
|
- [ ] `getent passwd | wc -l` shows base + admin only
|
||||||
|
- [ ] `id admin` shows `groups=...,wheel`
|
||||||
|
|
||||||
|
## Branding
|
||||||
|
|
||||||
|
- [ ] `hostnamectl` reports `veilor-os`
|
||||||
|
- [ ] `cat /etc/os-release` shows `NAME="veilor-os"` and `ID=veilor`
|
||||||
|
- [ ] `grep -ri onyx /etc /usr/local /usr/share/fonts` returns zero
|
||||||
|
- [ ] `grep -ri '192\.168\.0\.\|admin@gmail\|fedora\.local' /etc /usr/local` returns zero
|
||||||
|
|
||||||
|
## Theme
|
||||||
|
|
||||||
|
- [ ] KDE color scheme shows `veilor-black` in System Settings
|
||||||
|
- [ ] Konsole renders in DuckSans (`fc-match sans-serif` returns
|
||||||
|
`DuckSans` if the font was vendored)
|
||||||
|
- [ ] Background is pure black (#000000), not Breeze dark grey
|
||||||
|
|
||||||
|
## Power
|
||||||
|
|
||||||
|
- [ ] `veilor-power status` runs without sudo, shows current profile
|
||||||
|
- [ ] `veilor-power save` switches to `veilor-powersave`
|
||||||
|
- [ ] `veilor-power perf` switches to `veilor-performance`
|
||||||
|
- [ ] Unplugging AC auto-switches to `veilor-powersave` (udev rule)
|
||||||
|
- [ ] Plugging AC auto-switches to `veilor-performance`
|
||||||
|
|
||||||
|
## Hardening — services
|
||||||
|
|
||||||
|
- [ ] `systemctl is-active fail2ban` → active
|
||||||
|
- [ ] `systemctl is-active usbguard` → active
|
||||||
|
- [ ] `systemctl is-active auditd` → active
|
||||||
|
- [ ] `systemctl is-active firewalld` → active
|
||||||
|
- [ ] `systemctl is-active tuned` → active
|
||||||
|
- [ ] `systemctl is-active chronyd` → active
|
||||||
|
- [ ] `systemctl is-active sshd` → active
|
||||||
|
- [ ] `systemctl is-active cups` → inactive / not-found
|
||||||
|
- [ ] `systemctl is-active avahi-daemon` → inactive / not-found
|
||||||
|
- [ ] `systemctl is-active bluetooth` → inactive
|
||||||
|
- [ ] `systemctl is-active veilor-modules-lock` (after 30s) → active
|
||||||
|
|
||||||
|
## Hardening — kernel/sysctl
|
||||||
|
|
||||||
|
- [ ] `getenforce` → `Enforcing`
|
||||||
|
- [ ] `mokutil --sb-state` → `SecureBoot enabled`
|
||||||
|
- [ ] `sysctl kernel.yama.ptrace_scope` → `2`
|
||||||
|
- [ ] `sysctl kernel.kptr_restrict` → `2`
|
||||||
|
- [ ] `sysctl fs.suid_dumpable` → `0`
|
||||||
|
- [ ] `sysctl dev.tty.ldisc_autoload` → `0`
|
||||||
|
- [ ] `sysctl kernel.modules_disabled` (after 30s post graphical) → `1`
|
||||||
|
|
||||||
|
## Hardening — network
|
||||||
|
|
||||||
|
- [ ] `firewall-cmd --get-default-zone` → `drop`
|
||||||
|
- [ ] `firewall-cmd --zone=drop --list-services` → `ssh`
|
||||||
|
- [ ] `resolvectl status` shows DNSSEC + DoT, LLMNR off
|
||||||
|
- [ ] `chronyc sources -v` shows NTS-authenticated peers
|
||||||
|
|
||||||
|
## Hardening — SSH
|
||||||
|
|
||||||
|
- [ ] `sshd -T | grep -E 'permitrootlogin|passwordauth|allowusers|x11forwarding'`
|
||||||
|
shows: `permitrootlogin no`, `passwordauthentication no`,
|
||||||
|
`allowusers admin`, `x11forwarding no`
|
||||||
|
|
||||||
|
## Disk
|
||||||
|
|
||||||
|
- [ ] `lsblk -f` shows LUKS2 on the main partition
|
||||||
|
- [ ] `cryptsetup luksDump /dev/...` shows argon2id, aes-xts-plain64
|
||||||
|
- [ ] `swapon` shows `zram` device, no disk swap
|
||||||
|
|
||||||
|
## SELinux module
|
||||||
|
|
||||||
|
- [ ] `semodule -l | grep veilor-systemd` → present
|
||||||
|
- [ ] No SELinux denials in `ausearch -m AVC -ts boot` related to
|
||||||
|
`systemd_modules_load_t`
|
||||||
|
|
||||||
|
## USBGuard
|
||||||
|
|
||||||
|
- [ ] `systemctl status usbguard` → active
|
||||||
|
- [ ] `wc -l /etc/usbguard/rules.conf` → 0 (empty allowlist by design)
|
||||||
|
- [ ] After `sudo usbguard generate-policy > /etc/usbguard/rules.conf`
|
||||||
|
and restart, all currently-connected USB devices remain
|
||||||
|
functional
|
||||||
|
|
||||||
|
## Findings
|
||||||
|
|
||||||
|
Log issues and fixes here:
|
||||||
|
|
||||||
|
| Date | Item | Issue | Fix in kickstart? |
|
||||||
|
|------|------|-------|-------------------|
|
||||||
|
| | | | |
|
||||||
Loading…
Reference in a new issue