Skip to content

springcomp/self-hosted-simplelogin

Repository files navigation

SimpleLogin

This is a self-hosted docker-compose configuration for SimpleLogin.

Prerequisites

  • a Linux server (either a VM or dedicated server). This doc shows the setup for Ubuntu 18.04 LTS but the steps could be adapted for other popular Linux distributions. As most of components run as Docker container and Docker can be a bit heavy, having at least 2 GB of RAM is recommended. The server needs to have the port 25 (email), 80, 443 (for the webapp), 22 (so you can ssh into it) open.

  • a domain for which you can config the DNS. It could be a sub-domain. In the rest of the doc, let's say it's mydomain.com for the email and app.mydomain.com for SimpleLogin webapp. Please make sure to replace these values by your domain name and subdomain name whenever they appear in the doc. A trick we use is to download this README file on your computer and replace all mydomain.com and app.mydomain.com occurrences by your domain.

Except for the DNS setup that is usually done on your domain registrar interface, all the below steps are to be done on your server. The commands are to run with bash (or any bash-compatible shell like zsh) being the shell. If you use other shells like fish, please make sure to adapt the commands.

  • Some utility packages used to verify the setup. Install them by:
sudo apt update \
  && sudo apt install -y net-tools dnsutils

DNS Configuration

Please note that DNS changes could take up to 24 hours to propagate. In practice, it's a lot faster though (~1 minute or so in our test). In DNS setup, we usually use domain with a trailing dot (.) at the end to to force using absolute domain.

A record

Create an A record that points app.mydomain.com. to your server IP. To verify, the following command:

dig @1.1.1.1 app.mydomain.com a

should return your server IP.

MX record

Create a MX record that points mydomain.com. to app.mydomain.com. with priority 10.

To verify if the DNS works, the following command:

dig @1.1.1.1 mydomain.com mx

should return:

mydomain.com. 3600 IN MX 10 app.mydomain.com.

PTR record

From Wikipedia https://en.wikipedia.org/wiki/Reverse_DNS_lookup

A reverse DNS lookup or reverse DNS resolution (rDNS) is the querying technique of the Domain Name System (DNS) to determine the domain name associated with an IP address – the reverse of the usual "forward" DNS lookup of an IP address from a domain name.

Create a PTR record that point your IP address to your domain name. Important Some providers require PTR configuration to be done from their dashboard and ignore DNS records. Please, make sure to properly configure reverse DNS lookup for your domain.

To verify, the following command:

dig @1.1.1.1 -x $( ip addr show eth0 | grep 'inet ' | awk '{print $2}' | cut -d'/' -f1)

should return your domain name.

DKIM

From Wikipedia https://en.wikipedia.org/wiki/DomainKeys_Identified_Mail

DomainKeys Identified Mail (DKIM) is an email authentication method designed to detect forged sender addresses in emails (email spoofing), a technique often used in phishing and email spam.

Setting up DKIM is highly recommended to reduce the chance for your emails ending up in the recipient's Spam folder.

First you need to generate a private and public key for DKIM:

openssl genrsa -traditional -out dkim.key 1024
openssl rsa -in dkim.key -pubout -out dkim.pub.key

You will need the files dkim.key and dkim.pub.key for the next steps.

For email gurus, we have chosen 1024 key length instead of 2048 for DNS simplicity as some registrars don't play well with long TXT record.

Set up DKIM by adding a TXT record for dkim._domainkey.mydomain.com. with the following value:

v=DKIM1; k=rsa; p=PUBLIC_KEY

with PUBLIC_KEY being your dkim.pub.key but

  • remove the -----BEGIN PUBLIC KEY----- and -----END PUBLIC KEY-----
  • join all the lines on a single line.

For example, if your dkim.pub.key is

-----BEGIN PUBLIC KEY-----
ab
cd
ef
gh
-----END PUBLIC KEY-----

then the PUBLIC_KEY would be abcdefgh.

You can get the PUBLIC_KEY by running this command:

sed "s/-----BEGIN PUBLIC KEY-----/v=DKIM1; k=rsa; p=/g" $(pwd)/dkim.pub.key | \
  sed 's/-----END PUBLIC KEY-----//g' | \
  tr -d '\n' | awk 1

To verify, the following command:

dig @1.1.1.1 dkim._domainkey.mydomain.com txt

should return the above value.

SPF

From Wikipedia https://en.wikipedia.org/wiki/Sender_Policy_Framework

Sender Policy Framework (SPF) is an email authentication method designed to detect forging sender addresses during the delivery of the email

Similar to DKIM, setting up SPF is highly recommended.

Create a TXT record for mydomain.com. with the value:

v=spf1 mx -all

What it means is only your server can send email with @mydomain.com domain. To verify, the following command

dig @1.1.1.1 mydomain.com txt

should return the above value.

DMARC

From Wikipedia https://en.wikipedia.org/wiki/DMARC

It (DMARC) is designed to give email domain owners the ability to protect their domain from unauthorized use, commonly known as email spoofing

Setting up DMARC is also recommended.

Create a TXT record for _dmarc.mydomain.com. with the following value

v=DMARC1; p=quarantine; adkim=r; aspf=r

This is a relaxed DMARC policy. You can also use a more strict policy with v=DMARC1; p=reject; adkim=s; aspf=s value.

To verify, the following command

dig @1.1.1.1 _dmarc.mydomain.com txt

should return the set value.

For more information on DMARC, please consult https://tools.ietf.org/html/rfc7489

HSTS

From Wikipedia https://en.wikipedia.org/wiki/HTTP_Strict_Transport_Security

HTTP Strict Transport Security (HSTS) is a policy mechanism that helps to protect websites against man-in-the-middle attacks such as protocol downgrade attacks and cookie hijacking.

HTTP Strict Transport Security is an extra step you can take to protect your web app from certain man-in-the-middle attacks. It does this by specifying an amount of time (usually a really long one) for which you should only accept HTTPS connections, not HTTP ones.

This repository already enables HSTS, thanks to the traefik configuration for the simplelogin container

CAA

From Wikipedia https://en.wikipedia.org/wiki/DNS_Certification_Authority_Authorization

DNS Certification Authority Authorization (CAA) is an Internet security policy mechanism that allows domain name holders to indicate to certificate authorities whether they are authorized to issue digital certificates for a particular domain name.

Certificate Authority Authorization is a step you can take to restrict the list of certificate authorities that are allowed to issue certificates for your domains.

Use SSLMate’s CAA Record Generator to create a CAA record with the following configuration:

  • flags: 0
  • tag: issue
  • value: "letsencrypt.org"

To verify if the DNS works, the following command:

dig @1.1.1.1 mydomain.com caa

should return:

mydomain.com. 3600 IN CAA 0 issue "letsencrypt.org"

Warning: setting up a CAA record will restrict which certificate authority can successfully issue SSL certificates for your domain. This will prevent certificate issuance from Let’s Encrypt staging servers. You may want to differ this DNS record until after SSL certificates are successfully issued for your domain.

MTA-STS

From Wikipedia https://en.wikipedia.org/wiki/Simple_Mail_Transfer_Protocol#SMTP_MTA_Strict_Transport_Security

SMTP MTA Strict Transport Security defines a protocol for mail servers to declare their ability to use secure channels in specific files on the server and specific DNS TXT records.

SMTP MTA Strict Transport Security is an extra step you can take to broadcast the ability of your instance to receive and, optionally enforce, TSL-secure SMTP connections to protect email traffic.

Create an A record that points mta-sts.mydomain.com. to your server IP.

To verify, the following command:

dig @1.1.1.1 mta-sts.mydomain.com a

should return your server IP.

Create a TXT record for _mta-sts.mydomain.com. with the following value:

v=STSv1; id=UNIX_TIMESTAMP

With UNIX_TIMESTAMP being the current date/time.

Use the following command to generate the record:

echo "v=STSv1; id=$(date +%s)"

To verify if the DNS works, the following command:

dig @1.1.1.1 _mta-sts.mydomain.com txt

should return a result similar to this one:

_mta-sts.mydomain.com. 3600 IN TXT "v=STSv1; id=1689416399"

TLSRPT

SMTP TLS Reporting is used by SMTP systems to report failures in establishing TLS-secure sessions as broadcast by the MTA-STS configuration.

Configuring MTA-STS in mode: testing as shown in the previous section gives you time to review failures from some SMTP senders.

Create a TXT record for _smtp._tls.mydomain.com. with the following value:

v=TSLRPTv1; rua=mailto:YOUR_EMAIL

The TLSRPT configuration at the DNS level allows SMTP senders that fail to initiate TLS-secure sessions to send reports to a particular email address. We suggest creating a tls-reports alias in SimpleLogin for this purpose.

To verify if the DNS works, the following command

dig @1.1.1.1 _smtp._tls.mydomain.com txt

should return a result similar to this one:

_smtp._tls.mydomain.com. 3600 IN TXT "v=TSLRPTv1; rua=mailto:tls-reports@mydomain.com"

Docker

If you don't already have Docker installed on your server, please follow the steps on Docker CE for Ubuntu to install Docker.

You can also install Docker using the docker-install script which is

curl -fsSL https://get.docker.com | sh

Enable IPv6 for the default bridge network

{
  "ipv6": true,
  "fixed-cidr-v6": "2001:db8:1::/64",
  "experimental": true,
  "ip6tables": true
}

This procedure will guide you through running the entire stack using Docker containers. This includes:

Run SimpleLogin from Docker containers:

  1. Clone this repository in /opt/simplelogin

  2. Copy .env.example to .env and set appropriate values.

    • set the DOMAIN variable to your domain.
    • set the SUBDOMAIN variable to your domain. The default value is app.
    • set the POSTGRES_USER variable to match the postgres credentials (when starting from scratch, use simplelogin).
    • set the POSTGRES_PASSWORD to match the postgres credentials (when starting from scratch, set to a random key).
    • set the FLASK_SECRET to an arbitrary secret key.

Running the application

Run the application using the following commands:

docker compose up --detach --remove-orphans --build && docker compose logs -f

You may want to setup Certificate Authority Authorization (CAA) at this point.

Next steps

If all the above steps are successful, open https://app.mydomain.com/ and create your first account!

By default, new accounts are not premium so don't have unlimited aliases. To make your account premium, please go to the database, table "users" and set "lifetime" column to "1" or "TRUE":

docker compose exec -it postgres psql -U myuser simplelogin
> UPDATE users SET lifetime = TRUE;
> \q

Once you've created all your desired login accounts, add these lines to .env to disable further registrations:

DISABLE_REGISTRATION=1
DISABLE_ONBOARDING=true

Then, to restart the web app, apply: docker compose restart app

Miscellaneous

Wildcard subdomains

Note the following section documents wildcard certificates and subdomains. You may want to use builtin facility within SimpleLogin to achieve the same results.

If your DNS supports it, you can add a MX record to point *.mydomain.com to app.mydomain.com so that you can receive mails from any number of subdomains. To verify, the following command:

dig @1.1.1.1 *.mydomain.com mx

Should return:

*.mydomain.com. 3600  IN  MX    10 app.mydomain.com

SSL-Certificates are requested from Let`s Encrypt. Traefik is (by default) configured to use TLS-ALPN Challenge, because this works out-of-the-box without further configuration, as long as DNS resolves to your server.

Disadvantage of this configuration is, that letsencrypt does not allow requesting wildcard certificates via TLS Challenge.

To request a wildcard certificate, edit .env file to set LE_CHALLENGE=dns, identify your DNS provider by setting LE_DNS_PROVIDER, and provide further details (i.e. credentials/API-Key, depending on your DNS provider) as ENV.

You can find all supported DNS providers and corresponding instructions here: https://go-acme.github.io/lego/dns/

Postfix configuration

The postfix configuration supports virtual aliases using the postfix/conf.d/virtual and postfix/conf.d/virtual-regexp files. Those files are automatically created on startup based upon the corresponding postfix/templates/virtual.tpl and postfix/templates/virtual-regexp.tpl template files.

The default configuration is as follows:

virtual.tpl

The virtual file supports postfix virtual_alias_maps settings. It includes a rule that maps unknown@mydomain.com to contact@mydomain.com to demonstrate receiving and email from a specific address that does not correspond to an existing alias, to another one that does.

unknown@mydomain.com  contact@mydomain.com

virtual-regexp.tpl

The virtual-regexp file supports postfix virtual_alias_maps settings. It includes a rule that rewrite emails addressed to an arbitrary subdomain, which does not correspond to an existing alias, to a new alias that belongs to a directory whose name is taken from the subdomain. That alias may be created on the fly if it does not exist.

/^([^@]+)@([^.]+)\.mydomain.com/   $2/$1@mydomain.com

For instance, emails sent to someone@directory.mydomain.com will be routed to directory/someone@mydomain.com by postfix.

How-to Upgrade from 3.4.0

  • Change the image version in .env
SL_VERSION=4.6.5-beta

For instance, to upgrade from 3.4.0 to 4.6.x-beta, the following change must be done in simple-login-compose.yaml:

  migration:
    image: simplelogin/app:$SL_VERSION
-   command: [ "flask", "db", "upgrade" ]
+   command: [ "alembic", "upgrade", "head" ]
    container_name: sl-migration
    env_file: .env

Finally, the following command must be run in the database:

docker compose exec -it postgres psql -U myuser simplelogin
> UPDATE email_log SET alias_id=(SELECT alias_id FROM contact WHERE contact.id = email_log.contact_id);
> \q
  • Restart containers
docker compose stop && docker compose up --detach

After successfully upgrading to v4.6.x-beta you might want to upgrade to the latest stable version. Change the SL_IMAGE and SL_VERSION variables from the .env file:

SL_VERSION=v4.70.0
SL_IMAGE=app-ci

Caution: some underpowered VPS might exhibit some WORKER_TIMEOUT errors when running the sl-app image. To mitigate this issue, you may want to increase the starting timeout value in simple-login-compose.yaml:

  app:
    image: simplelogin/$SL_IMAGE:$SL_VERSION
    container_name: sl-app
    env_file: .env
    volumes:
      - ./pgp:/sl/pgp
      - ./upload:/code/static/upload
      - ./dkim.key:/dkim.key
      - ./dkim.pub.key:/dkim.pub.key
+   command: ["gunicorn","wsgi:app","-b","0.0.0.0:7777","-w","2","--timeout","30"]
    restart: unless-stopped

And restart the containers.

This will pull up the latest versions of the docker images, potentially running the updated sl-migration steps, and startup the application.

How-to Upgrade from previous NGinx-based setup

This section outlines the migration steps from a previous installation of self-hosted-simplelogin using the NGinx-based setup, to the current Traefik-based setup.

Backup your server

  1. Backup the database using the following command:
mkdir /tmp/sl-backup/

docker compose \
  -f /opt/simplelogin/docker-compose.yaml exec postgres \
  pg_dump -U <postgres-user-name> simplelogin -F c -b >/tmp/sl-backup/simplelogin.sql
  1. Backup your DKIM public and private keys.

  2. Backup your PGP keys, avatar picture and undelivered emails from the upload/ and pgp/ folders.

  3. Backup your existing .env file.

Postfix

The postfix container is running a private image that has changed from the previous NGinx-based setup to the current Traefik-based setup.

That image needs to be regenerated. You can remove the previous version using the command:

docker rmi private/postfix:latest

In-place upgrade

In-place upgrade refers to the fact that you will upgrade the stack from the previous setup to the current setup in the same directoy.

This is the easiest upgrade path as you only need to change the docker-compose and setup files. If you cloned this repository, you most likely need to use git pull to upgrade to the latest version.

Prerequisites: make sure you are running a recent version of SimpleLogin. This section assumes you are running app-ci:v4.70.0.

  1. Stop the stack using . ./down.sh.
  2. Upgrade to the latest version of the files.
  3. Create and update the .env file from .env.example.

The new .env file supports specifying parameters for certificate renewal using either the DNS-01 or TLS–ALPN-01 ACME challenge from Let’sEncrypt using LEGO , a Let’sEncrypt client library written in Go. Please, review the LEGO documentation for supported providers and their parameters.

  1. Start the stack using . ./up.sh.

You can now cleanup the folders that are no longer useful:

rm -rf acme.sh/
rm -rf nginx/

Backup / restore upgrade

If you want to keep the existing setup in a known working directory, you can use the backup - restore path to test the new setup from a separate folder.

  1. Clone this repository to get the latest version of the files.
  2. Create and update the .env file from .env.example.

The new .env file supports specifying parameters for certificate renewal using either the DNS-01 or TLS–ALPN-01 ACME challenge from Let’sEncrypt using LEGO , a Let’sEncrypt client library written in Go. Please, review the LEGO documentation for supported providers and their parameters.

  1. Restore the pgp/ and upload/ folders.
  2. Restore the dkim.pub.key and dkim.key files.
  3. Restore the postfix virtual and virtual-regexp files.
  4. Start the stack using . ./up.sh.

This will create the private/postfix:latest image and request new certificates from Let’s Encrypt.

Once the application is running successfully, you need to restore the database. The easiest way it to copy the backup file in the db/ folder:

sudo cp /tmp/sl-backup/simplelogin.sql db/
docker compose exec -it pg_restore -U <postgres-user-name> \
  --dbname=simplelogin \
  --clean \
  --verbose \
  /var/lib/postgresql/data/simplelogin.sql