Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

opensmtpd setup documentation #32

Closed
koolfy opened this issue Jul 23, 2017 · 7 comments
Closed

opensmtpd setup documentation #32

koolfy opened this issue Jul 23, 2017 · 7 comments

Comments

@koolfy
Copy link

koolfy commented Jul 23, 2017

Hello,

over the last two weeks or so, I discussed setting up sympa behind an opensmtpd MTA.

There were quite a lot of hoops to jump through to make it work as is, so as was requested in that channel, I'm submitting a draft for your documentation based on my experiences.
I figured this is as good place as any to submit it, I apologize if it's entirely the wrong place to do so :)

I don't really care if you modify it, update it, adapt it to your formatting standards, consider it "public domain" ;)

I tried making it as clear as I can so that people understand why I did some weird things to have it work properly, so if these mechanism do improve in the future (I'm looking at you, opensmtpd), it'll be easier to simplify this doc.
If some things are silly or do not match your redaction standards, you can rephrase it all you want :)

I hope I didn't leave out any important details, be sure to test it all from zero yourself to make sure!

I hope this helps some people out there at some point.


Opensmtpd, like probably any MTA, can be setup underneath Sympa.
However, there are some tricks for it to work properly.

In this guide I will consider that Sympa has already been installed and mostly configured, has its DB setup and its web frontend in place.
We will focus on opensmtpd's role.

His duties will be:

  • Receiving e-mails that will need to be handled by Sympa, and throwing them over to Sympa
  • Sending e-mails from sympa over to the outside world

What I will not cover:

  • MX dns setup
  • SPF, Dmarc, SPF setup with opensmtpd
  • spam filtering
  • delivering email to MDAs
  • TLS PKI
  • using that same opensmtpd to submit outgoing mails directly without sympa

I will thus consider that example.com is a A record to the server's public IP, and that lists.example.com are a MX 1 record pointing to example.com

Receiving Emails

The first step will be allowing opensmtpd to receive emails, for that, add this to your opensmtpd configuration (/etc/smtpd.conf on debian)

listen on 0.0.0.0 port 25 hostname example.com pki example.com tls

Here I enabled optionnal TLS PKI, you will need to specify those lines too:

pki example.com certificate "/PATH/TO/cert.pem"	
pki example.com key "/PATH/TO/key.pem"

If you do not wish for any TLS for your ingoing emails just use this shorter line

listen on 0.0.0.0 port 25 hostname example.com

Now we receive emails directed to example.com, but we need a rule on what to do with them in opensmtpd, or else they will simply be rejected.

accept from any for domain "lists.example.com" alias <lists> deliver to mbox

there are several interesting things to note with that line.

  • First, this is the first mention to "lists.example.com". As it points to the example.com dns record, emails sent to lists.example.com will be delivered to example.com by the MTAs, but will retain the email header "to: something@lists.example.com"
    We use this to have openstmpd filter these mails that are exclusively aimed at sympa. This makes it easier to integrate it to an opensmtpd that handles other domains, and does other things. Just keep in mind that opensmtpd only matches the first rule, so any mails directed to the domain lists.example.com in the email headers will need to be handled by this rule, as opensmtpd will not match it against subsequent lines.
    Also be careful with earlier rules like 'for domain "*.example.com" ' as those will follow the same behaviour, and mask our lower, but more specific rule against lists.example.com.

  • Second, this line refers to an alias file named "lists". You could name it however you want, but this name must match an entry like this, on the same smtpd.conf:

    table lists db:/etc/mail/sympa/aliases.db

Yes, it is imperative that this entry be a "db:" type of table, more on this later.
This is the file where sympa will create its lists aliases, and map them to sympa processing commands.

  • Third, the "deliver to mailbox" is a simple standard opensmtpd action. This will actually NOT be acted upon IF there is an entry in the table lists that maps to a CLI command (like sympa's internal aliases do), but only for standard alias -> user mappings. So this could be used ONLY if you want to have aliases manually added to the /etc/mail/sympa/aliases.db file that map to actual user accounts.

I don't know how or why you would need this, but I will mention this as email setups are a very personal thing, and most people come up with very intricate ways of routing them. So I'm pretty sure this will be of use to someone out there :)

Of course, this action can be removed if you don't need it, and only want this rule to deliver emails to the sympa processing engine.
In this case it would simply look like this:

accept from any for domain "lists.example.com" alias <lists>

You could also have it do something else, like deliver to maildir: or probably even relay it out somwhere else. From my exprience the alias file mapping to CLI commands takes precedence over this action, but I haven't tested it that much.

As a bonus, you could increase the default size of the messages (including attachments) if you expect these, with the adding the line:

max-message-size 35M

or setting it to whatever value you'd like.
Be aware though that most email providers will probably limit this value on their side, well before your opensmtpd even sees the message. So don't expect Gmail to allow sending 50MB messages your way!

Sending emails

This is the easy one, just add this to your opensmtpd configuration, if it's not already there:

accept for any relay

If you need to pass it through a proxy email (for dkim signing, for example), you can use:

accept for any relay via smtp://PROXY_IP:PORT

That's pretty much it, as sympa will use the standard sendmail command that opensmtpd replaces uppon being installed.

The aliases thing

To process incoming mails, sympa needs to invoque processing commands.
As other mailing lists, it does so using a neat aliases trick, mapping addresses to commands like this:

somelist: "| /usr/lib/sympa/bin/queue somelist@lists.example.com"

So when somelist@lists.example.com receives an email, it goes through this aliases file, which maps this alias to this particular command.
each list needs a few of these.

sympa is actually clever enough to manage its own aliases file, so that when you create a new mailing list, it will actually add all the correct lines to this aliases file.
Without this, somelist2@lists.example.com would simply generate a 550 Bad Recipient error when trying to deliver the mail, on opensmtpd side.

The very first thing to note is that within these sympa aliases, there are actually two commands that need to be run:

somelist: "| /usr/lib/sympa/bin/queue somelist@lists.example.com"
somelist-owner: "| /usr/lib/sympa/bin/bouncequeue somelist@lists.example.com"

/usr/lib/sympa/bin/queue and /usr/lib/sympa/bin/bouncequeue on a typical debian system

These are binary helpers for sympa.
the problem is, these aliases commands wil lactually be run by opensmtpd upon receiving new messages. And opensmtpd wisely runs these with its unprivileged user account: "opensmtpd".
While this is a very good security practice, it means that the queue and bouncequeue commands will inherint the opensmtpd permissions, which do NOT have access to, for example, the sympa.conf file (which contains DB credentials), and cannot write to sympa owned directories.

Other MTAs might not see this problem as they might run these commands as root, which has all permissions, but as sympa is designed to use its own "sympa" unprivileged account too, we'll try to respect that. After all, if sympa gets compromised, we don't want it to be able to run things as root. And remember it's handling emails and has a web UI, so it's a fairly exposed service as it is.

The best option rignt now is to run these commands:

chown sympa /usr/lib/sympa/bin/queue
chown sympa /usr/lib/sympa/bin/bouncequeue

then

chmod 4755 /usr/lib/sympa/bin/queue
chmod 4755 /usr/lib/sympa/bin/bouncequeue

this will enable the setuid for the owner of the file. Which means when any user executes these two commands, they will NOT inherit from their own permissions, but instead be run with the file's owner's permissions ("sympa", as we chown'ed those two files)

If you don't want to let any user in the system inject messages into sympa, you could use 4754 or 4750 permissions, and add opensmtpd to the group the files belong to.

the easiest would be to run:

chown sympa:sympa /usr/lib/sympa/bin/queue
chown sympa:sympa /usr/lib/sympa/bin/bouncequeue
    useradd -G sympa opensmtpd
chmod 4750 /usr/lib/sympa/bin/queue
chmod 4750 /usr/lib/sympa/bin/bouncequeue

CAUTION: always remember that these files MUST always be executable by both opensmtpd AND sympa user, as actions triggered through the web UI will be run under the sympa user. This might seem obvious but if you get errors, make sure you didn't lock execution of those files out from sympa's user! ;)

Now, the second problem is that sympa must be able to refresh these aliases for opensmtpd.
This sounds simple, but as I mentionned, this action will be attempted by the unprivileged "sympa" user, not root, nor opensmtpd.

For this, sympa has a built-in mechanism that works for most MTAs:

/usr/lib/sympa/bin/sympa_newaliases-wrapper

This small binary helper is actually a very simple wrapper around the "newaliases" command, that will be run automatically after each time sympa updates its aliases list.

However, there are three issues with this in our case:

  • currently the wrapper tries to run /usr/bin/newaliases, however opensmtpd replaces this file and puts it in /usr/sbin/newaliases, so the wrapper will simply not find it.
  • /usr/sbin/newaliases, in opensmtpd, will only refresh the /etc/aliases file, which is not where sympa should write its own aliases (for a lot of reasons I will not get into, one of them being security.)
  • in any case, opensmtpd actually creates /usr/sbin/newaliases as a symlink to /usr/sbin/smtpctl, which is a very powerful command, and requires root.

For all these reasons, there is only one proper workaround that I know of:

First, use the default file where sympa will drop its aliases.
In my case, for debian, it's in

/etc/mail/sympa/aliases

Make sure this file is owned and RW by the sympa user, and that its directory (/etc/mail/sympa) is also owned by sympa, as it will need to create a a aliases.db file there.

Then, add these lines on top of this /etc/mail/sympa/aliases file:

listmaster: "| /usr/lib/sympa/bin/queue listmaster@lists.example.com"
bounce+*: "| /usr/lib/sympa/bin/bouncequeue sympa@lists.example.com"
abuse-feedback-report: "| /usr/lib/sympa/bin/bouncequeue sympa@lists.example.com"
sympa-request: postmaster
sympa-owner: postmaster

these aliases are needed for basic sympa function, and they actually need to be included in this file in our setup (due to opensmtpd only matching one rule line for this domain, and only using the "lists" aliases table we saw earlier, in case you wonder why)
Please note that the postmaster aliases do not lead to a pipe command, so these, in this form, would be processed by the "deliver to mbox" part of our smtpd.conf rule line.)

Then, we will need to create a helper script. You can put it wherever and call it whatever, but for clarity, I'll use

/usr/lib/sympa/bin/opensmtpd_refresh_lists.sh

in this file, put the following exact content:

#!/usr/bin/env bash
PATH=/usr/sbin/:$PATH
/usr/sbin/makemap hash /etc/mail/sympa/aliases < /etc/mail/sympa/aliases
/bin/chmod 744 /etc/mail/sympa/aliases.db

if your sympa aliases file is in another directory, adapt it in the script.

the PATH hack is needed because this script will be run by the "sympa" user, which on a typical debian system does not have /usr/sbin directory in its PATH env variable. Even if we call directly /usr/sbin/makemap with its absolute path, makemap will actually call itself within its code, with a relative path, so without /usr/sbin/ in PATH, it will fail to call itself with a "execlp: No such file or directory" error.

In case you wonder, makemap is also a symlink to /usr/sbin/smtpctl, and this exact syntax will trigger a sendmail compatibility mode, that is the only practical way for a non-root user to generate an aliases.db file. As of July 2017 this is the only known way to do it.

Save the file and give it the right permissions:

chown sympa:sympa /usr/lib/sympa/bin/opensmtpd_refresh_lists.sh
chmod 755 /usr/lib/sympa/bin/opensmtpd_refresh_lists.sh

Again, you could chmod it to 750 or 700 if you really don't trust other users in this system.

Now, we need to override the default aliases refresh mechanism for sympa, luckily we have an option for that that we can add to out sympa.conf file:

aliases_program /usr/lib/sympa/bin/opensmtpd_refresh_lists.sh 

To recap quickly, everytime sympa created a new list, it will add new lines to the /etc/mail/sympa/aliases plaintext file, then it will call our opensmtpd_refresh_lists.sh script wich will compile /etc/mail/sympa/aliases to a binary /etc/mail/sympa/aliases.db file, that opensmtpd will read automatically for each received mail.

This is why we need it to be a "db:" entry in our smtpd.conf file:

	table lists db:/etc/mail/sympa/aliases.db

as using a "file:" plaintext reference to the /etc/mail/sympa/aliases file would need opensmtpd to re-read this file with a "smtpctl update table lists" command that the "sympa" user simply doesn't have the permission to run.

If everything is set up, you can try creating a new list from the sympa web UI, see that you don't get any error, then send a mail to this newly created list and check that it shows up in the message archive. Add some other subscribers to that list to make sure sympa is able to send mails t othe outside world.

Do not forget to add this line to your sympa.conf :

dmarc_protection_mode dmarc_reject

or else you won't be able to relay messages from yahoo and similarly dmarc-strict providers.

A big thanks to all the people on #sympa's freenode IRC channel who helped me overcome all the challenges you don't see behind these instructions :)

@ikedas
Copy link
Member

ikedas commented Jul 24, 2017

Great documentation!

Helping makemap

I guess, if sympa_newaliases.pl feeds PATH environment variable, there may be no need for helper script:

--- a/src/sbin/sympa_newaliases.pl.in
+++ b/src/sbin/sympa_newaliases.pl.in
@@ -120,6 +120,10 @@ if ($aliases_program =~ m{\A/}) {
     $log->syslog('debug2', 'Executing "%s %s %s < %s"',
         q{--MAKEMAP--}, $aliases_db_type, $aliases_file, $aliases_file);
 
+    # Since makemap(8) of OpenSMTPD reinvokes itself without full path, we
+    # have to feed PATH environment.  Helpful to OpenBSD and Debian.
+    $ENV{PATH} = '/usr/sbin';
+
     unless (open STDIN, '<', $aliases_file) {
         $log->syslog('err', 'Canot open %s', $aliases_file);
         exit 1;

Tracking feature

Sendmail command of OpenSMTPD seems not supporting options for DSN (-N) and envelope ID (-V). These options are used by message tracking feature of Sympa. I suppose sympa_smtpc may be used as a replacement of sendmail command to support this feature (adding a line such as

sendmail /path/to/sympa_smtpc --esmtp localhost

to sympa.conf).

(Unfortunately, currently sympa_smtpc looks disabled on Debian package).


Update: Correct sympa_smtpc setting.

@koolfy
Copy link
Author

koolfy commented Jul 24, 2017

Updated a ton of typos that were pointed out to me.

I agree for the PATH env, but in my case at the first aliases.db generation the chmod was still necessary… so for now it'll only remove one line

@ikedas
Copy link
Member

ikedas commented Jul 24, 2017

I agree for the PATH env, but in my case at the first aliases.db generation the chmod was still necessary… so for now it'll only remove one line

Maybe it's a bug: Perhaps umask (027) set by sympa.pl is applied to aliases.db (mode 640), but it should be readable by MTA user. I'll submit PR in a few days.

@ikedas
Copy link
Member

ikedas commented Jul 25, 2017

I agree for the PATH env, but in my case at the first aliases.db generation the chmod was still necessary… so for now it'll only remove one line

Maybe it's a bug: Perhaps umask (027) set by sympa.pl is applied to aliases.db (mode 640), but it should be readable by MTA user. I'll submit PR in a few days.

I found that at least Postfix doesn't mind permission: It can access to aliases.db owned by sympa:sympa and having mode 640. I'll investigate a bit to find the method not making aliases.db world-readable, if possible.

@ikedas
Copy link
Member

ikedas commented Jul 28, 2017

Maybe I found solution.

  1. If aliases file does not exist, create it.
# touch /etc/mail/sympa/aliases
  1. Make sympa_aliases writable/readable by sympa, readable by smtpd and not accessible by any other users:
# chmod 640 /etc/mail/sympa/aliases
# chown sympa:smtpd /etc/mail/sympa/aliases
  1. Use makemap to update table.
    Add following line to sympa.conf:
aliases_program /usr/sbin/makemap

@ikedas
Copy link
Member

ikedas commented Aug 22, 2017

@koolfy, could you contribute your work for Sympa documentation?
https://github.com/sympa-community/sympa-community.github.io

@ikedas
Copy link
Member

ikedas commented Feb 13, 2018

Let's continue on the new project: sympa-community/sympa-community.github.io#2

This issue is closed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants