How to process emails with Mailman3?

Good day!
I am setting up an email list for my local community (just myself right now). Being a nerd, I am using Amazon SES to send and receive emails. I have mailman installed on ec2 (using "pip install mailman") and it is working well enough to send an email to the list with
/opt/mailman/venv/bin/mailman inject -f <somefile>all@mymaillist.org
SES is putting incoming mail into an S3 bucket, one file per email. Using a small python program, I use the above command to send them to the list.
When I add a user (like myself) I get a welcome email from all-request@mymaillist.org. When I respond, the mail arrives in the S3 bucket, but I do not know how to give it to mailman3. The "inject" command will not work because there is no "all-request" mailing list.
How do I feed these administrative mails to mailman3?
Thank you.
I have tried netcat the email to the ?LMTP? port, but it fails:
(venv) [ec2-user@ip-10-0-3-253 mailman]$ nc localhost 8024 < /tmp/pbTw1oqmV1rZtHXV707Jcov264hr 220 ip-10-0-3-253.ec2.internal GNU Mailman LMTP runner 2.0 500 Error: command "RECEIVED-SPF:" not recognized 500 Error: command "AUTHENTICATION-RESULTS:" not recognized 500 Error: command "" not recognized 500 Error: command "" not recognized 502 5.5.1 Too many unrecognized commands, goodbye.
here is what the first few lines of an email file look like. (Some values have been changed)
Received-SPF: pass (spfCheck: domain of lahnakoski.com designates 8.8.8.8 as permitted sender) client-ip=8.8.8.8;envelope-from=kyle@lahnakoski.com; helo=cichlid.ash.relay.mailchannels.net; Authentication-Results: amazonses.com; spf=pass (spfCheck: domain of lahnakoski.com designates 8.8.8.8 as permitted sender) client-ip=8.8.8.8;envelope-from=kyle@lahnakoski.com; helo=cichlid.ash.relay.mailchannels.net; dkim=failheader.i=@lahnakoski.com; dmarc=none header.from=lahnakoski.com; Return-Path:<kyle@lahnakoski.com> Received: from cichlid.ash.relay.mailchannels.net (cichlid.ash.relay.mailchannels.net [8.8.8.8]) by fips.wmjb.mail-manager-smtp.amazonaws.com with ESMTPS id p78l98r6nfp05h5lbb24phnsrovpcomu57smml81 (version=TLSv1.3 cipher=TLS_AES_256_GCM_SHA384) for<all-request@mymaillist.org>; Sun, 09 Mar 2025 15:13:30 +0000 (UTC) ...

On 3/10/25 12:24 AM, Kyle Lahnakoski wrote:
When I add a user (like myself) I get a welcome email from all- request@mymaillist.org. When I respond, the mail arrives in the S3 bucket, but I do not know how to give it to mailman3. The "inject" command will not work because there is no "all-request" mailing list.
How do I feed these administrative mails to mailman3?
You can use inject
with something like
/opt/mailman/venv/bin/mailman inject -f <somefile> -q command
all@mymaillist.org
but
I have tried netcat the email to the ?LMTP? port, but it fails:
this is the better way. The issue is you are not using nc
correctly.
When you do
$ nc localhost 8024 < /tmp/pbTw1oqmV1rZtHXV707Jcov264hr
you are telling nc to take commands from the file /tmp/pbTw1oqmV1rZtHXV707Jcov264hr. You need to give it a command to send the file.
-- Mark Sapiro <mark@msapiro.net> The highway is for gamblers, San Francisco Bay Area, California better use your sense - B. Dylan

Mark Sapiro wrote:
The issue is you are not using nc correctly. When you do
$ nc localhost 8024 < /tmp/pbTw1oqmV1rZtHXV707Jcov264hr
you are telling nc to take commands from the file /tmp/pbTw1oqmV1rZtHXV707Jcov264hr. You need to give it a command to send the file.
Actually, what I said is not correct. The issue is you are just sending the data rather than engaging in an LMTP dialog.
You need to send LMTP commands so you might prepend the message with
LHLO mymaillist.org
MAIL FROM: <sender@example.com>
RCPT TO: <all-request@mymaillist.org>
DATA
and append to the message
.
QUIT
-- Mark Sapiro <mark@msapiro.net> The highway is for gamblers, San Francisco Bay Area, California better use your sense - B. Dylan

Thank you for this answer. In both the inject case and the netcat case I must know the recipient. My complex setup looses that metadata; leaving me with only the message.
I guess I will be parsing the message to find that recipient.
Thanks again
On 2025-03-09 10:50 p.m., Mark Sapiro wrote:
Mark Sapiro wrote:
The issue is you are not using nc correctly. When you do
$ nc localhost 8024 < /tmp/pbTw1oqmV1rZtHXV707Jcov264hr
you are telling nc to take commands from the file /tmp/pbTw1oqmV1rZtHXV707Jcov264hr. You need to give it a command to send the file. Actually, what I said is not correct. The issue is you are just sending the data rather than engaging in an LMTP dialog.
You need to send LMTP commands so you might prepend the message with
LHLO mymaillist.org MAIL FROM: <sender@example.com> RCPT TO: <all-request@mymaillist.org> DATA
and append to the message
. QUIT
-- Mark Sapiro <mark@msapiro.net> The highway is for gamblers, San Francisco Bay Area, California better use your sense - B. Dylan
Mailman-users mailing list -- mailman-users@mailman3.org To unsubscribe send an email to mailman-users-leave@mailman3.org https://lists.mailman3.org/mailman3/lists/mailman-users.mailman3.org/ Archived at: https://lists.mailman3.org/archives/list/mailman-users@mailman3.org/message/...
This message sent to kyle@lahnakoski.com

On 3/11/25 4:30 AM, Kyle Lahnakoski wrote:
I guess I will be parsing the message to find that recipient.
Since you are doing this in a Python script anyway, just use Python's email library to parse the message and Python's smtplib to send the message to Mailman's LMTP runner.
-- Mark Sapiro <mark@msapiro.net> The highway is for gamblers, San Francisco Bay Area, California better use your sense - B. Dylan

Kyle Lahnakoski writes:
I am setting up an email list for my local community (just myself right now). Being a nerd, I am using Amazon SES to send and receive emails.
Outgoing SES is full of unpleasant surprises. In particular, they restrict use of certain headers (DKIM-Signature is the most important one from Mailman's point of view) and commandeer Message-ID (which means archivers and explicit addressees see a different Message-ID from the one subscribers see).
I have mailman installed on ec2 (using "pip install mailman") and it is working well enough to send an email to the list with
/opt/mailman/venv/bin/mailman inject -f <somefile>all@mymaillist.org
SES is putting incoming mail into an S3 bucket, one file per email. Using a small python program, I use the above command to send them to the list.
It's not obvious to me how to make this work for administrative traffic. As Mark says, it needs to find its way into the 'command' queue ('-q command'), but you may also need to specify what the command is (ie, the extension such as '-join' or '-confirm') using added metadata (the '-m' option, see 'mailman inject -h'). I don't know offhand what the format of that would be, though. (I mention that because 'mailman inject -h' specifies that the target argument is a LISTSPEC, either the List-ID or the posting address, which would not include the extension that tells Mailman what to do with the message. Just putting it in the command queue is probably enough, though.)
As far as I know, SES does no filtering incoming. If that's true, you would make life a lot easier by installing an MTA and having it relay incoming mail to Mailman as usual, rather than queuing the mail in an S3 bucket (saving the cost of the bucket). If I'm wrong, you have to trade off the benefit of filtering vs. simplicity in your operations.
Alternatively, using the 'LMTP' class from 'smtplib' your Python script is almost a one-liner. You get the intended recipient from 'Delivered-To' (if it exists, if not life is considerably more complex), the sender from 'Sender' or 'From', and just pass that on to the LMTP object's send method to send to localhost:8024 (assuming the default port).
Steve

On 2025-03-10 12:22 a.m., Stephen J. Turnbull wrote:
Alternatively, using the 'LMTP' class from 'smtplib' your Python script is almost a one-liner. You get the intended recipient from 'Delivered-To' (if it exists, if not life is considerably more complex), the sender from 'Sender' or 'From', and just pass that on to the LMTP object's send method to send to localhost:8024 (assuming the default port).
Thank you, I will try that next. It seems simple injection of the *-request@... emails into the mailist has no effect.

On 2025-03-10 9:35 p.m., Kyle Lahnakoski wrote:
On 2025-03-10 12:22 a.m., Stephen J. Turnbull wrote:
Alternatively, using the 'LMTP' class from 'smtplib' your Python script is almost a one-liner. You get the intended recipient from 'Delivered-To' (if it exists, if not life is considerably more complex), the sender from 'Sender' or 'From', and just pass that on to the LMTP object's send method to send to localhost:8024 (assuming the default port).
Thank you, I will try that next. It seems simple injection of the *-request@... emails into the mailist has no effect.
... and that WORKED! thank you!
def send(file): content = file.read() recipient_email, sender_email = get_emails(content) if not recipient_email: return with smtplib.LMTP(config.lmtp.host, config.lmtp.port) as lmtp: lmtp.sendmail(sender_email, [recipient_email], content)
participants (3)
-
Kyle Lahnakoski
-
Mark Sapiro
-
Stephen J. Turnbull