Add Debian 12 (Bookworm) support alongside Debian 11 (Bullseye) - #6
Open
nonholamilza wants to merge 29 commits into
Open
Add Debian 12 (Bookworm) support alongside Debian 11 (Bullseye)#6nonholamilza wants to merge 29 commits into
nonholamilza wants to merge 29 commits into
Conversation
Let's Encrypt stopped including OCSP responder URLs in their certificates starting in May 2025, and shut down their OCSP responders permanently on August 6, 2025. With current Let's Encrypt certificates, nginx emits a warning on every reload: "ssl_stapling" ignored, no OCSP responder URL in the certificate Since OCSP stapling no longer works with Let's Encrypt, this commit removes the related directives (ssl_stapling, ssl_stapling_verify, ssl_trusted_certificate, and the resolver line used only by stapling). Browsers will rely on CRLite/CRLSets for revocation checking instead. References: - https://letsencrypt.org/2024/12/05/ending-ocsp/ - https://bugs.debian.org/1126960 (related)
- Detect SpamAssassin's systemd unit name (spamd on Bookworm, spamassassin on Bullseye) and use it consistently for the service, the /etc/default/ config path, and the certbot restart hook - Enable spamassassin-maintenance.timer on Bookworm since CRON= in /etc/default/spamassassin no longer triggers nightly rule updates there; keep CRON=1 on Bullseye - Fix missing notify on the dovecot passwd template so IMAP auth changes actually get reloaded; add force_handlers so a later task failure can't silently discard already-queued reload/restart handlers - Generate a random bcrypt salt per password instead of a hostname-derived one that produced identical salts across accounts - Replace hardcoded author domains in postfix mydestination with postfix's own $mydomain - Generate SPF/DKIM/A/AAAA records for server_hostname and MX records for each mail domain in generate_txt_records.yml
Moved "Migrate to Debian 12 (Bookworm)" into Done
Owner
|
Great! Thanks for this. I got swamped by other work and I was worried of the looming Debian 11 EOL. I'll check this out and do some testing. |
Debug output was hardcoded to 'yes' with a TODO that was never picked up. The 'unknown user' lines generated on intermediate passdb/userdb lookups were counted as failed attempts by the fail2ban dovecot filter, causing legitimate clients to be banned in a loop.
- jail.local.j2 rewritten: explicit [DEFAULT], stock values pinned - backend = systemd on postfix/postfix-sasl/dovecot (f2b 1.0.2 refuses to start if a logpath is missing; mail.log absent without rsyslog) - AbuseIPDB key moved out of jail.local into action.d via abuseipdb.local.j2; action now unconditional - fail2ban.local.j2 for daemon-level config - vars.yml documents the new knobs
"Debian 11" to "Debian 12"
…notify Escalation now happens via [DEFAULT]'s bantime.increment (overalljails false), so repeat offenses grow that jail's own bantime instead of relying on [recidive] watching across jails - an IP hammering IMAP gets longer IMAP bans without ever losing submission or the web. [recidive] stays available behind fail2ban_recidive_enabled for optional cross-jail escalation, now off by default. Also fixes destemail, which was configured but never actually sent mail (action = %(action_)s has no mail action). Added a second notification address (fail2ban_notify_email, private_vars.template) and a plain sendmail action on every jail except sshd, whose brute-force noise isn't worth alerting on. Comments in jail.local.j2, fail2ban.local.j2 and vars.yml updated to match: they no longer describe recidive as the primary escalation mechanism. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JhuwdctE7u7XJTDgaMAU3R
fail2ban bans via iptables directly (banaction = iptables-multiport), inserting a jump rule into the INPUT chain. `ufw --force reset` wipes that chain, silently dropping the jump on every run and leaving active bans unenforced until fail2ban happens to restart on its own. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Mjp7GWM8Wn5A8snxbZZwQZ
Hardening: firewall, fail2ban and Debian 12 compatibility
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
With Debian 11 reaching end of life on August 31, 2026, I upgraded my production server to Bookworm and found a few incompatibilities. This PR fixes them while keeping Bullseye working, using conditionals on ansible_distribution_major_version.
SpamAssassin service renamed
On Bookworm the systemd unit is spamd.service instead of spamassassin.service, and the defaults file moved from /etc/default/spamassassin to /etc/default/spamd. Both are now resolved via a set_fact so the playbook works on either release. The apt package name is unchanged.
Nightly rule updates
The CRON=1 setting is no longer read on Bookworm — rule updates are handled by spamassassin-maintenance.timer, which ships disabled. The playbook now enables the timer on Bookworm and keeps CRON=1 on Bullseye.
Services not reloaded after config changes
This one was subtle. On a clean install, Dovecot was running from before /etc/dovecot/passwd was written, so every IMAP login failed. Postfix wasn't listening on 465/587 despite a correct master.cf, and nginx wasn't listening on 443. A manual systemctl reload fixed all three. The handlers have been adjusted so services pick up the configuration the playbook just wrote.
bcrypt salts were not random
Two different passwords in /etc/dovecot/passwd produced hashes sharing the same 29-character salt prefix. bcrypt salts must be randomly generated per hash.
mydestination contained unrelated domains
A fresh install had mydestination including myplatform.dataengineering.co.ke and localhost.dataengineering.co.ke. Now parameterized.
DNS records for the server hostname
generate_txt_records.yml only covered domains in mail_domains, but the server hostname also sends mail and receives the postmaster@ and dmarc-reports@ aliases, so it needs SPF and DKIM too. The DKIM key was already being generated — only the record export was missing. Added a companion task that also emits MX and A/AAAA records.
Testing
Verified on a clean Debian 12 VPS and on an in-place Bullseye → Bookworm upgrade of a production mail server hosting five domains. Mail delivery confirmed in both directions, with SPF, DKIM and DMARC all passing at Gmail.