~piotr-machura/server

Personal server using Docker
Update README
Update roundcube version
Fix Roundcube context menu version collision

refs

master
browse  log 

clone

read-only
https://git.sr.ht/~piotr-machura/server
read/write
git@git.sr.ht:~piotr-machura/server

You can also use your local clone with git send-email.

#My digital home

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

#Setting up the server

Notation used:

  • X.X.X.X - the static host IPv4 address.
  • $DOMAIN - your domain name purchased at the registrar.
  • ./ - the root of the project (ie. the directory containing docker-compose.yml).

#Firewall requirements

The following ports should be allowed by the firewall (in addition to port 22 for SSH):

  • 80 (HTTP)
  • 443 (HTTPS)
  • 25 (SMTP)
  • 143 (IMAP)
  • 587 (ESMTP)
  • 993 (IMAPS)

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.

#Project structure

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.

#DNS records

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.

#First launch

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

#Mail server DNS records

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.

#Backup email servers

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"

#CardDAV administration

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.

#Roundcube GPG support

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.