haraka-plugin-vault-enhanced-dkim
v1.0.4
Published
Haraka plugin that enhances DKIM signing and verification with Vault integration
Maintainers
Readme
haraka-plugin-vault-enhanced-dkim
A Haraka plugin that integrates with HashiCorp Vault to securely manage DKIM signing keys for multiple domains.
Overview
This plugin implements the DKIM Core specification with enhanced security by storing DKIM keys in HashiCorp Vault instead of the filesystem.
Key Features
- Centralized DKIM key management using HashiCorp Vault
- Automatic key retrieval for multiple domains
- Secure key storage with audit trails
- No local key files required
Install
cd /path/to/local/haraka
npm install @brassnode/haraka-plugin-vault-enhanced-dkim
echo "vault-enhanced-dkim" >> config/plugins
service haraka restartVault Configuration
Prerequisites
- HashiCorp Vault server installed and configured
- KV v2 secrets engine enabled
- Appropriate Vault authentication configured
Key Storage Structure
DKIM keys are stored in Vault under the following path structure:
dkim/{domain-name}Each domain's secret should contain the following keys:
{
"selector": "your-selector",
"private_key": "-----BEGIN RSA PRIVATE KEY-----\n...",
"public_key": "-----BEGIN PUBLIC KEY-----\n...",
"domain": "example.com",
"created_at": "2024-01-01T00:00:00.000Z"
}Vault Permissions
The Vault token/role used by this plugin needs the following capabilities:
path "kv/data/dkim/*" {
capabilities=["read"]
}
path "kv/metadata/dkim/*" {
capabilities=["read", "list"]
}Generating and Storing Keys
This plugin includes a key generation script that creates DKIM keys and stores them directly in Vault or in the local filesystem:
Set Vault environment variables (if using vault storage)
export VAULT_ADDR="http://vault.example.com:8200"
export VAULT_TOKEN="your-vault-token"Usage:
./config/dkim/dkim_key_gen.sh <domain> [--store vault|local] [--path path_prefix]
Arguments:
DOMAIN Domain name to generate DKIM keys for (required)
--store vault|local Storage mode for the generated keys (default: vault)
--path path_prefix Optional path prefix:
- For vault: Path prefix in Vault (default: dkim)
- For local: Directory path (default: ./config/dkim)Examples:
# Generate and store in Vault (default path: ./config/dkim)
./config/dkim/dkim_key_gen.sh example.com
# Generate and store in local filesystem with default path (./config/dkim)
./config/dkim/dkim_key_gen.sh example.com --store local
# Generate and store in Vault with with default path (dkim/example.com)
./config/dkim/dkim_key_gen.sh example.com --store vault
# Generate and store in local filesystem with custom path (custom/local/path)
./config/dkim/dkim_key_gen.sh example.com --store local --path /custom/local/path
# Generate and store in Vault with custom path (custom/path/example.com)
./config/dkim/dkim_key_gen.sh example.com --store vault --path custom/pathThe script will:
- Generate a 2048-bit RSA key pair
- Create a selector in the format
mmmYYYY(e.g.,jan2024) - Store the keys in Vault at
dkim/example.comor in the local filesystem at./config/dkim/example.com(or custom paths if specified) - Display DNS configuration instructions
Plugin Configuration
dkim.ini
; The key store to use: 'local' or 'vault'
; 'local' - keys are stored in the file system under /config/dkim-keys
; 'vault' - keys are stored in HashiCorp Vault
[main]
key_store=local
[vault]
addr=http://vault.example.com:8200
token=your-vault-token
timeout=5000
[redis]
host=127.0.0.1
port=6379
password=
db=0
cache_ttl=3600
; Encryption key for caching private keys in Redis (optional)
; If set, private keys will be encrypted before being stored in Redis
; and decrypted when retrieved. Use a strong, random key.
; Example: cache_encryption_key=your-32-character-random-string
; Leave empty to disable encryption
;
; Generate a secure encryption key using either:
; openssl rand -hex 32
; or
; head -c 32 /dev/urandom | xxd -p -c 32
cache_encryption_key=
[sign]
enabled=false
selector=mail
domain=example.com
headers=From, Sender, Reply-To, Subject, Date, Message-ID, To, Cc, MIME-Version
[verify]
enabled=true
allowed_time_skew=60
sigerror_log_level=infoMain Configuration Notes
key_store: Specifies where DKIM keys are stored and retrieved from. Options are 'vault' (recommended) for HashiCorp Vault storage or 'local' for filesystem storage.
Vault Configuration Notes
addr: Your Vault server address (e.g.,http://vault:8200orhttps://vault.example.com:8200)token: Vault authentication token (can also be set viaVAULT_TOKENenvironment variable)timeout: Connection timeout for Vault requests (milliseconds)
Redis Cache Configuration Notes
host: Redis server hostname (default: 127.0.0.1)port: Redis server port (default: 6379)password: Redis password (if required)db: Redis database number (default: 0)cache_ttl: Time-to-live for cached DKIM keys in seconds (default: 3600)cache_encryption_key: Encryption key for caching private keys in Redis (optional)
Sign Configuration Notes
enabled: Set totrueto enable DKIM signingselector: Fallback selector if domain not found in Vaultdomain: Fallback domain if not determined from messageheaders: List of headers to include in DKIM signature. The From header is always included
Verify Configuration Notes
enabled: Set totrueto verify incoming DKIM signaturesallowed_time_skew: Tolerance for timestamp differences (seconds). Useful when clock is skewedsigerror_log_level: Logging level for signature errors (debug, info, warn, error)
DNS Configuration
For each domain stored in Vault, you need to publish the public key in DNS:
{selector}._domainkey.{domain}. IN TXT "v=DKIM1; k=rsa; p={public_key_base64}"Example:
202401._domainkey.example.com. IN TXT "v=DKIM1; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQ..."Signing Behavior
- When an email is received, the plugin extracts the sender's domain
- It queries Vault for DKIM keys at
dkim/{domain} - If keys are found, the message is signed with the domain's DKIM key
- If no keys are found, the message is not signed (or uses a default if configured)
Verification Behaviour
Verify DKIM signatures as defined by RFC 6376 and add an Authentication-Results header as appropriate.
When verification is enabled, the plugin:
- Extracts DKIM-Signature headers from incoming messages
- Retrieves the public key from DNS using the selector and domain specified in the signature
- Validates the signature against the message body and signed headers
- Adds Authentication-Results header with the verification result
Authentication-Results Header Format
The plugin adds headers in the following format:
Authentication-Results: mail.example.com;
dkim=pass (2048-bit key) header.d=sender.com [email protected] header.b="signature_fragment"Possible results:
pass: Signature validated successfullyfail: Signature validation failedneutral: Unable to verify (e.g., DNS lookup failed)temperror: Temporary failure during verificationpermerror: Permanent error in signature or key format
Verification Process Details
- Header Canonicalization: Headers are normalized according to the canonicalization method specified in the signature (simple or relaxed)
- Body Hash Verification: The body hash in the signature is compared against a computed hash of the message body
- Signature Validation: The signature is validated using the public key retrieved from DNS
- Time Skew Handling: Signatures are accepted within the configured
allowed_time_skewto handle clock differences
Migration from Standard DKIM Plugin
To migrate from the standard haraka-plugin-dkim:
- Extract your existing DKIM keys from the filesystem
- Store them in Vault using the structure shown above
- Update your Haraka configuration to use vault-enhanced-dkim
- Remove the old dkim plugin and configuration files
Testing
This plugin provides a command-line test tool that can be used to debug DKIM issues or to check results.
# dkimverify < message
identity="@gmail.com" domain="gmail.com" result=passYou can add --debug to the option arguments to see a full trace of the processing.
Troubleshooting
Common Issues
- Vault connection errors: Check your Vault address and network connectivity
- Authentication failures: Verify your token/credentials are valid
- Key not found: Ensure the domain exists in Vault at the correct path
- DNS verification failures: Verify your DNS TXT record matches the public key in Vault
Verify Vault Connectivity
# List all domains with DKIM keys
vault kv list dkim
# View keys for a specific domain
vault kv get dkim/example.comNotes
This plugin and underlying library do not currently support DKIM body length limits (l=).
Contributing
Contributions are what make the open source community such an amazing place to learn, inspire, and create. Any contributions you make are greatly appreciated.
If you have a suggestion that would make this better, please fork the repo and create a pull request. You can also simply open an issue with the tag "enhancement".
Top contributors
License
Distributed under the Unlicense License. See LICENSE for more information.
Contact
Abdulmatin Sanni - @abdulmatinsanni - [email protected]
