Skip to content

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:

sudo mb-config set spf.enabled true
sudo mb-config set spf.mode enforce  # or monitor

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:

sudo mb-config reload
sudo systemctl restart mb-filter

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:

sudo mb-spf-test sender@example.com 203.0.113.50

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:

sudo mb-spf-test sender@example.com 198.51.100.25

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):

example.com. IN TXT "v=spf1 mx a ip4:203.0.113.0/24 -all"

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:

dig +short TXT example.com | grep spf

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:

sudo mb-config set dkim.enabled true
sudo mb-config set dkim.mode enforce  # or monitor

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:

sudo mb-config reload
sudo systemctl restart mb-filter

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:

sudo mb-dkim-test /path/to/email.eml

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:

sudo mb-dkim-keygen --domain example.com --selector default

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:

sudo mb-config set dmarc.enabled true
sudo mb-config set dmarc.mode enforce  # or monitor

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:

sudo mb-config reload
sudo systemctl restart mb-filter

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:

sudo mb-dmarc-test /path/to/email.eml

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:

_dmarc.example.com. IN TXT "v=DMARC1; p=quarantine; rua=mailto:dmarc@example.com"
  • rua - Aggregate reports (daily summaries)
  • ruf - Forensic reports (individual failures)

Parse DMARC reports:

sudo mb-dmarc-parse-reports /var/mail/dmarc-reports/

Your Domain's DMARC Record

Basic DMARC record (monitor mode):

_dmarc.example.com. IN TXT "v=DMARC1; p=none; rua=mailto:dmarc@example.com"

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):

_dmarc.example.com. IN TXT "v=DMARC1; p=reject; pct=100; rua=mailto:dmarc@example.com"

Check your DMARC record:

dig +short TXT _dmarc.example.com

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:

sudo mb-auth-stats

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:

sudo mb-auth-stats --domain example.com

Real-time Monitoring

Watch authentication failures:

sudo tail -f /var/log/mailborder/auth.log | grep FAIL

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:

sudo mb-auth-analyze <message-id>

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:

dig +short TXT example.com | grep spf

If empty, domain has no SPF record.

Too many DNS lookups:

SPF record for example.com exceeds 10 DNS lookups

Solution: Flatten SPF record or use SPF macros.

IP not in SPF record:

sudo mb-spf-test sender@example.com <actual-sending-ip>

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

sudo mb-dkim-test /path/to/email.eml --verbose

DNS lookup failed:

dig +short TXT default._domainkey.example.com

Ensure DKIM public key published correctly.

Wrong selector:

Check DKIM-Signature header in email source:

DKIM-Signature: v=1; a=rsa-sha256; d=example.com; s=selector2;

Query correct selector:

dig +short TXT selector2._domainkey.example.com

DMARC Issues

DMARC record not found:

dig +short TXT _dmarc.example.com

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:

_dmarc.sub.example.com. IN TXT "v=DMARC1; p=quarantine"

Or use sp= in parent domain:

_dmarc.example.com. IN TXT "v=DMARC1; p=reject; sp=quarantine"

False Positives

Legitimate email failing authentication:

  1. Identify cause:

    sudo mb-auth-analyze <message-id>
    

  2. Temporary bypass:

    sudo mb-policy add auth-exception --from @legitimate-sender.com --skip-auth-check
    

  3. Contact sender to fix SPF/DKIM/DMARC

  4. Lower threshold:

    sudo mb-config set dmarc.fail_action quarantine  # instead of reject
    

Best Practices

Initial Deployment

  1. Start in monitor mode:

    sudo mb-config set spf.mode monitor
    sudo mb-config set dkim.mode monitor
    sudo mb-config set dmarc.mode monitor
    

  2. Enable logging:

    sudo mb-config set auth.detailed_logging true
    

  3. Review statistics for 2 weeks

    sudo mb-auth-stats
    

  4. Enable enforcement gradually:

    sudo mb-config set spf.mode enforce
    # Wait 1 week, monitor
    sudo mb-config set dkim.mode enforce
    # Wait 1 week, monitor
    sudo mb-config set dmarc.mode enforce
    

Your Domain Protection

  1. Publish SPF record (immediate)
  2. Implement DKIM signing (within 1 month)
  3. Publish DMARC at p=none (monitor for 1 month)
  4. Increase to p=quarantine (after fixing issues)
  5. Increase to p=reject (when 100% compliant)

Ongoing Maintenance

  1. Monitor daily authentication stats
  2. Review DMARC reports weekly
  3. Update SPF when adding mail servers
  4. Rotate DKIM keys annually
  5. 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