SPF, DKIM, and DMARC¶
Configure email authentication to verify sender identity and prevent spoofing.
Overview¶
Email authentication protocols verify that messages are genuinely from the claimed sender:
- SPF (Sender Policy Framework) - Validates sending server IP
- DKIM (DomainKeys Identified Mail) - Cryptographic email signature
- DMARC (Domain-based Message Authentication) - Policy enforcement using SPF + DKIM
Why Email Authentication Matters¶
Without authentication: - Easy to spoof sender addresses - Phishing attacks appear legitimate - No way to verify message origin - Damaged domain reputation
With authentication: - Verified sender identity - Protection against spoofing - Increased deliverability - Email security enforcement
Authentication Flow¶
Incoming Email
↓
SPF Check
• Is sending IP authorized?
↓
DKIM Check
• Is signature valid?
↓
DMARC Check
• Do SPF/DKIM align with From header?
• What does domain policy say?
↓
Apply Policy (Allow / Quarantine / Reject)
SPF (Sender Policy Framework)¶
How SPF Works¶
SPF validates that email comes from an authorized IP address for the sender's domain.
Email from user@example.com
↓
Query DNS: TXT record for example.com
↓
Check if sending IP is in SPF record
↓
Result: Pass / Fail / SoftFail / Neutral
SPF Configuration¶
Enable SPF checking:
SPF policy actions:
# On SPF fail
sudo mb-config set spf.fail_action quarantine # or reject, tag
# On SPF softfail (~all)
sudo mb-config set spf.softfail_action tag
# On SPF neutral (?all)
sudo mb-config set spf.neutral_action pass
Reload configuration:
SPF Results¶
Possible SPF results:
| Result | Meaning | Default Action |
|---|---|---|
| Pass | Sending IP authorized | Allow |
| Fail | Sending IP not authorized | Quarantine/Reject |
| SoftFail | Probably not authorized (~all) | Tag |
| Neutral | No policy (?all) | Pass |
| None | No SPF record | Pass |
| TempError | DNS lookup failed | Pass (temporary) |
| PermError | SPF record malformed | Pass |
Testing SPF¶
Test SPF for incoming email:
Example output:
Testing SPF for example.com
Sender: sender@example.com
Sending IP: 203.0.113.50
SPF Record: "v=spf1 ip4:203.0.113.0/24 include:_spf.google.com -all"
Result: PASS
Explanation: Sending IP 203.0.113.50 is authorized (matches ip4:203.0.113.0/24)
Test SPF failure:
Example output:
Result: FAIL
Explanation: Sending IP 198.51.100.25 is not authorized
Policy: -all (reject unauthorized)
Action: QUARANTINE (per configuration)
SPF Scoring¶
Add to spam score based on SPF result:
sudo mb-config set spf.scoring_mode true
sudo mb-config set spf.fail_score 5.0 # SPF fail adds 5.0
sudo mb-config set spf.softfail_score 2.0 # SoftFail adds 2.0
sudo mb-config set spf.neutral_score 0.5 # Neutral adds 0.5
Your Domain's SPF Record¶
To publish SPF for your domain (configured in DNS):
Components: - v=spf1 - SPF version 1 - mx - Allow MX records for this domain - a - Allow A records for this domain - ip4:203.0.113.0/24 - Allow this IP range - -all - Reject all others (use ~all for soft fail)
Check your SPF record:
DKIM (DomainKeys Identified Mail)¶
How DKIM Works¶
DKIM adds a cryptographic signature to email headers that can be verified using public key in DNS.
Sending Server
↓
Calculate hash of email content
↓
Sign hash with private key
↓
Add DKIM-Signature header
↓
Receiving Server
↓
Retrieve public key from DNS
↓
Verify signature
↓
Result: Pass / Fail
DKIM Configuration¶
Enable DKIM verification:
DKIM policy actions:
# On signature verification fail
sudo mb-config set dkim.fail_action quarantine # or reject, tag
# On missing signature
sudo mb-config set dkim.missing_action pass
# On signature expired
sudo mb-config set dkim.expired_action tag
Apply configuration:
DKIM Results¶
Possible DKIM results:
| Result | Meaning | Default Action |
|---|---|---|
| Pass | Signature valid | Allow |
| Fail | Signature invalid or forged | Quarantine/Reject |
| None | No DKIM signature | Pass |
| TempError | DNS lookup failed | Pass (temporary) |
| PermError | Invalid signature format | Tag |
Testing DKIM¶
Test DKIM signature on email file:
Example output:
DKIM Signature Analysis
=======================
Signature 1:
Domain: example.com
Selector: default
Algorithm: rsa-sha256
Canonicalization: relaxed/relaxed
Public Key Retrieved: YES
Signature Verification: PASS
Headers Signed: from, to, subject, date, message-id
Body Hash: MATCH
Result: PASS
Test DKIM failure:
DKIM Signature Analysis
=======================
Signature 1:
Domain: example.com
Selector: default
Public Key Retrieved: YES
Signature Verification: FAIL
Reason: Message content has been modified after signing
Headers Signed: from, to, subject
Body Hash: MISMATCH
Result: FAIL
Action: QUARANTINE
DKIM Scoring¶
Add to spam score based on DKIM result:
sudo mb-config set dkim.scoring_mode true
sudo mb-config set dkim.fail_score 5.0 # Failed signature adds 5.0
sudo mb-config set dkim.missing_score 1.0 # No signature adds 1.0
DKIM Signing (Outbound)¶
If Mailborder is your outbound mail server, configure DKIM signing:
Generate DKIM keys:
Example output:
Generating 2048-bit RSA key pair...
Private key: /etc/mailborder/dkim/example.com.default.key
Public key: /etc/mailborder/dkim/example.com.default.txt
Add this TXT record to DNS:
default._domainkey.example.com. IN TXT "v=DKIM1; k=rsa; p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC..."
Enable signing:
sudo mb-config set dkim.signing_enabled true
sudo mb-config set dkim.signing_domain example.com
sudo mb-config set dkim.signing_selector default
Test signing:
echo "Test message" | mail -s "DKIM Test" user@gmail.com
# Check Gmail source for DKIM-Signature header
DKIM Key Rotation¶
Recommended: Rotate keys annually
# Generate new key with new selector
sudo mb-dkim-keygen --domain example.com --selector 2025
# Publish new DNS record (keep old one)
# Enable new selector
sudo mb-config set dkim.signing_selector 2025
# Wait 48 hours (for DNS propagation)
# Remove old DNS record
DMARC (Domain-based Message Authentication)¶
How DMARC Works¶
DMARC builds on SPF and DKIM, adding: - Alignment checks - Does SPF/DKIM domain match From header? - Policy - What to do on failure (reject, quarantine, none) - Reporting - Receive reports about authentication failures
Check SPF (passes?) ────┐
├──> Check SPF Alignment
Check DKIM (passes?) ───┘ (Does domain match From header?)
↓
At least one aligned pass?
YES → Apply DMARC policy (typically allow)
NO → Apply DMARC policy (reject/quarantine/none)
DMARC Configuration¶
Enable DMARC checking:
DMARC policy enforcement:
# How to handle DMARC failures
sudo mb-config set dmarc.fail_action honor # Honor sender's policy
# or: override, reject, quarantine, pass
# Override sender policy (use with caution)
sudo mb-config set dmarc.override_policy quarantine
Alignment modes:
# Strict: domains must match exactly
sudo mb-config set dmarc.spf_alignment strict
# Relaxed: subdomains accepted (default)
sudo mb-config set dmarc.dkim_alignment relaxed
Apply configuration:
DMARC Results¶
Possible DMARC results:
| Result | Meaning | Action |
|---|---|---|
| Pass | SPF or DKIM aligned and passed | Allow |
| Fail | No aligned pass | Apply sender's policy |
| None | No DMARC record | Pass |
| TempError | DNS lookup failed | Pass (temporary) |
| PermError | Invalid DMARC record | Pass |
DMARC Policy Actions (from sender's DNS):
| Policy | Meaning |
|---|---|
| p=none | Monitor only (no action) |
| p=quarantine | Flag as suspicious |
| p=reject | Reject message |
Testing DMARC¶
Test DMARC for incoming email:
Example output:
DMARC Policy Check
==================
From Header: sender@example.com
Domain: example.com
DMARC Record: "v=DMARC1; p=quarantine; rua=mailto:dmarc@example.com"
Policy: QUARANTINE
Percentage: 100%
SPF Check:
Result: PASS
Domain: example.com
Aligned: YES (exact match)
DKIM Check:
Result: PASS
Domain: example.com
Aligned: YES (exact match)
DMARC Result: PASS
Explanation: At least one authentication method passed and aligned
Action: ALLOW
Test DMARC failure:
DMARC Policy Check
==================
From Header: ceo@example.com (SPOOFED)
SPF Domain: spammer.com
DKIM Domain: (no signature)
SPF Check: PASS (but for spammer.com, not example.com)
Aligned: NO (domain mismatch)
DKIM Check: NONE
Aligned: NO
DMARC Result: FAIL
Sender Policy: p=reject
Action: REJECT MESSAGE
Reason: Phishing attempt - authentication failure
DMARC Reporting¶
Send aggregate reports:
If you operate mail servers, configure aggregate reporting:
sudo mb-config set dmarc.reporting_enabled true
sudo mb-config set dmarc.report_email dmarc-reports@example.com
Mailborder will send daily XML reports to domains requesting feedback.
Receive reports (for your domain):
Configure DNS to receive DMARC reports:
rua- Aggregate reports (daily summaries)ruf- Forensic reports (individual failures)
Parse DMARC reports:
Your Domain's DMARC Record¶
Basic DMARC record (monitor mode):
Recommended DMARC record:
_dmarc.example.com. IN TXT "v=DMARC1; p=quarantine; pct=100; rua=mailto:dmarc@example.com; ruf=mailto:dmarc@example.com; aspf=r; adkim=r; fo=1"
Components: - p=quarantine - Quarantine failed messages - pct=100 - Apply to 100% of messages - rua - Aggregate report email - ruf - Forensic report email - aspf=r - Relaxed SPF alignment - adkim=r - Relaxed DKIM alignment - fo=1 - Report on any failure
Strict policy (when ready):
Check your DMARC record:
Authentication Integration¶
Combined Authentication Scoring¶
Weight authentication results in spam scoring:
# SPF
sudo mb-config set spf.scoring_mode true
sudo mb-config set spf.fail_score 3.0
# DKIM
sudo mb-config set dkim.scoring_mode true
sudo mb-config set dkim.fail_score 3.0
# DMARC
sudo mb-config set dmarc.scoring_mode true
sudo mb-config set dmarc.fail_score 5.0
Example scoring:
Email with:
SPF: Fail (+3.0)
DKIM: None (+1.0)
DMARC: Fail (+5.0)
Content spam indicators (+2.5)
────────────────────
Total: 11.5 → QUARANTINE
Policy-Based Authentication¶
Require authentication for external senders:
sudo mb-policy create auth-required
sudo mb-policy rule add auth-required \
--condition "not from @example.com" \
--action "require spf pass or dkim pass" \
--action "quarantine dmarc fail"
Strict authentication for VIPs:
sudo mb-policy create vip-auth
sudo mb-policy rule add vip-auth \
--condition "recipient in ceo@,cfo@" \
--action "require spf pass" \
--action "require dkim pass" \
--action "require dmarc pass" \
--action "reject any-fail"
Whitelist Authenticated Senders¶
Skip spam checks for authenticated mail:
sudo mb-policy create authenticated-skip
sudo mb-policy rule add authenticated-skip \
--condition "spf pass" \
--condition "dkim pass" \
--condition "dmarc pass" \
--action "skip spam check" \
--action "lower virus threshold"
Monitoring Authentication¶
Statistics¶
View authentication statistics:
Example output:
Email Authentication Statistics (Last 30 days)
===============================================
SPF Results:
Pass: 42,345 (82.1%)
Fail: 3,456 (6.7%)
SoftFail: 1,234 (2.4%)
Neutral: 567 (1.1%)
None: 3,890 (7.5%)
Error: 123 (0.2%)
DKIM Results:
Pass: 38,921 (75.5%)
Fail: 2,345 (4.5%)
None: 10,349 (20.1%)
DMARC Results:
Pass: 35,678 (69.2%)
Fail: 5,432 (10.5%)
None: 10,505 (20.4%)
Actions Taken:
Rejected: 2,890 (5.6%)
Quarantined: 3,234 (6.3%)
Tagged: 1,567 (3.0%)
Per-domain statistics:
Real-time Monitoring¶
Watch authentication failures:
Example log entries:
2025-01-13 14:23:45 [SPF-FAIL] from=spoof@example.com ip=198.51.100.25 result=fail action=quarantine
2025-01-13 14:24:12 [DKIM-FAIL] from=phish@bank.com domain=bank.com reason=signature-invalid action=reject
2025-01-13 14:25:03 [DMARC-FAIL] from=ceo@example.com spf=fail dkim=none policy=reject action=reject
Forensic Analysis¶
Investigate authentication failure:
Example output:
Message ID: <abc123@example.com>
From: spoofed@example.com
Subject: Urgent Wire Transfer
SPF Analysis:
Sending IP: 198.51.100.25
SPF Record: v=spf1 ip4:203.0.113.0/24 -all
Result: FAIL (IP not authorized)
Expected IP: 203.0.113.0/24
DKIM Analysis:
Signature: NONE
Expected: default._domainkey.example.com
DMARC Analysis:
Policy: p=reject
Result: FAIL (no aligned authentication)
Verdict: PHISHING ATTEMPT
Action: REJECTED
Troubleshooting¶
SPF Issues¶
SPF lookup failing:
If empty, domain has no SPF record.
Too many DNS lookups:
Solution: Flatten SPF record or use SPF macros.
IP not in SPF record:
Add IP to domain's SPF record.
DKIM Issues¶
DKIM signature invalid:
Check if: 1. Message content modified in transit 2. Public key changed (key rotation) 3. Signature expired
DNS lookup failed:
Ensure DKIM public key published correctly.
Wrong selector:
Check DKIM-Signature header in email source:
Query correct selector:
DMARC Issues¶
DMARC record not found:
DMARC policy too strict:
If legitimate email is rejected: 1. Check SPF and DKIM setup 2. Verify alignment 3. Consider relaxed alignment (aspf=r, adkim=r) 4. Temporarily use p=none for monitoring
Subdomain policy:
Or use sp= in parent domain:
False Positives¶
Legitimate email failing authentication:
-
Identify cause:
-
Temporary bypass:
-
Contact sender to fix SPF/DKIM/DMARC
-
Lower threshold:
Best Practices¶
Initial Deployment¶
-
Start in monitor mode:
-
Enable logging:
-
Review statistics for 2 weeks
-
Enable enforcement gradually:
Your Domain Protection¶
- Publish SPF record (immediate)
- Implement DKIM signing (within 1 month)
- Publish DMARC at p=none (monitor for 1 month)
- Increase to p=quarantine (after fixing issues)
- Increase to p=reject (when 100% compliant)
Ongoing Maintenance¶
- Monitor daily authentication stats
- Review DMARC reports weekly
- Update SPF when adding mail servers
- Rotate DKIM keys annually
- Keep aligned records in sync
Policy Recommendations¶
Minimum (all organizations):
sudo mb-config set spf.enabled true
sudo mb-config set dkim.enabled true
sudo mb-config set dmarc.enabled true
Recommended (most organizations):
sudo mb-config set spf.fail_action quarantine
sudo mb-config set dkim.fail_action quarantine
sudo mb-config set dmarc.fail_action honor
Strict (high-security environments):
sudo mb-config set spf.fail_action reject
sudo mb-config set dkim.fail_action reject
sudo mb-config set dmarc.fail_action reject
sudo mb-config set dmarc.override_policy reject
See Also¶
- Spam Detection - Spam filtering
- Policy Enforcement - Email policies
- Email Flow - Processing pipeline
- Whitelist/Blacklist - Sender filtering