mirror of
https://github.com/chatmail/relay.git
synced 2026-06-10 13:41:08 +00:00
Compare commits
15 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b8dcdcba91 | |||
| 6a0bf5abe7 | |||
| e7829672f8 | |||
| 62d19fd910 | |||
| e1a8d798d3 | |||
| a9dd9fe3e0 | |||
| aa846c3478 | |||
| 921080125f | |||
| d898f41064 | |||
| e9e012234b | |||
| bb40c5bb21 | |||
| a229f1bc45 | |||
| 4ba19b0031 | |||
| 5eab3a5a25 | |||
| 30729d9be0 |
@@ -20,9 +20,9 @@ concurrency:
|
||||
jobs:
|
||||
no-dns:
|
||||
name: LXC deploy and test
|
||||
uses: chatmail/cmlxc/.github/workflows/lxc-test.yml@v0.14.6
|
||||
uses: chatmail/cmlxc/.github/workflows/lxc-test.yml@main
|
||||
with:
|
||||
cmlxc_version: v0.14.6
|
||||
cmlxc_version: main
|
||||
cmlxc_commands: |
|
||||
cmlxc init
|
||||
# single cmdeploy relay test
|
||||
|
||||
@@ -29,7 +29,7 @@ jobs:
|
||||
ref: ${{ github.event.pull_request.head.sha }}
|
||||
persist-credentials: false
|
||||
- name: download filtermail
|
||||
run: curl -L https://github.com/chatmail/filtermail/releases/download/v0.6.6/filtermail-x86_64 -o /usr/local/bin/filtermail && chmod +x /usr/local/bin/filtermail
|
||||
run: curl -L https://github.com/chatmail/filtermail/releases/download/v0.7.0/filtermail-x86_64 -o /usr/local/bin/filtermail && chmod +x /usr/local/bin/filtermail
|
||||
- name: run chatmaild tests
|
||||
working-directory: chatmaild
|
||||
run: pipx run tox
|
||||
@@ -57,9 +57,9 @@ jobs:
|
||||
|
||||
lxc-test:
|
||||
name: LXC deploy and test
|
||||
uses: chatmail/cmlxc/.github/workflows/lxc-test.yml@v0.14.6
|
||||
uses: chatmail/cmlxc/.github/workflows/lxc-test.yml@main
|
||||
with:
|
||||
cmlxc_version: v0.14.6
|
||||
cmlxc_version: main
|
||||
cmlxc_commands: |
|
||||
cmlxc init
|
||||
# single cmdeploy relay test
|
||||
|
||||
@@ -19,6 +19,7 @@ class Config:
|
||||
params = dict(params)
|
||||
raw_domain = params.pop("mail_domain")
|
||||
self.mail_domain_bare = raw_domain
|
||||
self.ssh_host = params.pop("ssh_host", raw_domain)
|
||||
|
||||
if is_valid_ipv4(raw_domain):
|
||||
self.ipv4_relay = raw_domain
|
||||
|
||||
@@ -168,6 +168,16 @@ class Expiry:
|
||||
if mbox.last_login and mbox.last_login < cutoff_without_login:
|
||||
self.remove_mailbox(mbox.basedir)
|
||||
return
|
||||
elif mbox.last_login is None:
|
||||
try:
|
||||
if not self.dry:
|
||||
os.rmdir(mbox.basedir)
|
||||
self.del_mboxes += 1
|
||||
except OSError:
|
||||
print_info(
|
||||
f"Skipped deleting {mbox.basedir}, doesn't have last_login but isn't empty"
|
||||
)
|
||||
return
|
||||
|
||||
mboxname = os.path.basename(mbox.basedir)
|
||||
if self.verbose:
|
||||
|
||||
@@ -3,6 +3,9 @@
|
||||
# mail domain (MUST be set to fully qualified chat mail domain)
|
||||
mail_domain = {mail_domain}
|
||||
|
||||
# Where to deploy the relay - if unspecified, mail_domain will be used.
|
||||
ssh_host = localhost
|
||||
|
||||
#
|
||||
# If you only do private test deploys, you don't need to modify any settings below
|
||||
#
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import itertools
|
||||
import os
|
||||
import random
|
||||
import shutil
|
||||
import time
|
||||
from datetime import datetime
|
||||
from fnmatch import fnmatch
|
||||
@@ -9,6 +10,7 @@ from pathlib import Path
|
||||
import pytest
|
||||
|
||||
from chatmaild.expire import (
|
||||
Expiry,
|
||||
FileEntry,
|
||||
MailboxStat,
|
||||
expire_to_target,
|
||||
@@ -104,6 +106,32 @@ def test_stats_mailbox(mbox1):
|
||||
assert mbox3.last_login is None
|
||||
|
||||
|
||||
def test_mbox_without_password(mbox1, example_config, capsys):
|
||||
password = Path(mbox1.basedir).joinpath("password")
|
||||
os.remove(password)
|
||||
mbox_rescan = MailboxStat(mbox1.basedir)
|
||||
assert mbox_rescan.last_login is None
|
||||
|
||||
exp = Expiry(
|
||||
example_config, dry=False, now=datetime.now().timestamp(), verbose=False
|
||||
)
|
||||
exp.process_mailbox_stat(mbox_rescan)
|
||||
out, err = capsys.readouterr()
|
||||
assert "doesn't have last_login but isn't empty" in err
|
||||
assert os.path.isdir(mbox_rescan.basedir)
|
||||
|
||||
for entry in os.scandir(mbox_rescan.basedir):
|
||||
if os.path.isdir(entry):
|
||||
shutil.rmtree(entry)
|
||||
else:
|
||||
os.remove(entry)
|
||||
|
||||
exp.process_mailbox_stat(mbox_rescan)
|
||||
out, err = capsys.readouterr()
|
||||
assert "doesn't have last_login but isn't empty" not in err
|
||||
assert not os.path.isdir(mbox_rescan.basedir)
|
||||
|
||||
|
||||
def test_report_no_mailboxes(example_config):
|
||||
args = (str(example_config._inipath),)
|
||||
report_main(args)
|
||||
|
||||
@@ -96,7 +96,7 @@ def _warn_unused_settings(unused_keys, out):
|
||||
def run_cmd(args, out):
|
||||
"""Deploy chatmail services on the remote server."""
|
||||
|
||||
ssh_host = args.ssh_host if args.ssh_host else args.config.mail_domain_bare
|
||||
ssh_host = args.ssh_host if args.ssh_host else args.config.ssh_host
|
||||
sshexec = get_sshexec(ssh_host)
|
||||
require_iroh = args.config.enable_iroh_relay
|
||||
strict_tls = args.config.tls_cert_mode == "acme"
|
||||
@@ -116,7 +116,7 @@ def run_cmd(args, out):
|
||||
pyinf = "pyinfra --dry" if args.dry_run else "pyinfra"
|
||||
|
||||
cmd = f"{pyinf} --ssh-user root {ssh_host} {deploy_path} -y"
|
||||
if ssh_host == "localhost":
|
||||
if ssh_host in ["localhost", "@local"]:
|
||||
cmd = f"{pyinf} @local {deploy_path} -y"
|
||||
|
||||
if version.parse(pyinfra.__version__) < version.parse("3"):
|
||||
@@ -158,7 +158,7 @@ def dns_cmd(args, out):
|
||||
ipv4 = args.config.ipv4_relay
|
||||
print(f"[WARNING] {ipv4} is not a domain, skipping DNS checks.")
|
||||
return 0
|
||||
ssh_host = args.ssh_host if args.ssh_host else args.config.mail_domain
|
||||
ssh_host = args.ssh_host if args.ssh_host else args.config.ssh_host
|
||||
sshexec = get_sshexec(ssh_host, verbose=args.verbose)
|
||||
tls_cert_mode = args.config.tls_cert_mode
|
||||
strict_tls = tls_cert_mode == "acme"
|
||||
@@ -195,7 +195,7 @@ def status_cmd_options(parser):
|
||||
def status_cmd(args, out):
|
||||
"""Display status for online chatmail instance."""
|
||||
|
||||
ssh_host = args.ssh_host if args.ssh_host else args.config.mail_domain_bare
|
||||
ssh_host = args.ssh_host if args.ssh_host else args.config.ssh_host
|
||||
sshexec = get_sshexec(ssh_host, verbose=args.verbose)
|
||||
|
||||
out.green(f"chatmail domain: {args.config.mail_domain}")
|
||||
|
||||
@@ -171,16 +171,14 @@ class UnboundDeployer(Deployer):
|
||||
"unbound-anchor -a /var/lib/unbound/root.key || true",
|
||||
],
|
||||
)
|
||||
if self.config.disable_ipv6:
|
||||
self.ensure_directory(
|
||||
path="/etc/unbound/unbound.conf.d",
|
||||
)
|
||||
self.put_template(
|
||||
"unbound/unbound.conf.j2",
|
||||
"/etc/unbound/unbound.conf.d/chatmail.conf",
|
||||
disable_ipv6=self.config.disable_ipv6,
|
||||
)
|
||||
else:
|
||||
self.remove_file("/etc/unbound/unbound.conf.d/chatmail.conf")
|
||||
|
||||
def activate(self):
|
||||
server.shell(
|
||||
@@ -514,6 +512,8 @@ def deploy_chatmail(config_path: Path, disable_mail: bool, website_only: bool) -
|
||||
(["master", "smtpd"], config.postfix_reinject_port_incoming),
|
||||
("filtermail", config.filtermail_smtp_port),
|
||||
("filtermail", config.filtermail_smtp_port_incoming),
|
||||
("filtermail", config.filtermail_http_port_incoming),
|
||||
("filtermail", config.filtermail_lmtp_port_transport),
|
||||
]
|
||||
for service, port in port_services:
|
||||
print(f"Checking if port {port} is available for {service}...")
|
||||
|
||||
@@ -20,10 +20,10 @@ class FiltermailDeployer(Deployer):
|
||||
return
|
||||
|
||||
arch = host.get_fact(facts.server.Arch)
|
||||
url = f"https://github.com/chatmail/filtermail/releases/download/v0.6.6/filtermail-{arch}"
|
||||
url = f"https://github.com/chatmail/filtermail/releases/download/v0.7.0/filtermail-{arch}"
|
||||
sha256sum = {
|
||||
"x86_64": "05c7e7ac244606c2eeb275f2d282ffdbc2403e0169f1cdd3110ffcebdb994a92",
|
||||
"aarch64": "8cf8bbda4d907beca547b365cc7e6753532a74b1712492d0d2f3d2d8a553fb3d",
|
||||
"x86_64": "451f295a85b3b12dbb0f89e18ec319f742ee46dec218f20f7923bfb017a248bd",
|
||||
"aarch64": "6833061b2a2028264fdeb32f0a6123e1ff73de57dace125364016300b748452e",
|
||||
}[arch]
|
||||
self.download_executable(url, self.bin_path, sha256sum)
|
||||
|
||||
|
||||
@@ -1,3 +1,23 @@
|
||||
/^DKIM-Signature:/ IGNORE
|
||||
/^Authentication-Results:/ IGNORE
|
||||
/^Received:/ IGNORE
|
||||
# List of headers for incoming messages
|
||||
# that must be retained for functionality and compatibility reasons
|
||||
/^From:/ DUNNO
|
||||
/^Message-Id:/ DUNNO
|
||||
/^Chat-/ DUNNO
|
||||
/^Content-Type:/ DUNNO
|
||||
|
||||
# For receiving clear-text messages (still supported in May 2026)
|
||||
/^Subject:/ DUNNO
|
||||
/^Date:/ DUNNO
|
||||
/^To:/ DUNNO
|
||||
/^CC:/ DUNNO
|
||||
/^References:/ DUNNO
|
||||
/^In-Reply-To:/ DUNNO
|
||||
|
||||
# Senders might support Autocrypt 1 but not RFC9788 (Header Protection)
|
||||
/^Autocrypt:/ DUNNO
|
||||
|
||||
# SecureJoin V2 protocol headers (for backward compatibility)
|
||||
/^Secure-Join/ DUNNO
|
||||
|
||||
# Ignore all other headers
|
||||
/.*/ IGNORE
|
||||
|
||||
@@ -62,8 +62,8 @@ def maildomain(chatmail_config):
|
||||
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
def sshdomain(maildomain):
|
||||
return os.environ.get("CHATMAIL_SSH", maildomain)
|
||||
def sshdomain(chatmail_config):
|
||||
return os.environ.get("CHATMAIL_SSH", chatmail_config.ssh_host)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
@@ -349,9 +349,9 @@ class ChatmailACFactory:
|
||||
qr = (
|
||||
f"dclogin:{addr}"
|
||||
f"?p={password}&v=1"
|
||||
f"&ih={domain}&ip=993"
|
||||
f"&sh={domain}&sp=465"
|
||||
f"&ic=3&ss=default"
|
||||
f"&ih={domain}&ip=993&is=ssl"
|
||||
f"&sh={domain}&sp=465&ss=ssl"
|
||||
f"&ic=3"
|
||||
)
|
||||
future = account.add_transport_from_qr.future(qr)
|
||||
else:
|
||||
@@ -362,7 +362,7 @@ class ChatmailACFactory:
|
||||
|
||||
# ensure messages stay in INBOX so that they can be
|
||||
# concurrently fetched via extra IMAP connections during tests
|
||||
account.set_config("delete_server_after", "10")
|
||||
account.set_config("bcc_self", "1")
|
||||
accounts.append(account)
|
||||
|
||||
for future in futures:
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
# Managed by cmdeploy: disable IPv6 in unbound.
|
||||
# Managed by cmdeploy
|
||||
server:
|
||||
{% if disable_ipv6 %}
|
||||
interface: 127.0.0.1
|
||||
do-ip6: no
|
||||
{% endif %}
|
||||
cache-max-negative-ttl: 0
|
||||
|
||||
@@ -14,21 +14,14 @@ Minimal requirements and prerequisites
|
||||
|
||||
You will need the following:
|
||||
|
||||
- A Debian 12 **deployment server** with reachable SMTP/SUBMISSIONS/IMAPS/HTTPS ports.
|
||||
- Control over a domain through a DNS provider of your choice.
|
||||
(there is experimental support for :ref:`IP-only relays <iponly>`).
|
||||
|
||||
- A Debian 12 server with reachable SMTP/SUBMISSIONS/IMAPS/HTTPS ports.
|
||||
IPv6 is encouraged if available. Chatmail relay servers only require
|
||||
1GB RAM, one CPU, and perhaps 10GB storage for a few thousand active
|
||||
chatmail addresses.
|
||||
|
||||
- A Linux or Unix **build machine** with key-based SSH access to the root
|
||||
user of the deployment server.
|
||||
You must add a passphrase-protected private key to your local ssh-agent because you
|
||||
can’t type in your passphrase during deployment.
|
||||
(An ed25519 private key is required due to an `upstream bug in
|
||||
paramiko <https://github.com/paramiko/paramiko/issues/2191>`_)
|
||||
|
||||
- Control over a domain through a DNS provider of your choice
|
||||
(there is experimental support for :ref:`IP-only relays <iponly>`).
|
||||
|
||||
|
||||
.. _setup:
|
||||
|
||||
@@ -38,7 +31,7 @@ Setup with ``scripts/cmdeploy``
|
||||
We use ``chat.example.org`` as the chatmail domain in the following
|
||||
steps. Please substitute it with your own domain.
|
||||
|
||||
1. Setup the initial DNS records for your deployment server.
|
||||
1. Setup the initial DNS records for your relay.
|
||||
The following is an example in the
|
||||
familiar BIND zone file format with a TTL of 1 hour (3600 seconds).
|
||||
Please substitute your domain and IP addresses.
|
||||
@@ -58,22 +51,25 @@ steps. Please substitute it with your own domain.
|
||||
The ``mta-sts`` CNAME and ``_mta-sts`` TXT records
|
||||
are not needed for such domains.
|
||||
|
||||
2. On your local PC, clone the repository and bootstrap the Python
|
||||
2. Login to the server with SSH, clone the repository and bootstrap the Python
|
||||
virtualenv.
|
||||
|
||||
::
|
||||
|
||||
ssh root@chat.example.org
|
||||
git clone https://github.com/chatmail/relay
|
||||
cd relay
|
||||
scripts/initenv.sh
|
||||
|
||||
3. On your local build machine (PC), create a chatmail configuration file
|
||||
3. Then, create a chatmail configuration file
|
||||
``chatmail.ini``:
|
||||
|
||||
::
|
||||
|
||||
scripts/cmdeploy init chat.example.org # <-- use your domain
|
||||
|
||||
.. note::
|
||||
|
||||
To use self-signed TLS certificates
|
||||
instead of Let's Encrypt,
|
||||
use a domain name starting with ``_``
|
||||
@@ -84,13 +80,7 @@ steps. Please substitute it with your own domain.
|
||||
See the :doc:`overview`
|
||||
for details on certificate provisioning.
|
||||
|
||||
4. Verify that SSH root login to the deployment server server works:
|
||||
|
||||
::
|
||||
|
||||
ssh root@chat.example.org # <-- use your domain
|
||||
|
||||
5. From your local build machine, setup and configure the remote deployment server:
|
||||
4. Now run the deployment script to install the relay to the server:
|
||||
|
||||
::
|
||||
|
||||
@@ -102,7 +92,6 @@ steps. Please substitute it with your own domain.
|
||||
public).
|
||||
|
||||
|
||||
|
||||
Docker installation
|
||||
-------------------
|
||||
|
||||
@@ -110,27 +99,33 @@ There is experimental support for running chatmail via Docker.
|
||||
A monolithic image based on the above cmdeploy method is available `through a separate repository <https://github.com/chatmail/docker/pkgs/container/docker>`_.
|
||||
See the `chatmail/docker README <https://github.com/chatmail/docker>`_ for full setup instructions.
|
||||
|
||||
Other helpful commands
|
||||
----------------------
|
||||
|
||||
To check the status of your deployment server running the chatmail service:
|
||||
Next Steps
|
||||
----------
|
||||
|
||||
::
|
||||
|
||||
scripts/cmdeploy status
|
||||
|
||||
To display and check all recommended DNS records:
|
||||
Now you should display and check all recommended DNS records
|
||||
to enable federation with other relays:
|
||||
|
||||
::
|
||||
|
||||
scripts/cmdeploy dns
|
||||
|
||||
To test whether your chatmail service is working correctly:
|
||||
You should also test whether your chatmail service is working correctly:
|
||||
|
||||
::
|
||||
|
||||
scripts/cmdeploy test
|
||||
|
||||
Other Helpful Commands
|
||||
----------------------
|
||||
|
||||
To check the status of your chatmail relay:
|
||||
|
||||
::
|
||||
|
||||
scripts/cmdeploy status
|
||||
|
||||
|
||||
To measure the performance of your chatmail service:
|
||||
|
||||
::
|
||||
@@ -171,8 +166,9 @@ This starts a local live development cycle for chatmail web pages:
|
||||
directory and generating HTML files and copying assets to the
|
||||
``www/build`` directory.
|
||||
|
||||
- Starts a browser window automatically where you can “refresh” as
|
||||
needed.
|
||||
- if you are running scripts/cmdeploy webdev on the relay itself,
|
||||
you need to configure a route in /etc/nginx/nginx.conf
|
||||
to expose the build directory.
|
||||
|
||||
Custom web pages
|
||||
----------------
|
||||
@@ -190,7 +186,7 @@ Disable automatic address creation
|
||||
--------------------------------------------------------
|
||||
|
||||
If you need to stop address creation, e.g. because some script is wildly
|
||||
creating addresses, login with ssh to the deployment machine and run:
|
||||
creating addresses, login with ssh to the relay and run:
|
||||
|
||||
::
|
||||
|
||||
@@ -246,25 +242,3 @@ The deploy will verify that both files exist on the server.
|
||||
If you use such a setup, you must trigger the reload explicitly after renewal::
|
||||
|
||||
systemctl start tls-cert-reload.service
|
||||
|
||||
|
||||
Migrating to a new build machine
|
||||
----------------------------------
|
||||
|
||||
To move or add a build machine,
|
||||
clone the relay repository on the new build machine, and copy the ``chatmail.ini`` file from the old build machine.
|
||||
Make sure ``rsync`` is installed, then initialize the environment:
|
||||
|
||||
::
|
||||
|
||||
./scripts/initenv.sh
|
||||
|
||||
Run safety checks before a new deployment:
|
||||
|
||||
::
|
||||
|
||||
./scripts/cmdeploy dns
|
||||
./scripts/cmdeploy status
|
||||
|
||||
If you keep multiple build machines (ie laptop and desktop), keep ``chatmail.ini`` in sync between
|
||||
them.
|
||||
|
||||
+16
-2
@@ -1,6 +1,6 @@
|
||||
|
||||
Migrating to a new machine
|
||||
===========================
|
||||
Migrating the relay to a new server
|
||||
===================================
|
||||
|
||||
This migration tutorial provides a step-wise approach
|
||||
to safely migrate a chatmail relay from one remote machine to another.
|
||||
@@ -96,3 +96,17 @@ in this case, just run ``ssh-keygen -R "mail.example.org"`` as recommended.
|
||||
If you have lowered the Time-to-Live for DNS records in step 1,
|
||||
better use a higher value again (between 14400 and 86400 seconds) once you are sure everything works.
|
||||
|
||||
|
||||
Migrating a local chatmail/relay repository to the server
|
||||
=========================================================
|
||||
|
||||
To move the directory with your local chatmail/relay repository and ``chatmail.ini`` file,
|
||||
clone the `relay repository <https://github.com/chatmail/relay/>` to the server where the relay is running,
|
||||
and copy the ``chatmail.ini`` file from your local chatmail/relay repository there.
|
||||
|
||||
If you made local changes to the repository,
|
||||
you can store them in a file with ``git diff origin/main > local-changes.patch``,
|
||||
copy the file to the repository on the server,
|
||||
and run ``git apply local-changes.patch``.
|
||||
|
||||
Then you can proceed with the `installation steps <setup>`.
|
||||
|
||||
+56
-15
@@ -156,6 +156,7 @@ Chatmail relay dependency diagram
|
||||
postfix --- |10083|filtermail-transport;
|
||||
filtermail-outgoing --- |10025 reinject|postfix;
|
||||
filtermail-incoming --- |10026 reinject|postfix;
|
||||
postfix --- |milter opendkim.sock|OpenDKIM
|
||||
dovecot --- |doveauth.socket|doveauth;
|
||||
dovecot --- |message delivery|maildir["maildir
|
||||
/home/vmail/.../user"];
|
||||
@@ -179,26 +180,66 @@ Chatmail relay dependency diagram
|
||||
style nginx-right fill:#f66;
|
||||
style postfix fill:#f66;
|
||||
style dovecot fill:#f66;
|
||||
style OpenDKIM fill:#f66;
|
||||
style notification-proxy fill:#f66;
|
||||
|
||||
Message between users on the same relay
|
||||
---------------------------------------
|
||||
Accepting and delivering mail
|
||||
-----------------------------
|
||||
|
||||
.. mermaid::
|
||||
:caption: This diagram shows the path a non-federated message takes.
|
||||
:caption: This diagram shows all the paths a message can take.
|
||||
|
||||
graph LR;
|
||||
sender --> |465|smtps/smtpd;
|
||||
sender --> |587|submission/smtpd;
|
||||
smtps/smtpd --> |10080|filtermail;
|
||||
submission/smtpd --> |10080|filtermail;
|
||||
filtermail --> |10025|smtpd_reinject;
|
||||
smtpd_reinject --> cleanup;
|
||||
cleanup --> qmgr;
|
||||
qmgr --> smtpd_accepts_message;
|
||||
qmgr --> |lmtp|dovecot;
|
||||
dovecot --> recipient;
|
||||
dovecot --> sender's_other_devices;
|
||||
flowchart LR
|
||||
subgraph chatmail relay
|
||||
subgraph postfix
|
||||
qmgr .-> lmtp-filtermail["lmtp/lmtp-filtermail (default_transport)"]
|
||||
qmgr .-> lmtp["lmtp (local_transport)"]
|
||||
lmtp --> cleanup["cleanup (lmtp_header_cleanup)"]
|
||||
bounce
|
||||
smtpd-submission["smtpd/submission"]
|
||||
smtpd-smtps["smtpd/smtps"]
|
||||
smtpd-reinject-outgoing["smtpd/reinject-outgoing"] --> authclean["cleanup/authclean (submission_header_cleanup)"]
|
||||
authclean --> qmgr
|
||||
smtpd-smtp["smtpd/smtp"]
|
||||
smtpd-reinject-incoming["smtpd/reinject-incoming"] --> qmgr
|
||||
end
|
||||
lmtp-filtermail --LMTP inet:10083--> filtermail-transport
|
||||
cleanup --LMTP unix:private/dovecot-lmtp --> dovecot
|
||||
dovecot --> maildir
|
||||
smtpd-submission --SMTP inet:10080--> filtermail-outgoing
|
||||
smtpd-smtps --SMTP inet:10080--> filtermail-outgoing
|
||||
filtermail-outgoing --SMTP inet:10025--> smtpd-reinject-outgoing
|
||||
open-dkim["OpenDKIM (signing only)"] <--milter unix:opendkim/opendkim.sock--> smtpd-reinject-outgoing
|
||||
bounce <--milter unix:opendkim/opendkim.sock--> open-dkim
|
||||
bounce --> qmgr
|
||||
nginx
|
||||
smtpd-smtp -.SMTP inet:10081.-> filtermail-incoming
|
||||
nginx -.HTTP inet:10082.-> filtermail-incoming
|
||||
filtermail-incoming --SMTP inet:10026--> smtpd-reinject-incoming
|
||||
end
|
||||
filtermail-transport -.SMTP inet:25.-> mta1[Remote relay]
|
||||
filtermail-transport -.HTTPS /mxdeliv.-> mta1
|
||||
client[Client] -.SMTP inet:587.-> smtpd-submission
|
||||
client -.SMTP inet:465.-> smtpd-smtps
|
||||
client -.SMTP inet:443.-> nginx
|
||||
nginx -.SMTP inet:465.-> smtpd-smtps
|
||||
mta2[Remote relay] -.SMTP inet:25.-> smtpd-smtp
|
||||
mta2 -.HTTPS /mxdeliv.-> nginx
|
||||
style postfix fill:#363
|
||||
style qmgr fill:#252
|
||||
style authclean fill:#252
|
||||
style cleanup fill:#252
|
||||
style lmtp-filtermail fill:#252
|
||||
style lmtp fill:#252
|
||||
style bounce fill:#252
|
||||
style smtpd-submission fill:#252
|
||||
style smtpd-smtps fill:#252
|
||||
style smtpd-reinject-outgoing fill:#252
|
||||
style smtpd-reinject-incoming fill:#252
|
||||
style smtpd-smtp fill:#252
|
||||
style filtermail-outgoing fill:#225
|
||||
style filtermail-incoming fill:#225
|
||||
style filtermail-transport fill:#225
|
||||
|
||||
Operational details of a chatmail relay
|
||||
----------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user