Compare commits

...

7 Commits

Author SHA1 Message Date
missytake
df756db8ab postfix: do lmtp via local instead of virtual transport 2026-01-27 12:14:48 +01:00
missytake
09e95cbfb6 cmdeploy: deploy with IP address only 2026-01-25 13:30:26 +01:00
missytake
ee2b858661 postfix: hardcode IP addresses of relays without DNS, drop messages to nine 2026-01-25 13:30:26 +01:00
missytake
2a07626f82 postfix: don't verify TLS certs of receiving SMTP servers 2026-01-25 13:30:26 +01:00
missytake
7a43984ab1 doc: document setup without DNS 2026-01-25 13:30:26 +01:00
missytake
fae5568873 acmetool: disable acmetool, use dovecot's self-signed certs 2026-01-25 13:30:26 +01:00
missytake
6f8d7cbdec postfix: stop rejecting messages without DKIM 2026-01-25 13:30:26 +01:00
9 changed files with 49 additions and 29 deletions

View File

@@ -1,20 +1,25 @@
# Chatmail relays for end-to-end encrypted email
# No-DNS Chatmail relay
Chatmail relay servers are interoperable Mail Transport Agents (MTAs) designed for:
With this branch, you don't need DNS at all,
just a VPS with an IPv4 address,
let's take `77.42.80.106` as an example.
First, choose a random domain name (it doesn't need working DNS)
and create a chatmail.ini config file:
- **Zero State:** no private data or metadata collected, messages are auto-deleted, low disk usage
```
cmdeploy init [77.42.80.106]
```
- **Instant/Realtime:** sub-second message delivery, realtime P2P
streaming, privacy-preserving Push Notifications for Apple, Google, and Huawei;
Then, in `cmdeploy/src/cmdeploy/postfix/transport`,
remove the line corresponding to your relay,
and add other for relays you know.
Now you can deploy the relay to your IP address:
- **Security Enforcement**: only strict TLS, DKIM and OpenPGP with minimized metadata accepted
```
cmdeploy run --skip-dns-check --ssh-host 77.42.80.106
```
- **Reliable Federation and Decentralization:** No spam or IP reputation checks, federating
depends on established IETF standards and protocols.
This repository contains everything needed to setup a ready-to-use chatmail relay on an ssh-reachable host.
For getting started and more information please refer to the web version of this repositories' documentation at
[https://chatmail.at/doc/relay](https://chatmail.at/doc/relay)
Finally, you can login with a `dclogin://` code like this, with the correct "domain name" and IP address:
`dclogin:s0mer4nd0@[77.42.80.106]?p=w7i8da7h8uads92ycc2rufyl&v=1&ih=77.42.80.106&sh=77.42.80.106&sp=443&ip=443&ic=3&sc=3`

View File

@@ -89,6 +89,7 @@ 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
ssh_host = ssh_host.strip("[").strip("]")
sshexec = get_sshexec(ssh_host)
require_iroh = args.config.enable_iroh_relay
if not args.dns_check_disabled:

View File

@@ -17,7 +17,6 @@ from pyinfra.operations import apt, files, pip, server, systemd
from cmdeploy.cmdeploy import Out
from .acmetool import AcmetoolDeployer
from .basedeploy import (
Deployer,
Deployment,
@@ -533,7 +532,6 @@ def deploy_chatmail(config_path: Path, disable_mail: bool, website_only: bool) -
port_services = [
(["master", "smtpd"], 25),
("unbound", 53),
("acmetool", 80),
(["imap-login", "dovecot"], 143),
("nginx", 443),
(["master", "smtpd"], 465),
@@ -568,7 +566,6 @@ def deploy_chatmail(config_path: Path, disable_mail: bool, website_only: bool) -
UnboundDeployer(),
TurnDeployer(mail_domain),
IrohDeployer(config.enable_iroh_relay),
AcmetoolDeployer(config.acme_email, tls_domains),
WebsiteDeployer(config),
ChatmailVenvDeployer(config),
MtastsDeployer(),

View File

@@ -7,6 +7,7 @@ listen = *
protocols = imap lmtp
auth_mechanisms = plain
auth_username_chars = abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890.-_@[]
{% if debug == true %}
auth_verbose = yes
@@ -228,8 +229,8 @@ service anvil {
}
ssl = required
ssl_cert = </var/lib/acme/live/{{ config.mail_domain }}/fullchain
ssl_key = </var/lib/acme/live/{{ config.mail_domain }}/privkey
ssl_cert = </etc/ssl/certs/ssl-cert-snakeoil.pem
ssl_key = </etc/ssl/private/ssl-cert-snakeoil.key
ssl_dh = </usr/share/dovecot/dh.pem
ssl_min_protocol = TLSv1.3
ssl_prefer_server_ciphers = yes

View File

@@ -53,8 +53,8 @@ http {
ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers on;
ssl_certificate /var/lib/acme/live/{{ config.domain_name }}/fullchain;
ssl_certificate_key /var/lib/acme/live/{{ config.domain_name }}/privkey;
ssl_certificate /etc/ssl/certs/ssl-cert-snakeoil.pem;
ssl_certificate_key /etc/ssl/private/ssl-cert-snakeoil.key;
gzip on;

View File

@@ -60,7 +60,19 @@ class PostfixDeployer(Deployer):
mode="644",
)
need_restart |= lmtp_header_cleanup.changed
# Transport map that discards messages to nine.testrun.org
transport_map = files.put(
src=get_resource("postfix/transport"),
dest="/etc/postfix/transport",
user="root",
group="root",
mode="644",
)
need_restart |= transport_map.changed
if transport_map.changed:
server.shell(
commands=["postmap /etc/postfix/transport"],
)
# Login map that 1:1 maps email address to login.
login_map = files.put(
src=get_resource("postfix/login_map"),

View File

@@ -15,12 +15,12 @@ readme_directory = no
compatibility_level = 3.6
# TLS parameters
smtpd_tls_cert_file=/var/lib/acme/live/{{ config.mail_domain }}/fullchain
smtpd_tls_key_file=/var/lib/acme/live/{{ config.mail_domain }}/privkey
smtpd_tls_cert_file=/etc/ssl/certs/ssl-cert-snakeoil.pem
smtpd_tls_key_file=/etc/ssl/private/ssl-cert-snakeoil.key
smtpd_tls_security_level=may
smtp_tls_CApath=/etc/ssl/certs
smtp_tls_security_level=verify
smtp_tls_security_level=encrypt
# Send SNI extension when connecting to other servers.
# <https://www.postfix.org/postconf.5.html#smtp_tls_servername>
smtp_tls_servername = hostname
@@ -54,14 +54,15 @@ smtpd_tls_exclude_ciphers = aNULL, RC4, MD5, DES
tls_preempt_cipherlist = yes
smtpd_relay_restrictions = permit_mynetworks permit_sasl_authenticated defer_unauth_destination
myhostname = {{ config.mail_domain }}
alias_maps = hash:/etc/aliases
alias_database = hash:/etc/aliases
# Postfix does not deliver mail for any domain by itself.
# Primary domain is listed in `virtual_mailbox_domains` instead
# and handed over to Dovecot.
mydestination =
mydestination = {{ config.mail_domain }}
local_transport = lmtp:unix:private/dovecot-lmtp
local_recipient_maps =
relayhost =
mynetworks = 127.0.0.0/8 [::ffff:127.0.0.0]/104 [::1]/128
@@ -75,14 +76,15 @@ inet_protocols = ipv4
inet_protocols = all
{% endif %}
virtual_transport = lmtp:unix:private/dovecot-lmtp
virtual_mailbox_domains = {{ config.mail_domain }}
lmtp_header_checks = regexp:/etc/postfix/lmtp_header_cleanup
mua_client_restrictions = permit_sasl_authenticated, reject
mua_sender_restrictions = reject_sender_login_mismatch, permit_sasl_authenticated, reject
mua_helo_restrictions = permit_mynetworks, reject_invalid_helo_hostname, reject_non_fqdn_helo_hostname, permit
# Discard messages to nine.testrun.org
transport_maps = hash:/etc/postfix/transport
# 1:1 map MAIL FROM to SASL login name.
smtpd_sender_login_maps = regexp:/etc/postfix/login_map

View File

@@ -86,7 +86,7 @@ filter unix - n n - - lmtp
# Local SMTP server for reinjecting incoming filtered mail
127.0.0.1:{{ config.postfix_reinject_port_incoming }} inet n - n - 100 smtpd
-o syslog_name=postfix/reinject_incoming
-o smtpd_milters=unix:opendkim/opendkim.sock
# -o smtpd_milters=unix:opendkim/opendkim.sock
# Cleanup `Received` headers for authenticated mail
# to avoid leaking client IP.

View File

@@ -0,0 +1,2 @@
nine.testrun.org discard:
* :