Subsections of Nextcloud
Komplettanleitung Mariadb & Nextcloud mit Nginx Proxy
Um den Einstieg in die Installation von Nextcloud zu vereinfachen, habe ich mir überlegt eine Komplettanleitung zu schrieben, die folgende Komponenten auf einem Ubuntu (oder Debian basiertem OS) installiert:
- nginx (Webserver / Proxy)
- Let’s Encrypt
- Docker
- Mariadb (in Docker)
- Nextcloud (in Docker)
- NFTables + Firewallregeln
Vorbereitung
Es sollte vorab schon ein DNS Eintrag für die Cloud vorbereitet werden. In diesem Bespiel wird nextcloud.dr3st.de verwendet, dieser ist anzupassen. Bitte einen A oder CNAME Eintrag anlegen, der letztlich auf die öffentliche IP des Servers zeigt. Keine iFrame- / Weiterleitungs-Einstellung
Die Mariadb (Mysql-Server) wird so so einrichtet, dass diese auch für andere Dienste verwendet werden kann, damit nicht weitere Instanzen gestartet werden müssen. Viele anleitungen sind so gebaut, dass für jede Anwendung ein eigener MySQL-Server eingerichtet werden soll, was ich unsinnig finde.
Außerdem wird die Firewall-Einstellung von Docker nicht deaktiviert, die generelle Absicherung aber dennoch mit NFTables realisiert. Somit ist gewährleistet, dass bestehende Setups möglichst nicht beeinträchtigt werden. Wer dennoch die Firewall aufgeräumter haben möchte, kann den optinalen Hinweis in der Anleitung berücksichtigen. Hierbei aber bitte vorsichtig sein, da man bestehende Installationen unereichbar machen könnte!
Die Befehle sind mit root-Rechten ausgeführt! Enige davon, gerade die Konfigurationsdateien anlegen, können nicht mit sudo ausgeführt werden, weil die Berechtigungen sonst fehlerhaft sind. Daher bietet sich vorab der Login als root, oder “sudo -i” an.
Installation
apt update && \
apt install nftables docker.io docker-compose mariadb-client pwgen
Firewall
Konfiguration
Zuerst richten wir die Firewall ein um sicherzugehen, das der Datenbank-Server nach der Installation nicht direkt im Internet erreichbar ist.
Existiert die Konfiguration bereits, sollte Zeile 12 berücksichtigt werden.
|
|
Firewall Restart
# -c (Check) -f (File)
nft -c -f /etc/nftables.conf
#Keine Fehlermeldung? Gut ... :
systemctl restart nftables.service
systemctl enable nftables.service
MariaDB
zunächst legen wir die Konfiguration an, hierbei wird ein Passwort gewürfelt und die docker-compose.yml generiert.
Container Konfiguration
# Passwort generieren (32 Stellen, siehe head -c 32)
MDB_PW=$(openssl rand 512 | sha512sum | head -c 32)
# Ordner anlegen und betreten
mkdir -p /srv/docker/git/mariadb
# Konfiguration anlegen (einfach kopieren)
cat > /srv/docker/git/mariadb/docker-compose.yml <<EOF
version: "3"
services:
mariadb:
image: mariadb:10.6
container_name: mariadb
network_mode: host
environment:
TZ: "UTC"
MYSQL_ROOT_PASSWORD: "${MDB_PW}"
volumes:
- /srv/docker/containers/mariadb/data:/var/lib/mysql
restart: unless-stopped
EOF
Client Config (my.cnf)
Zusätzlich speichern wir die Verbindungsdaten in der lokalen “my.cnf” damit eine Verbindung ohne Passwort-Eingabe möglich ist:
[ ! -f "${HOME}/.my.cnf" ] && \
cat > ${HOME}/.my.cnf <<EOF
[mysql]
host=127.0.0.1
user=root
password=${MDB_PW}
[client]
host=127.0.0.1
user=root
password=${MDB_PW}
[mysqldump]
host=127.0.0.1
user=root
password=${MDB_PW}
EOF
Container start
cd /srv/docker/git/mariadb
docker-compose pull
docker-compose up -d
Datenbank Test & Zugangsdaten
Das Testen der Verbindung ist nun möglich und es werden die Logindaten für die Nextcloud-Datenbank angelegt.
# Passwort generieren und den Admin Username eintippen
NCDB_PW=$(openssl rand 512 | sha512sum | head -c 32)
ADMIN_PW=$(openssl rand 512 | sha512sum | head -c 32)
# Eingaben notwendig! Hier als Beispiel:
read -r -p "enter Nextcloud Admin User-Email:" ADMIN_USER
admin@dr3st.de
read -r -p "enter Nextcloud Domain:" NC_DOMAIN
nextcloud.dr3st.de
# diese Befehle legen die Datenbank und Zugangsdaten an
mysql -e 'CREATE DATABASE nextcloud /*!40100 DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci */;'
mysql -e "GRANT ALL PRIVILEGES ON nextcloud.* TO 'nextcloud'@'%' IDENTIFIED BY '${NCDB_PW}';"
Nextcloud
mkdir -p /srv/docker/git/nextcloud
# Netzwerk anlegen, damit dieses von mehreren Anwendungen
# genutzt werden kann und nicht an einer Docker-Compose Datei hängt
docker network create --gateway 10.11.12.1 --subnet 10.11.12.0/24 --opt com.docker.network.bridge.name=br_apps br_apps
# Nextcloud docker-compose.yml anlegen (Passwort wird durch Variable eingetragen!)
cat > /srv/docker/git/nextcloud/docker-compose.yml <<EOF
version: '3'
services:
nextcloud:
container_name: nextcloud
image: nextcloud:24
volumes:
- /srv/docker/containers/nextcloud/data/:/var/www/html/
# Passwörter ausgelagert
- ./secrets/:/run/secrets/:ro
environment:
MYSQL_DATABASE_FILE: "/run/secrets/mysql_database.txt"
MYSQL_USER_FILE: "/run/secrets/mysql_user.txt"
MYSQL_PASSWORD_FILE: "/run/secrets/mysql_password.txt"
MYSQL_HOST: "10.11.12.1"
NEXTCLOUD_ADMIN_USER_FILE: "/run/secrets/admin_user.txt"
NEXTCLOUD_ADMIN_PASSWORD_FILE: "/run/secrets/admin_password.txt"
NEXTCLOUD_TRUSTED_DOMAINS: "${NC_DOMAIN}"
TRUSTED_PROXIES: "10.11.12.1"
restart: unless-stopped
networks:
default:
ipv4_address: 10.11.12.2
networks:
default:
external:
name: br_apps
EOF
# Container starten
cd /srv/docker/git/nextcloud
mkdir -p secrets
echo "${NCDB_PW}" > secrets/mysql_password.txt
echo "${ADMIN_PW}" > secrets/admin_password.txt
echo "${ADMIN_USER}" > secrets/admin_user.txt
echo "nextcloud" > secrets/mysql_database.txt
echo "nextcloud" > secrets/mysql_user.txt
docker-compose pull
docker-compose up -d
# Admin Passwort zum notieren ausgeben
cat secrets/admin_password.txt
Nginx & Certbot (Let’s Encrypt)
Zuerst wird nginx installiert und der Autostart aktiviert.
apt install nginx certbot
systemctl enable nginx
Zertifikat erzeugen
Danach wird das Zertifikat von Let’s Encrypt angefordert. Dieses wird übrigens automatisch erneuert, weil das Certbot-Paket einen Cronjob mitliefert (/etc/cron.d/certbot).
# email@domain.de ersetzen! Dies muss nicht die Cloud Admin-Email sein!
certbot certonly --webroot -w /var/www/html/ --email email@domain.de --non-interactive --agree-tos -d ${NC_DOMAIN} --rsa-key-size 4096
# im Normalfall wird eine ähnliche Ausgabe angezeigt:
#IMPORTANT NOTES:
# - Congratulations! Your certificate and chain have been saved at:
# /etc/letsencrypt/live/..../fullchain.pem
Und die Nginx-Konfiguration angelegt. Wichtig hierbei ist die Domain anzupassen!
# NC_DOMAIN wurde weiter oben bereits erfragt und nun
# hier verwendet und automatisch eingetragen!
cat > /etc/nginx/sites-enabled/${NC_DOMAIN}.conf <<EOF
server {
server_name ${NC_DOMAIN};
listen 0.0.0.0:80;
location ~ .well-known {
root /var/www/html;
}
location / {
return 301 https://${NC_DOMAIN}$request_uri;
}
}
server {
server_name ${NC_DOMAIN};
listen 0.0.0.0:443 ssl http2;
access_log /var/log/nginx/${NC_DOMAIN}.access.log;
error_log /var/log/nginx/${NC_DOMAIN}.error.log;
root /var/www/html;
ssl_certificate /etc/letsencrypt/live/${NC_DOMAIN}/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/${NC_DOMAIN}/privkey.pem;
client_max_body_size 4096m;
proxy_max_temp_file_size 4096m;
# Proxy for nextcloud docker
location / {
add_header Strict-Transport-Security "max-age=63072000" always;
add_header X-Frame-Options SAMEORIGIN;
proxy_cookie_path / "/; secure; HttpOnly";
# And don't forget to include our proxy parameters
include /etc/nginx/proxy_params;
# Connect to local port
proxy_pass http://10.11.12.2:80;
}
}
EOF
Nginx reload
nginx -t && systemctl reload nginx.service
Zugangsdaten
Im Anschluss sollte die Nextcloud im Browser erreichbar sein. Die Logindaten entsprechen denen, die man selbst festgelegt hat. Ausgeben kann man diese aber noch mal nachträglich:
cd /srv/docker/git/nextcloud
# Benutzername
cat secrets/admin_user.txt
# Passwort
cat secrets/admin_password.txt
Nachwort und Netzwerk-Information
Warum das separate Netz?
Man möchte dem Nextcloud-Container und allen eventuell noch folgenden Anwendungen statische IP-Adressen geben um diese in der Nginx-Konfiguration hinterlegen zu können. In diesem Beispiel ist es die 10.11.12.2, hätte man allerdings das Docker Netzwerk genommen (docker0), hätte dies irgendeine IP aus dem Netz 172.17.0.0/16 sein können., weil dieses Netz keine statische Zuweisung erlaubt.
Ich bevorzuge allerdings planbare Konfigurationen und wähle daher diesen Weg, denn somit spart man sich den unnötigen Docker-Proxy, der in vielen Anleitungen genutzt wird. Nextcloud kann nämlich von Nginx direkt erreicht werden!
MariaDB und Nginx laufen im Standard Netzwerk (Namespace), sodass diese sowohl an öffentlichen IP-Adressen, als auch allen Docker Netzwerken lauschen können. Deshalb sind weitere Anwendungen auch in der Lage mit Mariadb zu kommunizieren (hier z.B. 10.11.12.1:3306). Anwendungen in anderen Netzen (z.B. docker0, oder anderen zukünftigen br-apps2, br-xyz, …) sind somit in der Lage über die IP-Adresse des Hosts Verbindungen aufzubauen.
Netzwerk | IP-Netz | Host-IP | Container IPs |
---|---|---|---|
br-apps | 10.11.12.0/24 | 10.11.12.1 | 10.11.12.2 - 10.11.13.254 |
br-apps2 | 192.168.22.0/24 | 192.168.22.1 | 192.168.22.2 - 192.168.22.254 |
neuland | 172.19.199.0/24 | 172.19.199.1 | 172.19.199.2 - 172.19.199.254 |
Wie man erkennen kann, erhält der Host selbst die .1 in den Netzen, sodass dieser jeden Container erreichen kann. Die Container erreichen den Host (und Anwendungen im selben Netzwerk).
Somit können Container die Datenbank, den Webserver und alle Anwendungen im Host-Namespace erreichen, sofern diese mittels Firewall nicht unterbunden werden.
Die Nutzung des Host Namespaces wird durch “network_mode: host” oder “–network host” Einstellung festgelegt. Weil jedes Netzwerk für sich eine Layer2 Domäne darstellt, können die Netze untereinander nur sprechen, wenn der Host die Anfragen zwischen den Netzen routet.
Migration Nextcloud FPM Image zu LinuxServerIO
Vorbereitung
Backups, Backups, Backups… Die Datenbank sollte ebenfalls gesichert werden (mysqldump / mariadb-dump / psqldump).
Zusätzlich empfiehlt es sich, Nextcloud auf die gleiche Version des LSIO-Images zu bringen. Ermitteln kann man die aktuelle Version mit Auslesen der version.php.
Beispiel:
grep OC_VersionString /srv/docker/nextcloud.old/html/version.php
$OC_VersionString = '28.0.2';
Passt die Version nicht, sollte vorher ein Update gemacht werden. Grundsätzlich unterstützt das LSIO Image ein Upgrade, aber weil es sich um eine “Neuinstallation” mit Migration handelt, kann es hier zu Problemen kommen…
Docker Compose
Aufgrund der vereinfachten Struktur des LinuxServer.IO Images kann das Docker Compose File leicht aufgebaut werden und die Daten hinein migriert werden.
Sollten eigene Pfade oder Anpassungen vorgenommen worden sein (zusätzliche Mounts, Konfigurationsdateien) müssen diese entweder hinein kopiert werden, oder wieder gemountet werden.
Das custom_apps
Verzeichnis, welches für installierte Apps genutzt wird, dient uns hier als Vorlage…
version: '3'
services:
nextcloud:
image: lscr.io/linuxserver/nextcloud:28.0.2
container_name: nextcloud_app
environment:
PUID: 1000
PGID: 1000
TZ: "Etc/UTC"
volumes:
- /srv/docker/nextcloud/config/:/config
- /srv/docker/nextcloud/data:/data
# zusätzlicher Mountpunkt für Apps die vom Standardset abweichen
- /srv/docker/nextcloud/custom/custom_apps:/app/www/public/custom_apps
restart: unless-stopped
networks:
default:
ipv4_address: 10.11.12.254
networks:
default:
driver: bridge
ipam:
driver: default
config:
- subnet: 10.11.12.0/24
driver_opts:
com.docker.network.bridge.name: br_nextcloud
Danach kann der Container erstmalig gestartet werden, damit die Verzeichnisse angelegt werden.
Hierbei werden Einrichtungen erledigt wie:
- Log Ordner
- Nextcloud SSL Zertifikate (Container spricht HTTPS)
- Nextcloud Verzeichnisstruktur
- Config Files
Nachdem der Container gestartet wurde, kann dieser wieder gestoppt werden, damit wir die Daten migrieren können.
Migration
Config
cp /srv/docker/nextcloud.old/html/config/config.php /srv/docker/nextcloud/config/www/nextcloud/config/config.php
Danach müssen noch Anpassungen vorgenommen werden:
$CONFIG = array (
'maintenance_window_start' => 1,
array (
0 =>
// Standardpfad fuer Apps, readonly
array (
'path' => '/app/www/public/apps',
'url' => '/apps',
'writable' => false,
),
// custom_apps vom Zusatzmountpoint, schreibbar
1 =>
array (
'path' => '/app/www/public/custom_apps',
'url' => '/custom_apps',
'writable' => true,
),
),
Eventuell ist es auch nötig aus den anderen Configfiles Inhalte zu übernehmen, weil das Nextcloud Image zusätzliche Dateien anbietet und Werte aus dem Environment zieht:
- apcu.config.php
- apps.config.php
- autoconfig.php
- config.php
- redis.config.php
- reverse-proxy.config.php
- s3.config.php
- smtp.config.php
- swift.config.php
- test.config.php
Hier hängt es stark von deiner Konfiguration ab, ob zusätzliche Einstellungen in der config.php hinterlegt werden müssen.
Am besten im docker-compose.yml die Variablen mit den Dateien abgleichen, falls Environment-Variablen genutzt wurden…
Datenmigration
Hier werden die Dateien übernommen, es bietet sich an die original Cloud herunterzufahren, damit keine Änderungen geschrieben werden.
Auch hier gilt, sind weitere Ordner nötig, müssen diese auch synchronisiert werden.
rsync -aH /srv/docker/nextcloud.old/html/data/ /srv/docker/nextcloud/data/
rsync -aH /srv/docker/nextcloud.old/html/custom_apps/ /srv/docker/nextcloud/custom/custom_apps/
chown -R 1000:1000 /srv/docker/nextcloud/data/ /srv/docker/nextcloud/custom/custom_apps/
Nginx Config
Die Konfiguration eines Proxies gestaltet sich im Vergleich zum FPM Image nun deutlich einfacher
server {
...
...
...
location / {
# man beachte das https://, denn der Container spricht selbst HTTPS
# nutzt man hier http://, wird die Cloud dies beim selfcheck bemängeln...
proxy_pass https://10.11.12.254:443;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
}
Gibt es beim Reload einen Fehler beim $http_upgrade, fehlt vermutlich die Map dafür.
Diese kann einfach in der nginx.conf hinterlegt werden:
http {
...
...
...
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
...
...
...
}
Post Migration
- Eventuell konfigurierte Cronjobs können nun entfernt werden, weil der Docker Container einen eigenen Cronjob beinhaltet.
- Zertifikate der Cloud können migriert werden, sind aber kein muss, falls die Nextcloud sich selbst über den Reverse-Proxy (mit offiziellem Zertifikat) erreichen
- Indicies fixen
Zertifikate importieren
docker exec -ti -u 1000 -w /app/www/public nextcloud_app php occ security:certificates
docker exec -ti -u 1000 -w /app/www/public nextcloud_app php occ security:certificates:import /config/keys/cert.crt
docker exec -ti -u 1000 -w /app/www/public nextcloud_app php occ security:certificates
Indicies anlegen
docker exec -ti -u 1000 -w /app/www/public nextcloud_app php occ db:add-missing-indices
OCC Skript korrigieren
Solltest du ein occ Script als Wrapper für Nextcloud verwenden, können Anpassungen nötig sein, weil eine andere User-ID verwendet wird.
Einfach in die Datei /usr/local/sbin/occ
packen:
#!/bin/bash
docker exec -ti -u 1000 -w /app/www/public nextcloud_app php occ $*
Danach die Berechtigung fixen
chmod 700 /usr/local/sbin/occ
Nextcloud PHP Einstellungen anpassen
In diesem Artikel werden Einstellungen für das Docker Image nextcloud:X-fpm, bzw. nextcloud:fpm beschrieben! Diese können ebenfalls beim Apache Image Anwendung finden, werden aber andere Pfade benötigen…
Allgemeines
Im Grundzustand sind alle nötigen Einstellungen des PHP-FPM Prozesses bereits vorgenommen. In den Meisten Fällen werden diese ausreichen, allerdings können je nach Bedarf Individualisierungen vorgenommen werden. Hierbei können Einstellungen wie zum Beispiel das Memory Limit (RAM Speicher), die Größe der Uploads, oder auch die Menge der zu erstellenden Prozesse angepasst werden. Gerade Letztes ist für größere Installationen mit vielen Benutzern relevant, denn wenn die Gesamtheit der Prozesse mit der Bearbeitung von Anfragen beschäftigt ist, können verlängerte Wartezeiten auftreten.
Konfigurationen
Alle Einstellungen befinden sich im Container unterhalb des folgenden Ordners:
root@4a850acce455:/var/www/html# ls -lha /usr/local/etc/php-fpm.d/
total 56K
drwxr-xr-x 1 root root 4.0K Jul 22 03:10 .
drwxr-xr-x 1 root root 4.0K Jul 22 03:10 ..
-rw-r--r-- 1 root root 357 Jul 22 03:10 docker.conf
-rw-r--r-- 1 root root 20K Jul 22 03:10 www.conf
-rw-r--r-- 1 root root 20K Jul 22 03:10 www.conf.default
-rw-r--r-- 1 root root 45 Jul 22 03:10 zz-docker.conf
Standardkonfiguration
Für nextcloud existiert ein Pool mit dem Namen www. Dies erkennt man in den Konfigurationsdateien durch die [www] Sektion:
# grep -v -e '^;' -e '^$' www.conf
[www]
user = www-data
group = www-data
listen = 127.0.0.1:9000
pm = dynamic
pm.max_children = 5
pm.start_servers = 2
pm.min_spare_servers = 1
pm.max_spare_servers = 3
# grep -v -e '^;' -e '^$' docker.conf
[global]
error_log = /proc/self/fd/2
log_limit = 8192
[www]
access.log = /proc/self/fd/2
clear_env = no
catch_workers_output = yes
decorate_workers_output = no
#grep -v -e '^;' -e '^$' zz-docker.conf
[global]
daemonize = no
[www]
listen = 9000
Im Standardfall bindet der PHP-FPM Prozess sich an Port 9000 und loggt seine Ausgaben an Stderr (/proc/self/fd/2), sodass die Ausgaben mittels docker logs eingesehen werden können.
Einstellungen überladen
Es bietet sich also an, entweder alle Dateien (den Ordner) via Volume, oder die zz-docker.conf zu überladen, falls lediglich einzelne Einstellungen notwendig sind. In diesem Beispiel wird der komplette php-fpm.d Ordner überladen (3. Zeile unter Volumes). Dadurch können wir alle Dateien individuell gestalten und vermischen diese nicht:
version: '3'
services:
nextcloud:
container_name: nextcloud
image: nextcloud:21-fpm
volumes:
- /srv/nextcloud/run:/var/run/php/
- /srv/nextcloud/html/:/var/www/html/
- /srv/nextcloud/config/php-fpm.d:/usr/local/etc/php-fpm.d/
restart: unless-stopped
env_file: /srv/nextcloud/config/env_nextcloud
Nun müssen noch die Einstellungen innerhalb einer Datei vorgenommen werden. Die anderen müssen nicht angelegt werden!
Beispiel-Konfiguration
# File: /srv/nextcloud/config/php-fpm.d/zz-docker.conf
[www]
# Benutzer / Gruppe für Datei-Operationen
user = www-data
group = www-data
# Socket an dem der PHP-FPM Prozess lauscht (Webserver kommuniziert hier mit PHP)
listen = /var/run/php/nc.socket
# Benutzer / Gruppe dem der Socket "gehoert"
listen.owner = www-data
listen.group = www-data
# dynamisches erstellen von PHP-Prozessen (je nach Bedarf)
# Alternative: ondemand (hier werden nur Prozesse bei Bedarf gestartet und nach einer Gewissen Zeit komplett abgeräumt)
pm = dynamic
# maximal 8 Prozesse koennen gestartet werden
pm.max_children = 8
# ein Prozess von Beginn starten und bereithalten
pm.start_servers = 1
pm.min_spare_servers = 1
pm.max_spare_servers = 8
env[PATH] = /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
# Upload Groeße erhoeht auf 4 GB
php_admin_value[post_max_size] = 4096M
php_admin_value[upload_max_filesize] = 4096M
[global]
# notwendig damit php-fpm im Vordergrund läuft und der Docker Container nicht abstürzt (siehe /entrypoint.sh)
daemonize = no
PHP-Einstellungen anpassen
Unabhängig von den hier genannten Einstellungen, können eine Vielzahl mitels php_admin_value und php_admin_flag gesetzt werden. Die offiziellen Flags / Werte findet man in der PHP-Dokumentation:
https://www.php.net/manual/de/install.fpm.configuration.php
Nginx-Anbindung mit Socket
Weil php-fpm nun nicht mehr an einem TCP-Port (9000) lauscht, sondern einen Unix-Socket bereitstellt (/var/run/php/nc.socket), müsste man diesen in der nginx Konfiguration hinterlegen:
Eine komplette Beschreibung der Nginx Konfiguration findet man in der offiziellen Dokumentation, welche den Socket-Betrieb bereits vorgesehen hat: https://docs.nextcloud.com/server/21/admin_manual/installation/nginx.html