Email in Sandstorm

This tutorial will show you how to setup your personal sandstorm instance with support for e-mail.

First, a quick rundown of how email works in sandstorm. Sandstorm is running an SMTP server on port 30025 that will accept email of the form publicId@hostname. publicId is randomly generated for every grain that handles e-mail, and hostname is extracted from the BASE_URL parameter in sandstorm.conf (which is initially created by the installer script). Outgoing e-mail is sent through an external SMTP server, controllable from the Admin settings -> Email configuration page. (Note that the MAIL_URL parameter in sandstorm.conf has been deprecated.) When sending e-mails, the only valid "From" addresses are the grain's generated publicId@hostname address, or the verified email from the user's account (e.g. the e-mail address obtained via e.g. Google or Github or email login). The interfaces for sending/receiving e-mails are available in hack-session.capnp.

Outgoing SMTP

Apps can send email out to the world, subject to rate limiting. To enable this feature, you must configure an SMTP relay in the Email configuration page of the Admin panel. The SMTP server needs to accept e-mails with the SMTP envelope's bounce address set to either your grain's local address or the "Sandstorm server's own email address" address.

If running at home or at work, you can usually use your ISP's or corporation's SMTP server. Otherwise, Sendgrid, Mailgun, and others provide SMTP services, some with free tiers. Note in our testing, Google Gmail is incompatible with the Sandstorm outbound SMTP requirements: it will modify the From header and SMTP envelope to be your personal address, rather than what the app specified. Therefore it may seem to work for the first user, but when you add other users to your server, any emails sent by their apps will appear to come from you!

Some cloud providers block outbound port 25, which you may experience as Sandstorm reporting "Connection timed out." In that case, check if your outbound SMTP provider supports alternative ports such as 587 or 2525.

sandcats.io users: Since the sandcats dynamic DNS & HTTPS service does not support special DNS records that improve email deliverability, we recommend you configure a Server's own email address on a domain you personally control, not your sandcats subdomain. Then, be sure to use an SMTP provider to configure that domain and your DNS provider to configure SPF/DKIM records.

Receiving email into Sandstorm grains

To allow Sandstorm grains to receive email, you need to do the following.

  • Set up DNS for a domain: Configure DNS so that other computers know how to deliver email to your server.

  • Configure port 25 on your server: When email messages arrive on inbound port 25, they must reach Sandstorm. Sandstorm will then route them to any specific grain.

Aside: How to test your configuration

One great way to test inbound email is to use the Roundcube app. To do that:

  • Install Roundcube from the Sandstorm app market into an account on your Sandstorm server.

  • Click Create a mailbox from Roundcube's app details page on your server.

  • Click Connect to Your Address within a Roundcube grain. You should now see an inbound email address for this Roundcube grain of the form.

Once you know Roundcube's inbound email address, use a separate email system such as Gmail to send it an email! If this test email appears within Roundcube, you can skip the rest of these steps! Inbound emails should appear within 5 seconds of when you send them; if they do not, click Roundcube's Refresh button to make sure.

Set up DNS

Grains can receive inbound email, and their email adddresses are always of the form {{publicId}}@{{BASE_DOMAIN}}. {{BASE_DOMAIN}} is the domain name value component of BASE_URL in your sandstorm.conf. {{publicId}} is a random unique ID that is assigned to this grain by Sandstorm. Therefore, the domain in your BASE_URL needs to have a DNS configuration that enables inbound email.

sandcats.io users: No action is required. Servers on the Internet that send you email will connect to the IP address of the DNS A record maintained by the sandcats.io service.

Sandstorm users on your own domain: No action is usually required, since presumably your server's BASE_URL resolves properly to your Sandstorm server. For extra standards compliance, you can add an MX record for the domain name that you use in your BASE_URL. You can use any number as the MX priority, for example, 10.

Configure port 25, the easy way: Sandstorm can listen on port 25

By default, Sandstorm's SMTP server runs on port 30025. You can adjust it to listen on port 25. This is the easiest way to configure inbound email; however, note that Sandstorm's SMTP server does not support inbound STARTTLS at this time.

Make sure nothing else is running on port 25 on this system. Then edit /opt/sandstorm/sandstorm.conf to add this new line:

SMTP_LISTEN_PORT=25

Now stop & start Sandstorm.

At this point, you should be able to test inbound email to your Roundcube grain.

Configure port 25, the advanced way: Proxy SMTP

If you need to share Sandstorm's SMTP service with port 25 used by other services, or you want inbound STARTTLS, you can configure Sandstorm's SMTP service to sit behind a reverse proxy. One way to do accomplish this is with nginx. Add the following to your nginx.conf:

mail {
    ssl_certificate /etc/keys/my-ssl.crt;
    ssl_certificate_key /etc/keys/my-ssl.key;
    ssl_session_timeout 5m;
    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
    ssl_ciphers ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:ECDH+3DES:DH+3DES:RSA+AESGCM:RSA+AES:RSA+3DES:!aNULL:!MD5:!DSS;
    ssl_prefer_server_ciphers on;

    server {
        listen 25;
        server_name sandstorm.example.com;
        auth_http localhost:8008/fake-smtp-auth;
        protocol smtp;
        timeout 30s;
        proxy on;
        xclient off;
        smtp_auth none;
        starttls on;
    }

    server {
        listen 465;
        server_name sandstorm.example.com;
        auth_http localhost:8008/fake-smtp-auth;
        protocol smtp;
        timeout 30s;
        proxy on;
        xclient off;
        smtp_auth none;
        ssl on;
    }
}

nginx requires that you provide an authentication handler for all SMTP proxies. We don't actually need authentication because our server is only meant to receive e-mail destined for this host (it will not relay). So, we have to set up a fake authentication handler. In the part of your nginx config where you defined your HTTP servers (e.g. /etc/nginx/sites-available/default), add this fake server for SMTP auth purposes:

# Fake SMTP authorizor which always accepts. Put this in your http block.
server {
    listen 127.0.0.1:8008;
    server_name localhost;

    location /fake-smtp-auth {
        add_header Auth-Server 127.0.0.1;
        add_header Auth-Port 30025;
        return 200;
    }
}