Techdudes

Der volle Kanne Blog!

Reverse Proxy, NextCloud, MailCow Server installieren

| 4 Kommentare

Der Titel lässt es schon vermuten hier will ich eine neuen Personal Cloud Server aufsetzen. Dieser soll mir als E-Mail, Kalender, Kontakt Server und Daten Cloud dienen. Das ganze ist als Docker System Konfiguration gedacht.

Die Architektur sieht vor, dass es vorne dran einen Reverse Proxy gibt der die Anfragen für verschiedene Seiten empfängt und an die verschiedene Docker Container verteilt.

Server Update

Zunächst habe ich ein Server-Update durchgeführt. Der frische Server war zwar ein 18.04 Ubuntu aber da innerhalb dieses Monats schon das offiziele Release der 20.04er Version kommen soll spare ich mir das spätere Systemupdaten.

Der Befehl zum Updaten lautet:

sudo do-release-upgrade -d

Danach werden verschiedene Prüfungen durchgeführt, dabei ist darauf zu achten, dass die alten Config-Files behalten werden müssen.

Docker Installation

Als nächstes installieren wir Docker.

sudo apt install docker.io

Docker sollten wir zum automatischen Start nach einem Reboot hinzufügen:

sudo systemctl enable --now docker

Die Docker Version kann mit folgenden Befehl überprüft werden:

docker --version

Als nächstes brauchen wir noch Docker Compose

sudo apt install docker-compose

ReverseProxy Installation

Nachdem wir nun eine lauffähige Dockerumgebung haben machen wir uns an unseren Reverse Proxy. Dieser steht vor unserer Mailcow und Nextcloud Installation welche in ihren eigenen Netzwerken laufen und generiert für diese jeweils auch die HTTPS Zertifikate.

cd /opt
mkdir nginx-rproxy
cd nginx-rproxy

In diesem Verzeichnis schreiben wir unsere docker-compose.yml, um die entsprechende Container des ReverseProxy Verbunds aufzubauen.

nano docker-compose.yml

Hier geben wir folgende Infos ein:

version: "3"

services:
  nginx-proxy:
    image: jwilder/nginx-proxy:alpine
    container_name: nginx-proxy
    restart: always
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - /var/run/docker.sock:/tmp/docker.sock:ro
      - /opt/nginx-rproxy/nginx-certs:/etc/nginx/certs
      - /opt/nginx-rproxy/nginx-vhost:/etc/nginx/vhost.d
      - /opt/nginx-rproxy/nginx-html:/usr/share/nginx/html
      - /opt/nginx-rproxy/uploadsize.conf:/etc/nginx/conf.d/uploadsize.conf
    networks:
      service_network:

  nginx-proxy-letsencrypt:
    image: jrcs/letsencrypt-nginx-proxy-companion
    environment:
      NGINX_PROXY_CONTAINER: "nginx-proxy"
    networks:
      service_network:
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro                                                             
      - /opt/nginx-rproxy/nginx-certs:/etc/nginx/certs                                                                        
      - /opt/nginx-rproxy/nginx-vhost:/etc/nginx/vhost.d
      - /opt/nginx-rproxy/nginx-html:/usr/share/nginx/html

networks:
  service_network:

Des weiteren brauchen wir noch eine kleine Datei damit Nextcloud auch größere Dateien verarbeiten kann.

nano uploadsize.conf

Ihr Inhalt ist:

client_max_body_size 10G;
proxy_request_buffering off;

Jetzt starten wir den Containerverbund mit

docker-compose pull
docker-compose up -d

Ich habe mich für diese zwei vorkonfigurierten Container (jwilder/nginx-proxy, jrcs/letsencrypt-nginx-proxy-companion) entschieden, weil sie mir ermöglichen durch ein paar wenige Zeilen neue Service verfügbar zu machen. Hier die wenigen Zeilen:

        - VIRTUAL_HOST={HOSTNAME_FQDN}
        - VIRTUAL_PORT=8080
        - VIRTUAL_PROTO=http
        - LETSENCRYPT_HOST={HOSTNAME_FQDN}
        - LETSENCRYPT_EMAIL=mail@example.org

MailCow Server Installation

Nun machen wir uns an die Installation von MailCow. Als erstes prüfen wir ober unsere User Mask gleich 0022 ist und holen uns dann die MailCow Daten.

umask
# 0022
cd /opt
git clone https://github.com/mailcow/mailcow-dockerized
cd mailcow-dockerized

Wir generieren zunächste ein Konfigurationsdatei. Bitte benutzt dazu eine FQDN (host.domain.tld) als hostname wenn ihr gefragt werdet. Diese Domain muss nicht die Mail Domain sein. Es ist aber die Domain die ihr dann in eurem E-Mailclient eintragt.

./generate_config.sh

Nun kommt aus meiner Sicht der wichtigste Schritt, dass ändern der Konfigurationsdatei.

nano mailcow.conf

Da wir unsere Mailcow hinter einem Reverse Proxy verwenden wollen ändern wir die Daten von HTTPS zu 127.0.0.1 auf Port 8443 und HTTP zu 127.0.0.1 auf Port 8080.

HTTP_PORT=8080 
HTTP_BIND=127.0.0.1 
HTTPS_PORT=8443 
HTTPS_BIND=127.0.0.1 

SKIP_LETS_ENCRYPT=y

Des weiteren passen wir noch die docker-compose.yml an. Damit unser Reverse Proxy zum einem die Container kennt und an diese den Datenverkehr weiterleitet und weiterhin wird unser Reverseproxy auch die Zertifikate generieren. Diese Zertifikate müssen aber auch in den Containern eingetragen werden.

nano docker-compose.yml

Dort suchen wir nach dem nginx Container „nginx-mailcow“ und fügen folgende Zeile hinzu bzw. ändern diese. Ersetz bitte die Stelle {deine gewählte FQDN} mit der vorher angegebenen Domain.

...
    nginx-mailcow:
...
      environment:
...
        - VIRTUAL_HOST=${MAILCOW_HOSTNAME}
        - VIRTUAL_PORT=8080
        - VIRTUAL_PROTO=http
        - LETSENCRYPT_HOST=${MAILCOW_HOSTNAME}
        - LETSENCRYPT_EMAIL=mail@example.org
...
      volumes:
        - /opt/nginx-rproxy/nginx-certs/{deine gewählte FQDN}/fullchain.pem:/etc/ssl/mail/cert.pem:ro
        - /opt/nginx-rproxy/nginx-certs/{deine gewählte FQDN}/key.pem:/etc/ssl/mail/key.pem:ro
...
#      ports:
#        - "${HTTPS_BIND:-0.0.0.0}:${HTTPS_PORT:-443}:${HTTPS_PORT:-443}"
#        - "${HTTP_BIND:-0.0.0.0}:${HTTP_PORT:-80}:${HTTP_PORT:-80}"
      expose:
         - "8080"
...
      networks:
        mailcow-network:
          aliases:
            - nginx
        nginx-rproxy_service_network:
          aliases:
            - mail.example.org

In der gleichen Datei fügen wir noch bei zwei Container (Postfix, Dovecot) die Zertifikatsänderungen ein.

...
    nginx-postfix
...
      volumes:
        - /opt/nginx-rproxy/nginx-certs/{deine gewählte FQDN}/fullchain.pem:/etc/ssl/mail/cert.pem:ro
        - /opt/nginx-rproxy/nginx-certs/{deine gewählte FQDN}/key.pem:/etc/ssl/mail/key.pem:ro
...
    nginx-dovecot
...
      volumes:
        - /opt/nginx-rproxy/nginx-certs/{deine gewählte FQDN}/fullchain.pem:/etc/ssl/mail/cert.pem:ro
        - /opt/nginx-rproxy/nginx-certs/{deine gewählte FQDN}/key.pem:/etc/ssl/mail/key.pem:ro

Als letzte Änderung machen wir unserem späteren Reverse Proxy das Mailcow Netzwerk bekannt.

...
networks:
...
  nginx-rproxy_service_network:
    external: true

Als letzten Schritt starten wir die ganze Container Bande mit

docker-compose pull
docker-compose up -d

Nextcloud Installation

Für die Nextcloud habe ich wieder einen eigenen Ordner angelegt.

cd /opt
mkdir nextcloud
cd nextcloud

In diesem Verzeichnis schreiben wir wieder unsere docker-compose.yml, um die entsprechende Container des Nextclouds Verbunds aufzubauen.

nano /opt/nextcloud/docker-compose.yml

Hier geben wir folgende Infos ein.
Bitte ersetzt {mysql root passwort} mit einem entsprechend starkem Passwort und {HOSTNAME_FQDN} wie auch {HOSTNAME_EMAIL} mit eurer (Sub-)Domain und E-Mail.

version: '3'

services:
  db:
    image: mariadb
    command: --transaction-isolation=READ-COMMITTED --binlog-format=ROW
    restart: always
    volumes:
      - db:/var/lib/mysql
    environment:
      - MYSQL_ROOT_PASSWORD={mysql root passwort}
    env_file:
      - db.env
    networks:
      nextcloud-network:
        aliases:
          - nextcloud-mariadb

  redis:
    image: redis:alpine
    restart: always
    networks:
      nextcloud-network:
        aliases:
          - nextcloud-redis

  app:
    image: nextcloud:fpm-alpine
    restart: always
    volumes:
      - nextcloud:/var/www/html
    environment:
      - MYSQL_HOST=db
      - REDIS_HOST=redis
    env_file:
      - db.env
    depends_on:
      - db
      - redis
    networks:
      nextcloud-network:
        aliases:
          - nextcloud-app

  web:
    image: nginx:alpine
    restart: always
    volumes:
      - nextcloud:/var/www/html:ro
      - /opt/nextcloud/nginx.conf:/etc/nginx/nginx.conf:ro
    environment:
      - VIRTUAL_HOST={HOSTNAME_FQDN}
      - LETSENCRYPT_HOST={HOSTNAME_FQDN}
      - LETSENCRYPT_EMAIL={HOSTNAME_EMAIL}
    depends_on:
      - app
    networks:
      nextcloud-network:
        aliases:
          - nextcloud
      nginx-rproxy_service_network:
        aliases:
          - nextcloud

  cron:
    image: nextcloud:fpm-alpine
    restart: always
    volumes:
      - nextcloud:/var/www/html
    entrypoint: /cron.sh
    depends_on:
      - db
      - redis
    networks:
      nextcloud-network:
        aliases:
          - nextcloud-cron


volumes:
  db:
  nextcloud:

networks:
  nginx-rproxy_service_network:
    external: true
  nextcloud-network:
    driver: bridge
    driver_opts:
      com.docker.network.bridge.name: br-nextcloud

Wie ihr seht sind da einige Container drin. Hierbei habe ich mich an dem Standard Beispiel von Nextcloud orientiert. Folgende Dateien werden dazu noch benötigt:

/opt/nextcloud/nginx.conf:

worker_processes auto;

error_log  /var/log/nginx/error.log warn;
pid        /var/run/nginx.pid;


events {
    worker_connections  1024;
}


http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;

    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

    access_log  /var/log/nginx/access.log  main;

    sendfile        on;
    #tcp_nopush     on;

    keepalive_timeout  65;

    set_real_ip_from  10.0.0.0/8;
    set_real_ip_from  172.16.0.0/12;
    set_real_ip_from  192.168.0.0/16;
    real_ip_header    X-Real-IP;

    #gzip  on;

    upstream php-handler {
        server app:9000;
    }

    server {
        listen 80;

        # Add headers to serve security related headers
        # Before enabling Strict-Transport-Security headers please read into this
        # topic first.
        #add_header Strict-Transport-Security "max-age=15768000; includeSubDomains; preload;" always;
        #
        # WARNING: Only add the preload option once you read about
        # the consequences in https://hstspreload.org/. This option
        # will add the domain to a hardcoded list that is shipped
        # in all major browsers and getting removed from this list
        # could take several months.
        add_header Referrer-Policy "no-referrer" always;
        add_header X-Content-Type-Options "nosniff" always;
        add_header X-Download-Options "noopen" always;
        add_header X-Frame-Options "SAMEORIGIN" always;
        add_header X-Permitted-Cross-Domain-Policies "none" always;
        add_header X-Robots-Tag "none" always;
        add_header X-XSS-Protection "1; mode=block" always;

        # Remove X-Powered-By, which is an information leak
        fastcgi_hide_header X-Powered-By;

        # Path to the root of your installation
        root /var/www/html;

        location = /robots.txt {
            allow all;
            log_not_found off;
            access_log off;
        }

        # The following 2 rules are only needed for the user_webfinger app.
        # Uncomment it if you're planning to use this app.
        #rewrite ^/.well-known/host-meta /public.php?service=host-meta last;
        #rewrite ^/.well-known/host-meta.json /public.php?service=host-meta-json last;

        # The following rule is only needed for the Social app.
        # Uncomment it if you're planning to use this app.
        #rewrite ^/.well-known/webfinger /public.php?service=webfinger last;

        location = /.well-known/carddav {
            return 301 $scheme://$host:$server_port/remote.php/dav;
        }

        location = /.well-known/caldav {
            return 301 $scheme://$host:$server_port/remote.php/dav;
        }

        # set max upload size
        client_max_body_size 10G;
        fastcgi_buffers 64 4K;

        # Enable gzip but do not remove ETag headers
        gzip on;
        gzip_vary on;
        gzip_comp_level 4;
        gzip_min_length 256;
        gzip_proxied expired no-cache no-store private no_last_modified no_etag auth;
        gzip_types application/atom+xml application/javascript application/json application/ld+json application/manifest+json application/rss+xml application/vnd.geo+json application/vnd.ms-fontobject application/x-font-ttf application/x-web-app-manifest+json application/xhtml+xml application/xml font/opentype image/bmp image/svg+xml image/x-icon text/cache-manifest text/css text/plain text/vcard text/vnd.rim.location.xloc text/vtt text/x-component text/x-cross-domain-policy;

        # Uncomment if your server is build with the ngx_pagespeed module
        # This module is currently not supported.
        #pagespeed off;

        location / {
            rewrite ^ /index.php;
        }

        location ~ ^\/(?:build|tests|config|lib|3rdparty|templates|data)\/ {
            deny all;
        }
        location ~ ^\/(?:\.|autotest|occ|issue|indie|db_|console) {
            deny all;
        }

        location ~ ^\/(?:index|remote|public|cron|core\/ajax\/update|status|ocs\/v[12]|updater\/.+|oc[ms]-provider\/.+)\.php(?:$|\/) {
            fastcgi_split_path_info ^(.+?\.php)(\/.*|)$;
            set $path_info $fastcgi_path_info;
            try_files $fastcgi_script_name =404;
            include fastcgi_params;
            fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
            fastcgi_param PATH_INFO $path_info;
            # fastcgi_param HTTPS on;

            # Avoid sending the security headers twice
            fastcgi_param modHeadersAvailable true;

            # Enable pretty urls
            fastcgi_param front_controller_active true;
            fastcgi_pass php-handler;
            fastcgi_intercept_errors on;
            fastcgi_request_buffering off;
        }

        location ~ ^\/(?:updater|oc[ms]-provider)(?:$|\/) {
            try_files $uri/ =404;
            index index.php;
        }

        # Adding the cache control header for js, css and map files
        # Make sure it is BELOW the PHP block
        location ~ \.(?:css|js|woff2?|svg|gif|map)$ {
            try_files $uri /index.php$request_uri;
            add_header Cache-Control "public, max-age=15778463";
            # Add headers to serve security related headers (It is intended to
            # have those duplicated to the ones above)
            # Before enabling Strict-Transport-Security headers please read into
            # this topic first.
            #add_header Strict-Transport-Security "max-age=15768000; includeSubDomains; preload;" always;
            #
            # WARNING: Only add the preload option once you read about
            # the consequences in https://hstspreload.org/. This option
            # will add the domain to a hardcoded list that is shipped
            # in all major browsers and getting removed from this list
            # could take several months.
            add_header Referrer-Policy "no-referrer" always;
            add_header X-Content-Type-Options "nosniff" always;
            add_header X-Download-Options "noopen" always;
            add_header X-Frame-Options "SAMEORIGIN" always;
            add_header X-Permitted-Cross-Domain-Policies "none" always;
            add_header X-Robots-Tag "none" always;
            add_header X-XSS-Protection "1; mode=block" always;

            # Optional: Don't log access to assets
            access_log off;
        }

        location ~ \.(?:png|html|ttf|ico|jpg|jpeg|bcmap|mp4|webm)$ {
            try_files $uri /index.php$request_uri;
            # Optional: Don't log access to other assets
            access_log off;
        }
    }
}

Und die Passwortdatei für die Datenbank
/opt/nextcloud/db.env

MYSQL_PASSWORD={starkes Passwort}
MYSQL_DATABASE=nextcloud
MYSQL_USER=nextcloud

Jetzt starten wir den letzten Containerverbund mit

docker-compose pull
docker-compose up -d

Was fehlt noch?

Mir fehlt jetzt noch der Docker Watchtower der mir die Container neustartet falls sie sich mal aufgehängt haben.

Links

Folgende Guides haben mir dabei geholfen das ganze umzusetzen:

https://dev.to/adamkdean/automatic-ssl-with-let-s-encrypt-nginx-4nfk
https://mailcow.github.io/mailcow-dockerized-docs/i_u_m_install/
https://forum.netcup.de/administration-eines-server-vserver/vserver-server-kvm-server/p121991-docker-mailcow-nginx-reverse-proxy-wordpress/#post121991

4 Kommentare

  1. Hallo,

    super Anleitung. Ich bekomme leider beim hochfahren von Mailcow docker-compose immer eine Fehlermeldung für die Volumes, die ich hinzufügen musste.

    ..
    nginx-postfix

    volumes:
    – /opt/nginx-rproxy/nginx-certs/{deine gewählte FQDN}/fullchain.pem:/etc/ssl/mail/cert.pem:ro
    – /opt/nginx-rproxy/nginx-certs/{deine gewählte FQDN}/key.pem:/etc/ssl/mail/key.pem:ro

    nginx-dovecot

    volumes:
    – /opt/nginx-rproxy/nginx-certs/{deine gewählte FQDN}/fullchain.pem:/etc/ssl/mail/cert.pem:ro
    – /opt/nginx-rproxy/nginx-certs/{deine gewählte FQDN}/key.pem:/etc/ssl/mail/key.pem:ro

    Fehlermeldung:

    ERROR: for mailcowdockerized_nginx-mailcow_1 Cannot start service nginx-mailcow: OCI runtime create failed: container_linux.go:349: starting container process caused „process_linux.go:449: container init caused \“rootfs_linux.go:58: mounting \\\“/opt/nginx-rproxy/nginx-certs/xxx.xx.xx/key.pem\\\“ to rootfs \\\“/var/lib/docker/overlay2/de17761ac1340a4a1ac2f37776e24556be700771bd35a40d97b2b21247cbbba8/merged\\\“ at \\\“/var/lib/docker/overlay2/de17761ac1340a4a1ac2f37776e24556be700771bd35a40d97b2b21247cbbba8/merged/etc/ssl/mail/key.pem\\\“ caused \\\“not a directory\\\“\““: unknown: Are you trying to mount a directory onto a

    Es liegt eigentlich nicht an dem Pfad, der Ordner ist da und hat auch Berechtigung. Könnt ihr mir da weiterhelfen?

    • Hallo Matthias,

      danke für deinen Kommentar. Leider schaffe ich es erst heute auf darauf zu reagieren. So direkt sehe ich keinen Fehler. Ich hatte das Problem auch mal beim experimentieren gehabt. Konnte es aber auch nicht genauer eingrenzen beim zweiten Versuch. Mit neuer docker-compose.yml hat es dann funktioniert.
      Wenn du noch immer das Problem hast melde dich nochmal gerne bei uns.

    • Das Problem sind die folgenden Zeilen in der docker-compose.yml des Mailcow Dienstes:
      volumes:
      – /opt/nginx-rproxy/nginx-certs/{deine gewählte FQDN}/fullchain.pem:/etc/ssl/mail/cert.pem:ro
      – /opt/nginx-rproxy/nginx-certs/{deine gewählte FQDN}/key.pem:/etc/ssl/mail/key.pem:ro

      Da die Zertifikate erst generiert werden wenn die Container das erste Mal starten sind diese beim initialen docker-compose up -d nicht vorhanden. Daher erstellt Docker Folder an Stelle der Dateien. Daher der Fehler.

      Lösung: Die Zeilen in allen Services kommentieren und einmal starten. Dann wieder auskommentieren und nochmal ein docker-compose up -d machen.

      • Danke Vincent, war mir auch noch nicht bewusst gewesen. Das erklärt wieso ich ein ähnliches Verhalten auch hatte.

        Werde dies bei Gelegenheit noch in der Anleitung erweitern.

Schreibe einen Kommentar

Pflichtfelder sind mit * markiert.


Durch das Fortsetzen der Benutzung dieser Seite, stimmst du der Benutzung von Cookies zu. Weitere Informationen

Die Cookie-Einstellungen auf dieser Website sind auf "Cookies zulassen", um Ihnen das beste Surferlebnis möglich zu geben. Wenn Sie diese Website ohne Änderung Ihrer Cookie-Einstellungen zu verwenden fortzufahren, oder klicken Sie auf "Akzeptieren" unten, dann erklären Sie sich mit diesen.

Schließen