From 1d8de3365fa3eed5ac958b4a2dddfe925288ff73 Mon Sep 17 00:00:00 2001 From: Julien Gautier Date: Sun, 24 May 2026 19:59:50 +0200 Subject: [PATCH] docs(setup): make fail2ban opt-in in 70-hardening.sh Three branches now: (1) already running -> skip; (2) installed but stopped -> prompt to enable+start; (3) not installed -> prompt to install+enable+start. Each decline path logs `(user choice)` so a re-run doesn't surprise the dev with a different state. Reasoning: some corp environments already ship brute-force protection at the network layer (ACL / corp firewall / appliance). fail2ban on the host is then redundant and can be the wrong layer to debug from when a rule misfires. The other three hardening steps (UFW, sshd lockdown) were already prompt-gated; fail2ban was the odd one out. Table descriptors in the main doc + setup README updated to make each sub-step's prompt posture visible. --- docs/setup/01-dev-debian-vm-setup.md | 20 ++++++++++---------- docs/setup/README.md | 2 +- docs/setup/scripts/70-hardening.sh | 24 ++++++++++++++++++++---- 3 files changed, 31 insertions(+), 15 deletions(-) diff --git a/docs/setup/01-dev-debian-vm-setup.md b/docs/setup/01-dev-debian-vm-setup.md index 971b49b..2f41ffb 100644 --- a/docs/setup/01-dev-debian-vm-setup.md +++ b/docs/setup/01-dev-debian-vm-setup.md @@ -160,16 +160,16 @@ The `bootstrap.sh` script runs every numbered script (`10-…` → `80-…`) in What each script does: -| Script | Effect | Idempotent? | -| ---------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------- | -| [`10-base-packages.sh`](scripts/10-base-packages.sh) | `apt update` + install base packages (curl wget git build-essential ca-certificates gnupg software-properties-common) | ✅ | -| [`20-zsh.sh`](scripts/20-zsh.sh) | Install zsh + Oh My Zsh + Powerlevel10k + plugins (autosuggestions, syntax-highlighting). Patch `~/.zshrc` (theme + plugins + fzf hook) and `~/.bashrc` (exec zsh on interactive shells — `chsh` is blocked on the corp VM). | ✅ | -| [`30-cli-tools.sh`](scripts/30-cli-tools.sh) | Install `bat eza fd-find ripgrep fzf zoxide ncdu keychain` + `jq yq httpie make tree htop tmux direnv dnsutils unzip`. Aliases for the Debian-renamed binaries (`batcat`→`bat`, `fdfind`→`fd`). | ✅ | -| [`40-node.sh`](scripts/40-node.sh) | Install nvm. Install Node from the repo's `.nvmrc` (currently `24`). Enable corepack, pin pnpm to `package.json`'s `packageManager`. | ✅ | -| [`50-docker.sh`](scripts/50-docker.sh) | Install Docker CE + compose plugin from the Docker apt repo. Add the current user to the `docker` group. Enable `docker.service`. | ✅ | -| [`60-tuning.sh`](scripts/60-tuning.sh) | `fs.inotify.max_user_watches=524288` (Vite / Nx watch). Prompt for hostname (only if generic). Add a 4 GB swapfile if none. | ✅ | -| [`70-hardening.sh`](scripts/70-hardening.sh) | Best-effort UFW (allow SSH only by default), `unattended-upgrades` on security channel, `fail2ban`, sshd hardening (no root, no password). **Probes before applying** — skips what's already done. | ✅ | -| [`80-dotfiles.sh`](scripts/80-dotfiles.sh) | Symlink `notes/aliases.zsh` → `~/.oh-my-zsh/custom/aliases.zsh`. Generate `~/.gitconfig` from `notes/gitconfig.txt`, prompt for identity (name + email). | ✅ | +| Script | Effect | Idempotent? | +| ---------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------- | +| [`10-base-packages.sh`](scripts/10-base-packages.sh) | `apt update` + install base packages (curl wget git build-essential ca-certificates gnupg software-properties-common) | ✅ | +| [`20-zsh.sh`](scripts/20-zsh.sh) | Install zsh + Oh My Zsh + Powerlevel10k + plugins (autosuggestions, syntax-highlighting). Patch `~/.zshrc` (theme + plugins + fzf hook) and `~/.bashrc` (exec zsh on interactive shells — `chsh` is blocked on the corp VM). | ✅ | +| [`30-cli-tools.sh`](scripts/30-cli-tools.sh) | Install `bat eza fd-find ripgrep fzf zoxide ncdu keychain` + `jq yq httpie make tree htop tmux direnv dnsutils unzip`. Aliases for the Debian-renamed binaries (`batcat`→`bat`, `fdfind`→`fd`). | ✅ | +| [`40-node.sh`](scripts/40-node.sh) | Install nvm. Install Node from the repo's `.nvmrc` (currently `24`). Enable corepack, pin pnpm to `package.json`'s `packageManager`. | ✅ | +| [`50-docker.sh`](scripts/50-docker.sh) | Install Docker CE + compose plugin from the Docker apt repo. Add the current user to the `docker` group. Enable `docker.service`. | ✅ | +| [`60-tuning.sh`](scripts/60-tuning.sh) | `fs.inotify.max_user_watches=524288` (Vite / Nx watch). Prompt for hostname (only if generic). Add a 4 GB swapfile if none. | ✅ | +| [`70-hardening.sh`](scripts/70-hardening.sh) | Best-effort UFW (allow SSH only, prompts before enabling), `unattended-upgrades` on security channel, `fail2ban` (opt-in — prompts before installing), sshd hardening (no root, no password — prompts before applying). **Probes before applying** — skips what's already done. | ✅ | +| [`80-dotfiles.sh`](scripts/80-dotfiles.sh) | Symlink `notes/aliases.zsh` → `~/.oh-my-zsh/custom/aliases.zsh`. Generate `~/.gitconfig` from `notes/gitconfig.txt`, prompt for identity (name + email). | ✅ | After bootstrap, reload your shell to pick up the new PATH + zsh: diff --git a/docs/setup/README.md b/docs/setup/README.md index 04a58ff..efda7d4 100644 --- a/docs/setup/README.md +++ b/docs/setup/README.md @@ -23,7 +23,7 @@ Modular setup scripts called from the doc above. Each is idempotent, each can be | [`40-node.sh`](scripts/40-node.sh) | nvm + Node from the repo's `.nvmrc` + corepack pinned to `package.json#packageManager`'s pnpm version. | | [`50-docker.sh`](scripts/50-docker.sh) | Docker CE + compose plugin from docker.com apt repo + user added to `docker` group + service enabled at boot. | | [`60-tuning.sh`](scripts/60-tuning.sh) | inotify watches → 524288, optional 4 GB swapfile, optional hostname rename (only prompts if generic). | -| [`70-hardening.sh`](scripts/70-hardening.sh) | Best-effort UFW + unattended-upgrades + fail2ban + sshd hardening drop-in. Probes before applying — skips done items. | +| [`70-hardening.sh`](scripts/70-hardening.sh) | Best-effort UFW + unattended-upgrades + fail2ban (opt-in) + sshd hardening drop-in. Probes before applying — skips done items, prompts before each install. | | [`80-dotfiles.sh`](scripts/80-dotfiles.sh) | Symlinks `notes/aliases.zsh` → `~/.oh-my-zsh/custom/aliases.zsh`. Copies `notes/gitconfig.txt` to `~/.gitconfig` with prompted identity. | ## `systemd/` diff --git a/docs/setup/scripts/70-hardening.sh b/docs/setup/scripts/70-hardening.sh index 7135e7d..1b3aa03 100755 --- a/docs/setup/scripts/70-hardening.sh +++ b/docs/setup/scripts/70-hardening.sh @@ -47,13 +47,29 @@ EOF ok "unattended-upgrades enabled." fi -# ─── 3. fail2ban ─────────────────────────────────────────────────────── -apt_install fail2ban +# ─── 3. fail2ban (opt-in) ────────────────────────────────────────────── +# Some VMs already ship with infra-managed brute-force protection at the +# network layer (corp firewall, ACLs, etc.). In that case fail2ban on the +# host is redundant — and its presence can be the wrong layer to debug +# from when a rule misfires. Make it a deliberate choice instead of +# imposing it. if systemctl is-active fail2ban.service >/dev/null 2>&1; then skip "fail2ban already running." +elif dpkg -s fail2ban >/dev/null 2>&1; then + if confirm "fail2ban is installed but not running. Enable + start it now?"; then + sudo systemctl enable --now fail2ban.service + ok "fail2ban started + enabled at boot." + else + skip "fail2ban left stopped (user choice)." + fi else - sudo systemctl enable --now fail2ban.service - ok "fail2ban started + enabled at boot." + if confirm "Install + enable fail2ban (SSH brute-force protection)?"; then + apt_install fail2ban + sudo systemctl enable --now fail2ban.service + ok "fail2ban installed + started + enabled at boot." + else + skip "fail2ban not installed (user choice)." + fi fi # ─── 4. sshd hardening ────────────────────────────────────────────────