mail-addon

mail-addon

Mail addon

This is the mail addon that implements mail support for Cloudron apps.

Design

The mail addon serves two purposes a) sendmail/recvmail addon for apps b) SMTP/IMAP access for users

This addon used LDAP to authenticate apps and users.

Outbound

  • SMTP Auth on port MSA port 587- Haraka authenticates the app or the user using LDAP. TLS in enforced. Once auth'ed, the mail is relay'able (Haraka term for outbound mail).

  • The dnsbl plugin will block users from bad IP addresses. The delay_deny plugin is configured to delay this denial for authenticated users.

  • The dkim plugin signs outgoing mail.

  • The cloudron_auth plugin has a mail from hook to ensure that the authenticated user and the FROM address match.

  • The relay plugin has an ACL to allow the box code to send emails without authentication (uses an IP based ACL).

  • When relaying is set, someone needs to vouch for the mail in the rcpt phase. This is allowed by the rcpt_to.in_host_list plugin for local senders.

Signing

Messages are signed using DKIM. DKIM plugin is an outbound hook and thus only signs outbound messages. This is important because an unauthenticate message headed to our domain with a spoofed FROM address will not be signed by it.

Inbound mail

  • Haraka receives mail on MTA port 25.

  • dnsbl blocks any spammer based on DNSBL lookup on zen.spamhaus.org

  • rcpt_to.in_host_list plugin enfores that inbound mail is addressed to our domain.

  • LMTP transfer - The LMTP transfer has no authentication but dovecot is setup to verify the RCPT to check if it a valid mailbox. It does so by checking the name against LDAP. Email is stored based on username instead of uid currently.

Bounces

The bounce plugin is currently configured to send bounces to webmaster@cloudron.io ($ALERTS_TO). This will be changed to the Cloudron owner at some point when our mail stack is more stable.

Spam

Spamassasin is setup in virtual user mode where it stores spam settings per user.

Haraka feeds messages via the SpamAssassin plugin. There is a sieve script that is run for all users (sieve-after) that moves messages as spam automatically into the Spam folder.

The Dovecot antispam plugin tracks move of messages in and out of the Spam folder and calls sa-learn.

DKIM and SPF verification

SPF verification is done in Haraka (because it requires envelope headers). It attaches a header which is then used by SpamAssassin.

DKIM verification is done by SpamAssassin.

Accessing mail

  • IMAP Auth - Dovecot uses LDAP to authenticate user.

  • IMAP currently stores the mail using the username but this will be fixed to use uid instead in a future version.

  • Sieve Auth is done by Dovecot by LDAP.

Aliases

Both apps and users can have aliases. There is a RCPT plugin in cloudron_auth plugin that translates aliases and mailing lists via LDAP.

  • SMTP Auth - Currently, we do not allow authentication with alias but only using the original credentials. This is for the sake of simplicity.

  • IMAP Auth - We do not allow authentication with alias.

  • FROM validation - Haraka loads the alias list and translates the FROM into the target address and matches against the authenticated address.

  • LMTP transfer - Haraka has been configured with the alias plugin to translate the envolope TO address. This means that any mail to alias ends up in the target mailbox (but the email message contents are itself not changed).

TLS

IMAP and SMTP (MSA) access is only allowed with TLS. For internal access by apps, TLS is optional. This is primarily because some apps are hard to configure to allow for self-signed certs. In addition, Haraka only allows AUTH for TLS connections. For this reason, we have a capabilities hook to allow AUTH even without TLS for internal connections.

Internal mail

If a user sends email to another user in the same Cloudron, then this comes in via port 587 and is marked for relay. Haraka delivers outbound mail via MX lookup and transfer even if the mail is intended for our own domain.

We have a get_mx hook to make Haraka use LMTP for own domain relay. This is not done for optimization but for making it easy to test internal mail relay without having to setup MX records.

Haraka does not require relay for mails to same domain. This also means that an app can simply send mails to same domain without AUTH (since it is the auth that makes it relay'able). To prevent this, we enable the antispoof feature of rcpt_to.in_host_list which rejects mail with our FROM domain and no relay flag.

For the case, where the unathenticated FROM address does not match our domain, such a mail is indistinguishable from an external mail. We rely on DKIM check to catch such mails.

Building

  1. Build the container as girish/mail (the name is hardcoded in the tests).

    docker build -t girish/mail .

  2. Run the tests: test/send-test.sh test/recv-test.sh

  3. Tag the new container. You can find the new version from looking at git tags.

    docker tag girish/mail cloudron/mail: docker push cloudron/mail:

  4. Bump the version in box repo's src/infra_version.js.

  5. Lastly, create a git tag when the release is out.

Running

generate cert

tls_key=/tmp/mail/server.key
tls_cert=/tmp/mail/server.cert
openssl req  -nodes -new -x509  -keyout "${tls_key}" -out "${tls_cert}" -subj '/CN=test'

Create mail ini

mail_domain=girish.in
mail_server_name=mail.girish.in
alerts_from=no-reply@girish.in
alerts_to=webmaster@cloudron.io

You can now run it as:

docker run --read-only -ti MAIL_DOMAIN=girish.in -v /run -v /tmp -v "/tmp/mail:/app/data" "/tmp/mail:/etc/mail" girish/mail /bin/bash