VPS dla początkujących cz. 5: poczta email, czyli Postfix, Dovecot i Roundcube

Napisano dnia 6.08.2021 r. o godzinie 7:00
Autor: Piotr Sperka

Wstęp

Witam Cię w piątym odcinku serii pod tytułem VPS dla początkujących. Po pracach z poprzednich części mamy już uruchomione podstawowe usługi. Dzisiaj zajmiemy się czymś nieco bardziej złożonym — uruchomimy serwer e-mail. Użyjemy do tego połączenia Postfixa i Dovecota, oraz Roundcube jako webmaila. Aby zaoszczędzić sobie pracy, wykorzystamy Mailu, które łączy w sobie te usługi w wygodnej formie kontenerów Dockerowych.

Tworzenie pliku docker-compose

Wygenerowanie pliku docker-compose.yml dla Mailu jest bardzo proste. Przechodzimy do strony setup.mailu.io, wybieramy stabilną wersję i generujemy potrzebne pliki, korzystając z kreatora. Opcje musisz dostosować pod swoją konfigurację, jednak pamiętaj, że wszystko, co wyklikasz w konfiguratorze, możesz potem łatwo zmienić w wygenerowanych plikach. Oto przykładowa konfiguracja, którą otrzymałem.

docker-compose.yml:

version: '2.2'

services:

  # External dependencies
  redis:
    image: redis:alpine
    restart: always
    volumes:
      - "/docker/mailu/redis:/data"

  # Core services
  front:
    image: ${DOCKER_ORG:-mailu}/${DOCKER_PREFIX:-}nginx:${MAILU_VERSION:-1.7}
    restart: always
    env_file: mailu.env
    logging:
      driver: json-file
    ports:
      - "25:25"
      - "465:465"
      - "587:587"
      - "110:110"
      - "995:995"
      - "143:143"
      - "993:993"
    expose:
      - 80
      - 443
    volumes:
      - "/docker/mailu/certs:/certs"
      - "/docker/mailu/overrides/nginx:/overrides"
    networks:
      - default
      - reverse_proxy


  admin:
    image: ${DOCKER_ORG:-mailu}/${DOCKER_PREFIX:-}admin:${MAILU_VERSION:-1.7}
    restart: always
    env_file: mailu.env
    volumes:
      - "/docker/mailu/data:/data"
      - "/docker/mailu/dkim:/dkim"
    depends_on:
      - redis

  imap:
    image: ${DOCKER_ORG:-mailu}/${DOCKER_PREFIX:-}dovecot:${MAILU_VERSION:-1.7}
    restart: always
    env_file: mailu.env
    volumes:
      - "/docker/mailu/mail:/mail"
      - "/docker/mailu/overrides:/overrides"
    depends_on:
      - front

  smtp:
    image: ${DOCKER_ORG:-mailu}/${DOCKER_PREFIX:-}postfix:${MAILU_VERSION:-1.7}
    restart: always
    env_file: mailu.env
    volumes:
      - "/docker/mailu/overrides:/overrides"
    depends_on:
      - front

  antispam:
    image: ${DOCKER_ORG:-mailu}/${DOCKER_PREFIX:-}rspamd:${MAILU_VERSION:-1.7}
    restart: always
    env_file: mailu.env
    volumes:
      - "/docker/mailu/filter:/var/lib/rspamd"
      - "/docker/mailu/dkim:/dkim"
      - "/docker/mailu/overrides/rspamd:/etc/rspamd/override.d"
    depends_on:
      - front

  # Webmail
  webmail:
    image: ${DOCKER_ORG:-mailu}/${DOCKER_PREFIX:-}roundcube:${MAILU_VERSION:-1.7}
    restart: always
    env_file: mailu.env
    volumes:
      - "/docker/mailu/webmail:/data"
    depends_on:
      - imap

# Database
  mailu-db:
    image: mariadb:latest
    restart: always
    environment:
      MYSQL_DATABASE: mailu
      MYSQL_USER: mailu
      MYSQL_PASSWORD: tajnehaslo
      MYSQL_RANDOM_ROOT_PASSWORD: '1'
    volumes:
      - /docker/mailu/mysql:/var/lib/mysql
    networks:
      - default
      - reverse_proxy

networks:
  default:
    driver: bridge
    ipam:
      driver: default
      config:
        - subnet: 192.168.203.0/24
  reverse_proxy:
    external: true

mailu.env:

###################################
# Common configuration variables
###################################

# Set to a randomly generated 16 bytes string
SECRET_KEY=0123456789ABCDEF

# Subnet of the docker network. This should not conflict with any networks to which your system is connected. (Internal and external!)
SUBNET=192.168.203.0/24

# Main mail domain
DOMAIN=twoja-domena.pl

# Hostnames for this server, separated with comas
HOSTNAMES=twoja-domena.pl,mail.twoja-domena.pl

# Postmaster local part (will append the main mail domain)
POSTMASTER=admin

# Choose how secure connections will behave (value: letsencrypt, cert, notls, mail, mail-letsencrypt)
TLS_FLAVOR=mail

# Authentication rate limit (per source IP address)
AUTH_RATELIMIT=60/minute;2500/hour

# Opt-out of statistics, replace with "True" to opt out
DISABLE_STATISTICS=False

###################################
# Optional features
###################################

# Expose the admin interface (value: true, false)
ADMIN=true

# Choose which webmail to run if any (values: roundcube, rainloop, none)
WEBMAIL=roundcube

# Dav server implementation (value: radicale, none)
WEBDAV=none

# Antivirus solution (value: clamav, none)
ANTIVIRUS=none

###################################
# Mail settings
###################################

# Message size limit in bytes
# Default: accept messages up to 50MB
# Max attachment size will be 33% smaller
MESSAGE_SIZE_LIMIT=50000000

# Networks granted relay permissions
# Use this with care, all hosts in this networks will be able to send mail without authentication!
RELAYNETS=

# Will relay all outgoing mails if configured
RELAYHOST=

# Fetchmail delay
FETCHMAIL_DELAY=600

# Recipient delimiter, character used to delimiter localpart from custom address part
RECIPIENT_DELIMITER=+

# DMARC rua and ruf email
DMARC_RUA=admin
DMARC_RUF=admin

# Welcome email, enable and set a topic and body if you wish to send welcome
# emails to all users.
WELCOME=false
WELCOME_SUBJECT=Welcome to your new email account
WELCOME_BODY=Welcome to your new email account, if you can read this, then it is configured properly!

# Maildir Compression
# choose compression-method, default: none (value: bz2, gz)
COMPRESSION=
# change compression-level, default: 6 (value: 1-9)
COMPRESSION_LEVEL=

###################################
# Web settings
###################################

# Path to redirect / to
WEBROOT_REDIRECT=/webmail

# Path to the admin interface if enabled
WEB_ADMIN=/admin

# Path to the webmail if enabled
WEB_WEBMAIL=/webmail

# Website name
SITENAME=Mail

# Linked Website URL
WEBSITE=https://twoja-domena.pl

###################################
# Advanced settings
###################################

# Log driver for front service. Possible values:
# json-file (default)
# journald (On systemd platforms, useful for Fail2Ban integration)
# syslog (Non systemd platforms, Fail2Ban integration. Disables `docker-compose log` for front!)
# LOG_DRIVER=json-file

# Docker-compose project name, this will prepended to containers names.
COMPOSE_PROJECT_NAME=mailu

# Default password scheme used for newly created accounts and changed passwords
# (value: BLF-CRYPT, SHA512-CRYPT, SHA256-CRYPT, MD5-CRYPT, CRYPT)
PASSWORD_SCHEME=BLF-CRYPT

# Header to take the real ip from
REAL_IP_HEADER=

# IPs for nginx set_real_ip_from (CIDR list separated by commas)
REAL_IP_FROM=

# choose wether mailu bounces (no) or rejects (yes) mail when recipient is unknown (value: yes, no)
REJECT_UNLISTED_RECIPIENT=

# Log level threshold in start.py (value: CRITICAL, ERROR, WARNING, INFO, DEBUG, NOTSET)
LOG_LEVEL=WARNING

###################################
# Database settings
###################################
DB_FLAVOR=mysql
DB_USER=mailu
DB_PW=tajnehaslodobazy
DB_HOST=mailu-db
DB_NAME=mailu

Uruchomienie

W teorii jedyne co musimy teraz zrobić to wykonać polecenie docker-compose up. W praktyce jednak okazało się, że plik wygenerował się błędnie. Wersja obrazów zamiast 1.7 była oznaczona jako 1_7, przez co nie było możliwości ich pobrania. Ponadto do pliku docker-compose.yml postanowiłem dodać również bazę danych. Z uwagi na używane zewnętrzne reverse proxy, zmieniłem również ustawienia portów w kontenerze mailu/nginx. Koniec końców, uzyskałem pliki docker-compose.yml oraz mailu.env takie, jak przedstawione powyżej. Oczywiście, z inną domeną oraz kluczem. Tym razem kontenery uruchomiły się poprawnie. Kolejnym krokiem było skopiowanie certyfikatów do katalogu /docker/mailu/certs.

cp /etc/letsencrypt/live/<twoja-domena>/fullchain.pem cert.pem
cp /etc/letsencrypt/live/<twoja-domena>/privkey.pem key.pem

Istotne jest, aby wykonać kopię plików, a nie jedynie dowiązania symboliczne. Nie znam dokładnej przyczyny, jednak same dowiązania symboliczne nie przyniosły pożądanych efektów. Dodałem również kolejny blok server do pliku nginx.conf:

server {
    server_name mail.twoja-domena.pl;

    location / {
      include /etc/nginx/includes/proxy.conf;
      proxy_pass http://front:80;
    }

    listen 443 ssl;
    ssl_certificate /etc/letsencrypt/live/twoja-domena.pl/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/twoja-domena.pl/privkey.pem;
  }

Po tym wykonałem polecenia docker-compose down oraz docker-compose up w katalogu /docker/mailu, a także zrestartowałem reverse proxy. Otwarłem również w firewallu porty odpowiedzialne za IMAP, POP3 oraz SMTP, wszystkie z szyfrowaniem:

ufw allow 465/tcp
ufw allow 993/tcp
ufw allow 995/tcp

Na potrzeby debugowania, dopóki nie uzyskamy działającej usługi, warto pominąć przełącznik -d podczas uruchamiania Mailu poprzez docker-compose. Kontenery zostaną wówczas uruchomione bez zwalniania konsoli i pojawią się w niej bieżące logi.

W toku uruchamiania Mailu miałem pewne problemy z dostępem do panelu administratora. Winne były błędne pliki certyfikatu. Jeżeli masz problem z dostępem do panelu administratora pod skonfigurowanym adresem, na przykład mail.twoja-domena.pl/admin, na początek debugowania sugeruję wyłączyć TLS. Jest to opcja TLS_FLAVOR=notls w pliku mailu.env. Jeśli po tej zmianie i restarcie kontenerów panel zadziała, oznacza to, że jest jakiś problem z certyfikatami. Ponadto musisz upewnić się, czy zmienne DOMAIN oraz HOSTNAMES są ustawione prawidłowo.

Jeśli udało się wyświetlić stronę logowania do panelu administracyjnego, przyszedł czas na stworzenie konta administratora. Zrobimy to poleceniem wykonanym z katalogu /docker/mailu:

docker-compose exec admin flask mailu admin root twoja-domena.pl tajnehaslo123

Polecenie te stworzy konto administratora o loginie i haśle tajnehaslo123. Tymi danymi logujemy się do panelu administracyjnego, gdzie możemy dodać kolejnych użytkowników, dodać aliasy, i tak dalej (zakładka domeny pocztowe).

Konfiguracja domeny

Pozostało jeszcze skonfigurować domenę. Wpisy, jakie powinniśmy dodać w konfiguracji DNS naszej domeny, znajdziemy w menu pod pozycją Domeny pocztowe po kliknięciu ikony „Szczegóły” przy wybranej domenie. Jeżeli dotąd nie wygenerowałeś kluczy DKIM, zrobisz to, klikając na Wygeneruj klucze we wspomnianej zakładce.

Wpisy DNS mojej domeny dotyczące e-mail

Testy poczty

Gdy wszystko jest ustawione, możemy spróbować czy poczta działa. Przechodzimy do mail.twoja-domena.pl/webmail i logujemy się na wybrane z założonych kont. Na początek sugeruję wysłać maila na drugie konto w tej samej domenie. Jeśli wiadomość dojdzie, możemy wypróbować konfigurację domeny i serwera. Do tego można wykorzystać wiele różnych serwisów. Ja testowałem poprzez https://www.mail-tester.com/. Test polega na wysłaniu dowolnej wiadomości (najlepiej z sensownym tekstem) na wyświetlony na stronie adres, a następnie kliknięciu sprawdź. Otrzymamy wynik i informacje o ewentualnych problemach w konfiguracji. W moim wypadku uzyskałem wynik 10/10, co oznacza, że wszystko jest ok.

Tutaj mała uwaga: radzę wstrzymać się z wysyłaniem maili do takich serwisów jak na przykład Gmail, do momentu uzyskania w pełni poprawnej konfiguracji wraz z szyfrowaniem. Do tego nie testuj działania poczty wysyłając dużo wiadomości o bezsensownej lub powtarzalnej treści, nawet na swoje konto u jakiegoś usługodawcy. W ten sposób można bardzo łatwo doprowadzić do sytuacji, w której wiadomości z naszej domeny będą trafiały do spamu u wszystkich użytkowników danego usługodawcy (na przykład Gmail).

Zauważyłem też jeszcze jedną zależność. Mniejszą szansę na trafienie na listę „podejrzanych” domen mamy mając domenę ze „starej” puli, na przykład com, pl, eu, info, org, itp. „Nowe” domeny jak na przykład online, tech, host, i tak dalej mają z nieznanej mi do końca przyczyny większe szanse na trafienie do spamu. Nie będę się już nawet rozpisywał o takim detalu jak różne walidatory w formularzach. Przykładowo, próba wysłania pliku JPK do urzędu skarbowego zakończy się błędem walidacji, jeśli podamy mail z domeną kończącą się na .online.

Posumowanie

Podsumowując, dzisiaj udało nam się uruchomić funkcjonujący serwer e-mail, obsługujący protokoły IMAP, POP3 oraz SMTP wraz z obsługą szyfrowania, oraz webmailem. W kolejnym i prawdopodobnie ostatnim odcinku zajmiemy się dodatkowym zabezpieczeniem serwera, kwestią automatycznego odświeżania certyfikatów oraz ostatecznymi szlifami.

Jak zawsze zapraszam do kontaktu i do następnego 😉