Merge pull request #174 from simple-login/spf-update

Spf update
This commit is contained in:
Son Nguyen Kim 2020-05-09 18:04:43 +02:00 committed by GitHub
commit 54b32be321
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 86 additions and 21 deletions

View File

@ -559,6 +559,7 @@ Below are pointers to different topics:
- [UFW - uncomplicated firewall](docs/ufw.md)
- [SES - Amazon Simple Email Service](docs/ses.md)
- [Upgrade existing SimpleLogin installation](docs/upgrade.md)
- [Enforce SPF](docs/enforce-spf.md)
## Contributing

View File

@ -64,7 +64,9 @@
</div>
<!-- END Change email -->
{% if spf_available %}
<!--
<div class="card">
<form method="post">
<input type="hidden" name="form-name" value="force-spf">
@ -92,6 +94,7 @@
</div>
</form>
</div>
-->
{% endif %}
<div class="card">

View File

@ -63,7 +63,7 @@
Profile
</div>
<div>
These informations will be filled up automatically when you use "Sign in with SimpleLogin" button
This information will be filled in automatically when you use "Sign in with SimpleLogin" button.
</div>
<div class="form-group mt-3">
<label class="form-label">Name</label>

51
docs/enforce-spf.md Normal file
View File

@ -0,0 +1,51 @@
Some email services like Gmail, Protonmail, etc don't have a strict SPF record (`-all`) to support the "classic" email forwarding
that is usually used for group mailing list. In this scenario, an email is sent to a group is forwarded as-is,
breaking therefore the SPF.
A malicious hacker could use this security fail to impersonate your alias via the reverse-alias. This rarely happens
as the reverse-alias is generated randomly and is unique for each sender.
However if you want to prevent this kind of attack, you can enforce the SPF policy even if your mailbox uses a "soft" policy.
1) Install `postfix-pcre`
```bash
apt install -y postfix-pcre
```
2) Add `/etc/postfix/body_checks.pcre` file with the following content
```
/^X-SimpleLogin-Client-IP:/ IGNORE
```
3) Add `/etc/postfix/client_headers.pcre` with the following content
```
/^([0-9a-f:.]+)$/ prepend X-SimpleLogin-Client-IP: $1
```
4) Add the following lines to your Postfix config file at `/etc/postfix/main.cf`
```
body_checks = pcre:/etc/postfix/body_checks.pcre
smtpd_client_restrictions = pcre:/etc/postfix/client_headers.pcre
```
5) Enable `ENFORCE_SPF` in your SimpleLogin config file
```
ENFORCE_SPF=true
```
6) Restart Postfix
```bash
systemctl restart postfix
```
7) Restart SimpleLogin mail handler
```bash
sudo docker restart sl-email
```

View File

@ -91,6 +91,8 @@ from server import create_app
# can happen when user "Reply All" on some email clients
_SELF_FORWARDING_STATUS = "550 SL self-forward"
_IP_HEADER = "X-SimpleLogin-Client-IP"
# fix the database connection leak issue
# use this method instead of create_app
@ -367,6 +369,8 @@ def handle_forward(envelope, smtp: SMTP, msg: Message, rcpt_to: str) -> (bool, s
delete_header(msg, "Reply-To")
delete_header(msg, "Sender")
delete_header(msg, _IP_HEADER)
# change the from header so the sender comes from @SL
# so it can pass DMARC check
# replace the email part in from: header
@ -470,25 +474,31 @@ def handle_reply(envelope, smtp: SMTP, msg: Message, rcpt_to: str) -> (bool, str
mailb: Mailbox = Mailbox.get_by(email=mailbox_email)
if ENFORCE_SPF and mailb.force_spf:
if msg["X-SimpleLogin-Client-IP"]:
r = spf.check2(
i=msg["X-SimpleLogin-Client-IP"], s=envelope.mail_from.lower(), h=None
)
# TODO: Handle temperr case (e.g. dns timeout)
# only an absolute pass, or no SPF policy at all is 'valid'
if r[0] not in ["pass", "none"]:
LOG.d(
"SPF validation failed for %s (reason %s)", mailbox_email, r[0],
if msg[_IP_HEADER]:
LOG.d("Enforce SPF")
try:
r = spf.check2(i=msg[_IP_HEADER], s=envelope.mail_from.lower(), h=None)
except Exception:
LOG.error(
"SPF error, mailbox %s, ip %s", mailbox_email, msg[_IP_HEADER]
)
return False, "550 SL E11"
else:
# TODO: Handle temperr case (e.g. dns timeout)
# only an absolute pass, or no SPF policy at all is 'valid'
if r[0] not in ["pass", "none"]:
LOG.error(
"SPF fail for mailbox %s, reason %s, failed IP %s",
mailbox_email,
r[0],
msg[_IP_HEADER],
)
return False, "451 SL E11"
else:
LOG.d(
"Could not find X-SimpleLogin-Client-IP header %s -> %s",
mailbox_email,
address,
LOG.warning(
"Could not find %s header %s -> %s", _IP_HEADER, mailbox_email, address,
)
delete_header(msg, "X-SimpleLogin-Client-IP")
delete_header(msg, _IP_HEADER)
# only mailbox can send email to the reply-email
if envelope.mail_from.lower() != mailbox_email.lower():

View File

@ -1,8 +1,8 @@
"""empty message
Revision ID: 126c5af661b3
Revises: 026e7a782ed6
Create Date: 2020-05-08 23:01:13.644821
Revision ID: bdf76f4b65a2
Revises: 925b93d92809
Create Date: 2020-05-09 14:38:21.695415
"""
import sqlalchemy_utils
@ -11,8 +11,8 @@ import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = '126c5af661b3'
down_revision = '026e7a782ed6'
revision = 'bdf76f4b65a2'
down_revision = '925b93d92809'
branch_labels = None
depends_on = None