This is a self-hosted docker-compose configuration for SimpleLogin.
-
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 andapp.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 allmydomain.com
andapp.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.
These packages are used to verify the setup. Install them by:
sudo apt update \
&& sudo apt install -y net-tools dnsutils
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.
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.
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.
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.
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.
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.
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
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 following line to the server
block of the Nginx configuration file:
add_header Strict-Transport-Security "max-age: 31536000; includeSubDomains" always;
(The max-age
is the time in seconds to not permit a HTTP connection, in this case it's one year.)
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
:"sectigo.com"
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 "sectigo.com"
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.
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.
Note: a file /var/www/.well-known/mta-sts.txt.tpl
is included in this repository with a content
similar to the text shown hereafter.
version: STSv1
mode: testing
mx: app.mydomain.com
max_age: 86400
It is recommended to start with mode: testing
for starters to get time to review failure reports.
You do not need to edit this file as it will be used to derive an appropriate file upon startup.
However, you do need to 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"
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:[email protected]"
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:
- nginx
- acme.sh to request and issue SSL certs.
- The SimpleLogin app containers
- postfix
- Clone this repository in
/opt/simplelogin
- 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 isapp
. - set the
POSTGRES_USER
variable to match the postgres credentials. - set the
POSTGRES_PASSWORD
to match the postgres credentials. - set the
FLASK_SECRET
to an arbitrary secret key.
The SSL certs are issued by the ACME server using either:
- HTTP-01 ACME challenge
- DNS-01 ACME challenge using acme.sh DNS integration
Set the following variables in .env
to appropriate values:
- set the
LE_STAGING
totrue
orfalse
. - set the
ACME_CHALLENGE
variable to eitherDNS-01
(default) orHTTP-01
. - set the
ACME_SERVER
variable to any of the supported servers by acme.sh. Default value iszerossl
.
If you are using DNS-01 ACME challenge, set ACME_SH_DNS_API
to one of the
supported acme.sh DNS API plugins.
This repository currently supports Microsoft Azure and Cloudflare DNS integrations.
If using Microsoft Azure, update the following values in .env
:
- set
AZUREDNS_TENANTID
to the Azure tenant hosting the domain DNS zone. - set
AZUREDNS_SUSCRIPTIONID
to the Azure subscription hosting the domain DNS zone. - set
AZUREDNS_CLIENTID
to the client id of a service principal with permissions to update the DNS zone. - set
AZUREDNS_CLIENTSECRET
to the client secret of a service principal with permissions to update the DNS zone.
If using Cloudflare, update the following values in .env
:
- set
CF_Token
to the Cloudflare API token. - set
CF_Zone_ID
to the Cloudflare DNS Zone identifier. - set
CF_Account_ID
to your Cloudflare account identifier.
The SSL certificates will be available at the following locations:
/etc/acme.sh/*.mydomain.com_ecc/fullchain.cer
/etc/acme.sh/*.mydomain.com_ecc/*.domain.tld.key
If you are using HTTP-01 challenge, update the SSL certificate and key locations in following files:
nginx/conf.d/default.conf.tpl
postfix/conf.d/main.cf.tpl
Specifically, using HTTP-01, the SSL certificates are available at the following locations:
/etc/acme.sh/mydomain.com_ecc/fullchain.cer
/etc/acme.sh/mydomain.com_ecc/domain.tld.key
- Run the application:
The up.sh
shell script updates important configuration files from templates provided in this repository,
so that it uses the correct domain and postgresql credentials. Here are the template files:
acme.sh/www/.well-known/mta-sts.txt.tpl
nginx/conf.d/default.conf.tpl
postfix/conf.d/aliases
postfix/conf.d/main.cf.tpl
postfix/conf.dl/pgsql-relay-domains.cf.tpl
postfix/conf.dl/pgsql-transport-maps.cf.tpl
postfix/conf.d/virtual.tpl
postfix/conf.d/virtual-regexp.tpl
Run the application using the following commands:
./up.sh --build && docker logs -f acme.sh
If you used the staging server to issue certificates, please review and troubleshoot.
Once you are happy, set the LE_STAGING
variable in .env
to false
and re-issue the certificates:
rm -rf acme.sh/conf.d/
./down.sh && ./up.sh && docker logs -f acme.sh
You may also want to setup Certificate Authority Authorization (CAA) at this point.
Note the following section documents wildcard certificates and subdomains. You may want to use builtin facility within SimpleLogin to achieve the same results.
This repository suppports issuing wildcard certificates for any number of subdomains using Letsencrypt DNS-01 challenge using acme.sh DNS integration.
It also suppports issuing certificates for the following subdomains app.mydomain.com
and mta-sts.mydomain.com
using Letsencrypt HTTP-01 challenge.
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
Alternatively, you can update the acme.sh/Dockerfiles/docker-entrypoint.sh
script and update the list of subdomains you want to issue SSL certificates for.
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/conf.d/virtual.tpl
and postfix/conf.d/virtual-regexp.tpl
template files.
The default configuration is as follows:
The virtual
file supports postfix virtual_alias_maps
settings.
It includes a rule that maps [email protected]
to [email protected]
to demonstrate receiving
and email from a specific address that does not correspond to an existing alias, to another one that does.
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/[email protected]
For instance, emails sent to [email protected]
will be routed to directory/[email protected]
by postfix.
If all the above steps are successful, open http://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 restart the web app to apply: docker compose restart app
- Change the image version in
.env
SL_VERSION=4.6.5-beta
- Check migration commands
For instance, to upgrade from 3.4.0
to 4.6.x-beta
, the following change must be done in simplelogin-compose.yaml
:
volumes:
- ./db:/var/lib/postgresql/data
restart: unless-stopped
migration:
image: simplelogin/app:$SL_VERSION
- command: [ "flask", "db", "upgrade" ]
+ command: [ "alembic", "upgrade", "head" ]
container_name: sl-migration
env_file: .env
- Restart containers
./down.sh && ./up.sh