Install strategy
How this repo behaves when a user clones it and runs it on a new machine. Intended for implementation engineers and people taking over the tool: what must work, what may fail, and how you can see afterwards what you got.
Philosophy — best-effort, opt-out per component
A fresh git clone + sudo bash bootstrap.sh should do what it can and
report what didn't work on the current distro + runtime. Components are
additive, not mutually required:
- Failure of an optional component (rkhunter, logrotate-systemd) is no reason to stop the installer.
- Failure of a required component (ClamAV scanner, signatures) is —
bootstrap.shreturns non-zero so an ops flow notices. - Every
install.shprints per step what worked and what didn't; it ends with aprint_summaryshowing the✓/✗status per component. check.shthen independently reports the actual runtime state, not the intended one — drift between "we installed X" and "X is running now" becomes visible.
Audit trail: every deviation from the expected picture should be resolvable
with check.sh output + a commit message in CHANGELOG.md. No silent skips, no
hidden state.
Components and their status
| Component | Status | Default source | What happens if it's missing? |
|---|---|---|---|
ClamAV (clamscan, clamd) |
Required | distro stock pkg | Installer aborts with exit 2 (critical function missing) |
ClamAV scan daemon (clamd@scan on Alma; clamav-daemon on Arch/Ubuntu) |
Required | distro stock | Installer enable_clamav_services fails visibly; check.sh reports ✗ |
ClamAV signatures via av-update.timer |
Required | repo-shipped unit | No fresh signatures → check.sh warning after 3+ days; cron mail on every run |
| rkhunter | Optional | distro stock (on Alma via EPEL) | Installer prints an explanation, sets rkhunter_ok=0, continues; check.sh skips rkhunter sections |
rkhunter property database (/var/lib/rkhunter/db/rkhunter.dat) |
Optional (requires rkhunter) | generated by rkhunter --propupd in rkhunter_init |
check.sh (as root) reports ✗ rkhunter database not found with a retry hint |
systemd timers (av-update, clamav-scan, rkhunter-check) |
Required if systemd | repo-shipped units | WSL without systemd: skip + WSL explanation; native without systemd: skip + manual commands |
Logrotate config (/etc/logrotate.d/workstation-security) |
Required if systemd | repo-shipped | Logs grow unbounded — visible in du, not in check.sh |
IR tooling (incident-token-revoke.sh, install-pm-cooldown.sh) |
Optional | repo-shipped | Not run during install; explicit user action |
Failure modes per component
ClamAV (required)
- Package unavailable → installer prints an error message +
exit 2. Audit: it is a non-conformity on any framework that requires AV. - Daemon package missing (clamd on Alma, clamav-daemon on Ubuntu) → same.
- freshclam signature download fails → installer logs
freshclam_safeoutput; signatures can be fetched later viasudo bash common/update.shorav-update.timer(04:00). - SELinux blocks clamscan on
/home(Alma) → installer setssetsebool -P antivirus_can_scan_system 1. Ifsetseboolis missing: warning, the scan would silently fail with "0 dirs / 0 files".
rkhunter (optional)
- Package unavailable (e.g. EPEL not enabled on Alma) → installer prints
the exact dnf/apt/pacman error (no
2>/dev/nullmute), setsrkhunter_ok=0, continues. rkhunter_initnon-zero exit → the wrapper aroundrkhunter --update && rkhunter --propupdlives incommon/install-base.sh::rkhunter_initand is defensive against rkhunter 1.4's deprecated-egrep quirk: both commands run underset +e, the return code is checked separately, and on non-zero the installer prints a retry hint (sudo rkhunter --propupd).- WSL →
rkhunter-check.shexplicitly skips (false positives on /proc and init); seethreat-model.mdfor the reasoning.
Systemd timers
- WSL without
[boot] systemd=true→install-timers.shskips cleanly with an opt-in instruction; manual scans/update viacommon/scan.shandcommon/update.shkeep working. - Native without systemd (rare) → same skip path, generic warning.
freshclam-daemon redundancy (historical)
The OS-stock clamav-freshclam.service (long-running signature-update daemon)
is not used by this repo — av-update.timer (04:00) is the only
signature-update mechanism. disable_freshclam_daemon in install-base.sh
disables it on installation. The re-enable risk per distro is in that
function's comment and in CHANGELOG 2026-05-24. Reason: two mechanisms race on
the freshclam log lock, and freshclam_safe cannot reliably restart the daemon
after a stop.
Per OS × runtime — expected end picture
✓ = works automatically. manual = installer prints an instruction, the user
runs one command. skip = deliberately off, see threat-model.
| Component | Alma 10 native | Arch native | Ubuntu native | WSL2 without systemd | WSL2 with systemd |
|---|---|---|---|---|---|
| ClamAV scanner + signatures | ✓ | ✓ | ✓ | ✓ | ✓ |
| ClamAV daemon | ✓ | ✓ | ✓ | manual | ✓ |
| systemd timers | ✓ | ✓ | ✓ | skip + opt-in hint | ✓ |
check.sh services/timers |
✓ | ✓ | ✓ | skip with explanation | ✓ |
| rkhunter pkg + init | ✓ (via EPEL) | ✓ | ✓ | ✓ (binary) | ✓ |
rkhunter-check.sh daily |
✓ | ✓ | ✓ | skip (deliberate) | skip (deliberate) |
| IR-tool clipboard/URL | ✓ | ✓ | ✓ | ✓ + Windows hints | ✓ + Windows hints |
How check.sh fits into this strategy
check.sh is the canonical runtime report, not the installer's
print_summary. Four statuses:
✓— component is running or fresh (audit evidence).!— deviation the user should resolve, but the system works (e.g. signatures 4 days old, rkhunter-db not readable without root). Counts in the final tally.✗— critical deviation (scan daemon dead, signatures entirely gone). Counts in the final tally.-— deliberately skipped (rkhunter on WSL, services on non-systemd).
The final block sums up every !/✗ so that a cron mail or audit screenshot
immediately shows what to do. The exit code equals the number of problems
(capped at 2 — bash exit codes above 125 have special meaning).
Manual fix-up after a partial install
Repeating bootstrap.sh is always safe — all installer paths are idempotent.
But for targeted fixes:
check.sh says |
What to do |
|---|---|
✗ <daemon> (inactive) |
sudo systemctl start <daemon>; check the journal for the crash reason |
! ClamAV signatures (N days old) |
sudo bash common/update.sh — or wait until 04:00 |
✗ ClamAV signatures not found |
sudo freshclam (in disabled-daemon mode); reinstall if that fails |
✗ rkhunter database not found |
sudo rkhunter --propupd — rebuild the property DB |
! rkhunter database not readable |
No action needed — only root can read this file; run check.sh with sudo |
✗ <timer> (inactive) |
sudo systemctl enable --now <timer>; check install-timers.sh output |
When in doubt: sudo bash bootstrap.sh again — idempotent, and it prints per
step what is already correct.
What this strategy does NOT attempt
- No forced alignment. If a component is genuinely unavailable on a distro
(e.g. a future Alma without EPEL rkhunter), we do not attempt a manual
tarball install or vendoring. The installer skips it and
check.shreports it — the user decides. - No retries on network flakes.
freshclamsometimes fails due to CDN blips; we rely on the daily timer for recovery. - No rollback. A failed install leaves the partial state behind.
common/uninstall.shexists to cleanly remove the repo's own units/logrotate config; OS packages remain unless the user removes them manually. - No "vendor your own clamav". If the distro-stock version is too old, that's a distro choice; we don't fork it.
Changing this strategy
A new component or a change in required/optional status comes together in four places:
- This doc — table + failure-mode paragraph.
CHANGELOG.md— entry with date and reason.install.shper OS — installer path for the new component.check.sh— runtime reporting (ws_ok/record_fail/ws_skip).
Forget one and you get drift between documentation and reality — exactly what this strategy tries to prevent.