diff --git a/.github/workflows/docs-preview.yaml b/.github/workflows/docs-preview.yaml new file mode 100644 index 00000000..f60032ee --- /dev/null +++ b/.github/workflows/docs-preview.yaml @@ -0,0 +1,80 @@ +name: documentation preview + +on: + pull_request: + paths: + - 'doc/**' + - 'scripts/build-docs.sh' + - '.github/workflows/docs-preview.yaml' + +jobs: + scripts: + name: build + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: initenv + run: scripts/initenv.sh + + - name: append venv/bin to PATH + run: echo `pwd`/venv/bin >>$GITHUB_PATH + + - name: build documentation + working-directory: doc + run: sphinx-build source build + + - name: build documentation second time (for TOC) + working-directory: doc + run: sphinx-build source build + + - name: Get Pullrequest ID + id: prepare + run: | + export PULLREQUEST_ID=$(echo "${{ github.ref }}" | cut -d "/" -f3) + echo "prid=$PULLREQUEST_ID" >> $GITHUB_OUTPUT + if [ $(expr length "${{ secrets.USERNAME }}") -gt "1" ]; then echo "uploadtoserver=true" >> $GITHUB_OUTPUT; fi + - run: | + echo "baseurl: /${{ steps.prepare.outputs.prid }}" >> _config.yml + + - name: Upload preview + run: | + mkdir -p "$HOME/.ssh" + echo "${{ secrets.CHATMAIL_STAGING_SSHKEY }}" > "$HOME/.ssh/key" + chmod 600 "$HOME/.ssh/key" + rsync -rILvh -e "ssh -i $HOME/.ssh/key -o StrictHostKeyChecking=no" $GITHUB_WORKSPACE/doc/build/ "${{ secrets.USERNAME }}@chatmail.at:/var/www/html/staging.chatmail.at/doc/relay/${{ steps.prepare.outputs.prid }}/" + + - name: "Post links to details" + id: details + if: steps.prepare.outputs.uploadtoserver + run: | + # URLs for API connection and uploads + export GITHUB_API_URL="https://api.github.com/repos/chatmail/relay/statuses/${{ github.event.after }}" + export PREVIEW_LINK="https://staging.chatmail.at/doc/relay/${{ steps.prepare.outputs.prid }}/" + export STATUS_DATA="{\"state\": \"success\", \ + \"description\": \"Preview the changed documentation here:\", \ + \"context\": \"Documentation Preview\", \ + \"target_url\": \"${PREVIEW_LINK}\"}" + curl -X POST --header "Accept: application/vnd.github+json" --header "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}" --url "$GITHUB_API_URL" --header "content-type: application/json" --data "$STATUS_DATA" + + #check if comment already exists, if not post it + export GITHUB_API_URL="https://api.github.com/repos/chatmail/relay/issues/${{ steps.prepare.outputs.prid }}/comments" + export RESPONSE=$(curl -L --header "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}" --url "$GITHUB_API_URL" --header "content-type: application/vnd.github+json" -H "X-GitHub-Api-Version: 2022-11-28") + echo $RESPONSE > response + grep -v '"Check out the page preview at https://staging.chatmail.at/doc/relay' response && echo "comment=true" >> $GITHUB_OUTPUT || true + - name: "Post link to comments" + if: steps.details.outputs.comment + uses: actions/github-script@v7 + with: + script: | + github.rest.issues.createComment({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + body: "Check out the page preview at https://staging.chatmail.at/doc/relay/${{ steps.prepare.outputs.prid }}/" + }) + + - name: check links + working-directory: doc + run: sphinx-build --builder linkcheck source build + diff --git a/.github/workflows/docs.yaml b/.github/workflows/docs.yaml new file mode 100644 index 00000000..0bb861ac --- /dev/null +++ b/.github/workflows/docs.yaml @@ -0,0 +1,44 @@ +name: build and upload documentation + +on: + push: + branches: + - main + - 'missytake/docs-ci' + paths: + - 'doc/**' + - 'scripts/build-docs.sh' + - '.github/workflows/docs.yaml' + +jobs: + scripts: + name: build + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: initenv + run: scripts/initenv.sh + + - name: append venv/bin to PATH + run: echo `pwd`/venv/bin >>$GITHUB_PATH + + - name: build documentation + working-directory: doc + run: sphinx-build source build + + - name: build documentation second time (for TOC) + working-directory: doc + run: sphinx-build source build + + - name: check links + working-directory: doc + run: sphinx-build --builder linkcheck source build + + - name: upload documentation + run: | + mkdir -p "$HOME/.ssh" + echo "${{ secrets.CHATMAIL_STAGING_SSHKEY }}" > "$HOME/.ssh/key" + chmod 600 "$HOME/.ssh/key" + rsync -rILvh -e "ssh -i $HOME/.ssh/key -o StrictHostKeyChecking=no" $GITHUB_WORKSPACE/doc/build/ "${{ secrets.USERNAME }}@chatmail.at:/var/www/html/chatmail.at/doc/relay/" + diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md deleted file mode 100644 index 9e42c007..00000000 --- a/ARCHITECTURE.md +++ /dev/null @@ -1,50 +0,0 @@ -This diagram shows components of the chatmail server; this is a draft -overview as of mid-August 2025: - -```mermaid -graph LR; - cmdeploy --- sshd; - letsencrypt --- |80|acmetool-redirector; - acmetool-redirector --- |443|nginx-right(["`nginx - (external)`"]); - nginx-external --- |465|postfix; - nginx-external(["`nginx - (external)`"]) --- |8443|nginx-internal["`nginx - (internal)`"]; - nginx-internal --- website["`Website - /var/www/html`"]; - nginx-internal --- newemail.py; - nginx-internal --- autoconfig.xml; - certs-nginx[("`TLS certs - /var/lib/acme`")] --> nginx-internal; - cron --- chatmail-metrics; - cron --- acmetool; - chatmail-metrics --- website; - acmetool --> certs[("`TLS certs - /var/lib/acme`")]; - nginx-external --- |993|dovecot; - autoconfig.xml --- postfix; - autoconfig.xml --- dovecot; - postfix --- echobot; - postfix --- |10080,10081|filtermail; - postfix --- users["`User data - home/vmail/mail`"]; - postfix --- |doveauth.socket|doveauth; - dovecot --- |doveauth.socket|doveauth; - dovecot --- users; - dovecot --- |metadata.socket|chatmail-metadata; - doveauth --- users; - chatmail-expire-daily --- users; - chatmail-fsreport-daily --- users; - chatmail-metadata --- iroh-relay; - certs-nginx --> postfix; - certs-nginx --> dovecot; - style certs fill:#ff6; - style certs-nginx fill:#ff6; - style nginx-external fill:#fc9; - style nginx-right fill:#fc9; -``` - -The edges in this graph should not be taken too literally; they -reflect some sort of communication path or dependency relationship -between components of the chatmail server. diff --git a/CHANGELOG.md b/CHANGELOG.md index 7854a382..12c08ce5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,9 @@ ## untagged +- docs: move readme.md docs to sphinx documentation rendered at https://chatmail.at/doc/relay + ([#711](https://github.com/chatmail/relay/pull/711)) + - acmetool: replace cronjob with a systemd timer ([#719](https://github.com/chatmail/relay/pull/719)) diff --git a/README.md b/README.md index 28cf92ee..f9a70620 100644 --- a/README.md +++ b/README.md @@ -1,564 +1,20 @@ - - -# Chatmail relays for end-to-end encrypted e-mail +# Chatmail relays for end-to-end encrypted email Chatmail relay servers are interoperable Mail Transport Agents (MTAs) designed for: -- **Convenience:** Low friction instant onboarding - -- **Privacy:** No name, phone numbers, email required or collected - -- **End-to-End Encryption enforced**: only OpenPGP messages with metadata minimization allowed - -- **Instant:** Privacy-preserving Push Notifications for Apple, Google, and Huawei - -- **Speed:** Message delivery in half a second, with optional P2P realtime connections - -- **Transport Security:** Strict TLS and DKIM enforced - -- **Reliability:** No spam or IP reputation checks; rate-limits are suitable for realtime chats - -- **Efficiency:** Messages are only stored for transit and removed automatically - -This repository contains everything needed to setup a ready-to-use chatmail relay -comprised of a minimal setup of the battle-tested -[Postfix SMTP](https://www.postfix.org) and [Dovecot IMAP](https://www.dovecot.org) MTAs/MDAs. - -The automated setup is designed and optimized for providing chatmail addresses -for immediate permission-free onboarding through chat apps and bots. -Chatmail addresses are automatically created at first login, -after which the initially specified password is required -for sending and receiving messages through them. - -Please see [this list of known apps and client projects](https://chatmail.at/clients.html) -and [this list of known public 3rd party chatmail relay servers](https://chatmail.at/relays). - - -## Minimal requirements, Prerequisites - -You will need the following: - -- Control over a domain through a DNS provider of your choice. - -- 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. - -- Key-based SSH authentication to the root user. - 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)) - - -## Getting started - -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. - 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. - - ``` - chat.example.com. 3600 IN A 198.51.100.5 - chat.example.com. 3600 IN AAAA 2001:db8::5 - www.chat.example.com. 3600 IN CNAME chat.example.com. - mta-sts.chat.example.com. 3600 IN CNAME chat.example.com. - ``` - -2. On your local PC, clone the repository and bootstrap the Python virtualenv. - - ``` - git clone https://github.com/chatmail/relay - cd relay - scripts/initenv.sh - ``` - -3. On your local PC, create chatmail configuration file `chatmail.ini`: - - ``` - scripts/cmdeploy init chat.example.org # <-- use your domain - ``` - -4. Verify that SSH root login to your remote server works: - - ``` - ssh root@chat.example.org # <-- use your domain - ``` - -5. From your local PC, deploy the remote chatmail relay server: - - ``` - scripts/cmdeploy run - ``` - This script will also check that you have all necessary DNS records. - If DNS records are missing, it will recommend - which you should configure at your DNS provider - (it can take some time until they are public). - -### Other helpful commands - -To check the status of your remotely running chatmail service: - -``` -scripts/cmdeploy status -``` - -To display and check all recommended DNS records: - -``` -scripts/cmdeploy dns -``` - -To test whether your chatmail service is working correctly: - -``` -scripts/cmdeploy test -``` - -To measure the performance of your chatmail service: - -``` -scripts/cmdeploy bench -``` - -## Overview of this repository - -This repository has four directories: - -- [cmdeploy](https://github.com/chatmail/relay/tree/main/cmdeploy) - is a collection of configuration files - and a [pyinfra](https://pyinfra.com)-based deployment script. - -- [chatmaild](https://github.com/chatmail/relay/tree/main/chatmaild) - is a Python package containing several small services - which handle authentication, - trigger push notifications on new messages, - ensure that outbound mails are encrypted, - delete inactive users, - and some other minor things. - chatmaild can also be installed as a stand-alone Python package. - -- [www](https://github.com/chatmail/relay/tree/main/www) - contains the html, css, and markdown files - which make up a chatmail relay's web page. - Edit them before deploying to make your chatmail relay stand out. - -- [scripts](https://github.com/chatmail/relay/tree/main/scripts) - offers two convenience tools for beginners; - `initenv.sh` installs the necessary dependencies to a local virtual environment, - and the `scripts/cmdeploy` script enables you - to run the `cmdeploy` command line tool in the local virtual environment. - -### cmdeploy - -The `cmdeploy/src/cmdeploy/cmdeploy.py` command line tool -helps with setting up and managing the chatmail service. -`cmdeploy init` creates the `chatmail.ini` config file. -`cmdeploy run` uses a [pyinfra](https://pyinfra.com/)-based [`script`](cmdeploy/src/cmdeploy/__init__.py) -to automatically install or upgrade all chatmail components on a relay, -according to the `chatmail.ini` config. - -The components of chatmail are: - -- [Postfix SMTP MTA](https://www.postfix.org) accepts and relays messages - (both from your users and from the wider e-mail MTA network) - -- [Dovecot IMAP MDA](https://www.dovecot.org) stores messages for your users until they download them - -- [Nginx](https://nginx.org/) shows the web page with your privacy policy and additional information - -- [acmetool](https://hlandau.github.io/acmetool/) manages TLS certificates for Dovecot, Postfix, and Nginx - -- [OpenDKIM](http://www.opendkim.org/) for signing messages with DKIM and rejecting inbound messages without DKIM - -- [mtail](https://google.github.io/mtail/) for collecting anonymized metrics in case you have monitoring - -- [Iroh relay](https://www.iroh.computer/docs/concepts/relay) - which helps client devices to establish Peer-to-Peer connections - -- [TURN](https://github.com/chatmail/chatmail-turn) - to enable relay users to start webRTC calls - even if a p2p connection can't be established - -- and the chatmaild services, explained in the next section: - -### chatmaild - -`chatmaild` implements various systemd-controlled services -that integrate with Dovecot and Postfix to achieve instant-onboarding and -only relaying OpenPGP end-to-end messages encrypted messages. -A short overview of `chatmaild` services: - -- [`doveauth`](https://github.com/chatmail/relay/blob/main/chatmaild/src/chatmaild/doveauth.py) - implements create-on-login address semantics and is used - by Dovecot during IMAP login and by Postfix during SMTP/SUBMISSION login - which in turn uses [Dovecot SASL](https://doc.dovecot.org/configuration_manual/authentication/dict/#complete-example-for-authenticating-via-a-unix-socket) - to authenticate logins. - -- [`filtermail`](https://github.com/chatmail/relay/blob/main/chatmaild/src/chatmaild/filtermail.py) - prevents unencrypted email from leaving or entering the chatmail service - and is integrated into Postfix's outbound and inbound mail pipelines. - -- [`chatmail-metadata`](https://github.com/chatmail/relay/blob/main/chatmaild/src/chatmaild/metadata.py) is contacted by a - [Dovecot lua script](https://github.com/chatmail/relay/blob/main/cmdeploy/src/cmdeploy/dovecot/push_notification.lua) - to store user-specific relay-side config. - On new messages, - it [passes the user's push notification token](https://github.com/chatmail/relay/blob/main/chatmaild/src/chatmaild/notifier.py) - to [notifications.delta.chat](https://delta.chat/help#instant-delivery) - so the push notifications on the user's phone can be triggered - by Apple/Google/Huawei. - -- [`delete_inactive_users`](https://github.com/chatmail/relay/blob/main/chatmaild/src/chatmaild/delete_inactive_users.py) - deletes users if they have not logged in for a very long time. - The timeframe can be configured in `chatmail.ini`. - -- [`lastlogin`](https://github.com/chatmail/relay/blob/main/chatmaild/src/chatmaild/lastlogin.py) - is contacted by Dovecot when a user logs in - and stores the date of the login. - -- [`echobot`](https://github.com/chatmail/relay/blob/main/chatmaild/src/chatmaild/echo.py) - is a small bot for test purposes. - It simply echoes back messages from users. - -- [`chatmail-metrics`](https://github.com/chatmail/relay/blob/main/chatmaild/src/chatmaild/metrics.py) - collects some metrics and displays them at `https://example.org/metrics`. - -### Home page and getting started for users - -`cmdeploy run` also creates default static web pages and deploys them -to a Nginx web server with: - -- a default `index.html` along with a QR code that users can click to - create an address on your chatmail relay - -- a default `info.html` that is linked from the home page - -- a default `policy.html` that is linked from the home page - -All `.html` files are generated -by the according markdown `.md` file in the `www/src` directory. - - -### Refining the web pages - -``` -scripts/cmdeploy webdev -``` - -This starts a local live development cycle for chatmail web pages: - -- uses the `www/src/page-layout.html` file for producing static - HTML pages from `www/src/*.md` files - -- continously builds the web presence reading files from `www/src` directory - and generating HTML files and copying assets to the `www/build` directory. - -- Starts a browser window automatically where you can "refresh" as needed. - -#### Custom web pages - -You can skip uploading a web page -by setting `www_folder=disabled` in `chatmail.ini`. - -If you want to manage your web pages outside this git repository, -you can set `www_folder` in `chatmail.ini` to a custom directory on your computer. -`cmdeploy run` will upload it as the server's home page, -and if it contains a `src/index.md` file, -will build it with hugo. - - -## Mailbox directory layout - -Fresh chatmail addresses have a mailbox directory that contains: - -- a `password` file with the salted password required for authenticating - whether a login may use the address to send/receive messages. - If you modify the password file manually, you effectively block the user. - -- `enforceE2EEincoming` is a default-created file with each address. - If present the file indicates that this chatmail address rejects incoming cleartext messages. - If absent the address accepts incoming cleartext messages. - -- `dovecot*`, `cur`, `new` and `tmp` represent IMAP/mailbox state. - If the address is only used by one device, the Maildir directories - will typically be empty unless the user of that address hasn't been online - for a while. - - -## Emergency Commands to disable automatic address creation - -If you need to stop address creation, -e.g. because some script is wildly creating addresses, -login with ssh and run: - -``` - touch /etc/chatmail-nocreate -``` - -Chatmail address creation will be denied while this file is present. - -### Ports - -[Postfix](http://www.postfix.org/) listens on ports 25 (SMTP) and 587 (SUBMISSION) and 465 (SUBMISSIONS). -[Dovecot](https://www.dovecot.org/) listens on ports 143 (IMAP) and 993 (IMAPS). -[Nginx](https://www.nginx.com/) listens on port 8443 (HTTPS-ALT) and 443 (HTTPS). -Port 443 multiplexes HTTPS, IMAP and SMTP using ALPN to redirect connections to ports 8443, 465 or 993. -[acmetool](https://hlandau.github.io/acmetool/) listens on port 80 (HTTP). -[chatmail-turn](https://github.com/chatmail/chatmail-turn) listens on UDP port 3478 (STUN/TURN), -and temporarily opens UDP ports when users request them. UDP port range is not restricted, any free port may be allocated. - -chatmail-core based apps will, however, discover all ports and configurations -automatically by reading the [autoconfig XML file](https://www.ietf.org/archive/id/draft-bucksch-autoconfig-00.html) from the chatmail relay server. - -## Email authentication - -Chatmail relays enforce [DKIM](https://www.rfc-editor.org/rfc/rfc6376) -to authenticate incoming emails. -Incoming emails must have a valid DKIM signature with -Signing Domain Identifier (SDID, `d=` parameter in the DKIM-Signature header) -equal to the `From:` header domain. -This property is checked by OpenDKIM screen policy script -before validating the signatures. -This correpsonds to strict [DMARC](https://www.rfc-editor.org/rfc/rfc7489) alignment (`adkim=s`), -but chatmail does not rely on DMARC and does not consult the sender policy published in DMARC records. -Other legacy authentication mechanisms such as [iprev](https://www.rfc-editor.org/rfc/rfc8601#section-2.7.3) -and [SPF](https://www.rfc-editor.org/rfc/rfc7208) are also not taken into account. -If there is no valid DKIM signature on the incoming email, -the sender receives a "5.7.1 No valid DKIM signature found" error. - -Outgoing emails must be sent over authenticated connection -with envelope MAIL FROM (return path) corresponding to the login. -This is ensured by Postfix which maps login username -to MAIL FROM with -[`smtpd_sender_login_maps`](https://www.postfix.org/postconf.5.html#smtpd_sender_login_maps) -and rejects incorrectly authenticated emails with [`reject_sender_login_mismatch`](reject_sender_login_mismatch) policy. -`From:` header must correspond to envelope MAIL FROM, -this is ensured by `filtermail` proxy. - -## TLS requirements - -Postfix is configured to require valid TLS -by setting [`smtp_tls_security_level`](https://www.postfix.org/postconf.5.html#smtp_tls_security_level) to `verify`. -If emails don't arrive at your chatmail relay server, -the problem is likely that your relay does not have a valid TLS certificate. - -You can test it by resolving `MX` records of your relay domain -and then connecting to MX relays (e.g `mx.example.org`) with -`openssl s_client -connect mx.example.org:25 -verify_hostname mx.example.org -verify_return_error -starttls smtp` -from the host that has open port 25 to verify that certificate is valid. - -When providing a TLS certificate to your chatmail relay server, -make sure to provide the full certificate chain -and not just the last certificate. - -If you are running an Exim server and don't see incoming connections -from a chatmail relay server in the logs, -make sure `smtp_no_mail` log item is enabled in the config -with `log_selector = +smtp_no_mail`. -By default Exim does not log sessions that are closed -before sending the `MAIL` command. -This happens if certificate is not recognized as valid by Postfix, -so you might think that connection is not established -while actually it is a problem with your TLS certificate. - -## Migrating a chatmail relay to a new host - -If you want to migrate chatmail relay from an old machine -to a new machine, -you can use these steps. -They were tested with a Linux laptop; -you might need to adjust some of the steps to your environment. - -Let's assume that your `mail_domain` is `mail.example.org`, -all involved machines run Debian 12, -your old site's IP address is `13.37.13.37`, -and your new site's IP address is `13.12.23.42`. - -Note, you should lower the TTLs of your DNS records to a value -such as 300 (5 minutes) so the migration happens as smoothly as possible. - -During the guide you might get a warning about changed SSH Host keys; -in this case, just run `ssh-keygen -R "mail.example.org"` as recommended. - -1. First, disable mail services on the old site. - - ``` - cmdeploy run --disable-mail --ssh-host 13.37.13.37 - ``` - - Now your users will notice the migration - and will not be able to send or receive messages - until the migration is completed. - -2. Now we want to copy `/home/vmail`, `/var/lib/acme`, `/etc/dkimkeys`, `/run/echobot`, and `/var/spool/postfix` to the new site. - Login to the old site while forwarding your SSH agent - so you can copy directly from the old to the new site with your SSH key: - ``` - ssh -A root@13.37.13.37 - tar c - /home/vmail/mail /var/lib/acme /etc/dkimkeys /run/echobot /var/spool/postfix | ssh root@13.12.23.42 "tar x -C /" - ``` - - This transfers all addresses, the TLS certificate, DKIM keys (so DKIM DNS record remains valid), and the echobot's password so it continues to function. - It also preserves the Postfix mail spool so any messages pending delivery will still be delivered. - -3. Install chatmail on the new machine: - - ``` - cmdeploy run --disable-mail --ssh-host 13.12.23.42 - ``` - Postfix and Dovecot are disabled for now; we will enable them later. - We first need to make the new site fully operational. - -3. On the new site, run the following to ensure the ownership is correct in case UIDs/GIDs changed: - - ``` - chown root: -R /var/lib/acme - chown opendkim: -R /etc/dkimkeys - chown vmail: -R /home/vmail/mail - chown echobot: -R /run/echobot - ``` - -4. Now, update DNS entries. - - If other MTAs try to deliver messages to your chatmail domain they may fail intermittently, - as DNS catches up with the new site settings - but normally will retry delivering messages - for at least a week, so messages will not be lost. - -5. Finally, you can execute `cmdeploy run --ssh-host 13.12.23.42` to turn on chatmail on the new relay. - Your users will be able to use the chatmail relay as soon as the DNS changes have propagated. - Voilà! - -## Setting up a reverse proxy - -A chatmail relay MTA does not track or depend on the client IP address -for its operation, so it can be run behind a reverse proxy. -This will not even affect incoming mail authentication -as DKIM only checks the cryptographic signature -of the message and does not use the IP address as the input. - -For example, you may want to self-host your chatmail relay -and only use hosted VPS to provide a public IP address -for client connections and incoming mail. -You can connect chatmail relay to VPS -using a tunnel protocol -such as [WireGuard](https://www.wireguard.com/) -and setup a reverse proxy on a VPS -to forward connections to the chatmail relay -over the tunnel. -You can also setup multiple reverse proxies -for your chatmail relay in different networks -to ensure your relay is reachable even when -one of the IPs becomes inaccessible due to -hosting or routing problems. - -Note that your chatmail relay still needs -to be able to make outgoing connections on port 25 -to send messages outside. - -To setup a reverse proxy -(or rather Destination NAT, DNAT) -for your chatmail relay, -put the following configuration in `/etc/nftables.conf`: -``` -#!/usr/sbin/nft -f - -flush ruleset - -define wan = eth0 - -# Which ports to proxy. -# -# Note that SSH is not proxied -# so it is possible to log into the proxy server -# and not the original one. -define ports = { smtp, http, https, imap, imaps, submission, submissions } - -# The host we want to proxy to. -define ipv4_address = AAA.BBB.CCC.DDD -define ipv6_address = [XXX::1] - -table ip nat { - chain prerouting { - type nat hook prerouting priority dstnat; policy accept; - iif $wan tcp dport $ports dnat to $ipv4_address - } - - chain postrouting { - type nat hook postrouting priority 0; - - oifname $wan masquerade - } -} - -table ip6 nat { - chain prerouting { - type nat hook prerouting priority dstnat; policy accept; - iif $wan tcp dport $ports dnat to $ipv6_address - } - - chain postrouting { - type nat hook postrouting priority 0; - - oifname $wan masquerade - } -} - -table inet filter { - chain input { - type filter hook input priority filter; policy drop; - - # Accept ICMP. - # It is especially important to accept ICMPv6 ND messages, - # otherwise IPv6 connectivity breaks. - icmp type { echo-request } accept - icmpv6 type { echo-request, nd-neighbor-solicit, nd-router-advert, nd-neighbor-advert } accept - - # Allow incoming SSH connections. - tcp dport { ssh } accept - - ct state established accept - } - chain forward { - type filter hook forward priority filter; policy drop; - - ct state established accept - ip daddr $ipv4_address counter accept - ip6 daddr $ipv6_address counter accept - } - chain output { - type filter hook output priority filter; - } -} -``` - -Run `systemctl enable nftables.service` -to ensure configuration is reloaded when the proxy relay reboots. - -Uncomment in `/etc/sysctl.conf` the following two lines: +- **Zero State:** no private data or metadata collected, messages are auto-deleted, low disk usage -``` -net.ipv4.ip_forward=1 -net.ipv6.conf.all.forwarding=1 -``` +- **Instant/Realtime:** sub-second message delivery, realtime P2P + streaming, privacy-preserving Push Notifications for Apple, Google, and Huawei; -Then reboot the relay or do `sysctl -p` and `nft -f /etc/nftables.conf`. +- **Security Enforcement**: only strict TLS, DKIM and OpenPGP with minimized metadata accepted -Once proxy relay is set up, -you can add its IP address to the DNS. +- **Reliable Federation and Decentralization:** No spam or IP reputation checks, federating + depends on established IETF standards and protocols. -## Neighbors and Acquaintances +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 -Here are some related projects that you may be interested in: +[https://chatmail.at/doc/relay](https://chatmail.at/doc/relay) -- [Mox](https://github.com/mjl-/mox): A Golang email server. [Work is in - progress](https://github.com/mjl-/mox/issues/251) to modify it to support all - of the features and configuration settings required to operate as a chatmail - relay. -- [Maddy-Chatmail](https://github.com/sadraiiali/maddy_chatmail): a plugin for the - [Maddy email server](https://maddy.email/) which aims to implement the - chatmail relay features and configuration options. diff --git a/doc/Makefile b/doc/Makefile new file mode 100644 index 00000000..c960b735 --- /dev/null +++ b/doc/Makefile @@ -0,0 +1,24 @@ +# Minimal makefile for Sphinx documentation +# + +# You can set these variables from the command line, and also +# from the environment for the first two. +SPHINXOPTS ?= +SPHINXBUILD ?= sphinx-build +SOURCEDIR = source +BUILDDIR = build + +# Put it first so that "make" without argument is like "make help". +help: + @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) + +auto: + sphinx-autobuild "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) + +.PHONY: help Makefile auto + +# Catch-all target: route all unknown targets to Sphinx using the new +# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). +%: Makefile + @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) + diff --git a/doc/README.md b/doc/README.md new file mode 100644 index 00000000..4c21f5db --- /dev/null +++ b/doc/README.md @@ -0,0 +1,17 @@ + + +## Building the documentation + +You can use the `make` command and `make html` to build web pages. + +You need a Python environment where the following install was excuted: + + pip install sphinx-build furo sphinx-autobuild + +To develop/change documentation, you can then do: + + make auto + +A page will open at https://127.0.0.1:8000/ serving the docs and it will +react to changes to source files pretty fast. + diff --git a/doc/source/_static/chatmail.svg b/doc/source/_static/chatmail.svg new file mode 100644 index 00000000..f9e0ff1d --- /dev/null +++ b/doc/source/_static/chatmail.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/doc/source/_static/custom.css b/doc/source/_static/custom.css new file mode 100644 index 00000000..bc9eef45 --- /dev/null +++ b/doc/source/_static/custom.css @@ -0,0 +1,21 @@ +/* Tweak how the sidebar logo is presented */ +.sidebar-logo { + width: 70%; +} +.sidebar-brand { + padding: 0; +} + +/* The landing pages' sidebar-in-content highlights */ +#features ul { + padding-left: 1rem; + list-style: none; +} +#features ul li { + margin-bottom: 0; +} +@media (min-width: 46em) { + #features { + width: 50%; + } +} diff --git a/doc/source/conf.py b/doc/source/conf.py new file mode 100644 index 00000000..24422df4 --- /dev/null +++ b/doc/source/conf.py @@ -0,0 +1,41 @@ +# Configuration file for the Sphinx documentation builder. +# +# For the full list of built-in configuration values, see the documentation: +# https://www.sphinx-doc.org/en/master/usage/configuration.html + +# -- Project information ----------------------------------------------------- +# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information + +project = 'chatmail relay documentation' +copyright = '2025, chatmail collective' +author = 'chatmail collective' + +# -- General configuration --------------------------------------------------- +# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration + +extensions = [ + #'sphinx.ext.autodoc', + #'sphinx.ext.viewdoc', + 'sphinxcontrib.mermaid', +] + +templates_path = ['_templates'] +exclude_patterns = [] + + + +# -- Options for HTML output ------------------------------------------------- +# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output + +html_theme = 'furo' +html_static_path = ['_static'] +html_css_files = [ + "custom.css", +] + +html_title = "chatmail relay documentation" +#html_short_title = f"chatmail-{release}" + +html_logo = "_static/chatmail.svg" + + diff --git a/doc/source/faq.rst b/doc/source/faq.rst new file mode 100644 index 00000000..3fd36998 --- /dev/null +++ b/doc/source/faq.rst @@ -0,0 +1,61 @@ + + +Frequently asked questions +=========================== + +What is the difference between chatmail relays and classic email servers? +-------------------------------------------------------------------------- + +A chatmail relay is a minimal Mail Transport Agent (MTA) setup that +goes beyond what classic email servers offer: + +- **Zero State:** no private data or metadata collected, messages are auto-deleted, low disk usage + +- **Instant/Realtime:** sub-second message delivery, realtime P2P + streaming, privacy-preserving Push Notifications for Apple, Google, and `Ubuntu Touch `_; + +- **Security Enforcement**: only strict TLS, DKIM and OpenPGP with minimized metadata accepted + +- **Reliable Federation and Decentralization:** No spam or IP reputation checks, federating + depends on established IETF standards and protocols. + + +How about interoperability with classic email servers? +------------------------------------------------------- + +Generally, chatmail relays interoperate well with classic email servers. +However, some chatmail relays may be blocked by Big-Tech email +providers that use intransparent and proprietary techniques for scanning +and looking at cleartext email messages between users, or because they +use questionable IP-reputation systems that break interoperability. + +**Chatmail relays instead use and require strong cryptography, allowing +anyone to participate, without having to submit to Big-Tech +restrictions.** + +.. _selfhosted: + +How are chatmail relays run? Can I run one myself? +-------------------------------------------------- + +Chatmail relays are designed to be very cheap to run, and are generally +self-funded by respective operators. All chatmail relays are +automatically deployed and updated using `the chatmail relay +repository `__. Chatmail relays are +composed of proven standard email server components, Postfix and +Dovecot, and are configured to run unattended without much maintenance +effort. Chatmail relays happily run on low-end hardware like a Raspberry +Pi. + + +How trustable are chatmail relays? +---------------------------------- + +Chatmail relays enforce end-to-end encryption, +and chatmail clients like `Delta Chat `_ +enforce end-to-end encryption on their own. + +The end-to-end encryption protection includes attached media, user +display names, avatars and group names. What is visible to operators is: +message date, sender and receiver addresses. +Please see the `Delta Chat FAQ on encryption and security `_ for further info. diff --git a/doc/source/getting_started.rst b/doc/source/getting_started.rst new file mode 100644 index 00000000..005390ad --- /dev/null +++ b/doc/source/getting_started.rst @@ -0,0 +1,169 @@ +Setting up a chatmail relay +=========================== + +This section contains everything needed to setup a ready-to-use chatmail relay. +The automated setup is designed and optimized for providing chatmail +addresses for immediate permission-free onboarding through chat apps and bots. +Chatmail addresses are automatically created at first login, +after which the initially specified password is required +for sending and receiving messages through them. + + +Minimal requirements and prerequisites +-------------------------------------- + +You will need the following: + +- Control over a domain through a DNS provider of your choice. + +- 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. + +- Key-based SSH authentication to the root user. 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 `_) + + +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. 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. + + :: + + chat.example.com. 3600 IN A 198.51.100.5 + chat.example.com. 3600 IN AAAA 2001:db8::5 + www.chat.example.com. 3600 IN CNAME chat.example.com. + mta-sts.chat.example.com. 3600 IN CNAME chat.example.com. + +2. On your local PC, clone the repository and bootstrap the Python + virtualenv. + + :: + + git clone https://github.com/chatmail/relay + cd relay + scripts/initenv.sh + +3. On your local PC, create chatmail configuration file + ``chatmail.ini``: + + :: + + scripts/cmdeploy init chat.example.org # <-- use your domain + +4. Verify that SSH root login to your remote server works: + + :: + + ssh root@chat.example.org # <-- use your domain + +5. From your local PC, deploy the remote chatmail relay server: + + :: + + scripts/cmdeploy run + + This script will also check that you have all necessary DNS records. + If DNS records are missing, it will recommend which you should + configure at your DNS provider (it can take some time until they are + public). + +Other helpful commands +---------------------- + +To check the status of your remotely running chatmail service: + +:: + + scripts/cmdeploy status + +To display and check all recommended DNS records: + +:: + + scripts/cmdeploy dns + +To test whether your chatmail service is working correctly: + +:: + + scripts/cmdeploy test + +To measure the performance of your chatmail service: + +:: + + scripts/cmdeploy bench + + + +Modifying the home page +----------------------- + +``cmdeploy run`` also creates default static web pages and deploys them +to a Nginx web server with: + +- a default ``index.html`` along with a QR code that users can click to + create an address on your chatmail relay + +- a default ``info.html`` that is linked from the home page + +- a default ``policy.html`` that is linked from the home page + +All ``.html`` files are generated by the according markdown ``.md`` file +in the ``www/src`` directory. + +Refining the web pages +---------------------- + +:: + + scripts/cmdeploy webdev + +This starts a local live development cycle for chatmail web pages: + +- uses the ``www/src/page-layout.html`` file for producing static HTML + pages from ``www/src/*.md`` files + +- continously builds the web presence reading files from ``www/src`` + directory and generating HTML files and copying assets to the + ``www/build`` directory. + +- Starts a browser window automatically where you can “refresh” as + needed. + +Custom web pages +---------------- + +You can skip uploading a web page by setting ``www_folder=disabled`` in +``chatmail.ini``. + +If you want to manage your web pages outside this git repository, you +can set ``www_folder`` in ``chatmail.ini`` to a custom directory on your +computer. ``cmdeploy run`` will upload it as the server’s home page, and +if it contains a ``src/index.md`` file, will build it with hugo. + + +Disable automatic address creation +-------------------------------------------------------- + +If you need to stop address creation, e.g. because some script is wildly +creating addresses, login with ssh and run: + +:: + + touch /etc/chatmail-nocreate + +Chatmail address creation will be denied while this file is present. + + diff --git a/doc/source/index.rst b/doc/source/index.rst new file mode 100644 index 00000000..d37a10f6 --- /dev/null +++ b/doc/source/index.rst @@ -0,0 +1,20 @@ +******************************************* +chatmail relay documentation +******************************************* + +.. image:: ../../www/src/collage-top.png + :target: https://testrun.org + +This documentation details how to setup, maintain and understand `chatmail `_ relays. + +Contributions and feedback welcome through the https://github.com/chatmail/relay repository. + +.. toctree:: + :maxdepth: 5 + + getting_started + proxy + migrate + overview + related + faq diff --git a/doc/source/migrate.rst b/doc/source/migrate.rst new file mode 100644 index 00000000..4086744f --- /dev/null +++ b/doc/source/migrate.rst @@ -0,0 +1,73 @@ + +Migrating to a new host +----------------------- + +If you want to migrate chatmail relay from an old machine to a new +machine, you can use these steps. They were tested with a Linux laptop; +you might need to adjust some of the steps to your environment. + +Let’s assume that your ``mail_domain`` is ``mail.example.org``, all +involved machines run Debian 12, your old site’s IP address is +``13.37.13.37``, and your new site’s IP address is ``13.12.23.42``. + +Note, you should lower the TTLs of your DNS records to a value such as +300 (5 minutes) so the migration happens as smoothly as possible. + +During the guide you might get a warning about changed SSH Host keys; in +this case, just run ``ssh-keygen -R "mail.example.org"`` as recommended. + +1. First, disable mail services on the old site. + + :: + + cmdeploy run --disable-mail --ssh-host 13.37.13.37 + + Now your users will notice the migration and will not be able to send + or receive messages until the migration is completed. + +2. Now we want to copy ``/home/vmail``, ``/var/lib/acme``, + ``/etc/dkimkeys``, ``/run/echobot``, and ``/var/spool/postfix`` to + the new site. Login to the old site while forwarding your SSH agent + so you can copy directly from the old to the new site with your SSH + key: + + :: + + ssh -A root@13.37.13.37 + tar c - /home/vmail/mail /var/lib/acme /etc/dkimkeys /run/echobot /var/spool/postfix | ssh root@13.12.23.42 "tar x -C /" + + This transfers all addresses, the TLS certificate, DKIM keys (so DKIM + DNS record remains valid), and the echobot’s password so it continues + to function. It also preserves the Postfix mail spool so any messages + pending delivery will still be delivered. + +3. Install chatmail on the new machine: + + :: + + cmdeploy run --disable-mail --ssh-host 13.12.23.42 + + Postfix and Dovecot are disabled for now; we will enable them later. + We first need to make the new site fully operational. + +4. On the new site, run the following to ensure the ownership is correct + in case UIDs/GIDs changed: + + :: + + chown root: -R /var/lib/acme + chown opendkim: -R /etc/dkimkeys + chown vmail: -R /home/vmail/mail + chown echobot: -R /run/echobot + +5. Now, update DNS entries. + + If other MTAs try to deliver messages to your chatmail domain they + may fail intermittently, as DNS catches up with the new site settings + but normally will retry delivering messages for at least a week, so + messages will not be lost. + +6. Finally, you can execute ``cmdeploy run --ssh-host 13.12.23.42`` to + turn on chatmail on the new relay. Your users will be able to use the + chatmail relay as soon as the DNS changes have propagated. Voilà! + diff --git a/doc/source/overview.rst b/doc/source/overview.rst new file mode 100644 index 00000000..c04394ba --- /dev/null +++ b/doc/source/overview.rst @@ -0,0 +1,299 @@ + +Technical overview +====================== + + +Directories of the relay repository +----------------------------------- + +The `chatmail relay repository `_ +has four main directories. + +``scripts/`` +~~~~~~~~~~~~~ + +`scripts `_ +offers two convenience tools for beginners: + +- ``initenv.sh`` installs a local virtualenv Python environment and + installs necessary dependencies + +- ``scripts/cmdeploy`` script enables you to run the ``cmdeploy`` + command line tool in the local Python virtual environment. + + +``cmdeploy/`` +~~~~~~~~~~~~~ + +The ``cmdeploy`` directory contains the Python package and command line tool +to setup a chatmail relay remotely via SSH: + +- ``cmdeploy init`` creates the ``chatmail.ini`` config file locally. + +- ``cmdeploy run`` under the hood uses pyinfra_ + to automatically install or upgrade all chatmail components on a relay, + according to the local ``chatmail.ini`` config. + +The deployed system components of a chatmail relay are: + +- Postfix_ is the Mail Transport Agent (MTA) and + accepts messages from, and sends messages to, the wider email MTA network + +- Dovecot_ is the Mail Delivery Agent (MDA) and + stores messages for users until they download them + +- Nginx_ shows the web page with privacy policy and additional information + +- `acmetool `_ manages TLS + certificates for Dovecot, Postfix, and Nginx + +- `OpenDKIM `_ for signing messages with + DKIM and rejecting inbound messages without DKIM + +- `mtail `_ for collecting anonymized + metrics in case you have monitoring + +- `Iroh relay `_ which + helps client devices to establish Peer-to-Peer connections + +- `TURN `_ to enable relay + users to start webRTC calls even if a p2p connection can’t be + established + +- and the chatmaild services, explained in the next section: + + +``chatmaild/`` +~~~~~~~~~~~~~~ + +`chatmaild `_ +is a Python package containing several small services which handle +authentication, trigger push notifications on new messages, ensure +that outbound mails are encrypted, delete inactive users, and some +other minor things. chatmaild can also be installed as a stand-alone +Python package. + +``chatmaild`` implements various systemd-controlled services +that integrate with Dovecot and Postfix to achieve instant-onboarding +and only relaying OpenPGP end-to-end messages encrypted messages. A +short overview of ``chatmaild`` services: + +- `doveauth `_ + implements create-on-login address semantics and is used by Dovecot + during IMAP login and by Postfix during SMTP/SUBMISSION login which + in turn uses `Dovecot SASL + `_ + to authenticate logins. + +- `filtermail `_ + prevents unencrypted email from leaving or entering the chatmail + service and is integrated into Postfix’s outbound and inbound mail + pipelines. + +- `chatmail-metadata `_ + is contacted by a `Dovecot lua + script `_ + to store user-specific relay-side config. On new messages, it `passes + the user’s push notification + token `_ + to + `notifications.delta.chat `_ + so the push notifications on the user’s phone can be triggered by + Apple/Google/Huawei. + +- `chatmail-expire `_ + deletes users if they have not logged in for a longer while. + The timeframe can be configured in ``chatmail.ini``. + +- `lastlogin `_ + is contacted by Dovecot when a user logs in and stores the date of + the login. + +- `echobot `_ + is a small bot for test purposes. It simply echoes back messages from + users. + +- `metrics `_ + collects some metrics and displays them at + ``https://example.org/metrics``. + +``www/`` +~~~~~~~~~ + +`www `_ contains +the html, css, and markdown files which make up a chatmail relay’s +web page. Edit them before deploying to make your chatmail relay +stand out. + + +Component dependency diagram +-------------------------------------- + +.. mermaid:: + :caption: This diagram shows relay components and dependencies/communication paths. + + graph LR; + cmdeploy --- sshd; + letsencrypt --- |80|acmetool-redirector; + acmetool-redirector --- |443|nginx-right(["`nginx + (external)`"]); + nginx-external --- |465|postfix; + nginx-external(["`nginx + (external)`"]) --- |8443|nginx-internal["`nginx + (internal)`"]; + nginx-internal --- website["`Website + /var/www/html`"]; + nginx-internal --- newemail.py; + nginx-internal --- autoconfig.xml; + certs-nginx[("`TLS certs + /var/lib/acme`")] --> nginx-internal; + cron --- chatmail-metrics; + cron --- acmetool; + chatmail-metrics --- website; + acmetool --> certs[("`TLS certs + /var/lib/acme`")]; + nginx-external --- |993|dovecot; + autoconfig.xml --- postfix; + autoconfig.xml --- dovecot; + postfix --- echobot; + postfix --- |10080,10081|filtermail; + postfix --- users["`User data + home/vmail/mail`"]; + postfix --- |doveauth.socket|doveauth; + dovecot --- |doveauth.socket|doveauth; + dovecot --- users; + dovecot --- |metadata.socket|chatmail-metadata; + doveauth --- users; + chatmail-expire-daily --- users; + chatmail-fsreport-daily --- users; + chatmail-metadata --- iroh-relay; + certs-nginx --> postfix; + certs-nginx --> dovecot; + style certs fill:#ff6; + style certs-nginx fill:#ff6; + style nginx-external fill:#fc9; + style nginx-right fill:#fc9; + + +Operational details of a chatmail relay +---------------------------------------- + +Mailbox directory layout +~~~~~~~~~~~~~~~~~~~~~~~~ + +Fresh chatmail addresses have a mailbox directory that contains: + +- a ``password`` file with the salted password required for + authenticating whether a login may use the address to send/receive + messages. If you modify the password file manually, you effectively + block the user. + +- ``enforceE2EEincoming`` is a default-created file with each address. + If present the file indicates that this chatmail address rejects + incoming cleartext messages. If absent the address accepts incoming + cleartext messages. + +- ``dovecot*``, ``cur``, ``new`` and ``tmp`` represent IMAP/mailbox + state. If the address is only used by one device, the Maildir + directories will typically be empty unless the user of that address + hasn’t been online for a while. + +Active ports +~~~~~~~~~~~~ + +Postfix_ listens on ports + +- 25 (SMTP) + +- 587 (SUBMISSION) and + +- 465 (SUBMISSIONS) + +Dovecot_ listens on ports + +- 143 (IMAP) and + +- 993 (IMAPS) + +Nginx_ listens on port + +- 8443 (HTTPS-ALT) and + +- 443 (HTTPS) which multiplexes HTTPS, IMAP and SMTP using ALPN + to redirect connections to ports 8443, 465 or 993. + +`acmetool `_ listens on port: + +- 80 (HTTP). + +`chatmail-turn `_ listens on port + +- 3478 UDP (STUN/TURN), and temporarily opens further UDP ports + when users request them. UDP port range is not restricted, any free port + may be allocated. + +chatmail-core based apps will, however, discover all ports and +configurations automatically by reading the `autoconfig XML +file `_ +from the chatmail relay server. + +Email domain authentication (DKIM) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Chatmail relays enforce :rfc:`DKIM <6376>` to authenticate incoming emails. +Incoming emails must have a valid DKIM signature with +Signing Domain Identifier (SDID, ``d=`` parameter in the DKIM-Signature +header) equal to the ``From:`` header domain. This property is checked +by OpenDKIM screen policy script before validating the signatures. This +correpsonds to strict :rfc:`DMARC <7489>` alignment (``adkim=s``). +If there is no valid DKIM signature on the incoming email, the +sender receives a “5.7.1 No valid DKIM signature found” error. + +Note that chatmail relays + +- do **not** rely on DMARC and do not consult the sender policy published in DMARC records; + +- do **not** rely on legacy authentication mechanisms such as + :rfc:`iprev <8601#section-2.7.3>` and :rfc:`SPF <7208>`. + Any IP address is accepted if the DKIM signature was valid. + +Outgoing emails must be sent over authenticated connection with envelope +``MAIL FROM`` (return path) corresponding to the login. +This is ensured by Postfix which maps login username to ``MAIL FROM`` with +`smtpd_sender_login_maps `_ +and rejects incorrectly authenticated emails with +`reject_sender_login_mismatch `_ policy. +``From:`` header must correspond to envelope ``MAIL FROM``, this is +ensured by ``filtermail`` proxy. + +TLS requirements +~~~~~~~~~~~~~~~~ + +Postfix is configured to require valid TLS by setting +`smtp_tls_security_level `_ +to ``verify``. If emails don’t arrive at your chatmail relay server, the +problem is likely that your relay does not have a valid TLS certificate. + +You can test it by resolving ``MX`` records of your relay domain and +then connecting to MX relays (e.g ``mx.example.org``) with +``openssl s_client -connect mx.example.org:25 -verify_hostname mx.example.org -verify_return_error -starttls smtp`` +from the host that has open port 25 to verify that certificate is valid. + +When providing a TLS certificate to your chatmail relay server, make +sure to provide the full certificate chain and not just the last +certificate. + +If you are running an Exim server and don’t see incoming connections +from a chatmail relay server in the logs, make sure ``smtp_no_mail`` log +item is enabled in the config with ``log_selector = +smtp_no_mail``. By +default Exim does not log sessions that are closed before sending the +``MAIL`` command. This happens if certificate is not recognized as valid +by Postfix, so you might think that connection is not established while +actually it is a problem with your TLS certificate. + + +.. _dovecot: https://dovecot.org +.. _postfix: https://www.postfix.org +.. _nginx: https://nginx.org +.. _pyinfra: https://pyinfra.com + diff --git a/doc/source/proxy.rst b/doc/source/proxy.rst new file mode 100644 index 00000000..3de0f732 --- /dev/null +++ b/doc/source/proxy.rst @@ -0,0 +1,114 @@ + +Setting up a reverse proxy +-------------------------- + +A chatmail relay MTA does not track or depend on the client IP address +for its operation, so it can be run behind a reverse proxy. This will +not even affect incoming mail authentication as DKIM only checks the +cryptographic signature of the message and does not use the IP address +as the input. + +For example, you may want to self-host your chatmail relay and only use +hosted VPS to provide a public IP address for client connections and +incoming mail. You can connect chatmail relay to VPS using a tunnel +protocol such as `WireGuard `_ and setup a +reverse proxy on a VPS to forward connections to the chatmail relay over +the tunnel. You can also setup multiple reverse proxies for your +chatmail relay in different networks to ensure your relay is reachable +even when one of the IPs becomes inaccessible due to hosting or routing +problems. + +Note that your chatmail relay still needs to be able to make outgoing +connections on port 25 to send messages outside. + +To setup a reverse proxy (or rather Destination NAT, DNAT) for your +chatmail relay, put the following configuration in +``/etc/nftables.conf``: + +:: + + #!/usr/sbin/nft -f + + flush ruleset + + define wan = eth0 + + # Which ports to proxy. + # + # Note that SSH is not proxied + # so it is possible to log into the proxy server + # and not the original one. + define ports = { smtp, http, https, imap, imaps, submission, submissions } + + # The host we want to proxy to. + define ipv4_address = AAA.BBB.CCC.DDD + define ipv6_address = [XXX::1] + + table ip nat { + chain prerouting { + type nat hook prerouting priority dstnat; policy accept; + iif $wan tcp dport $ports dnat to $ipv4_address + } + + chain postrouting { + type nat hook postrouting priority 0; + + oifname $wan masquerade + } + } + + table ip6 nat { + chain prerouting { + type nat hook prerouting priority dstnat; policy accept; + iif $wan tcp dport $ports dnat to $ipv6_address + } + + chain postrouting { + type nat hook postrouting priority 0; + + oifname $wan masquerade + } + } + + table inet filter { + chain input { + type filter hook input priority filter; policy drop; + + # Accept ICMP. + # It is especially important to accept ICMPv6 ND messages, + # otherwise IPv6 connectivity breaks. + icmp type { echo-request } accept + icmpv6 type { echo-request, nd-neighbor-solicit, nd-router-advert, nd-neighbor-advert } accept + + # Allow incoming SSH connections. + tcp dport { ssh } accept + + ct state established accept + } + chain forward { + type filter hook forward priority filter; policy drop; + + ct state established accept + ip daddr $ipv4_address counter accept + ip6 daddr $ipv6_address counter accept + } + chain output { + type filter hook output priority filter; + } + } + +Run ``systemctl enable nftables.service`` to ensure configuration is +reloaded when the proxy relay reboots. + +Uncomment in ``/etc/sysctl.conf`` the following two lines: + +:: + + net.ipv4.ip_forward=1 + net.ipv6.conf.all.forwarding=1 + +Then reboot the relay or do ``sysctl -p`` and +``nft -f /etc/nftables.conf``. + +Once proxy relay is set up, you can add its IP address to the DNS. + diff --git a/doc/source/related.rst b/doc/source/related.rst new file mode 100644 index 00000000..14f1288b --- /dev/null +++ b/doc/source/related.rst @@ -0,0 +1,20 @@ + +Community developments +====================== + +Active development takes place in the `chatmail/relay github repository `_. + +You can check out the `'chatmail' tag in the support.delta.chat forum `_ +and ask to get added to a non-public support chat for debugging issues. + +We know of two work-in-progress alternative implementation efforts: + +- `Mox `_: A Golang email server. `Work + is in progress `_ to modify + it to support all of the features and configuration settings required + to operate as a chatmail relay. + +- `Maddy-Chatmail `_: a + plugin for the `Maddy email server `_ which + aims to implement the chatmail relay features and configuration + options. diff --git a/scripts/build-docs.sh b/scripts/build-docs.sh new file mode 100644 index 00000000..ac066d4a --- /dev/null +++ b/scripts/build-docs.sh @@ -0,0 +1,7 @@ +#!/bin/sh +# +# Wrapper for building the docs +set -e +. venv/bin/activate +cd doc/ +make html diff --git a/scripts/initenv.sh b/scripts/initenv.sh index db70250a..d0e40a54 100755 --- a/scripts/initenv.sh +++ b/scripts/initenv.sh @@ -22,3 +22,4 @@ python3 -m venv --upgrade-deps venv venv/bin/pip install -e chatmaild venv/bin/pip install -e cmdeploy +venv/bin/pip install sphinx sphinxcontrib-mermaid sphinx-autobuild furo # for building the docs