This repository has no description
4.3 kB
Markdown
at main

TODO: configure unattended-upgrades and fail2ban #

Both packages are installed by the Install base packages task in tasks/main.yaml, but neither is currently configured or running. The first two TODOs on the original list (devict deploy user and sshd_config hardening) are intentionally handled out-of-band by the cloud-init user_data in devserver/terraform/droplet.tf so they are baked into the droplet at first boot, before Ansible ever runs. The remaining two belong in this Ansible role.

References:


1. Configure unattended-upgrades #

Goal: automatically apply security patches (and notify/clean up appropriately) without manual intervention.

Files to manage #

  • /etc/apt/apt.conf.d/20auto-upgrades — controls when the timers run. Should contain:

    APT::Periodic::Update-Package-Lists "1";
    APT::Periodic::Download-Upgradeable-Packages "1";
    APT::Periodic::AutocleanInterval "7";
    APT::Periodic::Unattended-Upgrade "1";
    
  • /etc/apt/apt.conf.d/50unattended-upgrades — controls what gets upgraded and what side effects happen. On Debian 13 the defaults file is shipped by the package; we should override (or lineinfile patch) the Unattended-Upgrade::Origins-Pattern list so only Debian-Security:* is auto-applied. Other knobs to set:

    • Unattended-Upgrade::Remove-Unused-Kernel-Packages "true";
    • Unattended-Upgrade::Remove-Unused-Dependencies "true";
    • Unattended-Upgrade::Automatic-Reboot "false"; (we don't want surprise reboots; handle this explicitly in a maintenance window)
    • Unattended-Upgrade::Mail "root"; (or wire to an external address via a variable in defaults/main.yaml)

Services / timers to enable #

The package ships two systemd timers; ensure both are enabled and started:

  • apt-daily.timer
  • apt-daily-upgrade.timer

Implementation notes #

  • Use a template task so the config files are checked into the role (e.g. templates/20auto-upgrades.j2, templates/50unattended-upgrades.j2) rather than inlined as multi-line copy blobs.
  • Put the bantime / findtime / maxretry / mail knobs (or any other tunables) into defaults/main.yaml so they're easy to override per-environment.

2. Configure fail2ban with sane defaults #

Goal: ban repeat SSH offenders using the shipped sshd filter, and have the fail2ban service running on boot.

Files to manage #

  • /etc/fail2ban/jail.local — overrides jail.conf without conflicting on package upgrades. Minimal content:
    [DEFAULT]
    # 1 hour ban, triggered by 5 failures within a 10 minute window
    bantime  = 1h
    findtime = 10m
    maxretry = 5
    
    [sshd]
    enabled = true
    port    = ssh
    filter  = sshd
    backend = systemd
    
    Notes:
    • backend = systemd is the right choice on Debian 13 — auth logs go to journald, not /var/log/auth.log, so the default auto backend will not work without manual symlinks.
    • We could add a [recidive] jail to ban repeat offenders for longer periods. Nice-to-have, not required for the first cut.

Services to enable #

  • fail2ban.service (enable + started, idempotent)

Implementation notes #

  • Same pattern as above: a templates/jail.local.j2 with tunables pulled from defaults/main.yaml.
  • Add a handler that restarts fail2ban whenever jail.local changes, so updated rules take effect on ansible-playbook reruns (not just on first apply).
  • Verify on a real apply with fail2ban-client status sshd and journalctl -u fail2ban — first-cut configs often have wrong logpath / backend combinations and silently never ban anything.

Suggested ordering #

  1. Land unattended-upgrades first — it's a single config file plus two systemd enables, low risk, and easy to verify by running unattended-upgrade --dry-run --debug.
  2. Land fail2ban second — same shape (one config file + one service) but the backend = systemd detail is the easy thing to get wrong, so it's worth a second look.
  3. Once both are merged, delete this file and the now-resolved TODO: comments from tasks/main.yaml.