// Field Guide — In the Field — entry 06

Keeping the Forest Safe

Read this before you deploy anywhere but your own laptop. The woods are lovely, dark, and deep — and your email metadata should stay in them.

Three layers of dashboard access control

SquatchMail checks these in order (SquatchMail.Web.Router + SquatchMail.Web.Plugs.Auth). Exactly one applies to any dashboard request:

Mount the dashboard inside your own authenticated pipeline and pass your own on_mount hooks, exactly like Oban Web or Phoenix LiveDashboard:

scope "/" do
  pipe_through [:browser, :require_admin_user]
  squatch_mail_dashboard "/squatch", on_mount: [MyAppWeb.AdminAuth]
end

This is the only layer that can express real authorization — roles, per-user scoping, SSO. The layers below are a safety net, not a substitute.

b) Built-in fallback: HTTP Basic Auth

For small deployments that want something stronger than wide open without standing up an admin pipeline:

config :squatch_mail,
  basic_auth: [
    username: "squatch",
    password: System.fetch_env!("SQUATCH_MAIL_PASSWORD")
  ]

c) Safe default: refuse

If neither (a) nor (b) applies, SquatchMail checks a runtime flag — deliberately not Mix.env(), which doesn’t exist in a release and would silently disable this exact safeguard in production — and renders a plain-language refusal page instead of dashboard data until access control is configured.

The gate stays shut until someone builds a fence.

The SNS webhook authenticates itself

The inbound webhook route is never covered by the dashboard layers above — it’s machine-to-machine and carries its own two locks:

  1. A URL token — the webhook path includes a per-source token.
  2. Hand-verified signaturesSquatchMail.SNS.MessageVerifier verifies every inbound SNS message’s SigV1/SigV2 signature against :public_key, with no third-party dependency. It validates the SigningCertURL host and scheme before ever fetching it, and caches parsed certificates in ETS for the certificate’s own validity window.

SquatchMail.SNS.Processor rejects any payload with a missing or invalid signature before it can touch your data. Every inbound payload — verified or not — is logged via SquatchMail.Tracker.log_webhook/1 for audit. Forged footprints get photographed, catalogued, and thrown out of court.

This is also why the raw-body reader matters: signatures are verified against the exact bytes SNS sent. Re-encoded JSON is not byte-identical, and tampered evidence is inadmissible.

Credentials at rest

  • credentials_mode: "ambient" (default) — AWS credentials come from the environment (IAM instance role, injected env vars). No keys touch your database. Use this.
  • credentials_mode: "static" — keys stored as plaintext columns on the sources table today. Encryption at rest is a known gap, tracked as a TODO in SquatchMail.Source. Until it lands, prefer ambient.

Found a security issue?

See SECURITY.md for how to report it responsibly. Please don’t open a public issue for vulnerabilities — some things really should stay hidden in the woods until they’re fixed.