Skip to content

fix: operations hardening, second pass - #62

Merged
grojof merged 1 commit into
mainfrom
fix/ops-hardening-2
Oct 8, 2026
Merged

grojof merged 1 commit into
mainfrom
fix/ops-hardening-2

Conversation

@grojof

@grojof grojof commented Oct 8, 2026

Copy link
Copy Markdown
Owner

Second audit, group E: operations. Each fix is checked by running it against real fail2ban, nginx and logrotate.

  • fail2ban
    • Web and Odoo bans now use the tool's own ufw-odoo-web action, which blocks only ports 80 and 443.
      • On 0.11.2 (Ubuntu 22.04, Debian 11), fail2ban's own ufw[application="Nginx Full"] split the profile name, and ufw refused every ban. A negative control reproduces this.
      • Checked on 0.11.2, 1.0.2 and 1.1.0: each version's rendered ban runs against a stub ufw.
    • backend is set per jail. Where auth.log is absent (Debian 12), sshd reads the journal.
    • Durations and the ignore list are validated, and the admin's SSH address is proposed for the ignore list.
    • The regex test no longer writes the filter.
  • Staged writes (fail2ban, logrotate, nginx): every file is backed up first and each write is checked. A failed write or INT/TERM restores the previous configuration. All three failure modes were run.
  • Certificates
    • Let's Encrypt uses /etc/letsencrypt/live/<domain>/.
    • "Leave certificates untouched" keeps the files the current vhost names, including certbot's.
    • Missing files or a cancelled file pick write no HTTPS vhost.
    • nginx -t accepts these vhosts, also when written for an unknown nginx version.
  • Update configuration: sets the role password, checks the login with the new values, then writes and restarts. With a new password for a remote role it does not restart. The backup path is shown even on failure.
  • UFW: enabling allows SSH first, deleting the SSH rule asks for confirmation, and the PostgreSQL source must be a single address.
  • logrotate: the policy is staged and validated with logrotate -d, and the size is validated.

CI gets Python 3.9 and 3.11 to run the older fail2ban releases.

OpenSpec: 2026-10-08-ops-hardening-2.

Assisted-by: Claude

- fail2ban: web and Odoo bans use the tool's ufw-odoo-web action (ports
  80/443), which works on 0.11.2 (whose ufw[application=...] passes the
  profile name unquoted) as on 1.0.2 and 1.1.0; backend is set per jail,
  never in [DEFAULT]; sshd reads the journal where auth.log is absent
  (Debian 12), with python3-systemd; durations and ignore networks are
  validated, the admin's SSH address proposed; the regex test is
  read-only.
- Staged writes and the nginx switch back up every file first, check
  each write, and restore on failure or INT/TERM.
- Certificates: Let's Encrypt names /etc/letsencrypt/live/<domain>/,
  untouched names the files the vhost names; missing files or a
  cancelled pick write no HTTPS vhost.
- Update configuration: role password, login check with the new values,
  then write and restart; no restart for a remote new password; the
  backup path is printed even on failure.
- UFW: enabling allows SSH first, deleting an SSH rule asks, the
  PostgreSQL source is one address.
- logrotate policies are staged and validated with logrotate -d; the size
  threshold is validated.
- verify_ops_configs runs fail2ban 1.1.0, 1.0.2 and 0.11.2 (with a
  negative control), staged-write failures, logrotate rollback, and
  Let's Encrypt / unknown-version vhosts.
@grojof
grojof merged commit bf1f82c into main Oct 8, 2026
1 check passed
@grojof
grojof deleted the fix/ops-hardening-2 branch October 8, 2026 13:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant