Commit Graph
2 Commits
Author SHA1 Message Date
kevinveenbirkenbachandClaude Opus 5.5 7bb0383e38 fix(web-app-stalwart): drop a replaced bot app password only after the load
The recover drill between the two CI passes restores /var/lib/infinito/secrets
with rsync --delete, so PASS 2 starts with an empty token store and the role
replaces the app password of every bot. It destroyed the old one at once,
while alertmanager, OpenWebUI and Baserow, rendered in PASS 1, kept sending
with it until the preload rendered them again 15 to 25 minutes later.
Stalwart counts every refused login against the client's address, which for
host-originated and cross-bridge clients is the Docker gateway 192.168.200.1.
After 100 refusals it banned that address for good, openresty's upstream,
alertmanager and the msmtp health check had their connections reset, and
Baserow#0 of #869 failed with exit 76. The rescue artifact shows 31 of those
refusals, all from alertmanager; Stalwart's own log, which names the account
behind the ban, was not collected.

A replaced app password is now renamed to
STALWART_BOT_APP_PASSWORD_RETIRED_DESCRIPTION once the new one is stored, and
destroyed in the service-loader post-load flush next to the deferred OIDC
restart, after the preload has rendered every consumer with the new one. Each
deploy also collects retired passwords an earlier deploy left behind, so a
deploy that stops before the flush neither replaces the password a second
time nor drops the one it handed out.

Verified: on Stalwart v0.16.25, 100 refused logins from one container address
banned it permanently (BlockedIp, reason authFailure, expiresAt null) and new
connections from it were reset, as in the CI log, and an app password's
description can be updated in place. A playbook running the changed task
files with JMAP stubbed (ansible-core 2.21.4) shows that a PASS 2 replacement
destroys the old ids only in the flush; that a deploy failing after the
Stalwart preload destroys nothing, while the next one mints nothing and
destroys only the retired ids; and that outside the service loader each bot's
old password is destroyed right after its new one is stored.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-13 15:09:39 +02:00
kevinveenbirkenbachandClaude Opus 5.5 08ef66d8de fix(web-app-stalwart): authenticate bots with an app password under SSO
Under SSO the role tried to let applications relay without SMTP AUTH by
widening the relay rule to the private networks. The rule compared the
peer address to the text of a CIDR, which never matches, so an
application could reach local mailboxes but got "550 5.1.2 Relay not
allowed" for every external recipient. Repairing the comparison would
have opened a relay, because published ports, Tor and the swarm mesh all
hand outside clients an address inside those networks.

Stalwart checks app passwords before it asks the external directory, so
a bot can authenticate while the directory is OIDC. With SSO enabled the
role now creates one app password per bot, acting for that account as
the recovery admin, and stores it in the token store the SMTP password
is already read from. A bot gets a new one when its entry is deleted in
Stalwart or its token is removed.

The relay rule is back to authenticated sessions only. The lookup loses
its SSO relay branch and the role its submission_via_relay flag, so an
SSO deployment submits on 465 with AUTH like every other one.

The role's CLI test now fails when an unauthenticated session is given
an external recipient, or when the bot credential is refused one.

Closes #793

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-08 08:57:22 +02:00