DKIM Signing Configuration Guide - Linux Installation
Overview
Configure DKIM signing for Virtru Gateway to maintain email authenticity after encrypt/decrypt operations.
Why it's required: The gateway modifies message bodies (encryption/decryption), which breaks original DKIM signatures. Re-signing prevents deliverability issues.
Prerequisites
- ✅ Virtru Gateway deployed on Linux via Docker
- ✅ SSH/terminal access to the gateway server
- ✅ DNS management access
- ✅ Root or sudo access
Note
This guide uses standard installation paths (/var/virtru/vg/). If you've customized your installation paths, adjust commands accordingly.
Multiple Domain Support
The DKIM key generation script produces keys for one domain at a time, but the Virtru Gateway supports DKIM signing for multiple sending domains simultaneously via the GATEWAY_DKIM_DOMAINS environment variable. See Step 1b for instructions on configuring additional domains.
Step 1: Generate DKIM Keys
Generate a 2048-bit RSA key pair for DKIM signing using OpenSSL.
Navigate to DKIM Directory
cd /var/virtru/vg/dkim
Generate Keys
Run these commands:
# Set your domain (must match GATEWAY_ORGANIZATION_DOMAIN in .env) gwDomain="yourdomain.com" gwDkimSelector="gw" dkimPath="/var/virtru/vg/dkim" # Generate private key (2048-bit) openssl genrsa -out $dkimPath/$gwDkimSelector._domainkey.$gwDomain.pem 2048 # Generate public key from private key openssl rsa -in $dkimPath/$gwDkimSelector._domainkey.$gwDomain.pem \ -out $dkimPath/$gwDkimSelector._domainkey.$gwDomain-public.pem \ -pubout -outform PEM
Expected output:
Generating RSA private key, 2048 bit long modulus .......+++ ........+++ e is 65537 (0x10001) writing RSA key
View Generated Keys
cat gw._domainkey.yourdomain.com.pem cat gw._domainkey.yourdomain.com-public.pem
Extract DNS TXT Record Value
You'll need to convert the public key into DNS TXT record format (without PEM headers).
Manual Extraction:
- Open the public key file in a text editor
nano gw._domainkey.yourdomain.com-public.pem
-
Copy only the key content (exclude the header/footer lines):
- Skip:
-----BEGIN PUBLIC KEY----- - Copy: The base64 content (multiple lines)
- Skip:
-----END PUBLIC KEY-----
- Skip:
- Remove all line breaks to create one continuous string
-
Prepend with DKIM tags:
v=DKIM1; k=rsa; p=
Example:
Original public key file:
-----BEGIN PUBLIC KEY----- MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1234567890abcdefghij klmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890abcdefghijklm nopqrstuvwxyz== -----END PUBLIC KEY-----
DNS TXT record value (for Step 5):
v=DKIM1; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890abcdefghijklmnopqrstuvwxyz==
Command-Line Extraction (Optional):
echo -n "v=DKIM1; k=rsa; p=" && grep -v "^-----" gw._domainkey.yourdomain.com-public.pem | tr -d '\n' && echo ""
Save Keys for Next Steps
You'll need the DNS TXT value for Step 5 (DNS configuration).
Security Notes
- Never commit private keys to version control
- Store private keys securely with restricted access
- Limit access to
/var/virtru/vg/dkim/directory to authorized personnel only - You can safely share/publish the public key and DNS TXT value
Tip
You can integrate these OpenSSL commands into your own automation, scripts, or configuration management tools as needed for your workflow.
Step 1b (Optional): Configure Additional Sending Domains
Security Disclaimer
Reusing the same DKIM key pair across multiple sending domains is optional and is one valid approach to multi-domain configuration. However, we strongly encourage you to discuss this decision internally with your security team before proceeding. Considerations such as key rotation schedules, blast radius in the event of key compromise, and organizational security standards may influence whether a shared key or individual per-domain keys are more appropriate for your environment. Virtru recommends following your organization's existing security policies.
Formatting GATEWAY_DKIM_DOMAINS for Multiple Domains
When configuring multiple domains, the GATEWAY_DKIM_DOMAINS variable accepts a comma-separated list of domainkey entries with no spaces between values. Each entry must follow the format <selector>._domainkey.<domain>. Incorrect formatting (such as spaces after commas, missing selectors, or extra characters) may cause DKIM signing to fail silently for one or more domains. Always verify your entry looks exactly like the example below before saving:
# Correct GATEWAY_DKIM_DOMAINS=gw._domainkey.domain1.com,gw._domainkey.domain2.com,gw._domainkey.domain3.com # Incorrect - spaces after commas GATEWAY_DKIM_DOMAINS=gw._domainkey.domain1.com, gw._domainkey.domain2.com, gw._domainkey.domain3.com # Incorrect - missing selector GATEWAY_DKIM_DOMAINS=domain1.com,domain2.com
If your mail environment has multiple sending domains that need to route through the same Virtru Gateway instance (for example, domain2.com and domain3.com in addition to your primary domain), you can reuse the same DKIM key pair across all of them rather than generating a new key pair for each domain.
Copy Key Files for Each Additional Domain
For each additional domain, create copies of the key files generated in Step 1 using the new domain name:
# Set your additional domain additionalDomain="domain2.com" gwDkimSelector="gw" dkimPath="/var/virtru/vg/dkim" # Copy the private key cp $dkimPath/$gwDkimSelector._domainkey.yourdomain.com.pem \ $dkimPath/$gwDkimSelector._domainkey.$additionalDomain.pem # Copy the public key cp $dkimPath/$gwDkimSelector._domainkey.yourdomain.com-public.pem \ $dkimPath/$gwDkimSelector._domainkey.$additionalDomain-public.pem
Repeat this for each additional domain. Your DKIM directory should then look like:
gw._domainkey.domain1.com.pem gw._domainkey.domain1.com-public.pem gw._domainkey.domain2.com.pem gw._domainkey.domain2.com-public.pem gw._domainkey.domain3.com.pem gw._domainkey.domain3.com-public.pem
Set Permissions for New Key Files
chmod 644 /var/virtru/vg/dkim/gw._domainkey*
Publish DNS TXT Records for Each Domain
Since all domains share the same public key, publish the same DNS TXT record value (extracted in Step 1) for each domain, using the appropriate domain name in the record host/name field:
-
gw._domainkey.domain1.com→ same public key value -
gw._domainkey.domain2.com→ same public key value -
gw._domainkey.domain3.com→ same public key value
Important: Order of Operations
We strongly recommend publishing each domain's DNS TXT record before adding that domain to GATEWAY_DKIM_DOMAINS in Step 2. This ensures that from the moment the Gateway begins signing for a domain, receiving mail servers can already look up and verify the signature. Both steps should ideally be completed on the same day per domain. For any domains whose DNS records are not yet ready, simply omit them from GATEWAY_DKIM_DOMAINS until they are — this will not affect other domains that are already configured correctly.
Step 2: Update Gateway Environment File
Locate Your Environment File
Environment files are located at:
/var/virtru/vg/env/<container-name>.env
Container Naming Convention
The Virtru Gateway install script uses the following naming convention for containers and their corresponding environment files:
| Topology | Mode | Container Name | Environment File |
|---|---|---|---|
| Outbound | Encrypt | oe-<port> |
oe-<port>.env |
| Outbound | Decrypt | od-<port> |
od-<port>.env |
| Outbound | DLP | dlp-out-<port> |
dlp-out-<port>.env |
| Inbound | Encrypt | ie-<port> |
ie-<port>.env |
| Inbound | Decrypt | id-<port> |
id-<port>.env |
| Inbound | DLP | dlp-in-<port> |
dlp-in-<port>.env |
Example: An outbound encrypt gateway on port 9001 would have container name oe-9001 and environment file /var/virtru/vg/env/oe-9001.env.
Edit Environment File
# Example for outbound encrypt on port 9001 sudo nano /var/virtru/vg/env/oe-9001.env
Enable DKIM Signing
Find the GATEWAY_DKIM_DOMAINS line (usually commented out) and uncomment/update it:
Before:
# GATEWAY_DKIM_DOMAINS=gw._domainkey.example.com
After (single domain):
GATEWAY_DKIM_DOMAINS=gw._domainkey.yourdomain.com
After (multiple domains):
GATEWAY_DKIM_DOMAINS=gw._domainkey.domain1.com,gw._domainkey.domain2.com,gw._domainkey.domain3.com
Use a comma-separated list with no spaces to enable DKIM signing for multiple sending domains simultaneously. Only include domains whose DNS TXT records have already been published and propagated.
Important:
- Replace domain values with your actual sending domains
- Selector is
gwby default (matches the key filenames from Step 1) - Format:
<selector>._domainkey.<domain>
Save and Exit
# In nano: press Ctrl+X, then Y to confirm, then Enter
Step 3 (Optional): Configure Inbound FROM Address Rewrite
Note
This step is only required if running the gateway in inbound topology. Skip this step if using outbound topology.
What it does: Enables FROM address rewriting for inbound mail to support DKIM signing. When enabled, the gateway can properly sign inbound messages.
Edit Environment File
# Open your inbound gateway environment file sudo nano /var/virtru/vg/env/<inbound-container-name>.env
Enable FROM Address Rewriting
Find the GATEWAY_REPLACEMENT_FROM_ENABLED setting and ensure it's set to 1:
# Inbound FROM address rewrite. # Enable or disable from address rewriting (inbound topology only). This feature allows the Virtru Gateway to support DKIM. # Required: No # Default: 1 # Values: # 1 - Enabled # 0 - Disabled GATEWAY_REPLACEMENT_FROM_ENABLED=1
Values:
-
1= Enabled (default, recommended for DKIM support) -
0= Disabled
Save and Exit
# In nano: press Ctrl+X, then Y to confirm, then Enter
Step 4: Set File Permissions
DKIM key files must be readable by the Docker container.
Set Permissions
Option 1: Set permissions for all DKIM files in directory
cd /var/virtru/vg/dkim chmod 644 gw._domainkey*
Option 2: Set permissions individually
chmod 644 /var/virtru/vg/dkim/gw._domainkey.yourdomain.com.pem chmod 644 /var/virtru/vg/dkim/gw._domainkey.yourdomain.com-public.pem
Verify Permissions
ls -la /var/virtru/vg/dkim/
Expected output:
-rw-r--r-- 1 root root 1675 Jan 15 10:30 gw._domainkey.domain1.com.pem -rw-r--r-- 1 root root 451 Jan 15 10:30 gw._domainkey.domain1.com-public.pem -rw-r--r-- 1 root root 1675 Jan 15 10:30 gw._domainkey.domain2.com.pem -rw-r--r-- 1 root root 451 Jan 15 10:30 gw._domainkey.domain2.com-public.pem
Step 5: Publish DNS Record
Create a TXT record in your DNS provider:
Record Details:
-
Type:
TXT -
Name/Host:
gw._domainkey.yourdomain.com -
Value:
v=DKIM1; k=rsa; p=MIIBIjAN...(from Step 1, remove all line breaks) -
TTL:
3600(or your default)
If configuring multiple domains (Step 1b), repeat this for each domain using the same public key value but the appropriate domain name in the host/name field.
Note
DNS provider interfaces vary in field names (e.g., "Host" vs "Name" vs "Hostname") and format (some require the full FQDN, others only the subdomain). Refer to your DNS provider's documentation for TXT record creation.
Additional DNS Information
For broader context on DNS configuration for the Virtru Gateway, see Customer-Hosted Gateway DNS Entries.
Important:
- Remove ALL line breaks from the public key value
- Do NOT include
-----BEGIN/END-----headers in the DNS record - Some providers require splitting long values into multiple quoted sections
Verify DNS Propagation
dig gw._domainkey.yourdomain.com TXT +short # Should return: "v=DKIM1; k=rsa; p=..."
Step 6: Rebuild Gateway Container
Apply the configuration changes by rebuilding the gateway container(s).
Stop and Remove Current Container(s)
# Find your container name docker ps # Stop the container docker stop <container-name> # Remove the container docker rm <container-name>
Rebuild Container
# Navigate to scripts directory cd /var/virtru/vg/scripts # Run the setup script for your gateway sh setup-<container-name>.sh
Example:
sh setup-oe-9001.sh
Verify Container is Running
docker ps # Check logs docker logs <container-name>
Step 7: Verify Configuration
Check Environment Variable
# View environment variables in running container docker exec <container-name> printenv | grep DKIM # Expected output (single domain): # GATEWAY_DKIM_DOMAINS=gw._domainkey.yourdomain.com # Expected output (multiple domains): # GATEWAY_DKIM_DOMAINS=gw._domainkey.domain1.com,gw._domainkey.domain2.com,gw._domainkey.domain3.com
Verify DKIM Keys are Mounted
# List DKIM directory contents inside container docker exec <container-name> ls -la /etc/opendkim/keys/ # Expected output (multiple domains): # gw._domainkey.domain1.com.pem # gw._domainkey.domain1.com-public.pem # gw._domainkey.domain2.com.pem # gw._domainkey.domain2.com-public.pem
Check Container Logs
# Monitor logs for DKIM-related messages docker logs <container-name> | grep -i dkim
Step 8: Test DKIM Signing
Send Test Email
Using swaks (if installed on gateway server):
swaks --to test@gmail.com \
--from user@yourdomain.com \
--server localhost \
--port 9001 \
--body "DKIM test message"Or configure your mail client to use the gateway as SMTP relay and send a test email.
Verify Signature
In Gmail:
- Open email → ⋮ → Show original
- Look for:
DKIM-Signature: v=1; a=rsa-sha256; d=yourdomain.com; s=gw; - Scroll down to see:
DKIM: 'PASS'✓
Troubleshooting
No DKIM Signature in Emails
Quick checks:
# 1. Is DKIM enabled in .env? grep GATEWAY_DKIM_DOMAINS /var/virtru/vg/env/<container-name>.env # Should show: GATEWAY_DKIM_DOMAINS=gw._domainkey.yourdomain.com (uncommented) # 2. Is env var set in container? docker exec <container-name> printenv GATEWAY_DKIM_DOMAINS # Should output the configured domain(s) # 3. Are keys mounted? docker exec <container-name> ls /etc/opendkim/keys/ # Should list .pem files for all configured domains # 4. Check file permissions ls -la /var/virtru/vg/dkim/ # Should show: -rw-r--r-- (644)
If missing:
- Verify
GATEWAY_DKIM_DOMAINSis uncommented in .env file - Ensure key files exist in
/var/virtru/vg/dkim/for all configured domains - Check file permissions are 644
- Restart container
DKIM Verification Fails (dkim=fail)
Check DNS:
dig gw._domainkey.yourdomain.com TXT +short
Common issues:
- No output → DNS record not published
- Truncated → Value too long; split into quoted sections
-
Wrong selector → Must match .env file selector (default:
gw) - Domain added to GATEWAY_DKIM_DOMAINS before DNS was published → Publish the DNS TXT record first, then add the domain to the env file and restart the container
Container Won't Start
# Check container status docker ps -a # View container logs docker logs <container-name> # Common errors: # "Permission denied" on /etc/opendkim/keys/ # Solution: Check file permissions (Step 4) # "Failed to load key" # Solution: Verify key files exist in /var/virtru/vg/dkim/ ls -la /var/virtru/vg/dkim/
Queue Directory Permission Issues
If you manually created directories or restored from backup, the queue directory may have incorrect ownership.
Symptoms:
- Container starts but mail stays stuck in queue
- Permission denied errors in container logs related to
/var/spool/postfix
Solution:
# Set correct ownership for Postfix user (UID 149) sudo chown -R 149:149 /var/virtru/vg/queue/<container-name> # Example for outbound encrypt on port 9001 sudo chown -R 149:149 /var/virtru/vg/queue/oe-9001 # Restart the container docker restart <container-name>
Note
The Virtru Gateway install script automatically sets queue directory ownership during initial setup. This fix is only needed if directories were manually created or modified.
Configuration Reference
File Structure:
/var/virtru/vg/
├── dkim/
│ ├── gw._domainkey.domain1.com.pem # Private key (domain 1)
│ ├── gw._domainkey.domain1.com-public.pem # Public key (domain 1)
│ ├── gw._domainkey.domain2.com.pem # Private key (domain 2, same key)
│ └── gw._domainkey.domain2.com-public.pem # Public key (domain 2, same key)
├── env/
│ └── <container-name>.env # Environment file (e.g., oe-9001.env)
├── queue/
│ └── <container-name>/ # Mail queue (owned by UID 149)
├── scripts/
│ └── setup-<container-name>.sh # Setup script (e.g., setup-oe-9001.sh)
├── test/
│ ├── checkendpoints.sh # Endpoint connectivity test
│ ├── runall.sh # Run command on all containers
│ └── sendtestmessage.sh # Send test email via swaks
└── tls/
└── <gateway-fqdn>/ # TLS certificates directory
├── client.key # TLS private key
└── client.pem # TLS certificateEnvironment File Setting:
# Single domain GATEWAY_DKIM_DOMAINS=gw._domainkey.yourdomain.com # Multiple domains (comma-separated, no spaces) GATEWAY_DKIM_DOMAINS=gw._domainkey.domain1.com,gw._domainkey.domain2.com,gw._domainkey.domain3.com
DNS Format:
<selector>._domainkey.<domain> TXT "v=DKIM1; k=rsa; p=<key>"
Docker Volume Mount:
-v /var/virtru/vg/dkim/:/etc/opendkim/keys
Security Best Practices
✅ Use 2048-bit keys minimum (4096-bit for high security)
✅ Never commit private keys to version control
✅ Rotate keys annually
✅ Restrict access to /var/virtru/vg/dkim/ directory
✅ Monitor DKIM pass rates via DMARC reports
✅ Ensure file permissions are 644 (readable, not writable by container)
✅ Publish DNS TXT records before enabling domains in GATEWAY_DKIM_DOMAINS
Key Rotation:
- Generate new keys with different selector (e.g.,
gw2) - Publish new DNS record:
gw2._domainkey.yourdomain.com - Update .env file:
GATEWAY_DKIM_DOMAINS=gw2._domainkey.yourdomain.com - Restart container
- Wait 48 hours
- Remove old DNS record:
gw._domainkey.yourdomain.com
Quick Reference
Essential commands from this guide:
# Generate DKIM keys (Step 1) cd /var/virtru/vg/dkim gwDomain="yourdomain.com" gwDkimSelector="gw" openssl genrsa -out $gwDkimSelector._domainkey.$gwDomain.pem 2048 openssl rsa -in $gwDkimSelector._domainkey.$gwDomain.pem \ -out $gwDkimSelector._domainkey.$gwDomain-public.pem \ -pubout -outform PEM # Copy keys for additional domains (Step 1b) cp gw._domainkey.domain1.com.pem gw._domainkey.domain2.com.pem cp gw._domainkey.domain1.com-public.pem gw._domainkey.domain2.com-public.pem # Set permissions (Step 4) chmod 644 /var/virtru/vg/dkim/gw._domainkey* # Edit environment file (Step 2) sudo nano /var/virtru/vg/env/<container-name>.env # Enable DKIM for multiple domains (Step 2) GATEWAY_DKIM_DOMAINS=gw._domainkey.domain1.com,gw._domainkey.domain2.com,gw._domainkey.domain3.com # Restart container (Step 6) docker stop <container-name> && docker rm <container-name> cd /var/virtru/vg/scripts && sh setup-<container-name>.sh # Verify configuration (Step 7) docker exec <container-name> printenv GATEWAY_DKIM_DOMAINS docker exec <container-name> ls -la /etc/opendkim/keys/ # Verify DNS record (Step 5) dig gw._domainkey.yourdomain.com TXT +short # Send test email (Step 8) swaks --to test@gmail.com --from user@yourdomain.com --server localhost --port 9001