mailcow / mailcow-dockerized

mailcow: dockerized - 🐮 + 🐋 = 💕
https://mailcow.email
GNU General Public License v3.0
8.94k stars 1.17k forks source link

Implement SRS for forwarded mail #2418

Open alvinhochun opened 5 years ago

alvinhochun commented 5 years ago

(Previously discussed in #110 and #1189)

Is your feature request related to a problem? Please describe. Forwarded mail will fail SPF on receiving end.

Describe the solution you'd like I would like to have SRS implemented for forwarded mail.

Additional context I've used postsrsd in the past and it worked without issues. Though its README does mention that using sender_canonical_maps will cause the rewriting to happen even if the mail is not forwarded at all, which could be an issue to some other users.

In order to test postsrsd again, I've set it up on a new testing mail server running mailcow-dockerized. I built and installed postsrsd on the host, changed its config with the correct mail domain and have it listen on 172.22.1.1. main.cf was changed like this (which I've probably done poorly):

diff --git a/data/conf/postfix/main.cf b/data/conf/postfix/main.cf
--- a/data/conf/postfix/main.cf
+++ b/data/conf/postfix/main.cf
@@ -117,8 +117,11 @@ virtual_mailbox_base = /var/vmail/
 virtual_mailbox_domains = proxy:mysql:/opt/postfix/conf/sql/mysql_virtual_domains_maps.cf
 recipient_bcc_maps = proxy:mysql:/opt/postfix/conf/sql/mysql_recipient_bcc_maps.cf
 sender_bcc_maps = proxy:mysql:/opt/postfix/conf/sql/mysql_sender_bcc_maps.cf
-recipient_canonical_maps = proxy:mysql:/opt/postfix/conf/sql/mysql_recipient_canonical_maps.cf
-recipient_canonical_classes = envelope_recipient
+sender_canonical_maps = tcp:172.22.1.1:10001
+sender_canonical_classes = envelope_sender
+recipient_canonical_maps = tcp:172.22.1.1:10002,
+  proxy:mysql:/opt/postfix/conf/sql/mysql_recipient_canonical_maps.cf
+recipient_canonical_classes = envelope_recipient, header_recipient
 virtual_mailbox_maps = proxy:mysql:/opt/postfix/conf/sql/mysql_virtual_mailbox_maps.cf
 virtual_minimum_uid = 104
 virtual_transport = lmtp:inet:dovecot:24

(I'll skip the wrestling with firewalld.)

I did some quick tests (setting forwarding via Roundcube to Gmail and checking the raw message there) and it seems to work fine. But this is a pretty hackish setup and I don't know if everything works (probably breaks apart when docker starts/stops).

(Btw I tried getting a bounce intentionally by forwarding to a non-existent user somewhere (with or without SRS) and the non-delivery notification ends up being sent to the original sender, which is probably very bad as it not only spams the original senders but also leaks the forwarding address. Perhaps this should be in a separate issue.)

andryyy commented 5 years ago

I am waiting for Rspamd to implement it.

alvinhochun commented 5 years ago

How much better is having it in Rspamd than using a simple postsrsd setup? (Rspamd is probably way more flexible with scripting but I wonder how necessary this is.)

Also, I just realized that BCC maps too does forwarding, but unlike the other forwarding (redirect through sieve filter) the forwarded mail doesn't show up in Rspamd's history (the other did) and that bounce non-delivery notification doesn't get sent to the original sender. Postsrsd still does SRS rewrite with my test setup, but what if it's implemented in Rspamd but the forwarded mail doesn't go through it?

extremeshok commented 5 years ago

This might help you out : https://github.com/rspamd/rspamd/issues/1789

andryyy commented 5 years ago

BCC maps are not meant to be used for forwarding at all btw.

stale[bot] commented 5 years ago

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

kwisatz commented 5 years ago

So what's the gist of this thread? We'll wait for https://github.com/rspamd/rspamd/issues/1789 to implement it? IMHO there's no way around SRS if you're for instance sending mail to an alias that forwards it to your own mailserver which will reject the mail that claims to be from you but is from the forwarding server rather than a SASL-authenticated user.

andryyy commented 5 years ago

You can implement it to Rspamd or help Vsevo with it.

There are also sieve redirects.

leifnel commented 5 years ago

I'd like to see this implemented too

ghost commented 5 years ago

Same here @leifnel , seeing as https://github.com/rspamd/rspamd/issues/1789 is closed and has the labels "bug" and "wontfix" it seems that rspamd won't be implementing this.

Is there anyone that has an alternative way to achieve this? With postsrsd for example, as the initial message explains. Or through sieve redirects as @andryyy suggested?

I've been unable to find the right instructions to get this sorted. If anyone is willing to help, I'll be glad to receive some advice.

TiiFuchs commented 3 years ago

I stumbled upon this issue now, since I have a few mail aliases to forward externally.

I'm somehow surprised that mailcow is not able to do SRS. :/ I think it's kinda important. So here's a little push.

Until then: Any ways to workaround this?

PAStheLoD commented 3 years ago

@TiiFuchs so depending on how much around working you can put up with, SRS Milter might work for you. (If I remember correctly I picked this because it was relatively the most up-to-date and straightforward to setup and configure.)

TiiFuchs commented 2 years ago

@PAStheLoD Do you have an example docker-compose.yml and maybe configuration files for SRS Milter so I have something to begin with?

PAStheLoD commented 2 years ago

@TiiFuchs

Sorry, no docker-compose. I'm using it with ISPConfig. So just a VM + Postfix + SRS Milter in Docker. There's not much to configure either. (I use the docker run command from the README. Except for the domain name it's identical. And simply modify postfix's main.cf, add the milter to the list of milters using the smtpd_milters variable. I added it at the end of the list.)

Does this help you get started?

TiiFuchs commented 2 years ago

I configured it accordingly, but it does not seem to work. I don't get any output or errors either :/ Absolutely no idea what goes wrong here. :(

TiiFuchs commented 2 years ago

It seems that srs-milter rewrites mails that gets delivered to a local mail account, but not if the mail gets redirected to another external mail account... I don't understand why...?

TiiFuchs commented 2 years ago

It seems that it's not working with mails redirected via a configured alias in the mailcow admin. But it seems too, that you don't need SRS, if you configure it (like it is described on the alias section in mailcow admin) as sieve filter via SOGo Filter settings. 🤔

So I'm fairly confused now, tbh.

PAStheLoD commented 2 years ago

I'm not familiar with the inner workings of mailcow, but if the milter setting is in the postfix config you can tcpdump the traffic between the milter and postfix.

[...] you don't need SRS, if you configure it (like it is described on the alias section in mailcow admin) as sieve filter via SOGo Filter settings

So it's not needed because there's some component that implements a similar rewrite functionality via sieve? :o How? :0

TiiFuchs commented 2 years ago

As far as my tests go, the mails redirected via sieve filter sends it with headers as if it were sent from your mailcow mailbox. So SPF passes, since it checks if your mailserver is eligible for sending mails for YOUR domain, not that other freemail domain the original mail was from. I'm not that deep in the matter, so I don't know for sure what it is.

I tested it from gmx.de -> my mailcow instance -> gmail, and gmail shows my mailbox in X-Sieve-Redirected-From, Delivered-To, To Headers. And it is listed in the ARC-Authentication-Results, Received and Authentication-Results Headers...

kwisatz commented 1 year ago

Coming back to this, seeing how I already posted in 2019. Frustrating to be 4 years later and still have the same issue without a good solution.

I found this blog-post here https://nowhere.dk/articles/implementing-srs-with-mailcow/ which dates from 2020 already, but it seems adapted to mailcow-dockerized, so I'll probably be giving this a go. Or alternatively try the same using srs-milter instead of postsrsd.

I'm not entirely happy with the way srs is handled through that regex in the blogpost though. I can see how it simplifies things, but it would be much better if somehow we could pass the mail through postsrsd / the milter every time there are non-local domains in the goto/alias list.

EDIT: This original post (from 2017) contains an example where the recipient domains that need rewriting aren't based on the regex, but rather just look at the list of local domains using an SQL query. IMHO that is the correct way to go about this.

Since then, postsrsd has been released in a newer version that brings support for the milter protocol, rather than only the canonicalization protocol. Unfortunately, looking at the postsrsd config file it doesn't seem to bring any support for lookup maps.

All in all, I'm afraid this won't be a quick fix-up and likely turn into a week-end project. Our users will have to bear with the current situation for a little while longer.

snevas commented 1 year ago

As far as my tests go, the mails redirected via sieve filter sends it with headers as if it were sent from your mailcow mailbox. So SPF passes, since it checks if your mailserver is eligible for sending mails for YOUR domain, not that other freemail domain the original mail was from. I'm not that deep in the matter, so I don't know for sure what it is.

I tested it from gmx.de -> my mailcow instance -> gmail, and gmail shows my mailbox in X-Sieve-Redirected-From, Delivered-To, To Headers. And it is listed in the ARC-Authentication-Results, Received and Authentication-Results Headers...

Seconded. SPF succeeds because the sieve filter redirect updates the Return-Path. Other mail forwarders do not do this and cause SPF to fail.

kwisatz commented 1 year ago

The sieve approach also seems to be recommended looking at a thread about postsrsd. Still, I think we'll need both solutions since the sieve approach will not (AFAIK) work for aliases.

I just wanted to add a small thought I had about this earlier this week:

I feel like, if mailcow does not currently support SRS out of the box, then maybe aliases should be restricted to local mail only? The question to ask here of course being: What is the lesser evil? Not allowing to use external domains for aliases or allowing it, but failing to make people aware that it will most likely not work (with a majority of email providers today).

At the very least a note on the alias form, informing people that this is not guaranteed to work if using external domains as targets would be a good thing to add, don't you agree?

kwisatz commented 1 year ago

I think I have a set-up that works. At least my latest tests suggest it does.

I'll write this up another time though, giving that it's already quite late and I haven't had dinner yet and I don't want to just dump my unsorted, raw notes on you.

Maybe @andryyy can let us know what the initial idea was behind using extra.cf because I will have a question about that.

kwisatz commented 1 year ago

OK, here goes nothing! Disclaimer: I am not a postfix export, far from it. What I'll supply here will be a gist of what I did as well as a series of (rhetorical?) questions. I am planning on creating an even more detailed post on this once I find the time and have hopefully received or found answers to some of my questions.

As I outlined before, I didn't want to go the regexp/pcre route because our customers would not understand or remember that. My goal was to have all emails that are sent from an external address and go back out to an external address be routed through postsrsd.

In postfix terminology, that means selecting a specific transport depending on the recipient. For local addresses (sender or receiver), we don't need to route the mail through postsrsd, but we do for external addresses (sender and recipients).

The first step is to add a container for postsrsd to the compose project by adding another service to your docker-compose.override.yml file:

version: '2.1'
services:

  […]

  postsrsd-mailcow:
    image: ajoergensen/postsrsd:latest
    restart: always
    environment:
      - SRS_DOMAIN=mail.mymx.lu
      - SRS_SECRET=************
    networks:
      mailcow-network:
        ipv4_address: ${IPV4_NETWORK:-172.22.1}.42

You can generate the secret any way you want, e.g. openssl rand -base64 32. Then run docker compose up -d to pull the image and spin up the container.

All the files that I'll mention from here on can be found on your host system at /opt/mailcow-dockerized/data/conf/postfix/. All proxy:mysql files are inside its sql/ subdirectory.

We'll start by visiting extra.cf. The reason I'm asking what the initial idea was, is because, well, while there is an extra.cf so that mailcow can modify main.cf without risk of losing customisations, the same isn't true for master.cf. And… even though the idea is good, it's not entirely practical, we'll see why in a bit.

In extra.cf, we'll add, resp. override the following settings:

## For postsrsd
## In order to disable postsrsd, just comment out the following two blocks and restart postfix-mailcow!
## There is also config in master.cf, but it shouldn't interfere without these config lines here

## postsrsd's reverse service is listening on port 10002
sender_canonical_classes = envelope_sender
recipient_canonical_maps = tcp:172.22.1.42:10002, proxy:mysql:/opt/postfix/conf/sql/mysql_recipient_canonical_maps.cf
recipient_canonical_classes = envelope_recipient, header_recipient

## Also for postsrsd, we override the default transport maps to use the smtpd on port 10029 for all non-local recipients
transport_maps = pcre:/opt/postfix/conf/custom_transport.pcre,
  pcre:/opt/postfix/conf/local_transport,
  proxy:mysql:/opt/postfix/conf/sql/mysql_relay_ne.cf,
  proxy:mysql:/opt/postfix/conf/sql/mysql_transport_maps.cf,
  proxy:mysql:/opt/postfix/conf/sql/mysql_non-local_srs.cf

First of all, we tell postfix to check with the service running on 172.22.1.42 port 10002 if a recipient address needs to be rewritten. This part is new. The mysql_recipient_canonical_maps.cf part is already included with mailcow and allows to rewrite certain addresses by configuring it, on an address by address basis from the web UI. The service running on IP address 172.22.1.42:10002 is postsrsd's reverse service. (I am, however, not entirely sure if we even need this configuration.)

The second part is overriding the transport_maps setting and is specific to how I want this to work. As I said, we want all external addresses to go through postsrsd, so here we're adding (main.cf already contains a transport_maps setting) the following line proxy:mysql:/opt/postfix/conf/sql/mysql_non-local_srs.cf which defines a config file that holds the SQL query necessary to determine whether or not to map to a transport (aka re-route the message).

user = mailcow
password = **********
hosts = unix:/var/run/mysqld/mysqld.sock
dbname = mailcow
query = SELECT IF(EXISTS(SELECT domain FROM domain WHERE domain = '%d' UNION SELECT alias_domain FROM alias_domain WHERE alias_domain = '%d'), NULL, 'smtp:127.0.0.1:10029') AS 'transport'

The query simply checks whether the recipient domain is a local domain. If a result is found (exists), then we return null, else we return the address of the transport we want the mail to be routed to, which is smtp:127.0.0.1:10029, another instance of the smtpd process.

And where we define that process is in master.cf (It doesn't matter where you put these lines, but I put them between the whitelist_fwd and the watchdog configuration):

# SRS config
# DR: We use a mysql query for sender_maps to ensure we do not rewrite for local senders
cleanup-srs unix  n       -       -       -       0       cleanup
  -o sender_canonical_maps=proxy:mysql:/opt/postfix/conf/sql/mysql_local_senders.cf,tcp:172.22.1.42:10001
  -o sender_canonical_classes=envelope_sender
# DR: I don't see what the use of that is
#  -o recipient_canonical_maps=regexp:/opt/postfix/conf/regex_sender_canonical_srs
# DR: Could I define this in main.cf?
# DR: proxy:mysql:... must be listed in proxy_read_maps in main.cnf

# Only non-local recipients should end up here per our transport map in extra.cf
127.0.0.1:10029 inet    n       -       -       -       -       smtpd
  -o cleanup_service_name=cleanup-srs
  -o smtpd_tls_security_level=none
  -o content_filter=smtp:
  -o smtpd_recipient_restrictions=permit_mynetworks,reject
  -o smtpd_milters=

I left my comments in to illustrate two points that we'll get to in a moment. First, what we see here is that we're asking postfix to start two processes when it starts up. One smtpd on port 10029 and one cleanup service named cleanup-srs. The smtpd is what we've returned from our SQL query to the transport_maps config setting.

The cleanup service is "cleaning up" headers. And the important bit here is the sender_canonical_maps setting. We pass two parameters, another SQL query as well as the IP and port to postsrsd's forward service. The purpose of the proxy:mysql entry is to filter out any sender addresses that are local since they do not need rewriting. The contents of mysql_local_senders.cf thus looks like this:

user = mailcow
password = ********************
hosts = unix:/var/run/mysqld/mysqld.sock
dbname = mailcow
query = SELECT '%s' FROM alias WHERE domain = '%d'

We're basically just returning a local address if the domain exists locally. Meaning, we're rewriting the original sender address to itself. The reason why querying the alias table is enough is that it also contains entries for mailboxes. mailcow uses a similar trick to what we did here. It always check the alias table to find the goto mailbox or external address. If we don't find the domain locally, then the cleanup service will use the next entry, which is the postsrsd forward service.

In essence this is all that there is to it, but, on to my comments:

  1. The blog post I have linked to above passes an additional recipient_canonical_maps option to the cleanup service. I don't see why that would be necessary or what it would achieve. Maybe someone can shed some light on this?
  2. When I had everything in place, restarted the postfix container and sent my first test-email, I came across the following message in the postfix logs:
Mar 19 21:13:10 139f3df59ee5 postfix/proxymap[379]: warning: request for unapproved table: "mysql:/opt/postfix/conf/sql/mysql_local_senders.cf"
Mar 19 21:13:10 139f3df59ee5 postfix/proxymap[379]: warning: to approve this table for read-only access, list proxy:mysql:/opt/postfix/conf/sql/mysql_local_senders.cf in main.cf:proxy_read_maps
Mar 19 21:13:10 139f3df59ee5 postfix/cleanup[414]: fatal: proxymap service is not configured for table "mysql:/opt/postfix/conf/sql/mysql_local_senders.cf"

This is easily solved by doing just that, adding it to the proxy_read_maps parameter, but this list is long and overriding it in extra.cf risks missing updates done to it, just as much as changing it in main.cf risks it getting overwritten by an update:

proxy_read_maps = proxy:mysql:/opt/postfix/conf/sql/mysql_sasl_passwd_maps_transport_maps.cf,
  proxy:mysql:/opt/postfix/conf/sql/mysql_mbr_access_maps.cf,
  proxy:mysql:/opt/postfix/conf/sql/mysql_tls_enforce_in_policy.cf,
  proxy:mysql:/opt/postfix/conf/sql/mysql_local_senders.cf,
  $sender_dependent_default_transport_maps,
  $smtp_tls_policy_maps,
 […]
  $smtp_sasl_password_maps

However, after restarting the postfix container once more, everything was working for me. To test this, I created an alias that forwards emails to an external address. I then sent an email from the same domain to that alias and it was correctly received and accepted by the external MX. I could also see that the return-path header was correctly modified

Return-Path: <SRS0=0K4v=7L=external.tld=david@this.mx.tld>

Problems

The problems that remain are those of the risk of main.cfor master.cf getting overwritten by a future mailcow update.

Feedback? / Questions?

I'd love some feedback on this. E.g. on the sql queries if you think that there's a better way to do them, on the various files and on how to handle the proxy_read_maps parameter, as well as on whether or not the recipient_canonical_maps parameters are required both in extra.cf and in master.cf, passing it to the cleanup service.

Also, if you have questions about my approach, if something isn't clear, please let me know.

Edit May 23rd 2023

If you pulled a new container after May 21st 2023, the configuration needs to be changed to adapt to a new postsrsd version.

In a nutshell, these changes are required:

extra.cf

+ sender_canonical_maps = socketmap:inet:172.22.1.42:10003:forward
  […]
  recipient_canonical_maps = 
- tcp:172.22.1.42:10002,
+ socketmap:inet:172.22.1.42:10003:reverse, 
  proxy:mysql:/opt/postfix/conf/sql/mysql_recipient_canonical_maps.cf

master.cf

  cleanup-srs unix  n       -       -       -       0       cleanup
-   -o sender_canonical_maps=proxy:mysql:/opt/postfix/conf/sql/mysql_local_senders.cf,tcp:172.22.1.42:10001
+   -o sender_canonical_maps=proxy:mysql:/opt/postfix/conf/sql/mysql_local_senders.cf,socketmap:inet:172.22.1.42:10003:forward
    -o sender_canonical_classes=envelope_sender

For more information, see these threads and @ajoergensen's original, updated blog post

TiiFuchs commented 1 year ago

@kwisatz I love that! Thanks for all the effort!

kwisatz commented 1 year ago

@andryyy did you have a chance to check my post above? Any ideas about the problems I mention, any feedback from your side?

Also, who maintains the list of third party integrations here https://docs.mailcow.email/third_party/borgmatic/third_party-borgmatic/ and how?

cjwalsh commented 1 year ago

@kwisatz thanks for this great write up.

It looks like this is SRS rewriting the local source address for any outgoing email which is sent from the local domain as well as anything external which is being forwarded by my Sieve filter. While this isn't preventing delivery, it feels like this is not really correct behaviour, so how can the configuration be updated accordingly do you think?

Also, can this solution be combined with using a smarthost relay for the SRS domain I'm using? Configuring that domain in the Mailcow UI to use a sender-dependent transport doesn't seem to do the trick.

kwisatz commented 1 year ago

@kwisatz thanks for this great write up.

It looks like this is SRS rewriting the local source address for any outgoing email which is sent from the local domain as well as anything external which is being forwarded by my Sieve filter. While this isn't preventing delivery, it feels like this is not really correct behaviour, so how can the configuration be updated accordingly do you think?

It shouldn't be doing that. If it is, then we need to figure out what is wrong about the config. The following configuration is supposed to prevent postsrsd from rewriting addresses on mails sent from local domains:

cleanup-srs unix  n       -       -       -       0       cleanup
  -o sender_canonical_maps=proxy:mysql:/opt/postfix/conf/sql/mysql_local_senders.cf,socketmap:inet:172.22.1.42:10003:forward

with mysql_local_senders.cf containing:

user = mailcow
password = ********************
hosts = unix:/var/run/mysqld/mysqld.sock
dbname = mailcow
query = SELECT '%s' FROM alias WHERE domain = '%d'

This query inside mysql_local_senders.cf is supposed to match and return the original domain, thus preventing the cleanup-srs service from ever querying postsrsd about a rewrite address.

@cjwalsh can you give an example of which addresses are being rewritten and how? Do your logs maybe show anything interesting about cleanup-srs or the SQL query that is run (but not matching)? (You might have to enable verbose logging within postfix).

Also, can this solution be combined with using a smarthost relay for the SRS domain I'm using? Configuring that domain in the Mailcow UI to use a sender-dependent transport doesn't seem to do the trick.

So what you mean is that after an external domain has been rewritten, it should get routed (transported) towards the smarthost?

With the current config that indeed won't work, because transports are queried before we even get to the SRS stuff:

transport_maps = pcre:/opt/postfix/conf/custom_transport.pcre,
  pcre:/opt/postfix/conf/local_transport,
  proxy:mysql:/opt/postfix/conf/sql/mysql_relay_ne.cf,
  proxy:mysql:/opt/postfix/conf/sql/mysql_transport_maps.cf,
  proxy:mysql:/opt/postfix/conf/sql/mysql_non-local_srs.cf

I'm thinking you should be able to switch out both config files, making it look up the non-local srs config first. But this would also require that the smtpd on port 10029 reroute the mail back into the main smtpd process, so that the transport_maps can get re-checked after the address has been rewritten. I'm not sure if that currently the case. We'd have to test that or maybe someone else can shed some light on this.

cjwalsh commented 1 year ago

@kwisatz I found the issue, the sender addresses were being rewritten by the default cleanup service, because I had added the sender_canonical_maps entry into extra.cf based on your update following the new version of postsrsd. This effectively meant that it was calling the SRS forwarder for all senders.

For clarity, this is what the postsrsd entries in my extra.cf ended up looking like for the working configuration:

data/conf/postfix/extra.cf

## For postsrsd
## In order to disable postsrsd, just comment out the following two blocks and restart postfix-mailcow!
## There is also config in master.cf, but it shouldn't interfere without these config lines here

## postsrsd's reverse service is listening on port 10002
sender_canonical_classes = envelope_sender
recipient_canonical_maps = socketmap:inet:172.22.1.42:10003:reverse, proxy:mysql:/opt/postfix/conf/sql/mysql_recipient_canonical_maps.cf
recipient_canonical_classes = envelope_recipient, header_recipient

## Also for postsrsd, we override the default transport maps to use the smtpd on port 10029 for all non-local recipients
transport_maps = pcre:/opt/postfix/conf/custom_transport.pcre,
  pcre:/opt/postfix/conf/local_transport,
  proxy:mysql:/opt/postfix/conf/sql/mysql_relay_ne.cf,
  proxy:mysql:/opt/postfix/conf/sql/mysql_transport_maps.cf,
  proxy:mysql:/opt/postfix/conf/sql/mysql_non-local_srs.cf

Also for reference, here is the section from master.cf. Note I added values for syslog_name to help with the detailed logging when I was trying to work out what was happening.

data/conf/postfix/master.cf

# SRS config
# DR: We use a mysql query for sender_maps to ensure we do not rewrite for local senders
cleanup-srs unix  n       -       -       -       0       cleanup
  -o sender_canonical_maps=proxy:mysql:/opt/postfix/conf/sql/mysql_local_senders.cf,socketmap:inet:172.22.1.42:10003:forward
  -o sender_canonical_classes=envelope_sender
# DR: I don't see what the use of that is
#  -o recipient_canonical_maps=regexp:/opt/postfix/conf/regex_sender_canonical_srs
# DR: Could I define this in main.cf?
# DR: proxy:mysql:... must be listed in proxy_read_maps in main.cnf
  -o syslog_name=cleanup-srs

# Only non-local recipients should end up here per our transport map in extra.cf
127.0.0.1:10029 inet    n       -       -       -       -       smtpd
  -o cleanup_service_name=cleanup-srs
  -o smtpd_tls_security_level=none
  -o content_filter=smtp:
  -o smtpd_recipient_restrictions=permit_mynetworks,reject
  -o smtpd_milters=
  -o syslog_name=srs

Once I got that resolved I then found that the Sieve filter for redirecting to my @gmail.com address wasn't using the SRS redirect, and it appears that this is because Dovecot in Mailcow seems to be configured in a non-default way for the sieve_redirect_envelope_from setting to use the final recipient address as the sender/return-path address for the forwarded copy of the email, so it was using a local address for the sender and hence not doing the SRS address rewrite.

This configuration can be changed, and it seems that the safest way to override the setting is by creating an extra.conf file in the data/conf/dovecot folder with the following setting:

data/conf/dovecot/extra.conf

plugin {
    sieve_redirect_envelope_from = sender
}

For the smarthost relaying, I was wondering if the smtpd settings in master.cf above for the SRS non-local recipients could be extended to specify the relayhost settings. I'm going to play around with this and see where I get to 😃

By the way, I think it is already routing the messages back from the SRS smtpd on port 10029 over to the main Postfix smtpd for delivery based on what I see in the logs, but at that point it is just doing direct delivery to the target MX server. I think because it had a match in transport_maps then it doesn't check with the sender_dependent_default_transport_maps options for example.

pgassmann commented 1 year ago

What's the current status of this? Is SRS working with this additional service and configuration? Or is there still a conflict with rspamd? Are there drawbacks to implementing SRS? Could/should this be integrated by default? If so, what's blocking this?

kwisatz commented 1 year ago

@pgassmann I'm not aware of any conflicts with rspamd. As to why this is not the default (or a checkbox within the mailcow UI), you'll need to talk to @andryyy and team… Right now this is only a howto, but with some feedback it could become a pull request including options and UI controls.

promasu commented 1 year ago

I tested this with the current Mailcow version and I don't see any problem with rspamd etc. It just works. I will have a look at this for a longer time in our setup and report back. I'd really like to see this upstream as this fixes some SPF related problems when using aliases.

kwisatz commented 11 months ago

@cjwalsh I'm now seing a similar behavior to what you described, but I can't say that I fully understand what is going on:

mailcowdockerized-postfix-mailcow-1  | Dec  5 15:53:56 ede301d27bd1 postfix/sogo/smtpd[458]: connect from mailcowdockerized-sogo-mailcow-1.mailcowdockerized_mailcow-network[172.22.1.248]
mailcowdockerized-postfix-mailcow-1  | Dec  5 15:53:56 ede301d27bd1 postfix/sogo/smtpd[458]: D18D030FB06: client=mailcowdockerized-sogo-mailcow-1.mailcowdockerized_mailcow-network[172.22.1.248], sasl_method=PLAIN, sasl_username=david@mydomain.tld
mailcowdockerized-postfix-mailcow-1  | Dec  5 15:53:56 ede301d27bd1 postfix/cleanup[459]: D18D030FB06: replace: header Received: from 56273ad8fbc2 (mailcowdockerized-sogo-mailcow-1.mailcowdockerized_mailcow-network [172.22.1.248])??(Authenticated sender: david@mydomain.tld)??by mail.mx.tld (Postcow) with ESMTPA id D18D0 from mailcowdockerized-sogo-mailcow-1.mailcowdockerized_mailcow-network[172.22.1.248]; from=<SRS0=guWu=HQ=mydomain.tld=david@mail.mx.tld> to=<david@destination.tld> proto=ESMTP helo=<56273ad8fbc2>: Received: from [127.0.0.1] (localhost [127.0.0.1]) by localhost (Mailerdaemon) with ESMTPA id D18D030FB06??for <david@destination.tld>; Tue,  5 Dec 2023 15:53:56 +0100 (CET)
mailcowdockerized-postfix-mailcow-1  | Dec  5 15:53:56 ede301d27bd1 postfix/cleanup[459]: D18D030FB06: message-id=<5a-656f3980-7-62280700@210026301>
mailcowdockerized-postfix-mailcow-1  | Dec  5 15:53:57 ede301d27bd1 postfix/qmgr[383]: D18D030FB06: from=<SRS0=guWu=HQ=mydomain.tld=david@mail.mx.tld>, size=902, nrcpt=1 (queue active)
mailcowdockerized-postfix-mailcow-1  | Dec  5 15:53:57 ede301d27bd1 postfix/sogo/smtpd[458]: disconnect from mailcowdockerized-sogo-mailcow-1.mailcowdockerized_mailcow-network[172.22.1.248] ehlo=1 auth=1 mail=1 rcpt=1 data=1 quit=1 commands=6
mailcowdockerized-postfix-mailcow-1  | Dec  5 15:53:57 ede301d27bd1 smtpd-srs/smtpd[456]: connect from localhost[127.0.0.1]
mailcowdockerized-postfix-mailcow-1  | Dec  5 15:53:57 ede301d27bd1 smtpd-srs/smtpd[456]: 2AE2830FB13: client=localhost[127.0.0.1]
mailcowdockerized-postfix-mailcow-1  | Dec  5 15:53:57 ede301d27bd1 cleanup-srs/cleanup[457]: 2AE2830FB13: message-id=<5a-656f3980-7-62280700@210026301>
mailcowdockerized-postfix-mailcow-1  | Dec  5 15:53:57 ede301d27bd1 smtpd-srs/smtpd[456]: disconnect from localhost[127.0.0.1] ehlo=1 mail=1 rcpt=1 data=1 quit=1 commands=5
mailcowdockerized-postfix-mailcow-1  | Dec  5 15:53:57 ede301d27bd1 postfix/smtp[455]: D18D030FB06: to=<david@destination.tld>, relay=127.0.0.1[127.0.0.1]:10029, delay=0.46, delays=0.4/0.01/0/0.04, dsn=2.0.0, status=sent (250 2.0.0 Ok: queued as 2AE2830FB13)
mailcowdockerized-postfix-mailcow-1  | Dec  5 15:53:57 ede301d27bd1 postfix/qmgr[383]: 2AE2830FB13: from=<SRS0=guWu=HQ=mydomain.tld=david@mail.mx.tld>, size=1491, nrcpt=1 (queue active)
mailcowdockerized-postfix-mailcow-1  | Dec  5 15:53:57 ede301d27bd1 postfix/qmgr[383]: D18D030FB06: removed

The from is already rewritten as the first, default instance of postfix/cleanup[459] receives it from the SoGo container. (Line 3 above.) Long before it reaches our postsrsd set-up. But how and when? Could it be SoGO itself just SRS'ing everything? (Not it's not, I'm seeing the same when sending from another MUA)

BTW, you also mentioned that it won't hurt delivery, but it can. I just had such a case where we got a 550 - Sender verify failed because it verified the sender domain against mx.tld (which does indeed itself not have an MX record since that domain does not normally send or receive mail).