Update README
Update roundcube version
Fix Roundcube context menu version collision
This is the root of all my web presence, including but not limited to:
... and anything else I wish to put up on the web. For more see the project home page.
A simplified scheme of what's happening is portrayed below.
┌──────────────────┐
Port 25/143/587/993 │ docker │
──────────────────────►│ mailserver │◄──┐
SMTP/ESMTP/IMAP4 └──────────────────┘ │
│
┌─────────────┐ │
┌──►│ Website │ │
│ └─────────────┘ │
│ $DOMAIN.com ▲
│ www.$DOMAIN.com │
│ ▼
Port 80/443 ┌───────┐ │ ┌─────────────┐ │
───────────►│ nginx ├─┼──►│ Roundcube ├─────┤
└───────┘ │ └─────────────┘ │
│ mail.$DOMAIN.com ▲
│ │
│ ▼
│ ┌─────────────┐ │
└──►│ Radicale ├─────┘
└─────────────┘
dav.$DOMAIN.com
Notation used:
$DOMAIN
- your domain name purchased at the registrar../
- the root of the project (ie. the directory containing docker-compose.yml).The following ports should be allowed by the firewall (in addition to port 22 for SSH):
Beware that Docker is known to ignore iptables wrappers such as ufw and firewall-cmd, so things may still work even without any firewall configuration. It is good practice for different iptables rule providers to at the very least agree (if not not interfere) witch each other, though.
The project structure is as follows:
./
│─ radicale/
│─ nginx-certbot/
│─ roundcube/
├─ data/
│ ├─ static/
│ ├─ radicale/
│ ├─ keys/
│ └─ mail/
└─ docker-compose.yml
The ./data
folder contains all of the important persistent Docker data, easy to backup and restore in the case of
failure. Static website content found on the root of $DOMAIN
and www.$DOMAIN
is hosted from ./data/static
.
The configuration assumes the following dns records (arrow meaning "pointing to"):
A | CNAME | MX | TXT |
---|---|---|---|
$DOMAIN → X.X.X.X |
dav.$DOMAIN → $DOMAIN |
empty → mail.$DOMAIN |
DKIM |
www.$DOMAIN → X.X.X.X |
DMARC | ||
mail.$DOMAIN → X.X.X.X |
SPF |
The TXT records ensure no email spoofing has taken place and greatly increase the probability of our messages not being flagged as spam. They require the server to be already running and are discussed in more detail in #Mail server DNS records.
To change the domain from piotr-machura.com to $DOMAIN
, execute
find ./docker-compose.yml ./nginx-certbot -type f \
-exec sed -i -e "s/piotr-machura.com/$DOMAIN/g" {} \;
Before the initial launch make sure you have fulfilled the firewall requirements and have both Docker and docker-compose installed.
Obtain SSL certificates:
docker-compose run --rm nginx-certbot \
certbot --nginx --register-unsafely-without-email \
-d "$DOMAIN" -d "www.$DOMAIN" -d "dav.$DOMAIN" -d "mail.$DOMAIN"
Create an email account (it's required for the mailserver to function properly):
docker-compose run --rm mailserver setup email add myuser@$DOMAIN
Provide a password when prompted.
Launch all services:
docker-compose up -d --build
At least one user must exist for the container to run properly (refer to first launch). Once a user has been created DNS text records used to prevent spoofing can be generated.
For DKIM run the following
docker exec mailserver \
setup config dkim; \
cat /tmp/docker-mailserver/opendkim/keys/$DOMAIN/mail.txt \
| tr -d '\n()' | sed 's/"[\t| ]*"//g' | sed "s/[\t| ];.*//"
For SPF all you need is
(leave hostname empty) IN TXT
"v=spf1 mx ~all"
For DMARC:
_dmarc IN TXT
"v=DMARC1; p=quarantine; rua=mailto:dmarc.report@$DOMAIN; ruf=mailto:dmarc.report@$DOMAIN; sp=none; ri=86400"
Add the contents in appropriate records in your DNS provider's interface.
The format is <host name> IN TXT <contents>
, note that the SPF record has an empty host name.
If you have backup servers with IP addresses $IP1
, $IP2
etc. you must whitelist incoming messages:
docker exec mailserver sh -c "echo 'Whitelist = $IP1,$IP2,(etc)' >> /etc/postfix-policyd-spf-python/policyd-spf.conf"
Add users with
docker exec -t radicale \
htpasswd -B -b /var/radicale/data/users $USERNAME $PASSWORD
and remove them with
docker exec -t radicale \
htpasswd -D /var/radicale/data/users $USERNAME
Note that this does not delete user data - only the registered entry. Dav collections must be manually removed from
./data/radicale/collections/collection-root/$USERNAME
.
To list all registered users run
docker exec radicale \
cat /var/radicale/data/users | while read -r line; do echo "${line%%:*}"; done
In order to sync the contacts with the Roundcube client log into your email account under example.com/mail and navigate
to "Settings/Preferences/CardDAV. Use your CardDAV username and password and dav.$DOMAIN
as the server's address.
Hit "Save" and the address book should appear in "Contacts". Add contacts to your heart's content.
GPG keys are stored server-side in ./data/keys
, in order to grant Roundcube access to them run
docker exec -it roundcube chown www-data /keys
If your key has no passphrase Roundcube will still ask for one when signing email - in that case you can input anything.