Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
6f2f4f5
The start-up credential probe runs off the event loop (BACKLOG #3164)
wshallwshall Oct 16, 2026
251080a
Directory role and disable notices name the directory, not an adminis…
wshallwshall Oct 16, 2026
c2337e6
TOTP enrolment follow-ups from PR 2080 (BACKLOG #3164 #3121 #3113)
wshallwshall Oct 16, 2026
d2c88da
Review round one on the D1 auth follow-ups (BACKLOG #3164 #3121 #3113)
wshallwshall Oct 16, 2026
1d5ff35
D1 round-two follow-ups: the enrolment refusal row survives any revok…
wshallwshall Oct 16, 2026
c67c178
A self-service session revoke that ends nothing writes one audit row …
wshallwshall Oct 16, 2026
c8fae10
OIDC step-up wave 1 follow-ups from engine PR 2078 (BACKLOG #3145)
wshallwshall Oct 16, 2026
7a6961a
The step-up header test reads the shared constant through auth_models…
wshallwshall Oct 16, 2026
6fcd43b
The API client's sign-in and queued elevations wait 30 seconds (BACKL…
wshallwshall Oct 16, 2026
aaa9f56
Simplify pass: one store-fault set, one step-up header name, one boun…
wshallwshall Oct 16, 2026
834697e
Review round one on the D2 auth follow-ups (BACKLOG #3260 #3145 #2950)
wshallwshall Oct 17, 2026
e6511df
The revocation-guard census lists the shared IdP opener wrapper as a …
wshallwshall Oct 17, 2026
2d1ddf1
A directory reconcile pass that newly grants Administrator raises adm…
wshallwshall Oct 17, 2026
f9df1af
UNKEYED reconcile follow-ups: referrals count the probes asked, an ou…
wshallwshall Oct 17, 2026
1027a1a
The reconcile-path store-fault set takes aiosqlite's closed-connectio…
wshallwshall Oct 17, 2026
7aae0c7
Simplify pass: the judged-nothing UNKEYED strikes are built only wher…
wshallwshall Oct 17, 2026
d7e3e9e
Review round one on the D3 reconcile follow-ups (BACKLOG #3141 #3170)
wshallwshall Oct 17, 2026
ed6efa2
Review round two: the docs stop over-claiming what an aborted pass do…
wshallwshall Oct 17, 2026
b1eccda
Merge origin/main into the b257 D1-D3 auth chain
wshallwshall Oct 17, 2026
71addac
Review round one on the b257-dchain merge: count and strike wording
wshallwshall Oct 17, 2026
1ca569a
Merge origin/main d8214ae80 into b257-dchain
wshallwshall Oct 17, 2026
3c8fdf6
Merge origin/main (064700f2d) into b257-dchain
wshallwshall Oct 17, 2026
edca1de
Merge origin/main (3f1f884d9) into b257-dchain
wshallwshall Oct 17, 2026
70f6c45
Bind the step-up floor marker into the refuse partial once
wshallwshall Oct 17, 2026
515d1c3
Merge origin/main into b257-dchain (fourth main merge)
wshallwshall Oct 17, 2026
9fd48a0
A self-revoke that loses the rotation race writes the miss row
wshallwshall Oct 17, 2026
1248224
Merge origin/main into b257-dchain (main 0ed70dcd4)
wshallwshall Oct 17, 2026
43abc1a
Merge origin/main into b257-dchain
wshallwshall Oct 18, 2026
30366c8
Merge remote-tracking branch 'origin/main' into up6
wshallwshall Oct 18, 2026
8d4d113
The reconcile grant race test's spy takes the watermark and returns t…
wshallwshall Oct 18, 2026
8a7fa2b
The reconcile grant race test reaches the grant check again
wshallwshall Oct 18, 2026
ee20db1
Merge origin/main 440d71aaf into b257-dchain (BACKLOG #3164 #3121 #31…
wshallwshall Oct 18, 2026
a4d2251
Merge review fixes: provision refusal says remove, doc limits stated …
wshallwshall Oct 18, 2026
a304763
Merge origin/main 032973b71 into b257-dchain (BACKLOG #3164 #3121 #31…
wshallwshall Oct 18, 2026
8843d5f
Merge main 501bfdcf5 into the batch 257 auth follow-ups (PR 2375)
wshallwshall Oct 18, 2026
2ec9197
Merge review fix: pin the unsealed-map step-up row, correct the seam …
wshallwshall Oct 18, 2026
cff00e1
Merge review fix 2: pin the floor marker on both role arms, rename, c…
wshallwshall Oct 18, 2026
fc325e9
Merge origin/main into b257-dchain (after PR 2428, BACKLOG #3070)
wshallwshall Oct 19, 2026
2b52200
Merge origin/main into b257-dchain (after PR 2431)
wshallwshall Oct 19, 2026
573fbd3
Merge review fix: name the paging refusal's count, detail and inverse
wshallwshall Oct 19, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion changelog.d/2434.security.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@
directory row with no `objectGUID` and no federated binding is now judged without a lookup: it
gets no roles written, and its sessions end at the strike threshold with the reason
`directory_object_id_missing`, as an absent account's do. It never moves the ADR 0195 hold. No
sign-in or step-up admits such a row, so a session it holds is anomalous.
sign-in or step-up admits such a row, so a session it holds is anomalous. The count on the
outage's `auth.ad_reconcile_skipped` audit row is now the `asked` field, the lookups the pass
sent, and it leaves such a row out. It was the `probed` field.
- **An empty password on the AD step-up re-bind is no longer counted as a wrong guess.** It is
refused before the directory is asked, charged to neither the account lockout nor the session's
re-proof budget, and audited on `auth.reauth` as `empty_password`. The caller sees a refused
Expand Down
6 changes: 5 additions & 1 deletion changelog.d/2610.security.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,8 @@
`user:<username>`, with `via` naming the route and `granted_by` the fixed marker `<directory>`.
A sign-in refused after the role was written still raises it, since a later sign-in gains nothing
to page on. A sign-in that gains only other roles, or by an account that already held
Administrator, raises nothing. (`vault BACKLOG #2610`)
Administrator, raises nothing. At least two cases still raise no alert. A sign-in that fails with
a server error after the role write loses the page, because the retry gains nothing. And an
account that never signs in again and holds no live session raises nothing, because the engine
never sees the grant. A directory reconcile pass that applies the grant first raises the alert
itself (vault BACKLOG #3141). See docs/SECURITY.md, *A directory grant*. (`vault BACKLOG #2610`)
8 changes: 8 additions & 0 deletions changelog.d/2950-sign-in-timeout.changed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
- **The API client waits longer for a sign-in's reply.** `EngineClient.login`, `reauth`,
`verify_mfa` and `confirm_mfa` now wait up to 30 seconds for the engine's reply, matching the
IDE's sign-in timeout, instead of the client's 5 second default. Each can wait in the engine's
per-account queue, and a request the client gave up on could still end or rotate the session it
held, leaving the client signed out (and, for `confirm_mfa`, without its recovery codes). Only the
read phase is raised: an unreachable engine is still reported within the client's own timeout.
The longer wait makes a lost reply rarer, not impossible, because the queue has no bound. Every
other request keeps the client's timeout. (`vault BACKLOG #2950`)
3 changes: 2 additions & 1 deletion changelog.d/3029.security.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,6 @@
`auth.logout` rows carry `detail.outcome`, `revoked` or `nothing_to_revoke`, and a console
sign-out's row names the owner of the session it ended instead of a NULL actor. Both sign-out
routes still answer the same either way. A self-revoke that
ends nothing answers 404 and writes no row. A sign-out or self-revoke that loses that race still
ends nothing answers 404 and writes no `auth.session_revoked` row, only the
`auth.session_revoke_missed` row an unknown id writes (vault BACKLOG #3260). A sign-out or self-revoke that loses that race still
leaves the session live under its new token; ending it there is not attempted. (`BACKLOG #3029`)
10 changes: 10 additions & 0 deletions changelog.d/3141.security.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
- **A directory reconcile pass that newly grants Administrator now raises the
`administrator_granted` operator alert.** The session reconciler re-diffs a signed-in directory
account's roles and writes them. When that write gave the account Administrator, it raised only
`ad_session_revoked`, which does not name the role, and the account's next sign-in then gained
nothing, so no `administrator_granted` alert ever fired. The pass now raises it right after the
role write, keyed `user:<username>`, with `via` set to `directory_reconcile` and `granted_by` the
fixed marker `<directory>`. An account that already held Administrator raises nothing. A pass the
mass-revoke breaker aborts writes no roles, so it raises nothing either. Like every
`administrator_granted` alert, it clears the account's `no_enabled_administrator` cooldown in the
engine process that raises it. (`vault BACKLOG #3141`)
8 changes: 8 additions & 0 deletions changelog.d/3145-oidc-step-up-follow-ups.security.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
- **A refused federated step-up records where it started.** An `auth.reauth` row for a step-up
refused after its flow was redeemed now carries `start_client` and `client_moved`, as the row of
a completed step-up does. Both come from the flow the start leg staged, never from the token. A
refusal before the `state` check carries neither. The refusal a step-up gate sends an `oidc`
session now says to repeat the action in the web console, because a bare `/ui/reauth` names no
action and returns to `/ui`. A store fault while that refusal reads the session's sign-in
mechanism now keeps the `403`, with the password wording, rather than answering `500`. See
[SECURITY.md](../docs/SECURITY.md). (`vault BACKLOG #3145`)
11 changes: 11 additions & 0 deletions changelog.d/3170.security.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
- **The session reconciler counts a referral over the lookups it sent, and strikes an id-less row
on every pass.** A directory row with no `objectGUID` is judged without a lookup, so no referral
can answer it. The referral's `auth.ad_reconcile_referred` audit row now carries the `asked`
field in place of `probed`, and that row, the referral's latched message and its
`ad_reconcile_aborted` alert count only the lookups the pass sent. The breaker's own alert still
counts every account it judged. Such a row is also struck now on a pass that is a directory
outage or aborted on referrals or probe faults, as on any other pass. Such a pass still revokes
nothing. Only a
pass that is not aborted can, once the row's strikes reach the threshold and the row is in that
pass's probe budget, so while every pass aborts its sessions last to their own expiry. Its revocation still counts toward the mass-revoke breaker, as before.
(`vault BACKLOG #3170`)
6 changes: 3 additions & 3 deletions changelog.d/3229.security.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@
`auth.no_enabled_administrator` audit row. With no enabled local Administrator holding a usable
password, `provision-admin` runs again at the host (BACKLOG #2711). An `administrator_granted` alert for an account clears that account's
`no_enabled_administrator` cooldown. It clears it only in the engine process that raises the
`administrator_granted` alert. A reconciler re-grant raises no `administrator_granted` alert, so
it leaves the cooldown in place. A directory disable or deletion of the last Administrator raises
no `no_enabled_administrator` alert. For at least these cases
`administrator_granted` alert. A reconciler re-grant raises `administrator_granted` too (vault
BACKLOG #3141), so it clears the cooldown the same way. A directory disable or deletion of the
last Administrator raises no `no_enabled_administrator` alert. For at least these cases
and others that raise no `no_enabled_administrator` alert, see docs/SECURITY.md, *A directory role
change can remove the last Administrator, and it alerts*. (`vault BACKLOG #3229`)
7 changes: 7 additions & 0 deletions changelog.d/3260-session-revoke-miss.security.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
- **A self-service revoke of one session that ends nothing is now audited.** `DELETE
/me/sessions/{session_id}` and the console's revoke button answer as before when the id names no
session or another account's. Each such miss now writes one `auth.session_revoke_missed` row. It
names the actor, the caller's address and the first 12 characters of the id, and nothing else, so
the row does not tell an unknown id from another account's any more than the answer does. Before,
the miss left no row of its own on either plane. The `auth.session_revoked` row now carries the
caller's address too. See [SECURITY.md](../docs/SECURITY.md). (`vault BACKLOG #3260`)
2 changes: 1 addition & 1 deletion docs/ASVS-L2-PHASE0-CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -468,7 +468,7 @@ is right.
| Off-box log-forward client key | 4, on the same condition as the API TLS server key | Loads the combined PEM in `_build_tls_context` ([`logging_setup.py`](../messagefoundry/logging_setup.py)). There is no passphrase setting | As for the API TLS server key. The file's permissions are the key's only protection, so setting them is the site's job |
| Native API client key | 4, on the same condition as the API TLS server key | Holds no copy. The client process loads it (`_build_verify_context` in [`apiclient/client.py`](../messagefoundry/apiclient/client.py)) | Every function in this row belongs to the operator of the client machine |
| Non-production self-signed key | Two keys, each 4, private authentication key, like the API TLS placeholder key. The `cert self-signed` key is a placeholder a site may serve. The load-test harness pair is also test keying material under section 8.1.4 | Mints both with `make_self_signed` in [`pki.py`](../messagefoundry/pki.py), the CLI key on request. The CLI writes its key through `_write_private_key`, which refuses to overwrite an existing file. The harness writes its pair with a plain write and `chmod`, as the lifecycle row above says. Nothing is audited | The `cert self-signed` key is a placeholder key, like the API TLS placeholder key: a site may serve it until it supplies a chain. Set its cryptoperiod under section 5.3.6 item 4 through `--days`, because the engine never renews it. Replace it with an operator-supplied chain, then delete both files. The load-test harness pair is test keying material, which section 8.1.4 says shall not be used operationally, and the engine does not stop that |
| TOTP shared secret | 3, symmetric authentication key: an HMAC key shared by exactly two parties | **Pre-activation (7.1):** `begin_mfa_enrollment` in [`auth/service.py`](../messagefoundry/auth/service.py) stores a fresh staged secret and audits `auth.mfa_enroll_started`. A later call replaces it. A staged secret nobody confirms stays in the column until then, or until a disable or reset; nothing moves it on by time. **Key confirmation, then active (7.1, transition 4):** `confirm_mfa_enrollment` turns MFA on only after a live code proves the user's copy, and audits `auth.mfa_enrolled`. **Out of use:** `disable_totp` sets the live column to NULL on a disable or an administrator reset, audited as `auth.mfa_disabled` or `auth.mfa_reset`. That removes the live copy. It is not destruction to the 8.3.4 bar (point 3), because older copies can survive. **Backup:** where a DR archive carries the store, it carries the secret; the lifecycle row above says when discarding the DEK erases those copies. Table 7 allows backup of this key type, and B.3.2 (its case 3) says a key that can be re-established need not be backed up. Re-enrolment re-establishes this one. **Cryptoperiod: none.** The secret is exempt from a calendar lifetime (owner ruling 2026-09-27, BACKLOG #1931). Section 5.3.6 item 3 suggests an originator-usage period of no more than two years. So this is a recorded departure from that suggestion, not an alignment with it | Keep the user's copy in one authenticator. After an administrator reset, tell the user to delete the old entry. Section 8.3.5 says the party revoking a key shared by two shall inform the other |
| TOTP shared secret | 3, symmetric authentication key: an HMAC key shared by exactly two parties | **Pre-activation (7.1):** `begin_mfa_enrollment` in [`auth/service.py`](../messagefoundry/auth/service.py) stores a fresh staged secret and audits `auth.mfa_enroll_started`. A later call replaces it. A staged secret nobody confirms stays in the column until then, or until a disable or reset; nothing moves it on by time. **Key confirmation, then active (7.1, transition 4):** `confirm_mfa_enrollment` turns MFA on only after a live code checks out against the secret it read, and audits `auth.mfa_enrolled`. The enable is a compare-and-set on the staged secret the code proved, so a secret re-staged in between is refused rather than enabled; [`SECURITY.md`](SECURITY.md), in its TOTP paragraph, states the refusals (BACKLOG #3025; this sentence corrected under vault BACKLOG #3113, and again once #3025 merged). **Out of use:** `disable_totp` sets the live column to NULL on a disable or an administrator reset, audited as `auth.mfa_disabled` or `auth.mfa_reset`. That removes the live copy. It is not destruction to the 8.3.4 bar (point 3), because older copies can survive. **Backup:** where a DR archive carries the store, it carries the secret; the lifecycle row above says when discarding the DEK erases those copies. Table 7 allows backup of this key type, and B.3.2 (its case 3) says a key that can be re-established need not be backed up. Re-enrolment re-establishes this one. **Cryptoperiod: none.** The secret is exempt from a calendar lifetime (owner ruling 2026-09-27, BACKLOG #1931). Section 5.3.6 item 3 suggests an originator-usage period of no more than two years. So this is a recorded departure from that suggestion, not an alignment with it | Keep the user's copy in one authenticator. After an administrator reset, tell the user to delete the old entry. Section 8.3.5 says the party revoking a key shared by two shall inform the other |
| Anonymizer re-identification salt | None of the 19 types in 5.1.1 fits. It keys a hash that pseudonymizes one dataset. The nearest fit is 5.1.2's *other secret information*, so this row borrows 5.3.7's rule for that and takes no cryptoperiod from 5.3.6 | Never mints or stores it. `Keyer` in [`anon/keying.py`](../messagefoundry/anon/keying.py) refuses a visibly weak salt, and the export reads it from the environment for one run | Mint it with a CSPRNG, one per dataset, and hold it with one person. Section 5.3.7 says other secret information shall not be kept longer than necessary, so its period is one dataset. Keep no backup unless the dataset must be regenerated, and destroy it when that need ends |
| Sealed-cache key | 6, symmetric data-encryption key. It protects the confidentiality and integrity of decoded cache values in the engine's own memory | Mints it per process (`_Sealer` in [`store/sealed_cache.py`](../messagefoundry/store/sealed_cache.py)) and uses it only to seal and open cache values, with each value bound to its cache key as associated data. Pre-activation and active are one step, and the key is destroyed with the process, so its cryptoperiod (5.3.6) is the process lifetime. There is no backup or archive of it (Table 7 is moot) | Protect the engine process's memory: the in-use residual in `docs/PHI.md` §3 and the host requirements in §10 apply, because this key sits in the same memory as the values it seals |

Expand Down
Loading
Loading