How Openship's Built-in Mail Server Handles DKIM, SPF, and DMARC
Openship automatically provisions DKIM keys, generates SPF and DMARC DNS records, and validates their publication to ensure authenticated email delivery.
The oblien/openship repository ships a production-ready mail server built on iRedMail and amavisd-new that eliminates manual DNS configuration. When you add a domain, Openship handles the complete email authentication stack—generating 2048-bit RSA keys for DKIM, creating restrictive SPF policies, and setting up DMARC reporting—without requiring you to manually edit zone files.
Three-Layer Authentication Architecture
Openship splits email authentication into three logical layers, each handled by distinct modules in the codebase.
1. Key Provisioning and Configuration
In packages/core/src/mail-server/routing/build-routes.ts (lines 109-118), the system executes amavisd genrsa to create a 2048-bit RSA key pair. The private key is written to /var/lib/dkim/<domain>.pem, while the public key is parsed and prepared for DNS publication. The configuration is then injected into Amavis via the 50-user configuration file using the dkim_key directive.
2. DNS Record Generation
The apps/api/src/modules/mail/mail-state.ts file (lines 203-267) defines the PersistedDnsRecord interface that stores three critical TXT records:
- DKIM:
dkim._domainkey.<domain>containing the public key - SPF:
<domain>withv=spf1 a mx -all - DMARC:
_dmarc.<domain>withv=DMARC1; p=reject; rua=mailto:postmaster@<domain>
These records are persisted in the mail_state database table and exposed through the application's API.
3. Administration and Validation
The administration layer validates that published DNS records match the generated values. In apps/api/src/modules/mail/admin/domain-dns.service.ts (lines 221-242), the provisionDomainDkim function orchestrates key generation. The dns-scan.service.ts (lines 295-327) periodically verifies that DKIM, SPF, and DMARC records are reachable and correctly formatted, warning users of truncated keys or missing entries.
The Domain Onboarding Flow
When you add a domain via the dashboard or CLI, Openship executes a four-step authentication pipeline:
- Domain Registration: Triggered by
openship mail add-domain <domain>or the dashboard UI, calling the domain-DNS service. - Key Generation:
mail.service.ts(lines 1020-1034) executesamavisd-new genrsa /var/lib/dkim/<domain>.pemand updates the Amavis configuration withdkim_key('<domain>', 'dkim', '<keyPath>'). - State Persistence: The system creates
PersistedDnsRecordentries for A, MX, SPF, DKIM, and DMARC records in themail_statetable (mail-state.ts, lines 960-1000). - DNS Validation: The DNS-scan service verifies that
dkim._domainkey.<domain>resolves correctly and that SPF and DMARC policies are publicly accessible before marking the domain as active.
Implementation Details
Here is how specific authentication mechanisms are implemented in the source code:
DKIM Signing
Outbound mail is DKIM-signed by Amavis with enable_dkim_signing = 1. The private key resides at /var/lib/dkim/<domain>.pem, and the public key is exposed via the TXT record dkim._domainkey.<domain>.
SPF Policy
The default SPF record (v=spf1 a mx -all) restricts sending to only the server's IP and MX records. This is generated in mail.service.ts (lines 960-962) and can be overridden if your infrastructure requires additional authorized senders.
DMARC Enforcement
Openship defaults to a strict p=reject policy, ensuring that receivers quarantine or reject mail failing authentication checks. The reporting URI (rua=mailto:postmaster@${domain}) aggregates forensic reports for monitoring.
Retrieving DNS Records via API and CLI
Once generated, you can access the records through multiple interfaces:
REST API
Query GET /mail/dns/:domain to receive a JSON payload containing a, mx, spf, dkim, and dmarc records. This endpoint is implemented in apps/api/src/modules/mail/mail.service.ts (lines 960-1000) and consumed by the dashboard at apps/dashboard/src/lib/api/mail.ts (line 153).
Command Line Interface
Use the CLI helper for quick copy-paste into your DNS provider:
openship mail dns
This command, implemented in apps/cli/src/commands/mail.ts (line 87), prints a markdown table with the exact TXT values required for your domain registrar.
Summary
- Openship automates DKIM key generation using
amavisd-newand stores private keys in/var/lib/dkim/. - The system creates three DNS records (DKIM, SPF, DMARC) and persists them in the
mail_statetable viaPersistedDnsRecord. - Validation services continuously scan public DNS to ensure records match the generated values before allowing mail flow.
- Records are accessible via REST API (
GET /mail/dns/:domain) and CLI (openship mail dns).
Frequently Asked Questions
Where are the DKIM private keys stored on the server?
The private keys are stored in /var/lib/dkim/<domain>.pem with restricted permissions. The path is registered in Amavis's 50-user configuration file using the dkim_key directive, enabling the mail transfer agent to sign outgoing messages automatically.
Can I customize the DMARC policy from the default reject setting?
Yes. While Openship defaults to v=DMARC1; p=reject; rua=mailto:postmaster@<domain> for maximum security, you can modify the DMARC record value in the mail_state table or override it directly in your DNS provider after the initial provisioning.
How does Openship validate that my DNS records are correct?
The dns-scan.service.ts module performs periodic lookups against public DNS resolvers to verify that TXT records for DKIM, SPF, and DMARC match the expected values stored in the database. If the DKIM key is truncated or a record is missing, the dashboard displays a validation warning.
What happens if I add a domain using the CLI instead of the dashboard?
Both interfaces trigger the same provisionDomainDkim workflow in domain-dns.service.ts. The CLI command openship mail add-domain <domain> invokes the API, which generates the keys, creates the DNS records, and persists them to mail_state identically to the dashboard workflow.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →