Facing this issue in DKIM signing when injecting using HTTP API but with same addresses it works well for SMTP injection.
“test-4w2hq705u@srv1.mail-tester.com: runtime error: /opt/kumomta/share/policy-extras/dkim_sign.lua:160: attempt to index a nil value\nstack traceback:\n\t/opt/kumomta/share/policy-extras/dkim_sign.lua:160: in upvalue ‘do_dkim_sign’\n\t/opt/kumomta/share/policy-extras/dkim_sign.lua:264: in upvalue ‘dkim_signer’\n\t[string "/opt/kumomta/etc/policy/init.lua"]:177: in function <[string "/opt/kumomta/etc/policy/init.lua"]:173>”
Hey, we’re going to need more context, please see How to Get Help - KumoMTA Docs
kumod 2023.12.04-70fe55a8
local kumo = require ‘kumo’
local utils = require ‘policy-extras.policy_utils’
local shaping = require ‘policy-extras.shaping’
local queue_module = require ‘policy-extras.queue’
local listener_domains = require ‘policy-extras.listener_domains’
local sources = require ‘policy-extras.sources’
local dkim_sign = require ‘policy-extras.dkim_sign’
local log_hooks = require ‘policy-extras.log_hooks’
sources:setup { ‘/opt/kumomta/etc/policy/sources.toml’ }
local dkim_signer =
dkim_sign:setup { ‘/opt/kumomta/etc/policy/dkim_data.toml’ }
kumo.on(‘get_egress_path_config’, shaping:setup())
local queue_helper =
queue_module:setup { ‘/opt/kumomta/etc/policy/queues.toml’ }
kumo.on(‘init’, function()
kumo.define_spool {
name = ‘data’,
path = ‘/var/spool/kumomta/data’,
kind = ‘RocksDB’,
}
kumo.define_spool {
name = ‘meta’,
path = ‘/var/spool/kumomta/meta’,
kind = ‘RocksDB’,
}
kumo.configure_local_logs {
log_dir = ‘/var/log/kumomta’,
max_segment_duration = ‘1 minute’,
}
kumo.configure_bounce_classifier {
files = {
‘/opt/kumomta/share/bounce_classifier/iana.toml’,
},
}
kumo.start_http_listener {
listen = ‘0.0.0.0:2081’,
trusted_hosts = { ‘127.0.0.1’, ‘::1’ },
}
kumo.start_http_listener {
use_tls = true,
tls_certificate = ‘/opt/kumomta/etc/tlscerts/certificate.pem’,
tls_private_key = ‘/opt/kumomta/etc/tlscerts/private.pem’,
listen = ‘0.0.0.0:443’,
trusted_hosts = { ‘127.0.0.1’, ‘::1’ },
}
kumo.start_esmtp_listener {
listen = ‘0.0.0.0:25’,
relay_hosts = { ‘127.0.0.1’, ‘192.168.1.0/24’ },
}
kumo.on(‘smtp_server_message_received’, function(msg)
queue_helper:apply(msg)
dkim_signer(msg)
end)
kumo.on(‘http_message_generated’, function(msg)
queue_helper:apply(msg)
dkim_signer(msg)
end)
Ok, can you show your dkim_data.toml, your swaks call that worked, and your API call that didn’t?
This gives error
curl -ik ‘https://localhost/api/inject/v1’ -H ‘Content-Type: application/json’ -H ‘x-affinity-id: dd43814d-00d8-429e-a0b4-3355c68ee302’ -d ‘{“envelope_sender”: "noreply@<domain_with_dkim_key_on_dns>,“content”: “Subject: hello\n\nHello there. This is a test email for Kumo.”,“recipients”: [{“email”: “test-4w2hq705u@srv1.mail-tester.com”}]}’
This works
echo “This is the body of a test email”|swaks -f test@<domain_with_dkim_key_on_dns> -t test-cgl06906s@srv1.mail-tester.com --header “Subject: Hello There” --server localhost:25 --header ‘x-affinity-id: dd43814d-00d8-429e-a0b4-3355c68ee302’
dkim_data.toml
[base]
Default selector to assume if the domain/signature block
doesn’t specify one
selector = “test”
The default set of headers to sign if otherwise unspecified
This reccommended set comes from section 5.4.1 of RFC 6376
See https://datatracker.ietf.org/doc/html/rfc6376#section-5.4
headers = [
“From”, “Reply-To”, “Subject”, “Date”, “To”, “Cc”,
“Resent-Date”, “Resent-From”, “Resent-To”, “Resent-Cc”,
“In-Reply-To”, “References”, “List-Id”, “List-Help”,
“List-Unsubscribe”, “List-Subscribe”, “List-Post”,
“List-Owner”, “List-Archive”
]
Domain blocks match based on the sender domain of the
incoming message
[domain.“<domain_with_dkim_key_on_dns>”]
selector = “test”
headers = [“From”, “To”, “Subject” ]
algo = “sha256” # or “ed25519”. Omit to use the default of “sha256”
optional overridden filename.
Default is “/opt/kumomta/etc/dkim/DOMAIN/SELECTOR.key”
filename = “/opt/kumomta/etc/dkim/<domain_with_dkim_key_on_dns>/.key”
TODO: reception-time policy for signing based on DNS.
#policy = “TempFailIfNotInDNS” # Reject
#policy = “SignAlways” # Sign and relay
policy = “SignOnlyIfInDNS” # Don’t sign. Allow fallback to additional_signatures
You specified no From header address, and DKIM keys on the From header domain. The envelope sender is not relevant to DKIM.
This part of the error message:
/opt/kumomta/share/policy-extras/dkim_sign.lua:160: attempt to index a nil value\nstack traceback:\n\t/opt/kumomta/share/policy-extras/dkim_sign.lua:160: in upvalue 'do_dkim_sign'
corresponds to this code in the helper:
local sender_domain = msg:from_header().domain
that’s trying to read the From: header from the message, but when you inject via HTTP you’re not including one.
swaks will automatically insert a From: header.
The HTTP injection API will also add one for you if you use the builder API rather than passing in a complete message string.
I’ve pushed dkim helper: give a clearer error message when From: header is missing · KumoCorp/kumomta@7ff5c41 · GitHub which makes this error condition more clear
Also: markdown works in discord, by putting three backticks and toml at the start of your TOML and then three backticks at the end, you get formatted toml:
[base]
Default selector to assume if the domain/signature block
doesn't specify one
selector = "test"
The default set of headers to sign if otherwise unspecified
This reccommended set comes from section 5.4.1 of RFC 6376
See https://datatracker.ietf.org/doc/html/rfc6376#section-5.4
headers = [
"From", "Reply-To", "Subject", "Date", "To", "Cc",
"Resent-Date", "Resent-From", "Resent-To", "Resent-Cc",
"In-Reply-To", "References", "List-Id", "List-Help",
"List-Unsubscribe", "List-Subscribe", "List-Post",
"List-Owner", "List-Archive"
]
Domain blocks match based on the sender domain of the
incoming message
[domain."<domain_with_dkim_key_on_dns>"]
selector = "test"
headers = ["From", "To", "Subject" ]
algo = "sha256" # or "ed25519". Omit to use the default of "sha256"
optional overridden filename.
Default is "/opt/kumomta/etc/dkim/DOMAIN/SELECTOR.key"
filename = "/opt/kumomta/etc/dkim/<domain_with_dkim_key_on_dns>/<selector>.key"
TODO: reception-time policy for signing based on DNS.
#policy = "TempFailIfNotInDNS" # Reject
#policy = "SignAlways" # Sign and relay
policy = "SignOnlyIfInDNS" # Don't sign. Allow fallback to additional_signatures
Thanks a lot for the help @yearning-hyena & @free-spirited-yorksh. It works fine now.
Also, I’ve never used discord before so thanks for the tip about Markdown.
Happy to help with both!
What are you testing to replace?
(Also, the same tip works with lua, three backticks and lua and it’s also nicely formatted.
Hopefully Momentum.
Aah, well we’ve had a ton of experience with it. @free-spirited-yorksh here was it’s chief architect back in the day.
And depending on where you’re at, either @faithful-ostrich or myself probably was responsible for the initial installation.
![]()
Converting Momentum configs to KumoMTA is one of my favourite things.
