Skip to content

✨(inbound) ARC relay-trust: inbound_auth "arc" + arc_gate#756

Closed
mosa-riel wants to merge 2 commits into
suitenumerique:mainfrom
mosacloud:feat/inbound-arc-trust
Closed

✨(inbound) ARC relay-trust: inbound_auth "arc" + arc_gate#756
mosa-riel wants to merge 2 commits into
suitenumerique:mainfrom
mosacloud:feat/inbound-arc-trust

Conversation

@mosa-riel

@mosa-riel mosa-riel commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

What

Add ARC-based sender trust for inbound mail, in SPAM_CONFIG:

  • inbound_auth: "arc" — verdict backend that reads dkim/dmarc only from an ARC-Authentication-Results sealed by a trusted sealer. Plaintext headers are never read.
  • arc_gate — enforce that mail is sealed by a trusted relay: off | spam | drop.

Why

With inbound_auth: authentication-results, dkim/dmarc come from the top-level Authentication-Results, trusted by Received-block position. A host delivering straight to the MDA can forge that header (and From:) and show as verified. When inbound is relayed by a provider that ARC-seals (a filtering relay in front of a public MX), the seal is an unforgeable anchor — verify it instead of trusting a plaintext header.

Config

{
  "inbound_auth": "arc",
  "trusted_arc_sealers": ["relay.example"],  // empty = any valid seal
  "arc_gate": "spam"                          // off | spam | drop
}
  • arc mode → verdict from the trusted sealer's sealed AAR; unsealed → unverified.
  • arc_gate → action when not trusted-sealed: spam (is_spam=True) / drop (discard).

Design choices

  • Sealer allowlist, not just "valid ARC". A valid chain only proves some domain sealed it; an attacker self-seals. The outermost sealer (highest i=) must be allowlisted. Empty list = any valid seal (weak; only safe when the MDA is IP-restricted to the relay).
  • No plaintext-header fallback in arc mode — falling back reopens the hole.
  • DNS/verify failure never spams or drops — can't-verify ≠ forged.
  • Gate runs first among spam steps so untrusted is authoritative.
  • Kept in SPAM_CONFIG beside inbound_auth, reusing per-domain overrides. (These keys are auth/trust, not spam — a split is possible but out of scope.)

Tests

core/mda/arc.py verifier, arc verdict mode, gate step — incl. a hermetic real-crypto case (synthetic seal + stub DNS). No new dependency (dkimpy already vendored). Config reference: docs/spam-config.md.

Follow-up (PR2)

  • arc_gate: quarantine — hold untrusted mail (recoverable, admin-visible) via a new InboundMessage state — a first-class "untrusted" bucket instead of reusing spam/drop.
  • Tighten dnsfail: separate transient DNS from malformed/hostile input.

Summary by CodeRabbit

  • New Features
    • Added ARC-based sender authentication for inbound messages.
    • Added trusted_arc_sealers configuration and arc_gate actions to allow, mark as spam, or drop based on verified ARC sealing.
    • Ensures forged or untrusted top-level authentication headers are ignored when ARC trust isn’t established.
  • Documentation
    • Expanded SPAM_CONFIG documentation with detailed environment-variable semantics, examples, and security guidance.
  • Tests
    • Added coverage for ARC sealer trust matching, ARC verdict derivation, and arc_gate pipeline behavior.

@coderabbitai

coderabbitai Bot commented Jul 20, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: d2055213-4f3a-4f67-9e6c-05f60dd83184

📥 Commits

Reviewing files that changed from the base of the PR and between c660f0f and a2ffa20.

📒 Files selected for processing (1)
  • src/backend/core/mda/inbound_auth.py

📝 Walkthrough

Walkthrough

ARC verification and trusted-sealer matching now feed cached inbound pipeline results for configurable spam/drop gating and ARC-based authentication. Configuration documentation and tests cover verification outcomes, verdicts, DNS failures, and gate actions.

Changes

ARC inbound trust and authentication

Layer / File(s) Summary
ARC verification and result contract
src/backend/core/mda/arc.py, src/backend/core/tests/mda/test_arc.py
Adds ARC chain verification, sealer allowlist matching, normalized results, failure handling, and unit/crypto verification coverage.
Pipeline gating and ARC authentication
src/backend/core/mda/inbound_pipeline.py, src/backend/core/mda/inbound_auth.py, src/backend/core/tests/mda/test_inbound_auth.py
Caches ARC results, applies off/spam/drop actions, and derives authentication outcomes from trusted ARC data.
SPAM_CONFIG documentation
src/backend/messages/settings.py, docs/spam-config.md
Documents ARC authentication, trusted sealers, gate actions, matching semantics, warnings, and configuration examples.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant InboundMessage
  participant InboundPipeline
  participant arc_result
  participant check_inbound_authentication
  InboundMessage->>InboundPipeline: raw inbound bytes
  InboundPipeline->>arc_result: verify ARC chain and trusted sealers
  arc_result-->>InboundPipeline: trusted, sealer, aar, dnsfail
  InboundPipeline->>InboundPipeline: apply arc_gate action
  InboundPipeline->>check_inbound_authentication: cached ARC result
  check_inbound_authentication-->>InboundPipeline: authentication verdict
Loading

Suggested reviewers: jbpenrath

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 31.25% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: ARC-based inbound auth with the new arc gate.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/backend/core/mda/arc.py`:
- Around line 31-34: Remove the result["dnsfail"] = True assignment from the
broad-exception handler in arc_verify so parser errors remain untrusted and
follow the normal drop/spam path; retain the warning and returned result
behavior. Update src/backend/core/tests/mda/test_arc.py lines 40-45 to expect
dnsfail=False for this error case.

In `@src/backend/core/mda/inbound_auth.py`:
- Around line 237-239: Update the trusted ARC sealer normalization comprehension
in the spam configuration parsing flow to filter or otherwise validate each item
as a string before calling strip(), while preserving the existing trimming,
trailing-period removal, lowercasing, and exclusion of empty values.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 226bf65e-4ead-4b1f-9f0a-16ad187c99bb

📥 Commits

Reviewing files that changed from the base of the PR and between f058ee4 and 5b602cd.

📒 Files selected for processing (7)
  • docs/spam-config.md
  • src/backend/core/mda/arc.py
  • src/backend/core/mda/inbound_auth.py
  • src/backend/core/mda/inbound_pipeline.py
  • src/backend/core/tests/mda/test_arc.py
  • src/backend/core/tests/mda/test_inbound_auth.py
  • src/backend/messages/settings.py

Comment thread src/backend/core/mda/arc.py
Comment thread src/backend/core/mda/inbound_auth.py Outdated
New "arc" inbound_auth mode derives dkim/dmarc only from an ARC-sealed
ARC-Authentication-Results whose outermost sealer is in
SPAM_CONFIG["trusted_arc_sealers"] (empty = any valid seal); plaintext
headers are never trusted.

New SPAM_CONFIG["arc_gate"] (off|spam|drop) marks or drops inbound mail
lacking a valid trusted seal, so a publicly-reachable mta-in effectively
accepts only mail sealed by a trusted relay. The chain is verified with
dkimpy; a verification/DNS failure never spams or drops.

core/mda/arc.py holds the shared verifier. Config reference in
docs/spam-config.md.
@mosa-riel
mosa-riel force-pushed the feat/inbound-arc-trust branch from 5b602cd to c660f0f Compare July 20, 2026 20:10

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/backend/core/mda/inbound_auth.py`:
- Around line 234-240: Update trusted_arc_sealers to normalize the configured
value before iterating: treat a single string as a one-item list and convert
other supported iterable configuration values to a list, while using an empty
list for non-iterable types such as integers or booleans. Preserve the existing
string filtering and domain normalization for each entry.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 8b2609bd-f549-465b-b6f9-06edb9b5d7b1

📥 Commits

Reviewing files that changed from the base of the PR and between 5b602cd and c660f0f.

📒 Files selected for processing (7)
  • docs/spam-config.md
  • src/backend/core/mda/arc.py
  • src/backend/core/mda/inbound_auth.py
  • src/backend/core/mda/inbound_pipeline.py
  • src/backend/core/tests/mda/test_arc.py
  • src/backend/core/tests/mda/test_inbound_auth.py
  • src/backend/messages/settings.py

Comment thread src/backend/core/mda/inbound_auth.py
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
@sylvinus

Copy link
Copy Markdown
Member

Thanks!! merged as cf0b70e

@sylvinus sylvinus closed this Jul 23, 2026
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.

2 participants