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

Rework the anti-spoofing rule #2479

Merged
merged 19 commits into from
Nov 9, 2022
Merged

Rework the anti-spoofing rule #2479

merged 19 commits into from
Nov 9, 2022

Conversation

nextgens
Copy link
Contributor

@nextgens nextgens commented Oct 18, 2022

What type of PR?

Feature

What does this PR do?

We shouldn't assume that Mailu is the only MTA allowed to send emails on behalf of the domains it hosts.
We should also ensure that it's non-trivial for email-spoofing of hosted domains to happen

Previously we were preventing any spoofing of the envelope from; Now we are preventing spoofing of both the envelope from and the header from unless some form of authentication passes (is a RELAYHOST, SPF, DKIM, ARC)

Related issue(s)

Prerequisites

Before we can consider review and merge, please make sure the following list is done and checked.
If an entry in not applicable, you can check it or remove it from the list.

  • In case of feature or enhancement: documentation updated accordingly
  • Unless it's docs or a minor change: add changelog entry file.

@nextgens nextgens added type/feature Introduces a new feature type/backport Automatic backport this PR to the current stable release labels Oct 18, 2022
@mergify
Copy link
Contributor

mergify bot commented Oct 18, 2022

Thanks for submitting this pull request.
Bors-ng will now build test images. When it succeeds, we will continue to review and test your PR.

bors try

Note: if this build fails, read this.

bors bot added a commit that referenced this pull request Oct 18, 2022
@bors
Copy link
Contributor

bors bot commented Oct 18, 2022

try

Build succeeded:

@ghostwheel42
Copy link
Contributor

I'm not sure if completely removing the anti spoofing is the best idea - even in 2022.
We should at least make sure that rspamd rejects emails coming from outside without dkim etc.
Maybe we also could allow to disable the anti spoofing per domain (but this would raise complexity and require a change in the database schema).

@Diman0
Copy link
Member

Diman0 commented Oct 19, 2022

This PR will introduce a change in behaviour. The anti-spoofing rule is now only covered by the SPF check. We should warn about this in the release notes. If there are people who did not publish the DNS records, we have at least warned them via the release notes that envelope-from (and also header-from) spoofing is possible now.

For the linked issue, the user can use RELAYNETS as a workaround for now.

And as ghostwheel said in the chat, we must test this thoroughly.

@nextgens
Copy link
Contributor Author

Just to clarify:

  • Users only care about "header from" spoofing. The current code doesn't protect against that
  • SPF only cares about "envelope from" spoofing; it's only when used in conjunction with DMARC that it will check "alignment" of both "froms"

I will investigate how we can get rspamd to do the right thing here: reject emails that pretend to be from locally hosted domains where DMARC validation fails (ie: neither SPF nor DKIM pass), regardless of the published DMARC policy

@nextgens
Copy link
Contributor Author

bors try

bors bot added a commit that referenced this pull request Oct 19, 2022
@bors
Copy link
Contributor

bors bot commented Oct 19, 2022

try

Build succeeded:

@nextgens
Copy link
Contributor Author

bors try

bors bot added a commit that referenced this pull request Oct 19, 2022
@bors
Copy link
Contributor

bors bot commented Oct 19, 2022

try

Build succeeded:

@nextgens
Copy link
Contributor Author

nextgens commented Oct 19, 2022

If we want to go further than this, and match exactly what was done before, we should add a force_action rule:

in multimap.conf

IS_LOCAL_DOMAIN {
  type = "from";
  filter = "email:domain";
  map = "http://{{ ADMIN_ADDRESS }}/internal/rspamd/local_domains";
}

in force_actions.conf:

rules {
  WHITELIST_EXCEPTION {
    action = "reject";
    expression = "AUTH_NA & IS_LOCAL_DOMAIN";
    message = "Rejected (anti-spoofing)";
  }
}

@nextgens
Copy link
Contributor Author

bors try

bors bot added a commit that referenced this pull request Oct 19, 2022
@bors
Copy link
Contributor

bors bot commented Oct 19, 2022

try

Build succeeded:

@nextgens
Copy link
Contributor Author

bors try

bors bot added a commit that referenced this pull request Oct 19, 2022
bors bot added a commit that referenced this pull request Oct 25, 2022
@bors
Copy link
Contributor

bors bot commented Oct 25, 2022

try

Build succeeded:

@Diman0
Copy link
Member

Diman0 commented Oct 25, 2022

It looks like rspamd does not retry to load the map from the /internal/rspamd/local_domains endpoint.
If this fails on startup, then the anti-spoofing does not work.

Can we tell rspamd to retry accessing the URL on failure?
If not possible, then we could add depends_on: admin to the antispam service in the docker-compose.yml file. If we chose to do this, we must create an issue for the helm-chart project that they must add this dependency.

On first startup I saw the following in rspamd log

dimitri@mailutest:/mailu$ docker-compose logs antispam | grep local_domain
antispam_1   | 2022-10-25 11:08:10 #1(main) <jbzrdy>; map; rspamd_map_add: added map http://192.168.203.6/internal/rspamd/local_domains
antispam_1   | 2022-10-25 11:08:10 #1(main) <93me4h>; lua; lua_maps.lua:127: reuse url for http://192.168.203.6/internal/rspamd/local_domains(hash)
antispam_1   | 2022-10-25 11:08:10 #1(main) <jbzrdy>; map; rspamd_map_add: added map http://192.168.203.6/internal/rspamd/local_domains
antispam_1   | 2022-10-25 11:08:12 #8(controller) <jbzrdy>; map; http_map_error: error reading http://192.168.203.6/internal/rspamd/local_domains(192.168.203.6:80): connection with http server terminated incorrectly: IO write error: Connection refused
antispam_1   | 2022-10-25 11:08:12 #8(controller) <jbzrdy>; map; http_map_error: error reading http://192.168.203.6/internal/rspamd/local_domains(192.168.203.6:80): connection with http server terminated incorrectly: IO write error: Connection refused

When sending a test email that spoofed the a local user, it was accepted.

After restarting antispam, the anti-spoofing protection worked. The email was blocked.

dimitri@mailutest:/mailu$ docker-compose restart antispam
Restarting mailu_antispam_1 ... done
dimitri@mailutest:/mailu$ docker-compose logs antispam | grep local_domain
antispam_1   | 2022-10-25 11:08:10 #1(main) <jbzrdy>; map; rspamd_map_add: added map http://192.168.203.6/internal/rspamd/local_domains
antispam_1   | 2022-10-25 11:08:10 #1(main) <93me4h>; lua; lua_maps.lua:127: reuse url for http://192.168.203.6/internal/rspamd/local_domains(hash)
antispam_1   | 2022-10-25 11:08:10 #1(main) <jbzrdy>; map; rspamd_map_add: added map http://192.168.203.6/internal/rspamd/local_domains
antispam_1   | 2022-10-25 11:08:12 #8(controller) <jbzrdy>; map; http_map_error: error reading http://192.168.203.6/internal/rspamd/local_domains(192.168.203.6:80): connection with http server terminated incorrectly: IO write error: Connection refused
antispam_1   | 2022-10-25 11:08:12 #8(controller) <jbzrdy>; map; http_map_error: error reading http://192.168.203.6/internal/rspamd/local_domains(192.168.203.6:80): connection with http server terminated incorrectly: IO write error: Connection refused
antispam_1   | 2022-10-25 11:12:38 #1(main) <jbzrdy>; map; rspamd_map_add: added map http://192.168.203.6/internal/rspamd/local_domains
antispam_1   | 2022-10-25 11:12:38 #1(main) <m4xh84>; lua; lua_maps.lua:127: reuse url for http://192.168.203.6/internal/rspamd/local_domains(hash)
antispam_1   | 2022-10-25 11:12:38 #1(main) <jbzrdy>; map; rspamd_map_add: added map http://192.168.203.6/internal/rspamd/local_domains
antispam_1   | 2022-10-25 11:12:39 #9(controller) <jbzrdy>; map; http_map_finish: need to reread map from http://192.168.203.6/internal/rspamd/local_domains
antispam_1   | 2022-10-25 11:12:39 #9(controller) <jbzrdy>; map; http_map_finish: need to reread map from http://192.168.203.6/internal/rspamd/local_domains
antispam_1   | 2022-10-25 11:12:39 #9(controller) <jbzrdy>; map; http_map_finish: http://192.168.203.6/internal/rspamd/local_domains(192.168.203.6:80): read map data 105 bytes, next check at Tue, 25 Oct 2022 11:17:39 GMT
antispam_1   | 2022-10-25 11:12:39 #9(controller) <jbzrdy>; map; rspamd_map_save_http_cached_file: saved data from http://192.168.203.6/internal/rspamd/local_domains in /var/lib/rspamd/295c3280d50d03c568f7faeb2a2d4642eb17e147.map, 145 bytes
antispam_1   | 2022-10-25 11:12:39 #9(controller) <jbzrdy>; map; rspamd_kv_list_fin: read hash of 8 elements from http://192.168.203.6/internal/rspamd/local_domains
antispam_1   | 2022-10-25 11:12:39 #9(controller) <jbzrdy>; map; http_map_finish: http://192.168.203.6/internal/rspamd/local_domains(192.168.203.6:80): read map data 105 bytes, next check at Tue, 25 Oct 2022 11:17:39 GMT
antispam_1   | 2022-10-25 11:12:39 #9(controller) <jbzrdy>; map; rspamd_map_save_http_cached_file: saved data from http://192.168.203.6/internal/rspamd/local_domains in /var/lib/rspamd/295c3280d50d03c568f7faeb2a2d4642eb17e147.map, 145 bytes
antispam_1   | 2022-10-25 11:12:39 #9(controller) <jbzrdy>; map; rspamd_kv_list_fin: read hash of 8 elements from http://192.168.203.6/internal/rspamd/local_domains

@nextgens
Copy link
Contributor Author

bors try

bors bot added a commit that referenced this pull request Oct 25, 2022
@bors
Copy link
Contributor

bors bot commented Oct 25, 2022

try

Build succeeded:

@nextgens
Copy link
Contributor Author

nextgens commented Oct 27, 2022

Hmmff. I will just add a sleep-loop workaround

The level of complexity to get this to work properly is very high and I really think that it's out of scope for this PR.
To do it properly we need:

  1. to ensure that redis has loaded fully (before admin). That may require Own redis Dockerfile #2055
  2. then load admin and ensure that admin is working (that requires the socrates fixes)
  3. then load up rspamd

We probably can't and shouldn't load up nginx until all the containers are up.

@nextgens
Copy link
Contributor Author

bors try

bors bot added a commit that referenced this pull request Oct 27, 2022
@bors
Copy link
Contributor

bors bot commented Oct 27, 2022

try

Build succeeded:

@Diman0
Copy link
Member

Diman0 commented Oct 28, 2022

I don't see the print line. It doesn't bother me. People will not notice it anyway. But I suspect it will be logged when using the module logging:

import logging
logging.warning ("Admin is not up just yet, retrying in 1 second")

I'm not sure if level info will be visible.

At least it is working. The map is successfully downloaded on every redeployment (with recreating all containers).

@Diman0
Copy link
Member

Diman0 commented Oct 28, 2022

I already tested that sending email with spoofed envelope-from/header-from works if the SPF allows it.
It is rejected (the new spoof protection) when your IP is not in the SPF.

A small list we need to further test:

  • relayMX
  • RELAYNETs
  • authenticated users (after all we also run those emails through rspamd too)
  • fetchmail

@Aaron-Ritter
Copy link

@Diman0 what do you mean by relayMX, Relayed domain?
what I tested successfuly so far on our productive system:

  • RELAYNETs
  • authenticated users
  • mx02 (Relay Domain) pointing to mx01 (with Rework the anti-spoofing rule #2479 build active). I will activate this build on the mx02 as well and update you.

@Diman0
Copy link
Member

Diman0 commented Oct 29, 2022

@Diman0 what do you mean by relayMX, Relayed domain? what I tested successfuly so far on our productive system:

* RELAYNETs

* authenticated users

* mx02 (Relay Domain) pointing to mx01 (with [Rework the anti-spoofing rule #2479](https://github.com/Mailu/Mailu/pull/2479) build active). I will activate this build on the mx02 as well and update you.

Yes. I mean relayed domain. So I will test the same things you apparently already tested successfully.
It is good to know all my tests should be successful (since you already confirmed this).

It is important to test all these paths because rspamd is used when email is received and when it is send.

It might also be relevant to test fetchmail. Email inboxes synced with fetchmail should also trigger rspamd.

@Aaron-Ritter
Copy link

Aaron-Ritter commented Nov 1, 2022

the latest change around delaying rspamd start-up did not work for me, but I realize you changed the admin container as well:

antispam_1   | Admin is not up just yet, retrying in 1 second
antispam_1   | Admin is not up just yet, retrying in 1 second
antispam_1   | Admin is not up just yet, retrying in 1 second
antispam_1   | Admin is not up just yet, retrying in 1 second
antispam_1   | Admin is not up just yet, retrying in 1 second
antispam_1   | Admin is not up just yet, retrying in 1 second
antispam_1   | Admin is not up just yet, retrying in 1 second
antispam_1   | Admin is not up just yet, retrying in 1 second

so I have now 3 images live for testing:

docker-compose.yml:    image: mailuci/admin:pr-2479
docker-compose.yml:    image: mailuci/postfix:pr-2479
docker-compose.yml:    image: mailuci/rspamd:pr-2479

@Aaron-Ritter
Copy link

Aaron-Ritter commented Nov 7, 2022

The test build is working well so far. We have all our mail servers running with the latest build for the past 6 days and I found on our primary mail servers and relay servers emails rejected successfully and the rest working as expected, the only thing we don't use at all and therefore couldn't test is fetchmail.

And its really beneficial for debugging misconfigured sender to have all the rejected mails in rspamd history

@Diman0
Copy link
Member

Diman0 commented Nov 9, 2022

The test build is working well so far. We have all our mail servers running with the latest build for the past 6 days and I found on our primary mail servers and relay servers emails rejected successfully and the rest working as expected, the only thing we don't use at all and therefore couldn't test is fetchmail.

And its really beneficial for debugging misconfigured sender to have all the rejected mails in rspamd history

Using this specific test build might be fine. Using master is not recommended. The active development branch (master) will contain breaking changes. We also do not guarantee that the master images offer a working Mailu stack (although we strive to achieve this of course).

Thank you for confirming everything works as expected!
I can confirm that fetchmail is working as expected. I still need to test the other points.

Copy link
Member

@Diman0 Diman0 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm

@mergify
Copy link
Contributor

mergify bot commented Nov 9, 2022

bors r+

@Aaron-Ritter
Copy link

Using this specific test build might be fine. Using master is not recommended. The active development branch (master) will contain breaking changes. We also do not guarantee that the master images offer a working Mailu stack (although we strive to achieve this of course).

Thank you for confirming everything works as expected!
I can confirm that fetchmail is working as expected. I still need to test the other points.

Thanks for the clarification, and yes we use 1.9 for all other services. plus the test build for rspamd admin and postfix.

@bors
Copy link
Contributor

bors bot commented Nov 9, 2022

Build succeeded:

@bors bors bot merged commit 0839490 into Mailu:master Nov 9, 2022
@Aaron-Ritter
Copy link

will this be added to the 1.9 container build?

@nextgens
Copy link
Contributor Author

That's not planned. While it's undeniably broken, this PR adds new features too and changes the current behavior.

@Aaron-Ritter
Copy link

Thanks @nextgens, that makes sense :) How would you address this temporarily in a production system.
As based on my understanding override wont work for all the changes would you rather:

  • Replace / add files at container creation with a combination of e.g. an init-container or file mount and override
  • Create a custom build (container image) similar to mailuci/admin:pr-2479
  • Only use override where possible and wait for a new official build

@nextgens
Copy link
Contributor Author

I'd override the minimum to make it work (probably disabling anti-spoofing altogether) and wait for next release

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type/feature Introduces a new feature
Projects
None yet
Development

Successfully merging this pull request may close these issues.

The anti-spoofing control in Mailu is over-zealous
4 participants