PageSourceSearch

https://docs.rspamd.com/assets/js/b0dc293c.47294805.js

js rspamd.com collected 2026-10-02 02:30:49 UTC 49,326 bytes, 1 lines download raw bytes

1"use strict";(self.webpackChunkrspamd_docs=self.webpackChunkrspamd_docs||[]).push([[6540],{28453:(e,n,i)=>{i.d(n,{R:()=>r,x:()=>l});var a=i(96540);const s={},t=a.createContext(s);function r(e){const n=a.useContext(t);return a.useMemo((function(){return"function"==typeof e?e(n):{...n,...e}}),[n,e])}function l(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(s):e.components||s:r(e.components),a.createElement(t.Provider,{value:n},e.children)}},66253:(e,n,i)=>{i.r(n),i.d(n,{assets:()=>o,contentTitle:()=>l,default:()=>m,frontMatter:()=>r,metadata:()=>a,toc:()=>d});const a=JSON.parse('{"id":"tutorials/dkim_signing_guide","title":"DKIM Signing Guide","description":"DKIM (DomainKeys Identified Mail) signing allows you to cryptographically sign outbound emails, proving they originated from your domain and haven\'t been tampered with. This tutorial provides step-by-step instructions for implementing DKIM signing with Rspamd.","source":"@site/docs/tutorials/dkim_signing_guide.md","sourceDirName":"tutorials","slug":"/tutorials/dkim_signing_guide","permalink":"/tutorials/dkim_signing_guide","draft":false,"unlisted":false,"editUrl":"https://github.com/rspamd/docs.rspamd.com/edit/master/docs/tutorials/dkim_signing_guide.md","tags":[],"version":"current","frontMatter":{"title":"DKIM Signing Guide"},"sidebar":"docs","previous":{"title":"Settings Module Guide","permalink":"/tutorials/settings_guide"},"next":{"title":"ClickHouse Analytics Platform","permalink":"/tutorials/clickhouse_analytics"}}');var s=i(74848),t=i(28453);const r={title:"DKIM Signing Guide"},l="DKIM Signing: Complete Setup Guide",o={},d=[{value:"What is DKIM?",id:"what-is-dkim",level:2},{value:"Basic Setup",id:"basic-setup",level:2},{value:"Step 1: Generate DKIM Keys",id:"step-1-generate-dkim-keys",level:3},{value:"Step 2: Configure DKIM Signing",id:"step-2-configure-dkim-signing",level:3},{value:"Step 3: Set File Permissions",id:"step-3-set-file-permissions",level:3},{value:"Step 4: Publish DNS Records",id:"step-4-publish-dns-records",level:3},{value:"Step 5: Test and Restart",id:"step-5-test-and-restart",level:3},{value:"Advanced Configurations",id:"advanced-configurations",level:2},{value:"Multi-Domain Setup",id:"multi-domain-setup",level:3},{value:"Enterprise Configuration: DKIM with Hashicorp Vault and LDAP Backend",id:"enterprise-configuration-dkim-with-hashicorp-vault-and-ldap-backend",level:3},{value:"Architecture Overview",id:"architecture-overview",level:4},{value:"Prerequisites",id:"prerequisites",level:4},{value:"Step 1: LDAP Server Setup",id:"step-1-ldap-server-setup",level:4},{value:"Step 2: Hashicorp Vault Setup",id:"step-2-hashicorp-vault-setup",level:4},{value:"Step 3: Integration Scripts",id:"step-3-integration-scripts",level:4},{value:"Step 4: Rspamd Configuration",id:"step-4-rspamd-configuration",level:4},{value:"Step 5: Automated Key Management",id:"step-5-automated-key-management",level:4},{value:"Step 6: Testing and Validation",id:"step-6-testing-and-validation",level:4},{value:"Step 7: Production Hardening",id:"step-7-production-hardening",level:4},{value:"Key Rotation Strategy",id:"key-rotation-strategy",level:3},{value:"Conditional Signing",id:"conditional-signing",level:3},{value:"Per-User DKIM Signing",id:"per-user-dkim-signing",level:3},{value:"DKIM with ARC Signing",id:"dkim-with-arc-signing",level:3},{value:"Integration Examples",id:"integration-examples",level:2},{value:"Postfix Integration",id:"postfix-integration",level:3},{value:"Multi-Server Setup",id:"multi-server-setup",level:3},{value:"Cloud Provider Setup",id:"cloud-provider-setup",level:3},{value:"Best Practices",id:"best-practices",level:2},{value:"Security",id:"security",level:3},{value:"DNS Configuration",id:"dns-configuration",level:3},{value:"Performance Optimization",id:"performance-optimization",level:3},{value:"Troubleshooting",id:"troubleshooting",level:2},{value:"Common Issues",id:"common-issues",level:3},{value:"Testing DKIM",id:"testing-dkim",level:3},{value:"Monitoring and Maintenance",id:"monitoring-and-maintenance",level:2},{value:"Monitoring Scripts",id:"monitoring-scripts",level:3},{value:"Log Analysis",id:"log-analysis",level:3}];function c(e){const n={code:"code",h1:"h1",h2:"h2",h3:"h3",h4:"h4",header:"header",li:"li",mermaid:"mermaid",ol:"ol",p:"p",pre:"pre",strong:"strong",ul:"ul",...(0,t.R)(),...e.components};return(0,s.jsxs)(s.Fragment,{children:[(0,s.jsx)(n.header,{children:(0,s.jsx)(n.h1,{id:"dkim-signing-complete-setup-guide",children:"DKIM Signing: Complete Setup Guide"})}),"\n",(0,s.jsxs)(n.p,{children:[(0,s.jsx)(n.strong,{children:"DKIM (DomainKeys Identified Mail)"})," signing allows you to cryptographically sign outbound emails, proving they originated from your domain and haven't been tampered with. This tutorial provides step-by-step instructions for implementing DKIM signing with Rspamd."]}),"\n",(0,s.jsx)(n.h2,{id:"what-is-dkim",children:"What is DKIM?"}),"\n",(0,s.jsx)(n.p,{children:"DKIM adds a digital signature to your outbound emails using public-key cryptography:"}),"\n",(0,s.jsxs)(n.ul,{children:["\n",(0,s.jsxs)(n.li,{children:[(0,s.jsx)(n.strong,{children:"Private key"}),": Kept on your mail server to sign messages"]}),"\n",(0,s.jsxs)(n.li,{children:[(0,s.jsx)(n.strong,{children:"Public key"}),": Published in DNS for verification"]}),"\n",(0,s.jsxs)(n.li,{children:[(0,s.jsx)(n.strong,{children:"Selector"}),": Allows multiple keys per domain"]}),"\n"]}),"\n",(0,s.jsx)(n.p,{children:"DKIM helps with:"}),"\n",(0,s.jsxs)(n.ul,{children:["\n",(0,s.jsx)(n.li,{children:"Email authentication and deliverability"}),"\n",(0,s.jsx)(n.li,{children:"Protection against spoofing"}),"\n",(0,s.jsx)(n.li,{children:"Building domain reputation"}),"\n",(0,s.jsx)(n.li,{children:"Meeting security compliance requirements"}),"\n"]}),"\n",(0,s.jsx)(n.h2,{id:"basic-setup",children:"Basic Setup"}),"\n",(0,s.jsx)(n.h3,{id:"step-1-generate-dkim-keys",children:"Step 1: Generate DKIM Keys"}),"\n",(0,s.jsx)(n.p,{children:"Create DKIM key pairs for your domains:"}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-bash",children:"# Generate keys for your main domain\nsudo mkdir -p /etc/rspamd/dkim\ncd /etc/rspamd/dkim\n\n# Generate a 2048-bit RSA key (recommended)\nrspamadm dkim_keygen -b 2048 -s mail -d example.com -k mail.key\n\n# This creates:\n# - mail.key (private key)  \n# - mail.txt (DNS record to publish)\n"})}),"\n",(0,s.jsx)(n.p,{children:"Example output:"}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{children:'# DNS record to publish:\nmail._domainkey.example.com. IN TXT "v=DKIM1; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA..."\n\n# Private key written to mail.key\n'})}),"\n",(0,s.jsx)(n.h3,{id:"step-2-configure-dkim-signing",children:"Step 2: Configure DKIM Signing"}),"\n",(0,s.jsx)(n.p,{children:"Create the DKIM signing configuration:"}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-hcl",children:'# /etc/rspamd/local.d/dkim_signing.conf\n\n# Enable DKIM signing\nenabled = true;\n\n# Default signing configuration\ndomain {\n  example.com {\n    selector = "mail";
1\n    path = "/etc/rspamd/dkim/mail.key";\n  }\n}\n\n# Sign outbound mail only\nsign_authenticated = true;\nsign_local = true;\nsign_inbound = false;\n\n# Default settings\nuse_esld = true;\ncheck_pubkey = true;\n'})}),"\n",(0,s.jsx)(n.h3,{id:"step-3-set-file-permissions",children:"Step 3: Set File Permissions"}),"\n",(0,s.jsx)(n.p,{children:"Secure the private keys:"}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-bash",children:"# Set proper ownership and permissions\nsudo chown -R _rspamd:_rspamd /etc/rspamd/dkim/\nsudo chmod 600 /etc/rspamd/dkim/*.key\nsudo chmod 644 /etc/rspamd/dkim/*.txt\n"})}),"\n",(0,s.jsx)(n.h3,{id:"step-4-publish-dns-records",children:"Step 4: Publish DNS Records"}),"\n",(0,s.jsxs)(n.p,{children:["Add the DNS record from the generated ",(0,s.jsx)(n.code,{children:".txt"})," file:"]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-dns",children:'; Add this to your DNS zone\nmail._domainkey.example.com. IN TXT "v=DKIM1; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4f5wg5l2hKdVBqpxdqTqDPbaohNcGI94vs9lxjzHZl9j7u2B..."\n'})}),"\n",(0,s.jsx)(n.h3,{id:"step-5-test-and-restart",children:"Step 5: Test and Restart"}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-bash",children:'# Test configuration\nsudo rspamadm configtest\n\n# Restart Rspamd\nsudo systemctl restart rspamd\n\n# Test DKIM signing\necho "Test message" | rspamc -d example.com -f [email protected]\n'})}),"\n",(0,s.jsx)(n.h2,{id:"advanced-configurations",children:"Advanced Configurations"}),"\n",(0,s.jsx)(n.h3,{id:"multi-domain-setup",children:"Multi-Domain Setup"}),"\n",(0,s.jsx)(n.p,{children:"Configure signing for multiple domains:"}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-hcl",children:'# /etc/rspamd/local.d/dkim_signing.conf\n\nenabled = true;\n\n# Multiple domain configuration\ndomain {\n  example.com {\n    selector = "mail";\n    path = "/etc/rspamd/dkim/example.com/mail.key";\n  }\n  \n  subdomain.example.com {\n    selector = "sub";\n    path = "/etc/rspamd/dkim/subdomain.example.com/sub.key";\n  }\n  \n  anotherdomain.org {\n    selector = "rspamd";\n    path = "/etc/rspamd/dkim/anotherdomain.org/rspamd.key";\n  }\n}\n\n# Sign authenticated and local mail\nsign_authenticated = true;\nsign_local = true;\n'})}),"\n",(0,s.jsx)(n.p,{children:"Generate keys for each domain:"}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-bash",children:"# Create directory structure\nsudo mkdir -p /etc/rspamd/dkim/{example.com,subdomain.example.com,anotherdomain.org}\n\n# Generate keys for each domain\nrspamadm dkim_keygen -s mail -d example.com -k /etc/rspamd/dkim/example.com/mail.key\nrspamadm dkim_keygen -s sub -d subdomain.example.com -k /etc/rspamd/dkim/subdomain.example.com/sub.key  \nrspamadm dkim_keygen -s rspamd -d anotherdomain.org -k /etc/rspamd/dkim/anotherdomain.org/rspamd.key\n"})}),"\n",(0,s.jsx)(n.h3,{id:"enterprise-configuration-dkim-with-hashicorp-vault-and-ldap-backend",children:"Enterprise Configuration: DKIM with Hashicorp Vault and LDAP Backend"}),"\n",(0,s.jsx)(n.p,{children:"This section provides a comprehensive guide for implementing an enterprise-grade DKIM signing solution using Hashicorp Vault with LDAP backend storage for per-domain key management."}),"\n",(0,s.jsx)(n.h4,{id:"architecture-overview",children:"Architecture Overview"}),"\n",(0,s.jsx)(n.mermaid,{value:'graph LR\n    A["Rspamd<br/>DKIM Signing"] --\x3e B["Hashicorp<br/>Vault"]\n    B --\x3e C["LDAP Server<br/>(OpenLDAP/AD)"]\n    \n    A --\x3e D["Mail signing<br/>operations"]\n    B --\x3e E["Secret management<br/>and security"]\n    C --\x3e F["Domain key storage<br/>and organization"]\n    \n    style A fill:#e1f5fe\n    style B fill:#f3e5f5\n    style C fill:#e8f5e8\n    style D fill:#fff3e0\n    style E fill:#fce4ec\n    style F fill:#f1f8e9'}),"\n",(0,s.jsx)(n.h4,{id:"prerequisites",children:"Prerequisites"}),"\n",(0,s.jsxs)(n.ul,{children:["\n",(0,s.jsx)(n.li,{children:"Hashicorp Vault server (version 1.8+)"}),"\n",(0,s.jsx)(n.li,{children:"LDAP server (OpenLDAP, Active Directory, or similar)"}),"\n",(0,s.jsx)(n.li,{children:"Rspamd (version 1.9.3+)"}),"\n",(0,s.jsx)(n.li,{children:"Administrative access to all systems"}),"\n",(0,s.jsx)(n.li,{children:"SSL/TLS certificates for secure communication"}),"\n"]}),"\n",(0,s.jsx)(n.h4,{id:"step-1-ldap-server-setup",children:"Step 1: LDAP Server Setup"}),"\n",(0,s.jsx)(n.p,{children:"First, configure your LDAP server to store DKIM keys. This example uses OpenLDAP:"}),"\n",(0,s.jsx)(n.p,{children:(0,s.jsx)(n.strong,{children:"1.1 Create LDAP Schema for DKIM Keys"})}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-ldif",children:"# /etc/ldap/schema/dkim.ldif\ndn: cn=dkim,cn=schema,cn=config\nobjectClass: olcSchemaConfig\ncn: dkim\nolcAttributeTypes: ( 1.3.6.1.4.1.99999.1.1 NAME 'dkimPrivateKey'\n  DESC 'DKIM Private Key in PEM format'\n  EQUALITY case
1ExactMatch\n  SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE )\nolcAttributeTypes: ( 1.3.6.1.4.1.99999.1.2 NAME 'dkimSelector'\n  DESC 'DKIM Selector'\n  EQUALITY caseExactMatch\n  SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE )\nolcAttributeTypes: ( 1.3.6.1.4.1.99999.1.3 NAME 'dkimKeyType'\n  DESC 'DKIM Key Type (rsa, ed25519)'\n  EQUALITY caseExactMatch\n  SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE )\nolcAttributeTypes: ( 1.3.6.1.4.1.99999.1.4 NAME 'dkimKeySize'\n  DESC 'DKIM Key Size in bits'\n  EQUALITY integerMatch\n  SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )\nolcAttributeTypes: ( 1.3.6.1.4.1.99999.1.5 NAME 'dkimPublicKey'\n  DESC 'DKIM Public Key for DNS record'\n  EQUALITY caseExactMatch\n  SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE )\nolcObjectClasses: ( 1.3.6.1.4.1.99999.2.1 NAME 'dkimKey'\n  DESC 'DKIM Key Object'\n  SUP top\n  STRUCTURAL\n  MUST ( cn $ dkimPrivateKey $ dkimSelector $ dkimKeyType )\n  MAY ( dkimKeySize $ dkimPublicKey $ description ) )\n"})}),"\n",(0,s.jsx)(n.p,{children:(0,s.jsx)(n.strong,{children:"1.2 Apply the Schema"})}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-bash",children:"# Add the schema to LDAP\nsudo ldapadd -Y EXTERNAL -H ldapi:/// -f /etc/ldap/schema/dkim.ldif\n"})}),"\n",(0,s.jsx)(n.p,{children:(0,s.jsx)(n.strong,{children:"1.3 Create Organizational Structure"})}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-ldif",children:"# /tmp/dkim_structure.ldif\ndn: ou=dkim,dc=example,dc=com\nobjectClass: organizationalUnit\nou: dkim\ndescription: DKIM Keys Storage\n\ndn: ou=domains,ou=dkim,dc=example,dc=com\nobjectClass: organizationalUnit\nou: domains\ndescription: Domain-specific DKIM keys\n\ndn: ou=selectors,ou=dkim,dc=example,dc=com\nobjectClass: organizationalUnit\nou: selectors\ndescription: DKIM selectors management\n"})}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-bash",children:'# Apply the structure\nldapadd -x -D "cn=admin,dc=example,dc=com" -W -f /tmp/dkim_structure.ldif\n'})}),"\n",(0,s.jsx)(n.h4,{id:"step-2-hashicorp-vault-setup",children:"Step 2: Hashicorp Vault Setup"}),"\n",(0,s.jsx)(n.p,{children:(0,s.jsx)(n.strong,{children:"2.1 Install and Initialize Vault"})}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-bash",children:"# Download and install Vault (adjust version as needed)\ncurl -O https://releases.hashicorp.com/vault/1.15.0/vault_1.15.0_linux_amd64.zip\nunzip vault_1.15.0_linux_amd64.zip\nsudo mv vault /usr/local/bin/\n\n# Create vault user and directories\nsudo useradd --system --home /etc/vault --shell /bin/false vault\nsudo mkdir -p /etc/vault /opt/vault/data\nsudo chown vault:vault /etc/vault /opt/vault/data\n"})}),"\n",(0,s.jsx)(n.p,{children:(0,s.jsx)(n.strong,{children:"2.2 Configure Vault"})}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-hcl",children:'# /etc/vault/vault.hcl\nstorage "file" {\n  path = "/opt/vault/data"\n}\n\nlistener "tcp" {\n  address     = "127.0.0.1:8200"\n  tls_disable = 1  # Only for development; use TLS in production\n}\n\n# Production configuration should include:\n# listener "tcp" {\n#   address       = "0.0.0.0:8200"\n#   tls_cert_file = "/etc/vault/tls/vault.crt"\n#   tls_key_file  = "/etc/vault/tls/vault.key"\n# }\n\napi_addr = "http://127.0.0.1:8200"\ncluster_addr = "https://127.0.0.1:8201"\nui = true\n\n# Enable audit logging\naudit {\n  file {\n    file_path = "/opt/vault/logs/audit.log"\n  }\n}\n'})}),"\n",(0,s.jsx)(n.p,{children:(0,s.jsx)(n.strong,{children:"2.3 Start and Initialize Vault"})}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-bash",children:"# Create systemd service\nsudo tee /etc/systemd/system/vault.service > /dev/null <<EOF\n[Unit]\nDescription=HashiCorp Vault\nDocumentation=https://www.vaultproject.io/docs/\nRequires=network-online.target\nAfter=network-online.target\nConditionFileNotEmpty=/etc/vault/vault.hcl\n\n[Service]\nType=notify\nUser=vault\nGroup=vault\nProtectSystem=full\nProtectHome=read-only\nPrivateTmp=yes\nPrivateDevices=yes\nSecureBits=keep-caps\nAmbientCapabilities=CAP_IPC_LOCK\nNoNewPrivileges=yes\nExecStart=/usr/local/bin/vault server -config=/etc/vault/vault.hcl\nExecReload=/bin/kill -HUP \\$MAINPID\nKillMode=process\nRestart=on-failure\nRestartSec=5\n\n[Install]\nWantedBy=multi-user.target\nEOF\n\n# Start Vault\nsudo systemctl enable vault\nsudo systemctl start vault\n\n# Set environment variables\nexport VAULT_ADDR='http://127.0.0.1:8200'\n\n# Initialize Vault (save these keys securely!)\nvault operator init -key-shares=5 -key-threshold=3\n\n# Unseal Vault with 3 of the 5 keys\nvault operator unseal <key1>\nvault operator unseal <key2>\nvault operator unseal <key3>\n\n# Authenticate with root token\nvault auth <root_token>\n"})}),"\n",(0,s.jsx)(n.p,{children:(0,s.jsx)(n.strong,{children:"2.4 Configure LDAP Secrets Engine"})}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-bash",children:'# Enable LDAP secrets engine\nvault secrets enable -path=ldap-dkim ldap\n\n# Configure LDAP connection\nvault write ldap-dkim/config \\\n    binddn="cn=admin,dc=example,dc=com" \\\n    bindpass="admin_password" \\\n    url="ldap://localhost:389" \\\n    userdn="ou=domains,ou=dkim,dc=example,dc=com"\n\n# Create a role for DKIM key management\nvault write ldap-dkim/role/dkim-manager \\\n    creation_ldif=\'dn: cn={{.Username}},ou=domains,ou=dkim,dc=example,dc=com\nobjectClass: dkimKey\ncn: {{.Username}}\ndkimPrivateKey: {{.Password}}\ndkimSelector: {{.DisplayName}}
1\ndkimKeyType: rsa\' \\\n    deletion_ldif=\'dn: cn={{.Username}},ou=domains,ou=dkim,dc=example,dc=com\nchangetype: delete\' \\\n    rollback_ldif=\'dn: cn={{.Username}},ou=domains,ou=dkim,dc=example,dc=com\nchangetype: delete\'\n'})}),"\n",(0,s.jsx)(n.p,{children:(0,s.jsx)(n.strong,{children:"2.5 Configure KV Secrets Engine for DKIM Keys"})}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-bash",children:'# Enable KV version 1 for DKIM keys\nvault secrets enable -path=dkim -version=1 kv\n\n# Create policies for DKIM access\nvault policy write dkim-read - <<EOF\npath "dkim/*" {\n  capabilities = ["read", "list"]\n}\nEOF\n\nvault policy write dkim-admin - <<EOF\npath "dkim/*" {\n  capabilities = ["create", "read", "update", "delete", "list"]\n}\n\npath "ldap-dkim/*" {\n  capabilities = ["create", "read", "update", "delete", "list"]\n}\nEOF\n\n# Create token for Rspamd\nvault token create -policy=dkim-read -ttl=24h -renewable=true\n'})}),"\n",(0,s.jsx)(n.h4,{id:"step-3-integration-scripts",children:"Step 3: Integration Scripts"}),"\n",(0,s.jsx)(n.p,{children:(0,s.jsx)(n.strong,{children:"3.1 DKIM Key Management Script"})}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-bash",children:'#!/bin/bash\n# /usr/local/bin/dkim-vault-manager.sh\n\nset -euo pipefail\n\nVAULT_ADDR="${VAULT_ADDR:-http://127.0.0.1:8200}"\nVAULT_TOKEN="${VAULT_TOKEN:-}"\nLDAP_BASE="ou=domains,ou=dkim,dc=example,dc=com"\nLDAP_BIND_DN="cn=admin,dc=example,dc=com"\nLDAP_PASSWORD="${LDAP_PASSWORD:-}"\n\nusage() {\n    cat << EOF\nUsage: $0 <command> <domain> [options]\n\nCommands:\n    create      Create new DKIM key for domain\n    rotate      Rotate existing DKIM key\n    delete      Delete DKIM key\n    list        List all domains with DKIM keys\n    sync        Sync LDAP keys to Vault\n\nOptions:\n    -s, --selector     DKIM selector (default: auto-generated)\n    -t, --type         Key type: rsa or ed25519 (default: rsa)\n    -b, --bits         Key size for RSA (default: 2048)\n    -h, --help         Show this help\n\nExamples:\n    $0 create example.com\n    $0 create example.com -s mail2024 -t rsa -b 4096\n    $0 rotate example.com\n    $0 list\n    $0 sync\nEOF\n}\n\ngenerate_selector() {\n    local domain="$1"\n    local key_type="${2:-rsa}"\n    echo "${key_type}-$(date +%Y%m)"\n}\n\ncreate_dkim_key() {\n    local domain="$1"\n    local selector="${2:-$(generate_selector "$domain" "$3")}"\n    local key_type="${3:-rsa}"\n    local key_size="${4:-2048}"\n    \n    echo "Creating DKIM key for domain: $domain"\n    echo "Selector: $selector"\n    echo "Key type: $key_type"\n    \n    # Generate key using rspamadm\n    local temp_dir=$(mktemp -d)\n    local private_key_file="$temp_dir/private.key"\n    local public_key_file="$temp_dir/public.txt"\n    \n    if [[ "$key_type" == "ed25519" ]]; then\n        rspamadm dkim_keygen -s "$selector" -d "$domain" -t ed25519 -k "$private_key_file" -o plain > "$public_key_file"\n    else\n        rspamadm dkim_keygen -s "$selector" -d "$domain" -b "$key_size" -k "$private_key_file" -o plain > "$public_key_file"\n    fi\n    \n    # Read the generated keys\n    local private_key=$(cat "$private_key_file")\n    local public_key=$(cat "$public_key_file")\n    \n    # Store in Vault\n    vault kv put "dkim/$domain" \\\n        private_key="$private_key" \\\n        public_key="$public_key" \\\n        selector="$selector" \\\n        key_type="$key_type" \\\n        key_size="$key_size" \\\n        created_at="$(date -Iseconds)"\n    \n    # Store in LDAP\n    ldif_file="$temp_dir/dkim.ldif"\n    cat > "$ldif_file" << EOF\ndn: cn=$domain,$LDAP_BASE\nobjectClass: dkimKey\ncn: $domain\ndkimPrivateKey: $(echo "$private_key" | tr \'\\n\' \'|\' | sed \'s/|$//\')\ndkimPublicKey: $(echo "$public_key" | tr \'\\n\' \' \')\ndkimSelector: $selector\ndkimKeyType: $key_type\ndkimKeySize: $key_size\ndescription: DKIM key for $domain created $(date)\nEOF\n    \n    ldapadd -x -D "$LDAP_BIND_DN" -w "$LDAP_PASSWORD" -f "$ldif_file"\n    \n    echo "DKIM key created successfully!"\n    echo "DNS re
1cord to publish:"\n    echo "$public_key"\n    \n    # Cleanup\n    rm -rf "$temp_dir"\n}\n\nrotate_dkim_key() {\n    local domain="$1"\n    local key_type="${2:-rsa}"\n    \n    echo "Rotating DKIM key for domain: $domain"\n    \n    # Get current key info\n    local current_info=$(vault kv get -format=json "dkim/$domain" 2>/dev/null || echo "{}")\n    local current_selector=$(echo "$current_info" | jq -r \'.data.selector // empty\')\n    \n    if [[ -z "$current_selector" ]]; then\n        echo "No existing key found for $domain. Use \'create\' command instead."\n        exit 1\n    fi\n    \n    # Archive current key\n    vault kv put "dkim/$domain/archive/$(date +%Y%m%d)" \\\n        private_key="$(echo "$current_info" | jq -r \'.data.private_key\')" \\\n        selector="$current_selector" \\\n        key_type="$(echo "$current_info" | jq -r \'.data.key_type\')" \\\n        archived_at="$(date -Iseconds)"\n    \n    # Create new key\n    create_dkim_key "$domain" "" "$key_type"\n    \n    echo "Key rotation completed. Update DNS records and remove old ones after TTL expires."\n}\n\nlist_domains() {\n    echo "Domains with DKIM keys:"\n    vault kv list dkim/ 2>/dev/null | tail -n +3 || echo "No domains found"\n}\n\nsync_ldap_to_vault() {\n    echo "Syncing LDAP keys to Vault..."\n    \n    # Search for all DKIM entries in LDAP\n    ldapsearch -x -D "$LDAP_BIND_DN" -w "$LDAP_PASSWORD" -b "$LDAP_BASE" \\\n        "(objectClass=dkimKey)" cn dkimPrivateKey dkimSelector dkimKeyType dkimKeySize \\\n        | awk \'/^dn:/ {domain=""} /^cn:/ {domain=$2} /^dkimPrivateKey:/ {key=$2} \n               /^dkimSelector:/ {selector=$2} /^dkimKeyType:/ {type=$2} \n               /^dkimKeySize:/ {size=$2} \n               /^$/ && domain && key {\n                   printf "vault kv put dkim/%s private_key=\\"%s\\" selector=\\"%s\\" key_type=\\"%s\\" key_size=\\"%s\\"\\n", \n                   domain, key, selector, type, size\n               }\' | bash\n    \n    echo "Sync completed."\n}\n\n# Parse command line arguments\nCOMMAND="${1:-}"\nDOMAIN="${2:-}"\nSELECTOR=""\nKEY_TYPE="rsa"\nKEY_SIZE="2048"\n\nshift 2 2>/dev/null || true\n\nwhile [[ $# -gt 0 ]]; do\n    case $1 in\n        -s|--selector)\n            SELECTOR="$2"\n            shift 2\n            ;;\n        -t|--type)\n            KEY_TYPE="$2"\n            shift 2\n            ;;\n        -b|--bits)\n            KEY_SIZE="$2"\n            shift 2\n            ;;\n        -h|--help)\n            usage\n            exit 0\n            ;;\n        *)\n            echo "Unknown option: $1" >&2\n            usage >&2\n            exit 1\n            ;;\n    esac\ndone\n\n# Validate Vault connectivity\nif ! vault status >/dev/null 2>&1; then\n    echo "Error: Cannot connect to Vault at $VAULT_ADDR" >&2\n    exit 1\nfi\n\n# Execute command\ncase "$COMMAND" in\n    create)\n        [[ -n "$DOMAIN" ]] || { echo "Domain required for create command" >&2; exit 1; }\n        create_dkim_key "$DOMAIN" "$SELECTOR" "$KEY_TYPE" "$KEY_SIZE"\n        ;;\n    rotate)\n        [[ -n "$DOMAIN" ]] || { echo "Domain required for rotate command" >&2; exit 1; }\n        rotate_dkim_key "$DOMAIN" "$KEY_TYPE"\n        ;;\n    delete)\n        [[ -n "$DOMAIN" ]] || { echo "Domain required for delete command" >&2; exit 1; }\n        vault kv delete "dkim/$DOMAIN"\n        ldapdelete -x -D "$LDAP_BIND_DN" -w "$LDAP_PASSWORD" "cn=$DOMAIN,$LDAP_BASE"\n        echo "DKIM key deleted for $DOMAIN"\n        ;;\n    list)\n        list_domains\n        ;;\n    sync)\n        sync_ldap_to_vault\n        ;;\n    "")\n        echo "Command required" >&2\n        usage >&2\n        exit 1\n        ;;\n    *)\n        echo "Unknown command: $COMMAND" >&2\n        usage >&2\n        exit 1\n        ;;\nesac\n'})}),"\n",(0,s.jsx)(n.p,{children:(0,s.jsx)(n.strong,{children:"3.2 Make the script executable"})}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-bash",children:"chmod +x /usr/local/bin/dkim-vault-manager.sh\n"})}),"\n",(0,s.jsx)(n.h4,{id:"step-4-rspamd-configuration",children:"Step 4: Rspamd Configuration"}),"\n",(0,s.jsx)(n.p,{children:(0,s.jsx)(n.strong,{children:"4.1 Configure DKIM Signing with Vault"})}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-hcl",children:'# /etc/rspamd/local.d/dkim_signing.conf\n\n# Enable DKIM signing\nenabled = true;\n\n# Use Vault for key storage\nuse_vault = true;\n\n# Vault configuration\nvault_url = "http://127.0.0.1:8200";\nvault_token = "{= VAULT_DKIM_TOKEN =}";  # Use environment variable\n\n# Domains to load from Vault\nvault_domains = "/etc/rspamd/vault_domains.map";\n\n# Fallback settings\ntry_fallback = false;  # Only use Vault keys\n\n# Signing conditions\nsign_authenticated = true;\nsign_local = true;\nsign_inbound = false;\n\n# Additional security settings\ncheck_pubkey = true;\nallow_pubkey_mismatch = false;\n\n# Use domain from header\nuse_domain = "header";\nuse_esld = true;\n'})}),"\n",(0,s.jsx)(n.p,{children:(0,s.jsx)(n.strong,{children:"4.2 Create Vault Domains Map"})}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-bash",children:"# /etc/rspamd/vault_domains.map\n# List domains that should use Vault for DKIM keys\nexample.com\nsubdomain.example.com\nanotherdomain.org\ntest.example.com\n*.marketing.example.com  # Wildcard support\n"})}),"\n",(0,s.jsx)(n.p,{children:(0,s.jsx)(n.strong,{children:"4.3 Environment Configuration"})}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-bash",children:'# /etc/systemd/system/rspamd.service.d/vault.conf\n[Service]\nEnvironment="VAULT_DKIM_TOKEN=s.AhTThjWhKZAf97VowxG6blyu"\nEnvironment="VAULT_ADDR=http://127.0.0.1:8200"\n'})}),"\n",(0,s.jsx)(n.h4,{id:"step-5-automated-key-management",children:"Step 5: Automated Key Management"}
1),"\n",(0,s.jsx)(n.p,{children:(0,s.jsx)(n.strong,{children:"5.1 Key Rotation Cron Job"})}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-bash",children:"# /etc/cron.d/dkim-rotation\n# Rotate DKIM keys monthly\n0 2 1 * * root /usr/local/bin/dkim-vault-manager.sh rotate example.com\n0 2 2 * * root /usr/local/bin/dkim-vault-manager.sh rotate subdomain.example.com\n0 2 3 * * root /usr/local/bin/dkim-vault-manager.sh rotate anotherdomain.org\n\n# Sync LDAP to Vault daily\n0 3 * * * root /usr/local/bin/dkim-vault-manager.sh sync\n"})}),"\n",(0,s.jsx)(n.p,{children:(0,s.jsx)(n.strong,{children:"5.2 Monitoring Script"})}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-bash",children:'#!/bin/bash\n# /usr/local/bin/dkim-monitor.sh\n\nVAULT_ADDR="${VAULT_ADDR:-http://127.0.0.1:8200}"\nLOG_FILE="/var/log/dkim-monitor.log"\n\nlog() {\n    echo "$(date \'+%Y-%m-%d %H:%M:%S\') $*" | tee -a "$LOG_FILE"\n}\n\ncheck_vault_connectivity() {\n    if vault status >/dev/null 2>&1; then\n        log "INFO: Vault connectivity OK"\n        return 0\n    else\n        log "ERROR: Cannot connect to Vault"\n        return 1\n    fi\n}\n\ncheck_ldap_connectivity() {\n    if ldapsearch -x -D "cn=admin,dc=example,dc=com" -w "$LDAP_PASSWORD" \\\n                 -b "ou=dkim,dc=example,dc=com" -s base >/dev/null 2>&1; then\n        log "INFO: LDAP connectivity OK"\n        return 0\n    else\n        log "ERROR: Cannot connect to LDAP"\n        return 1\n    fi\n}\n\ncheck_key_expiration() {\n    log "INFO: Checking for expiring keys..."\n    \n    # Check keys older than 11 months (rotate monthly)\n    local expire_date=$(date -d "11 months ago" +%Y%m)\n    \n    vault kv list dkim/ 2>/dev/null | tail -n +3 | while read domain; do\n        local key_info=$(vault kv get -format=json "dkim/$domain" 2>/dev/null)\n        local created_at=$(echo "$key_info" | jq -r \'.data.created_at // empty\')\n        \n        if [[ -n "$created_at" ]]; then\n            local created_month=$(date -d "$created_at" +%Y%m)\n            if [[ "$created_month" < "$expire_date" ]]; then\n                log "WARNING: Key for $domain is approaching expiration (created: $created_at)"\n            fi\n        fi\n    done\n}\n\ncheck_dns_records() {\n    log "INFO: Checking DNS records..."\n    \n    vault kv list dkim/ 2>/dev/null | tail -n +3 | while read domain; do\n        local key_info=$(vault kv get -format=json "dkim/$domain" 2>/dev/null)\n        local selector=$(echo "$key_info" | jq -r \'.data.selector // empty\')\n        \n        if [[ -n "$selector" ]]; then\n            if dig TXT "${selector}._domainkey.${domain}" +short | grep -q "v=DKIM1"; then\n                log "INFO: DNS record OK for $domain (selector: $selector)"\n            else\n                log "WARNING: DNS record missing or invalid for $domain (selector: $selector)"\n            fi\n        fi\n    done\n}\n\ncheck_rspamd_signing() {\n    log "INFO: Testing DKIM signing..."\n    \n    # Test signing with a sample message\n    local test_result=$(echo "Test message" | rspamc -d example.com -f [email protected] 2>&1)\n    \n    if echo "$test_result" | grep -q "DKIM-Signature:"; then\n        log "INFO: DKIM signing test passed"\n    else\n        log "ERROR: DKIM signing test failed: $test_result"\n    fi\n}\n\nmain() {\n    log "INFO: Starting DKIM monitoring check"\n    \n    check_vault_connectivity || exit 1\n    check_ldap_connectivity || exit 1\n    check_key_expiration\n    check_dns_records\n    check_rspamd_signing\n    \n    log "INFO: DKIM monitoring check completed"\n}\n\nmain "$@"\n'})}),"\n",(0,s.jsx)(n.p,{children:(0,s.jsx)(n.strong,{children:"5.3 Make monitoring script executable and schedule it"})}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-bash",children:'chmod +x /usr/local/bin/dkim-monitor.sh\n\n# Add to crontab\necho "0 4 * * * root /usr/local/bin/dkim-monitor.sh" >> /etc/cron.d/dkim-monitor\n'})}),"\n",(0,s.jsx)(n.h4,{id:"step-6-testing-and-validation",children:"Step 6: Testing and Validation"}),"\n",(0,s.jsx)(n.p,{children:(0,s.jsx)(n.strong,{children:"6.1 Create Test Keys"})}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-bash",children:'# Set required environment variables\nexport VAULT_ADDR="http://127.0.0.1:8200"\nexport VAULT_TOKEN="your_vault_token"\nexport LDAP_PASSWORD="your_ldap_password"\n\n# Create DKIM keys for test domains\n/usr/local/bin/dkim-vault-manager.sh create example.com\n/usr/local/bin/dkim-vault-manager.sh create subdomain.example.com -s mail2024 -t rsa -b 4096\n'})}),"\n",(0,s.jsx)(n.p,{children:(0,s.jsx)(n.strong,{children:"6.2 Verify Configuration"})}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-bash",children:'# Test Rspamd configuration\nrspamadm configtest\n\n# Check Vault connectivity from Rspamd\nrspamc stat\n\n# Test DKIM signing\necho "Test message" | rspamc -d example.com -f [email protected]\n'})}),"\n",(0,s.jsx)(n.p,{children:(0,s.jsx)(n.strong,{children:"6.3 Monitor Logs"})}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-bash",children:"# Watch Rspamd logs for DKIM operations\ntail -f /var/log/rspamd/rspamd.log | grep -i dkim\n\n# Watch Vault logs\njournalctl -u vault -f\n\n# Monitor DKIM operations\ntail -f /var/log/dkim-monitor.log\n"})}),"\n",(0,s.jsx)(n.h4,{id:"step-7-production-hardening",children:"Step 7: Production Hardening"}),"\n",(0,s.jsx)(n.p,{children:(0,s.jsx)(n.strong,{children:"7.1 Secure Vault Configuration"})}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-hcl",children:'# /etc/vault/vault.hcl (Production)\nstorage "consul" {\n  address = "127.0.0.1:8500"\n  path    = "vault/"\n}\n\nlistener "tcp" {\n  address       = "0.0.0.0:8200"\n  tls_cert_file = "/etc/vault/tls/vault.crt"\n  tls_key_file  = "/etc/vault/tls/vault.key"\n  tls_min_ver
1sion = "tls12"\n}\n\nseal "awskms" {\n  region     = "us-west-2"\n  kms_key_id = "alias/vault-seal-key"\n}\n\nui = true\napi_addr = "https://vault.example.com:8200"\ncluster_addr = "https://vault.example.com:8201"\n'})}),"\n",(0,s.jsx)(n.p,{children:(0,s.jsx)(n.strong,{children:"7.2 LDAP Security"})}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-bash",children:'# Enable TLS for LDAP\n# /etc/ldap/ldap.conf\nTLS_REQCERT allow\nTLS_CACERT /etc/ssl/certs/ca-certificates.crt\n\n# Update Vault LDAP config for TLS\nvault write ldap-dkim/config \\\n    binddn="cn=admin,dc=example,dc=com" \\\n    bindpass="$LDAP_PASSWORD" \\\n    url="ldaps://ldap.example.com:636" \\\n    userdn="ou=domains,ou=dkim,dc=example,dc=com" \\\n    certificate="/etc/ssl/certs/ldap-ca.crt"\n'})}),"\n",(0,s.jsx)(n.p,{children:(0,s.jsx)(n.strong,{children:"7.3 Access Controls"})}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-bash",children:'# Create limited policies for different roles\nvault policy write dkim-operator - <<EOF\n# Read-only access to DKIM keys\npath "dkim/*" {\n  capabilities = ["read", "list"]\n}\n\n# No access to archived keys\npath "dkim/*/archive/*" {\n  capabilities = ["deny"]\n}\nEOF\n\nvault policy write dkim-admin - <<EOF\n# Full access to DKIM keys\npath "dkim/*" {\n  capabilities = ["create", "read", "update", "delete", "list"]\n}\n\n# Full access to LDAP backend\npath "ldap-dkim/*" {\n  capabilities = ["create", "read", "update", "delete", "list"]\n}\nEOF\n\n# Create service tokens with appropriate policies\nvault token create -policy=dkim-operator -ttl=30d -renewable=true\n'})}),"\n",(0,s.jsx)(n.p,{children:"This comprehensive guide provides a complete enterprise-grade DKIM signing solution integrating Rspamd, Hashicorp Vault, and LDAP for secure, scalable key management across multiple domains."}),"\n",(0,s.jsx)(n.h3,{id:"key-rotation-strategy",children:"Key Rotation Strategy"}),"\n",(0,s.jsx)(n.p,{children:"Implement regular key rotation for security:"}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-hcl",children:'# /etc/rspamd/local.d/dkim_signing.conf\n\ndomain {\n  example.com {\n    # Current key\n    selector = "2024a";\n    path = "/etc/rspamd/dkim/example.com/2024a.key";\n    \n    # Alternative selectors for rotation\n    selector_map = "/etc/rspamd/dkim_selectors.map";\n  }\n}\n'})}),"\n",(0,s.jsx)(n.p,{children:"Create selector mapping:"}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-bash",children:"# /etc/rspamd/dkim_selectors.map\n# Format: domain selector_name path_to_key\n\nexample.com 2024a /etc/rspamd/dkim/example.com/2024a.key\nexample.com 2024b /etc/rspamd/dkim/example.com/2024b.key\nexample.com 2023  /etc/rspamd/dkim/example.com/2023.key\n"})}),"\n",(0,s.jsx)(n.p,{children:"Key rotation script:"}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-bash",children:'#!/bin/bash\n# rotate_dkim.sh\n\nDOMAIN="example.com"\nNEW_SELECTOR="2024b"\nOLD_SELECTOR="2024a"\n\n# Generate new key\nrspamadm dkim_keygen -s $NEW_SELECTOR -d $DOMAIN -k /etc/rspamd/dkim/$DOMAIN/$NEW_SELECTOR.key\n\n# Update DNS with new public key\necho "Publish this DNS record:"\ncat /etc/rspamd/dkim/$DOMAIN/$NEW_SELECTOR.txt\n\necho "After DNS propagation, update selector in configuration"\necho "Then remove old DNS record after 30 days"\n'})}),"\n",(0,s.jsx)(n.h3,{id:"conditional-signing",children:"Conditional Signing"}),"\n",(0,s.jsx)(n.p,{children:"Sign based on specific conditions:"}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-hcl",children:"# /etc/rspamd/local.d/dkim_signing.conf\n\nenabled = true;\n\n# Basic domain configuration\ndomain {\n  example.com {\n    selector = \"mail\";\n    path = \"/etc/rspamd/dkim/mail.key\";\n  }\n}\n\n# Conditional signing rules\nsign_condition = <<EOD\nreturn function(task)\n  local from = task:get_from('mime')\n  if not from or not from[1] then\n    return false\n  end\n  \n  local from_domain = from[1]['domain']\n  local from_addr = from[1]['addr']\n  \n  -- Only sign if sender is authenticated\n  if not task:get_user() then\n    return false\n  end\n  \n  -- Don't sign forwarded mail\n  if task:has_symbol('FORWARDED') then\n    return false\n  end\n  \n  -- Don't sign if From domain differs from auth domain\n  local auth_domain = task:get_user():match(\"@(.+)\")\n  if from_domain ~= auth_domain then\n    return false\n  end\n  \n  return true\nend\nEOD\n"})}),"\n",(0,s.jsx)(n.h3,{id:"per-user-dkim-signing",children:"Per-User DKIM Signing"}),"\n",(0,s.jsx)(n.p,{children:"Different keys based on authenticated user:"}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-hcl",children:'# /etc/rspamd/local.d/dkim_signing.conf\n\nenabled = true;\n\n# Use selector based on user domain\nselector_map = "/etc/rspamd/user_selectors.map";\npath_map = "/etc/rspamd/user_keys.map";\n\n# Fallback configuration\ndomain {\n  "*" {\n    selector = "default";\n    path = "/etc/rspamd/dkim/default.key";\n  }\n}\n'})}),"\n",(0,s.jsx)(n.p,{children:"Create user mapping files:"}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-bash",children:"# /etc/rspamd/user_selectors.map\[email protected]     sales2024\[email protected]   support\[email protected] marketing\n\n# /etc/rspamd/user_keys.map  \[email protected]     /etc/rspamd/dkim/sales/sales2024.key\[email protected]   /etc/rspamd/dkim/support/support.key\[email protected] /etc/rspamd/dkim/marketing/marketing.key\n"})}),"\n",(0,s.jsx)(n.h3,{id:"dkim-with-arc-signing",children:"DKIM with ARC Signing"}),"\n",(0,s.jsx)(n.p,{children:"Enable both DKIM and ARC signing:"}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-hcl",children:'# /etc/rspamd/local.d/dkim_signing.conf\nenabled = true;\n\n# DKIM configuration\ndomain {\n  example.com {\n    selector = "mail";
1\n    path = "/etc/rspamd/dkim/mail.key";\n  }\n}\n\n# /etc/rspamd/local.d/arc.conf\nenabled = true;\n\n# ARC signing configuration  \ndomain {\n  example.com {\n    selector = "arc";\n    path = "/etc/rspamd/dkim/arc.key";\n  }\n}\n\nsign_authenticated = true;\nsign_local = true;\n'})}),"\n",(0,s.jsx)(n.h2,{id:"integration-examples",children:"Integration Examples"}),"\n",(0,s.jsx)(n.h3,{id:"postfix-integration",children:"Postfix Integration"}),"\n",(0,s.jsx)(n.p,{children:"DKIM signing works automatically with Postfix milter integration:"}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-bash",children:"# /etc/postfix/main.cf\n# Rspamd milter configuration\nsmtpd_milters = inet:localhost:11332\nnon_smtpd_milters = inet:localhost:11332  # Important for DKIM signing\nmilter_default_action = accept\nmilter_protocol = 6\n"})}),"\n",(0,s.jsx)(n.h3,{id:"multi-server-setup",children:"Multi-Server Setup"}),"\n",(0,s.jsx)(n.p,{children:"For multiple mail servers sharing DKIM keys:"}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-hcl",children:'# /etc/rspamd/local.d/dkim_signing.conf\n\nenabled = true;\n\n# Shared key location (e.g., NFS mount)\ndomain {\n  example.com {\n    selector = "shared";\n    path = "/shared/dkim/example.com/shared.key";\n  }\n}\n\n# Server-specific keys\nserver_keys = "/etc/rspamd/local_dkim_keys.map";\n'})}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-bash",children:"# /etc/rspamd/local_dkim_keys.map\n# server-specific keys for different mail servers\nmx1.example.com mx1 /etc/rspamd/dkim/mx1.key\nmx2.example.com mx2 /etc/rspamd/dkim/mx2.key\n"})}),"\n",(0,s.jsx)(n.h3,{id:"cloud-provider-setup",children:"Cloud Provider Setup"}),"\n",(0,s.jsx)(n.p,{children:"Configuration for cloud email services:"}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-hcl",children:'# /etc/rspamd/local.d/dkim_signing.conf\n\nenabled = true;\n\n# Use environment-specific selectors\ndomain {\n  example.com {\n    selector = "aws-prod";\n    path = "/etc/rspamd/dkim/aws-prod.key";\n  }\n}\n\n# Cloud-specific settings\nsign_networks = [\n  "10.0.0.0/8",      # AWS VPC\n  "172.16.0.0/12",   # Docker networks\n  "192.168.0.0/16"   # Local networks\n];\n'})}),"\n",(0,s.jsx)(n.h2,{id:"best-practices",children:"Best Practices"}),"\n",(0,s.jsx)(n.h3,{id:"security",children:"Security"}),"\n",(0,s.jsxs)(n.ol,{children:["\n",(0,s.jsxs)(n.li,{children:["\n",(0,s.jsxs)(n.p,{children:[(0,s.jsx)(n.strong,{children:"Secure key storage"}),":"]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-bash",children:"# Proper permissions\nchmod 600 /etc/rspamd/dkim/*.key\nchown _rspamd:_rspamd /etc/rspamd/dkim/*.key\n\n# Consider using external key storage\n# - Hardware Security Modules (HSM)\n# - Cloud key management services\n# - Encrypted filesystems\n"})}),"\n"]}),"\n",(0,s.jsxs)(n.li,{children:["\n",(0,s.jsxs)(n.p,{children:[(0,s.jsx)(n.strong,{children:"Key rotation"}),":"]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-bash",children:"# Rotate keys annually\n# Keep old keys available for 30 days\n# Use date-based selectors: 2024a, 2024b, etc.\n"})}),"\n"]}),"\n",(0,s.jsxs)(n.li,{children:["\n",(0,s.jsxs)(n.p,{children:[(0,s.jsx)(n.strong,{children:"Monitoring"}),":"]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-bash",children:'# Monitor DKIM signing\ngrep "dkim.*signed" /var/log/rspamd/rspamd.log\n\n# Check for signing failures\ngrep "dkim.*failed" /var/log/rspamd/rspamd.log\n'})}),"\n"]}),"\n"]}),"\n",(0,s.jsx)(n.h3,{id:"dns-configuration",children:"DNS Configuration"}),"\n",(0,s.jsxs)(n.ol,{children:["\n",(0,s.jsxs)(n.li,{children:["\n",(0,s.jsxs)(n.p,{children:[(0,s.jsx)(n.strong,{children:"Recommended DNS record"}),":"]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-dns",children:'; Use specific flags\nmail._domainkey.example.com. IN TXT "v=DKIM1; k=rsa; t=s; p=MIIBIjAN..."\n\n; Flags explanation:\n; v=DKIM1 - Version\n; k=rsa   - Key type  \n; t=s     - Strict mode (recommended)\n; p=...   - Public key\n'})}),"\n"]}),"\n",(0,s.jsxs)(n.li,{children:["\n",(0,s.jsxs)(n.p,{children:[(0,s.jsx)(n.strong,{children:"DNS testing"}),":"]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-bash",children:"# Test DNS propagation\ndig TXT mail._domainkey.example.com\n\n# Test DKIM validation\nrspamadm dkim_keygen -t -s mail -d example.com\n"})}),"\n"]}),"\n"]}),"\n",(0,s.jsx)(n.h3,{id:"performance-optimization",children:"Performance Optimization"}),"\n",(0,s.jsxs)(n.ol,{children:["\n",(0,s.jsxs)(n.li,{children:["\n",(0,s.jsxs)(n.p,{children:[(0,s.jsx)(n.strong,{children:"Key cac
1hing"}),":"]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-hcl",children:"# /etc/rspamd/local.d/dkim_signing.conf\n\n# Cache keys in memory\ncache_key = true;\ncache_expiry = 3600;\n"})}),"\n"]}),"\n",(0,s.jsxs)(n.li,{children:["\n",(0,s.jsxs)(n.p,{children:[(0,s.jsx)(n.strong,{children:"Selective signing"}),":"]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-hcl",children:"# Only sign necessary mail\nsign_authenticated = true;   # Sign authenticated mail\nsign_local = false;          # Skip local mail if not needed\nsign_inbound = false;        # Never sign inbound\n"})}),"\n"]}),"\n"]}),"\n",(0,s.jsx)(n.h2,{id:"troubleshooting",children:"Troubleshooting"}),"\n",(0,s.jsx)(n.h3,{id:"common-issues",children:"Common Issues"}),"\n",(0,s.jsxs)(n.ol,{children:["\n",(0,s.jsxs)(n.li,{children:["\n",(0,s.jsxs)(n.p,{children:[(0,s.jsx)(n.strong,{children:"Keys not loading"}),":"]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-bash",children:"# Check file permissions\nls -la /etc/rspamd/dkim/\n\n# Check configuration syntax\nrspamadm configtest\n\n# Check key format\nopenssl rsa -in /etc/rspamd/dkim/mail.key -check\n"})}),"\n"]}),"\n",(0,s.jsxs)(n.li,{children:["\n",(0,s.jsxs)(n.p,{children:[(0,s.jsx)(n.strong,{children:"DNS issues"}),":"]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-bash",children:"# Verify DNS record\ndig TXT mail._domainkey.example.com\n\n"})}),"\n"]}),"\n",(0,s.jsxs)(n.li,{children:["\n",(0,s.jsxs)(n.p,{children:[(0,s.jsx)(n.strong,{children:"Signing not working"}),":"]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-bash",children:'# Enable debug logging\n# /etc/rspamd/local.d/logging.inc\ndebug_modules = ["dkim_signing"];\n\n# Check logs\ntail -f /var/log/rspamd/rspamd.log | grep -i dkim\n\n# Test manually\necho "test" | rspamc -d example.com -f [email protected]\n'})}),"\n"]}),"\n"]}),"\n",(0,s.jsx)(n.h3,{id:"testing-dkim",children:"Testing DKIM"}),"\n",(0,s.jsxs)(n.ol,{children:["\n",(0,s.jsxs)(n.li,{children:["\n",(0,s.jsxs)(n.p,{children:[(0,s.jsx)(n.strong,{children:"Manual testing"}),":"]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-bash",children:"# Test signing with rspamc\nrspamc -d example.com -f [email protected] -r [email protected] < test_message.eml\n\n# Check for DKIM-Signature header\n"})}),"\n"]}),"\n",(0,s.jsxs)(n.li,{children:["\n",(0,s.jsxs)(n.p,{children:[(0,s.jsx)(n.strong,{children:"External validation"}),":"]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-bash",children:"# Send test message to:\n# - Gmail (check Authentication-Results header)\n# - mail-tester.com\n# - dkimvalidator.com\n"})}),"\n"]}),"\n",(0,s.jsxs)(n.li,{children:["\n",(0,s.jsxs)(n.p,{children:[(0,s.jsx)(n.strong,{children:"Automated testing"}),":"]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-bash",children:'#!/bin/bash\n# dkim_test.sh\n\nDOMAIN="example.com"\nSELECTOR="mail"\n\n# Test key generation\nif rspamadm dkim_keygen -t -s $SELECTOR -d $DOMAIN; then\n  echo "DKIM validation: PASS"\nelse\n  echo "DKIM validation: FAIL"\nfi\n\n# Test signing\nif echo "test" | rspamc -d $DOMAIN | grep -q "DKIM-Signature"; then\n  echo "DKIM signing: PASS"\nelse\n  echo "DKIM signing: FAIL"\nfi\n'})}),"\n"]}),"\n"]}),"\n",(0,s.jsx)(n.h2,{id:"monitoring-and-maintenance",children:"Monitoring and Maintenance"}),"\n",(0,s.jsx)(n.h3,{id:"monitoring-scripts",children:"Monitoring Scripts"}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-bash",children:'#!/bin/bash\n# dkim_monitor.sh\n\n# Check key file permissions\nfind /etc/rspamd/dkim/ -name "*.key" ! -perm 600 -exec echo "Bad permissions: {}" \\;\n\n# Check expiring certificates (if using cert-based keys)\nfor key in /etc/rspamd/dkim/*.key; do\n  if openssl rsa -in "$key" -noout -check 2>/dev/null; then\n    echo "Key OK: $key"\n  else\n    echo "Key ERROR: $key"\n  fi\ndone\n\n# Check DNS records\nfor domain in $(awk \'/domain.*{/ {print $1}\' /etc/rspamd/local.d/dkim_signing.conf); do\n  if dig TXT mail._domainkey.$domain +short | grep -q "v=DKIM1"; then\n    echo "DNS OK: $domain"\n  else\n    echo "DNS MISSING: $domain"\n  fi\ndone\n'})}),"\n",(0,s.jsx)(n.h3,{id:"log-analysis",children:"Log Analysis"}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-bash",children:'# Analyze DKIM signing statistics\ngrep "dkim.*signed" /var/log/rspamd/rspamd.log | \\\n  awk \'{print $6}\' | sort | uniq -c | sort -rn\n\n# Check for signing failures\ngrep "dkim.*failed\\|dkim.*error" /var/log/rspamd/rspamd.log | tail -20\n\n# Monitor key usage\ngrep "dkim.*selector" /var/log/rspamd/rspamd.log | \\\n  awk \'{print $8}\' | sort | uniq -c\n'})}),"\n",(0,s.jsx)(n.p,{children:"This comprehensive guide covers all aspects of DKIM signing with Rspamd, from basic setup to advanced enterprise configurations."})]})}function m(e={}){const{wrapper:n}={...(0,t.R)(),...e.components};return n?(0,s.jsx)(n,{...e,children:(0,s.jsx)(c,{...e})}):c(e)}}}]);

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.