How to get DKIM Public Keys for a specific domain programmatically

Since we are planning to use KumoMTA for our clients, we want to know a way to get the generated DKIM Public Key of a specific domain (client’s domain) using API maybe or another way.

do you guys have any idea if we need to build something using an SSH Tunnel or maybe we miss something from kumomta docs?

Thank you all

Yeah, you can do this with an API

kumo.on("smtp_server_message_received", function(msg)
    
    -- Fetch the DKIM key and selector dynamically using the domain
    local value = fetch_dkim_key_and_selector(msg:from_header().domain)
    
    local key = value[1]
    local selector = value[2]    
    
    if not key or not selector then
        error("Failed to fetch DKIM key and selector")
    end

    -- Create a DKIM signer
    local signer = kumo.dkim.rsa_sha256_signer {
        domain = msg:from_header().domain,
        selector = selector,
        headers = {
            "From", "Sender", "Reply-To", "Subject", "Date", "Message-ID", "To",
            "Cc", "MIME-Version", "Content-Type", "Content-Transfer-Encoding", "Content-ID",
            "Content-Description", "Resent-Date", "Resent-From", "Resent-Sender",
            "Resent-To", "Resent-Cc", "Resent-Message-ID", "In-Reply-To", "References",
            "List-Id", "List-Help", "List-Unsubscribe", "List-Subscribe", "List-Post",
            "List-Owner", "List-Archive", "List-Unsubscribe-Post", "Feedback-ID"
        },
        key = {
            key_data = key,
        }
    }

    -- Sign the message with DKIM
    msg:dkim_sign(signer)
end)
-- Define the DKIM API URL as a constant
local DKIM_API_URL = "http://127.0.0.1/get_dkim.php"

local function fetch_dkim_key_and_selector(domain)
    -- Build the HTTP client
    local client = kumo.http.build_client {}

    -- Create the POST request
    local request = client:post(DKIM_API_URL)

    -- Set the request body with the domain
    request:form_multipart_data {
        domain = domain,
    }

    -- Send the request and capture the response
    local response = request:send()

    -- Handle the response
    if response:status_is_success() then
        local data = kumo.serde.json_parse(response:text())
        if data.success and data.data and data.data.private_key and data.data.selector then
            return data.data.private_key, data.data.selector
        else
            error("Invalid response structure: Missing key or selector")
        end
    else
        error(
            string.format(
                "Failed to fetch DKIM key: HTTP %d %s",
                response:status_code(),
                response:status_reason()
            )
        )
    end
end

Something like this should do the trick :slightly_smiling_face:

This should be a local program caching the DKIM keys for a few hours. Looking up from a remote server every time an email is sent would slowdown the MTA

The above shows how to dynamically sign messages with private keys, but what you said is talking about the public key:

get the generated DKIM Public Key of a specific domain (client’s domain)

Did you mean the private key? Why do you need the public key? Are you validating signatures?

Any time you need to cache you can use memoize.

It seems there might be a misunderstanding. I’m asking about getting the DKIM public key for a specific domain (client’s domain), not the private key used for signing emails.

When a client configures DKIM for their domain, they need to publish their DKIM public key as a TXT record in their DNS. This is how receiving mail servers can verify the authenticity of emails sent from that domain.

To clarify:

Why do I need the public key? The public key is used by recipient mail servers to verify the authenticity of the emails coming from the domain. It’s critical for DNS configuration. For each domain using DKIM, the public key must be published as a DNS TXT record under a specific selector.

What do I want to get programmatically? I’m looking for a way to retrieve the generated DKIM public key for a specific domain that is set up with KumoMTA so that the client can add it to their DNS records.

The public key is associated with a selector (e.g., selector1._domainkey.domain.com), and the DNS TXT record will look something like this:

selector1._domainkey.domain.com IN TXT “v=DKIM1; k=rsa; p=MIIBIjANBgkqh..etc”

I need to retrieve this public key so that it can be added to the client’s DNS records for verification purposes and passing it. maybe an API to access and retrieve each domain’s pubkey.

So here’s the catch in my mind: the public key has to already be in the client’s DNS in order to retrieve it.

Hey, I think there’s a bit of confusion here. The DKIM key generation is done inside KumoMTA instance, and the public key needs to be published in the client’s DNS to verify the DKIM signature.

To clarify:

We generates both the private and public DKIM keys for each domain same as mentioned in the docs.

The private key is used to sign the emails, while the public key must be added to the client’s DNS as a TXT record so that receiving mail servers can verify the authenticity of the emails.

Now, to retrieve the public DKIM key, we can’t simply fetch it from DNS (since it’s not there yet), but you can get it directly from KumoMTA’s server.

what we need or hope it’s there is How?

That will be up to you, you have to choose how you generate the key, and how you deliver it from there. You could write it to a specific spot on disk and then expose it via API that you make, or your web UI could read it from disk.

The public key is not required to sign a message, only the private key

KumoMTA plays no part in how you choose to provision the key pair, or in how transfer and deploy the public key to DNS

last question, can we Centralized DKIM Configuration for KumoMTA to sign all messages including different domains of our clients ?

Maybe this solution can save the day

So you can use any number of methods to centralize config, including things like Git or a central API. It’s common to store in a database and write files to disk with an agent.

You can sign and multi-sign. Many ESPs will sign their domain and the client domain.

You can generate the key pair yourself and supply it to KumoMTA if you prefer