mirror of
https://github.com/chatmail/relay.git
synced 2026-05-10 16:04:37 +00:00
feat: Sign bounce messages
Closes #873 Signed-off-by: Jagoda Ślązak <jslazak@jslazak.com>
This commit is contained in:
@@ -84,6 +84,7 @@ jobs:
|
|||||||
ssh root@staging-ipv4.testrun.org "cd relay && scripts/cmdeploy init staging-ipv4.testrun.org"
|
ssh root@staging-ipv4.testrun.org "cd relay && scripts/cmdeploy init staging-ipv4.testrun.org"
|
||||||
ssh root@staging-ipv4.testrun.org "sed -i 's#disable_ipv6 = False#disable_ipv6 = True#' relay/chatmail.ini"
|
ssh root@staging-ipv4.testrun.org "sed -i 's#disable_ipv6 = False#disable_ipv6 = True#' relay/chatmail.ini"
|
||||||
ssh root@staging-ipv4.testrun.org "sed -i 's/#\s*mtail_address/mtail_address/' relay/chatmail.ini"
|
ssh root@staging-ipv4.testrun.org "sed -i 's/#\s*mtail_address/mtail_address/' relay/chatmail.ini"
|
||||||
|
ssh root@staging-ipv4.testrun.org "sed -i 's/max_mailbox_size = 500M/max_mailbox_size = 10k/' relay/chatmail.ini"
|
||||||
|
|
||||||
- run: ssh root@staging-ipv4.testrun.org "cd relay && scripts/cmdeploy run --verbose --skip-dns-check --ssh-host localhost"
|
- run: ssh root@staging-ipv4.testrun.org "cd relay && scripts/cmdeploy run --verbose --skip-dns-check --ssh-host localhost"
|
||||||
|
|
||||||
|
|||||||
2
.github/workflows/test-and-deploy.yaml
vendored
2
.github/workflows/test-and-deploy.yaml
vendored
@@ -76,7 +76,7 @@ jobs:
|
|||||||
|
|
||||||
- run: |
|
- run: |
|
||||||
cmdeploy init staging2.testrun.org
|
cmdeploy init staging2.testrun.org
|
||||||
sed -i 's/#\s*mtail_address/mtail_address/' chatmail.ini
|
sed -i 's/#\s*mtail_address/mtail_address/;s/max_mailbox_size = 500M/max_mailbox_size = 10k/' chatmail.ini
|
||||||
|
|
||||||
- run: cmdeploy run --verbose --skip-dns-check
|
- run: cmdeploy run --verbose --skip-dns-check
|
||||||
|
|
||||||
|
|||||||
@@ -376,14 +376,14 @@ def get_parser():
|
|||||||
return parser
|
return parser
|
||||||
|
|
||||||
|
|
||||||
def get_sshexec(ssh_host: str, verbose=True):
|
def get_sshexec(ssh_host: str, verbose=True, **kwargs):
|
||||||
if ssh_host in ["localhost", "@local"]:
|
if ssh_host in ["localhost", "@local"]:
|
||||||
return LocalExec(verbose, docker=False)
|
return LocalExec(verbose, docker=False)
|
||||||
elif ssh_host == "@docker":
|
elif ssh_host == "@docker":
|
||||||
return LocalExec(verbose, docker=True)
|
return LocalExec(verbose, docker=True)
|
||||||
if verbose:
|
if verbose:
|
||||||
print(f"[ssh] login to {ssh_host}")
|
print(f"[ssh] login to {ssh_host}")
|
||||||
return SSHExec(ssh_host, verbose=verbose)
|
return SSHExec(ssh_host, verbose=verbose, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
def main(args=None):
|
def main(args=None):
|
||||||
|
|||||||
@@ -51,12 +51,15 @@ smtps inet n - y - 5000 smtpd
|
|||||||
-o smtpd_proxy_filter=127.0.0.1:{{ config.filtermail_smtp_port }}
|
-o smtpd_proxy_filter=127.0.0.1:{{ config.filtermail_smtp_port }}
|
||||||
#628 inet n - y - - qmqpd
|
#628 inet n - y - - qmqpd
|
||||||
pickup unix n - y 60 1 pickup
|
pickup unix n - y 60 1 pickup
|
||||||
|
-o cleanup_service_name=signlocal
|
||||||
cleanup unix n - y - 0 cleanup
|
cleanup unix n - y - 0 cleanup
|
||||||
qmgr unix n - n 300 1 qmgr
|
qmgr unix n - n 300 1 qmgr
|
||||||
#qmgr unix n - n 300 1 oqmgr
|
#qmgr unix n - n 300 1 oqmgr
|
||||||
tlsmgr unix - - y 1000? 1 tlsmgr
|
tlsmgr unix - - y 1000? 1 tlsmgr
|
||||||
rewrite unix - - y - - trivial-rewrite
|
rewrite unix - - y - - trivial-rewrite
|
||||||
bounce unix - - y - 0 bounce
|
bounce unix - - y - 0 bounce
|
||||||
|
-o internal_mail_filter_classes=bounce
|
||||||
|
-o cleanup_service_name=signlocal
|
||||||
defer unix - - y - 0 bounce
|
defer unix - - y - 0 bounce
|
||||||
trace unix - - y - 0 bounce
|
trace unix - - y - 0 bounce
|
||||||
verify unix - - y - 1 verify
|
verify unix - - y - 1 verify
|
||||||
@@ -100,3 +103,9 @@ filter unix - n n - - lmtp
|
|||||||
# cannot send unprotected Subject.
|
# cannot send unprotected Subject.
|
||||||
authclean unix n - - - 0 cleanup
|
authclean unix n - - - 0 cleanup
|
||||||
-o header_checks=regexp:/etc/postfix/submission_header_cleanup
|
-o header_checks=regexp:/etc/postfix/submission_header_cleanup
|
||||||
|
|
||||||
|
# Signs locally generated bounce messages.
|
||||||
|
# These can't be signed using smtpd as they are not non-smtpd.
|
||||||
|
signlocal unix n - - - 0 cleanup
|
||||||
|
-o milter_macro_daemon_name=ORIGINATING
|
||||||
|
-o non_smtpd_milters=unix:opendkim/opendkim.sock
|
||||||
|
|||||||
@@ -49,8 +49,9 @@ class SSHExec:
|
|||||||
RemoteError = execnet.RemoteError
|
RemoteError = execnet.RemoteError
|
||||||
FuncError = FuncError
|
FuncError = FuncError
|
||||||
|
|
||||||
def __init__(self, host, verbose=False, python="python3", timeout=60):
|
def __init__(self, host, verbose=False, python="python3", timeout=60, ssh_options=None):
|
||||||
self.gateway = execnet.makegateway(f"ssh=root@{host}//python={python}")
|
ssh_options = f"{ssh_options.strip()} " if ssh_options is not None else ""
|
||||||
|
self.gateway = execnet.makegateway(f"ssh={ssh_options}root@{host}//python={python}")
|
||||||
self._remote_cmdloop_channel = bootstrap_remote(self.gateway, remote)
|
self._remote_cmdloop_channel = bootstrap_remote(self.gateway, remote)
|
||||||
self.timeout = timeout
|
self.timeout = timeout
|
||||||
self.verbose = verbose
|
self.verbose = verbose
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import pytest
|
|||||||
|
|
||||||
from cmdeploy import remote
|
from cmdeploy import remote
|
||||||
from cmdeploy.cmdeploy import get_sshexec
|
from cmdeploy.cmdeploy import get_sshexec
|
||||||
|
from cmdeploy.sshexec import FuncError
|
||||||
|
|
||||||
|
|
||||||
class TestSSHExecutor:
|
class TestSSHExecutor:
|
||||||
@@ -117,6 +118,33 @@ def test_reject_forged_from(cmsetup, maildata, gencreds, lp, forgeaddr):
|
|||||||
assert "500" in str(e.value)
|
assert "500" in str(e.value)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.slow
|
||||||
|
def test_bounces_are_signed(cmsetup, cmsetup2, maildata, sshdomain2):
|
||||||
|
"""Test that bounce messages are dkim signed"""
|
||||||
|
|
||||||
|
dkim_rejects_dir = "/tmp/filtermail-rejected/dkim-verify"
|
||||||
|
sshexec2 = get_sshexec(sshdomain2, ssh_options="-oStrictHostKeyChecking=accept-new")
|
||||||
|
sshexec2(call=remote.rdns.shell, kwargs=dict(command=f"rm -rf {dkim_rejects_dir}"))
|
||||||
|
|
||||||
|
our_user = cmsetup.gen_users(1)[0]
|
||||||
|
other_user = cmsetup2.gen_users(1)[0]
|
||||||
|
msg = maildata("encrypted.eml", from_addr=other_user.addr, to_addr=our_user.addr)
|
||||||
|
|
||||||
|
# exceed the 10kB quota of our_user mailbox to trigger a bounce message.
|
||||||
|
def bounce_received():
|
||||||
|
other_user.smtp.sendmail(
|
||||||
|
from_addr=other_user.addr, to_addrs=[our_user.addr], msg=msg.as_string()
|
||||||
|
)
|
||||||
|
out = sshexec2(call=remote.rdns.shell, kwargs=dict(command=f"journalctl -n 5 -u filtermail-incoming"))
|
||||||
|
assert "Filtering unencrypted mail." in out
|
||||||
|
|
||||||
|
try_n_times(20, bounce_received)
|
||||||
|
|
||||||
|
time.sleep(1)
|
||||||
|
# if bounce was dkim-signed, filtermail shouldn't log the eml.
|
||||||
|
with pytest.raises(FuncError):
|
||||||
|
sshexec2(call=remote.rdns.shell, kwargs=dict(command=f"ls {dkim_rejects_dir}"))
|
||||||
|
|
||||||
def test_authenticated_from(cmsetup, maildata):
|
def test_authenticated_from(cmsetup, maildata):
|
||||||
"""Test that envelope FROM must be the same as login."""
|
"""Test that envelope FROM must be the same as login."""
|
||||||
user1, user2, user3 = cmsetup.gen_users(3)
|
user1, user2, user3 = cmsetup.gen_users(3)
|
||||||
|
|||||||
@@ -456,6 +456,11 @@ def cmsetup(maildomain, gencreds, ssl_context):
|
|||||||
return CMSetup(maildomain, gencreds, ssl_context)
|
return CMSetup(maildomain, gencreds, ssl_context)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def cmsetup2(maildomain2, gencreds, ssl_context):
|
||||||
|
return CMSetup(maildomain2, gencreds, ssl_context)
|
||||||
|
|
||||||
|
|
||||||
class CMSetup:
|
class CMSetup:
|
||||||
def __init__(self, maildomain, gencreds, ssl_context):
|
def __init__(self, maildomain, gencreds, ssl_context):
|
||||||
self.maildomain = maildomain
|
self.maildomain = maildomain
|
||||||
@@ -466,7 +471,7 @@ class CMSetup:
|
|||||||
print(f"Creating {num} online users")
|
print(f"Creating {num} online users")
|
||||||
users = []
|
users = []
|
||||||
for i in range(num):
|
for i in range(num):
|
||||||
addr, password = self.gencreds()
|
addr, password = self.gencreds(self.maildomain)
|
||||||
user = CMUser(self.maildomain, addr, password, self.ssl_context)
|
user = CMUser(self.maildomain, addr, password, self.ssl_context)
|
||||||
assert user.smtp
|
assert user.smtp
|
||||||
users.append(user)
|
users.append(user)
|
||||||
|
|||||||
Reference in New Issue
Block a user