Skip to content

root-ubuntu: size-capped log rotation, fail2ban, quiet networkd-dispatcher - #107

Merged
ralyodio merged 1 commit into
masterfrom
feat/logrotate-fail2ban
Sep 25, 2026
Merged

ralyodio merged 1 commit into
masterfrom
feat/logrotate-fail2ban

Conversation

@ralyodio

Copy link
Copy Markdown
Contributor

Three additions to root-ubuntu.sh. Each one is idempotent, has its own *_ENABLE-style switch, and only restarts a service when something actually changed. They run right after the OOM section.

configure_logrotate. On dev2 today, supabase-crawlproof.access.log was 1.2 GB and access.log 516 MB after two days, because stock nginx rotation is daily-only and logrotate runs once a day.

  • Writes the whole /etc/logrotate.d/nginx stanza with maxsize $LOGROTATE_MAXSIZE (200M) and rotate $LOGROTATE_KEEP (14). It can't be a drop-in: logrotate rejects a second stanza for the same files ("duplicate log entry").
  • logrotate.timer.d/hourly.conf, so maxsize is checked every hour. Small logs still rotate daily.
  • Runs logrotate -d /etc/logrotate.conf and warns loudly if any config on the box is broken, since one bad stanza stops all rotation.

configure_fail2ban.

  • Adds jail.d/zz-root-ubuntu.local on top of Ubuntu's defaults-debian.conf: sshd on $SSH_PORT, bantime.increment = true (1h, doubling up to 1w), findtime 10m, maxretry 5.
  • ignoreip is loopback, 100.64.0.0/10 (the tailnet), FAIL2BAN_IGNOREIP (your admin boxes, from server.conf or env; this public script hardcodes no IPs), and every peer connected to sshd while it runs, so it can't lock out the operator.
  • Reads auth.log where rsyslog writes one. Otherwise it uses backend = systemd and installs python3-systemd.
  • Warns if the service is up but the sshd jail didn't load.

quiet_networkd_dispatcher. With Docker, every container stop deletes a veth, and networkd-dispatcher then runs networkctl status on it after it's gone and logs ERROR:Failed to get interface … not found. That was 3,895 err-priority journal lines in 6h on dev2, which ThreatCrush shows as HIGH. dev2's dispatcher has no hook scripts at all, so it does nothing else. The function stops it only when no hook exists anywhere, and keeps it otherwise (NETWORKD_DISPATCHER_QUIET=0 to never touch it).

Tests. 14 new, in the file's existing style: functions extracted and run in real bash with a temp directory standing in for /etc, and systemctl/apt/dpkg stubbed.

  • The generated nginx stanza passes the real logrotate -d.
  • The second run changes nothing.
  • The knobs work, and ENABLE=0 really means off.
  • ignoreip has the expected order and is deduplicated, SSH peers included.
  • Journal fallback when there's no auth.log, SSH_PORT honoured, no restart when already correct.
  • The dispatcher is stopped with no hooks, kept with one, and untouched when absent.

Full suite 1,289/1,289. bash -n and shellcheck are clean on the new code.

🤖 Generated with Claude Code

…tcher

configure_logrotate: nginx logs rotate daily or past LOGROTATE_MAXSIZE
(200M), 14 kept, and logrotate.timer goes hourly so the cap is checked.
dev2 had a 1.2 GB and a 516 MB access log after two days.

configure_fail2ban: sshd jail layered on Ubuntu's defaults, bans double
on each repeat (1h up to 1w), ignoreip = loopback, the tailnet, the
operator's FAIL2BAN_IGNOREIP and whoever is connected over ssh while it
runs. Reads auth.log, or the journal on a box without rsyslog.

quiet_networkd_dispatcher: with Docker, every container stop makes the
dispatcher run networkctl on a veth that is already gone and log an
error: 3,895 in six hours on dev2. Stopped only when it has no hooks.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown

ThreatCrush Security Scan

25 finding(s)

HIGH/CRITICAL: 4 | MEDIUM: 12 | LOW: 9

Severity Rule Location
HIGH sh-remote-script-execution root-ubuntu.sh:3236
HIGH sh-remote-script-execution root-ubuntu.sh:3237
HIGH sh-remote-script-execution root-ubuntu.sh:5057
HIGH sh-remote-script-execution root-ubuntu.sh:5061
MEDIUM sql-template-interpolation dev2/dev2-site:754
MEDIUM sql-template-interpolation dev2/dev2-site:831
MEDIUM sh-remote-script-execution root-ubuntu.sh:5221
MEDIUM redos-nested-quantifier src/domain-free.ts:56
MEDIUM redos-nested-quantifier src/emoji.ts:167
MEDIUM redos-nested-quantifier src/icon.ts:166
MEDIUM redos-nested-quantifier src/mail.ts:1042
MEDIUM sql-template-interpolation src/users-dump.ts:487
MEDIUM sql-string-concatenation src/users-dump.ts:507
MEDIUM sql-template-interpolation src/users-dump.ts:540
MEDIUM sql-string-concatenation src/users-dump.ts:574
MEDIUM redos-nested-quantifier src/wcag.ts:556
LOW secret-generic-credential src/credentials.ts:36
LOW secret-generic-credential src/user-export.ts:632
LOW secret-generic-credential src/user-export.ts:638
LOW secret-generic-api-key test/credentials.test.ts:208
LOW secret-generic-credential test/mail.test.ts:141
LOW secret-generic-credential test/shorten.test.ts:36
LOW secret-database-url test/users-dump.test.ts:108
LOW secret-database-url test/users-dump.test.ts:119
LOW secret-database-url test/users-dump.test.ts:120

Snippets are redacted; ThreatCrush never prints matched credential material.

@ralyodio
ralyodio merged commit 9f4377d into master Sep 25, 2026
5 checks passed
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