Subsections of Container
Bitwarden
Subsections of Bitwarden
self-hosted Bitwarden Server
Was ist Bitwarden
Für alle die Bitwarden noch nicht kennen:
Es handelt sich hierbei um einen quelloffenen Passwort-Manager, der mittels Ende-zu-Ende-Verschlüsselung Passwörter speichert, synchronisiert und per Webinterface, aber auch vielen Apps und Browsererweiterungen bereitstellt. Ohne Master-Kennwort verliert man den Zugang zum Passwortspeicher und Serverbetreiber können Kennwörter auch nicht auslesen. Zusätzlich kann ein Eintrag für mehrere Apps und Webseiten gelten, indem man durch Regeln festlegt, wofür der Eintrag verwendet werden darf. Ein Beispiel:
Ihr nutzt die Nextcloud-App, aber auch das Webinterface. So kann in Bitwarden ein gemeinsamer Login für die Webseite https://meine.nextcloud.com, aber auch die App com.nextcloud.client hinterlegt werden. Bitwaren bietet beim automatischen Ausfüllen die Speicherung der URL an. Viele Firmen nutzen auch multiple Dienste mit LDAP mit einem gemeinsamen Login, diese können ebenfalls abgebildet werden.
Für Logindaten mit Zertifikaten bietet Bitwarden die Speicherung von Dateianhängen an und jedem Datensatz können weitere Klartext, aber auch verdeckte Zusatzfelder, die man selbst benennen kann, hinzugefügt werden.
Grundsätzlich kann man Bitwarden als den Passwort-Manager schlecht hin bezeichnen, da man seine Daten selbst hosted und mit den vielen Zugriffsmöglichkeiten endlich eine Lösung für alle gängigen Geräte hat.
Und braucht man für die 2-Faktor-Authentifizierung einen OTP-Generator, dann kann Bitwarden auch das übernehmen …
Aber genug der einleitenden Worte, auf zur Installation, basierend auf Docker.
Vorbereitung
Eine grundlegende Anleitung findet man direkt beim Hersteller, wobei in dieser Anleitung leichte Anpassungen vorgenommen werden: https://bitwarden.com/help/install-on-premise-linux/.
Zunächst legen wir die Struktur an, der Ordner kann frei gewählt werden:
# Ordner anlegen und betreten
mkdir -p /srv/docker/bitwarden
cd /srv/docker/bitwarden
# lädt das Verwaltungsskript von Bitwarden runter
curl -Lso bitwarden.sh https://go.btwrdn.co/bw-sh && chmod 700 bitwarden.sh
Dann wird Bitwarden installiert (Images gepullt, Verzeichnisse angelegt):
cd /srv/docker/bitwarden
./bitwarden.sh install
Wärend der Installation werden verschiedene Fragen gestellt:
# Hier wird die Domain für den Aufruf festgelegt
Enter the domain name for your Bitwarden instance (ex. bitwarden.example.com): bitwarden.mydomain.com
# Let's Encrypt lassen wir vom vorgelagerten Nginx regeln, daher nein
Do you want to use Let's Encrypt to generate a free SSL certificate? (y/n): n
# Datenbankname, kann Standard bleiben, einfach Enter
Enter the database name for your Bitwarden instance (ex. vault): <ENTER>
# die Installations-ID & der Key die vorher beschafft wurde (siehe oben)
Enter your installation id (get at https://bitwarden.com/host): xxxxxxxxxxx
Enter your installation key: xxxxxxxxxxxx
# wir nutzen ein selbstsigniertes Zertifikat, was Bitwarden anlegen wird, daher hier nein auswählen
Do you have a SSL certificate to use? (y/n): n
# ja bitte!
Do you want to generate a self-signed SSL certificate? (y/n): y
Generating key for IdentityServer.
Generating a RSA private key
...................++++
writing new private key to 'identity.key'
-----
!!!!!!!!!! WARNING !!!!!!!!!!
You are not using a SSL certificate. Bitwarden requires HTTPS to operate.
You must front your installation with a HTTPS proxy or the web vault (and
other Bitwarden apps) will not work properly.
# Da wir einen eigenen nginx mit SSL Zertifikat für den öffentlichen Zugriff verwenden, benötigt Bitwarden kein LE, oder gar ein öffentliches Zertifikat. Es reicht daher aus, bei der Bitwarden # Installation ein eigenes Zertifikat generieren zu lassen. Die Kommunikation zwischen Bitwarden und dem eigenständigen nginx wird somit dennoch verschlüsselt durchgeführt.
# Ohne Zertifikats wäre es eben HTTP anstatt HTTPS und somit innerhalb des Servers unverschlüsselt...
Konfiguration
Die Mailkonfiguration ist nötig um Zugang zum Admininterface zu erhalten.
Für jeden Zugriff wird ein einmalig gültiger Link per E-Mail verschickt!
Der Bitwarden-Installer hat nun eine Verzeichnisstruktur angelegt und wir können weitere Konfigurationen vornehmen:
#File: /srv/docker/bitwarden/bwdata/config.yml
# Wir sorgen dafür, dass Bitwarden sich nicht auf Port 80 und 443 bindet, da hier bereits ein Webserver lauscht. Deswegen binden wir den Docker-Proxy der von Bitwarden vorgesehen ist, auf eine eigene IP-Adresse mit Port 8080 und 8443.
http_port: 172.16.1.1:8080
https_port: 172.16.1.1:8443
Datei: /srv/docker/bitwarden/bwdata/env/global.override.env
globalSettings__mail__replyToEmail=no-reply@bitwarden.mydomain.com
globalSettings__mail__smtp__host=REPLACE
globalSettings__mail__smtp__port=587
globalSettings__mail__smtp__ssl=false
globalSettings__mail__smtp__username=REPLACE
globalSettings__mail__smtp__password=REPLACE
# Hier müssen die SMTP-Auth Daten und die gewünschte E-Mailadresse hinterlegt werden, zum Beispiel:
globalSettings__mail__replyToEmail=no-reply@bitwarden.mydomain.com
globalSettings__mail__smtp__host=smtp.google.com
globalSettings__mail__smtp__port=465
globalSettings__mail__smtp__ssl=true
globalSettings__mail__smtp__username=myaccount@gmail.com
globalSettings__mail__smtp__password=super_tolles_pasword
Im späteren Verlauf kann man, nachdem ein Account angelegt wurde, die Registration für Außenstehende deaktivieren.
globalSettings__disableUserRegistration=true
Dies ist aber erst sinnvoll, wenn alle Accounts eingerichtet wurden die man benötigt ;)
Nach Anpassungen in den oben genannten Dateien müssen die Docker-Files neu genriert werden:
./bitwarden.sh rebuild
Weil wir eine feste IP-Adresse für den Bitwarden-Nginx und seine Dienste möchten, bietet es sich an, Docker mitzuteilen, dass ein bestimmtes Netz gewünscht wird. Dies lässt sich über eine sogeannte override-Datei regeln. Diese ist im Standardfall noch nicht existent und muss angelegt werden!
# File /srv/docker/bitwarden/bwdata/docker/docker-compose.override.yml
---
version: '3'
services:
nginx:
networks:
default:
ipv4_address: 172.16.1.250
networks:
default:
driver: bridge
ipam:
driver: default
config:
- subnet: 172.16.1.0/24
driver_opts:
com.docker.network.bridge.name: br_bitwarden
Hiermit wird sichergestellt, dass der nginx-Container die IP-Adresse 172.16.1.250 erhält und man über diese den Dienst erreicht. Andernfalls könnte Docker die IP-Adressen der Container nach und nach neu würfeln und man bekommt keine konsistente Konfiguration für den vorgelagerten Nginx.
Start und Test
Nun kann man Bitwarden starten und die interne Erreichbarkeit testen:
./bitwarden.sh start
# -k weil selbstsigniertes Zertifikat
curl -k https://172.16.1.250:8443
<!doctype html>......
Erhält man eine Antwort, kann nun der vorgelagerte Nginx konfiguriert werden. Je nach lokalen gegebenheiten kann dies sehr unterschiedlich passieren, gehen wir mal vom Standardfall (Ubuntu, Archlinux) aus…
Nginx und Bitwarden verbinden
# File: /etc/nginx/sites-enabled/bitwarden.mydomain.com.conf
# Dieser vHost nimmt auf Port 80 alle Anfragen an und leitet diese auf HTTPS um, außer Let's Encrypt ACME Challenges.
server {
server_name bitwarden.mydomain.com;
listen *:80;
# Dies ist sehr individuell, mehr dazu im Bereich Let's Encrypt
location ~ .well-known {
root /srv/www/letsencrypt/;
}
location / {
return 301 https://$host$request_uri;
}
}
Danach den Ordner für die LE Validierung anlegen und den Nginx neustarten / reloaden:
mkdir -p /srv/www/letsencrypt/
nginx -t && nginx -s reload
Let’s Encrypt Zertifikat anfragen
Hier wird nun der DNS-Eintrag benötigt, dieser wird nämlich öffentlich abgerufen. Wenn er noch nicht existiert, ist spätestens nun der richtige Zeitpunkt. Der Certbot legt eine ACME-Challenge-Datei unterhalb von /srv/www/letsencrypt an.
certbot certonly --webroot -w /srv/www/letsencrypt/ --email admin@mydomain.com --non-interactive --agree-tos -d bitwarden.mydomain.com
Nun ergänzen wir die Konfiguration mit dem HTTPS Host, weil das SSL-Zertifikat vorliegt und ein Reload keinen Fehler mehr produziert:
# File: /etc/nginx/sites-enabled/bitwarden.mydomain.com.conf
# Dieser bereich sollte erst eingerichtet werden, wenn ein SSL Zertifikat vorliegt!
server {
server_name bitwarden.mydomain.com;
listen *:443 ssl;
access_log /var/log/nginx/bitwarden.mydomain.com.access.log;
error_log /var/log/nginx/bitwarden.mydomain.com.error.log;
rewrite_log on;
# Platzhalter, alle Anfragen werden sowieos an Bitwarden geleitet
root /srv/www/default;
index index.php index.html index.htm;
ssl_certificate /etc/letsencrypt/live/bitwarden.mydomain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/bitwarden.mydomain.com/privkey.pem;
client_max_body_size 1024M;
location = /robots.txt {
allow all;
log_not_found off;
access_log off;
}
location / {
# die IP-Adresse ist nun statisch, da wir diese im docker override hinterlegt haben...
proxy_pass https://172.16.1.250:8443$request_uri;
proxy_redirect off;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Protocol $scheme;
proxy_set_header X-Url-Scheme $scheme;
}
}
Danach noch einmal den nginx reloaden:
nginx -t && nginx -s reload
Im Anschluss sollte Bitwarden öffentlich erreichbar sein (Adresse ist ein Beispiel!):
https://bitwarden.mydomain.com
Sofern die Mailkonfiguration korrekt ist, dürfte eine Registrierung auch klappen. Diese kann im Anschluss wie bereits erwähnt deaktiviert werden (siehe oben). Ein rebuild ist dann nötig, wodurch die Container neugestartet werden.
Viel Erfolg und Spaß mit Bitwarden ;)
Updates
Möchte man Bitwarden updaten:
cd /srv/docker/bitwarden
./bitwarden.sh update
Docker
Subsections of Docker
self hosted Docker Image Registry mit UI
Warum eine eigene Registry?
In dieser Anleitung wird eine Docker Registry eingerichtet und mittels Frontend zugängig gemacht.
Gründe hierfür können vielschichtig sein, zum Beispiel:
- eigene Images sind nicht öffentlich erreichbar (z.B. für selbstentwickelte Software)
- Cache um vom öffentlichen Netzwerk unabhängig zu sein
- keine Veränderung der Images, weil man selbst diue Kontrolle über die Registry hat
Idee
Es werden 2 Container gestartet, die Registry selbst und das Frontend. Das Frontend selbst benötigt keine Persistenz und die nötigen Informationen werden vom Browser beschafft.
Die UI verhält sich somit wie eine Clientanwendung, die selbst nicht mit der Registry spricht, sondern lediglich das visualisiert, was der Browser anfragt.
Die Registry und UI werden im selben nginx vHost laufen, können aber auch getrennt werden, hierzu sind weitere Einstellungen nötig, siehe Hinweise.
Zusätzlich kann das Frontend und die Registry mit Basic Auth geschützt werden, dies wird ebenfalls konfiguriert.
Konfiguration
Docker
Die REGISTRY_URL
muss auf den Hostname, unter dem die Registry erreichbar ist, gesetzt werden (z.B. myregistry.mydomain.com).
Ob Images gelöscht werden können, stellt REGISTRY_STORAGE_DELETE_ENABLED
sicher, das Frontend bietet die Option an, wenn DELETE_IMAGES
gesetzt ist.
Weil die Aktionen selbst vom Browser ausgeführt werden und nicht vom Frontend, sind beide Einstellungen auf true zu setzen.
Weitere Einstellungen können der Doku entnommen werden: https://github.com/Joxit/docker-registry-ui
Docker Compose
version: "3"
services:
registry:
container_name: registry
image: registry:2
restart: unless-stopped
volumes:
- /srv/docker/registry/data/:/var/lib/registry
environment:
REGISTRY_STORAGE_DELETE_ENABLED: "true"
networks:
default:
ipv4_address: 172.16.2.2
registry-ui:
container_name: registry-ui
image: joxit/docker-registry-ui:latest
restart: unless-stopped
environment:
REGISTRY_URL: "https://<REGISTRY_HOSTNAME>"
SINGLE_REGISTRY: "true"
DELETE_IMAGES: "true"
networks:
default:
ipv4_address: 172.16.2.3
networks:
default:
driver: bridge
ipam:
driver: default
config:
- subnet: 172.16.2.0/24
driver_opts:
com.docker.network.bridge.name: br_registry
Danach können die Container gestartet werden.
HTTP Proxy (nginx)
Hier kann auch Apache, Traefik oder ein anderer Proxy der persönlichen Wahl genutzt werden.
Nginx ist aber bekannt und deswegen hier die spezifische Konfiguration für die beiden Dienste.
Um die Konfiguration einfach darstellen zu können, verzichte ich auf den ganzen Binding, SSL, Logging Overhead (siehe TODO)!
Basic Auth File generieren
Hier gibt es verschiedene Wege, Beispiele findest du hier: Anleitung zu Salted Password Hashes
Der Inhalt muss dann in die Datei /etc/nginx/htpasswd/myregistry.htpasswd
gepackt werden und hat den folgenden Syntax:
username:<PASSWORD_HASH>
test:<PASSWORD_HASH>
# siehe add_header 'Docker-Distribution-Api-Version'
map $upstream_http_docker_distribution_api_version $docker_distribution_api_version {
'' 'registry/2.0';
}
server {
################## TODO ###############################
ADD NGINX CONFIGURATION (LISTEN, LOG, SERVERNAME, ....)
#######################################################
# disable any limits to avoid HTTP 413 for large image uploads
client_max_body_size 0;
# required to avoid HTTP 411: see Issue #1486 (https://github.com/moby/moby/issues/1486)
chunked_transfer_encoding on;
auth_basic "Registry realm";
auth_basic_user_file /etc/nginx/htpasswd/myregistry.htpasswd;
location / {
proxy_pass http://172.16.2.3:80;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_read_timeout 900;
}
location /v2/ {
# Do not allow connections from docker 1.5 and earlier
# docker pre-1.6.0 did not properly set the user agent on ping, catch "Go *" user agents
if ($http_user_agent ~ "^(docker\/1\.(3|4|5(?!\.[0-9]-dev))|Go ).*$" ) {
return 404;
}
# To add basic authentication to v2 use auth_basic setting.
satisfy any;
# disable basic auth for specific IP
# allow x.x.x.x;
## If $docker_distribution_api_version is empty, the header is not added.
## See the map directive above where this variable is defined.
add_header 'Docker-Distribution-Api-Version' $docker_distribution_api_version always;
proxy_pass http:/172.16.2.3:5000;
proxy_set_header Host $http_host; # required for docker client's sake
proxy_set_header X-Real-IP $remote_addr; # pass on real client's IP
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_read_timeout 900;
}
}
Zugriff
Wenn man sich das Passwort gemerkt hat (…), sollte ein Login möglich sein:
docker login myregistry.domain.com
Der Login kann auch automatisch passieren, wenn in der Datei PASSWORD_FILE
das Klartextpasswort steht:
cat PASSWORD_FILE | docker login --username testuser
Hinweise
unterschiedliche Hostnames für Registry & Frontend
Soll das Frontend unter einer anderen URL als die Registry verfügbar gemacht werden, kann es nötig sein CORS Einstellungenin nginx vorzunehmen um dem Browser gültige URLs für API Calls mitzugeben. Links zum Thema:
- https://enable-cors.org/server_nginx.html
Ansonsten kann auch der nginx im Frontend Container als Proxy fungieren und hierfür sind Einstellungen vorgesehen: https://github.com/Joxit/docker-registry-ui?tab=readme-ov-file#using-cors.
Hashicorp Vault
Subsections of Hashicorp Vault
HashiCorp Vault mit Client Config
Tokens und Zugangsdaten sind unbedingt sicher aufzubewahren. Verliert man diese, kann die Vault unbrauchbar werden und die Daten verloren gehen!
Konfiguration
Docker
Zuerst legen wir die Ordnerstruktur an:
mkdir -p /srv/docker/vault/config
mkdir -p /srv/docker/vault/file
mkdir -p /srv/docker/vault/logs
Danach benötigen wir typischerweise ein docker-compose.yml
version: "3"
services:
vault:
image: hashicorp/vault
container_name: vault
volumes:
- /srv/docker/vault/config:/vault/config
- /srv/docker/vault/file:/vault/file
- /srv/docker/vault/logs:/vault/logs
cap_add:
- IPC_LOCK
restart: unless-stopped
networks:
default:
ipv4_address: 172.16.3.2
command: server
networks:
default:
driver: bridge
ipam:
driver: default
config:
- subnet: 172.16.3.0/24
driver_opts:
com.docker.network.bridge.name: br_vault
Und dazu noch die Vault Config:
ui = true
api_addr = "http://0.0.0.0:8200"
disable_mlock = true
storage "file" {
path = "/vault/file"
}
listener "tcp" {
address = "0.0.0.0:8200"
tls_disable = 1
}
Danach kann der Container gestartet werden:
docker-compose up -d
Vault Ersteinrichtung mit CLI
Wenn die Vault gestartet wurde und erreichbar ist, muss die Einrichtung einmalig durchgeführt werden.
Hierzu kann man den CLI Client nutzen und die URL des Containers temporär setzen.
Mit -key-shares=
gibt man die Menge der Schlüssel an die man erzeugen lassen möchte und mit -key-threshold=
wie viele benötigt werden um beim Neustart die Vault zu öffnen.
Beispiel: 10 Nutzer, von denen mindestens 3 benötigt werden um die Vault zu öffnen: -key-shares=10 -key-threshold=3
Sind nicht genügend Schlüssel zum öffnen verfügbar (können über den Browser eingegeben werden), bleibt die Vault dicht!
Ich kann es nicht oft genug erwähnen: SCHLÜSSEL / TOKEN SICHERN
export VAULT_ADDR=http://172.16.3.2:8200
vault operator init -key-shares=1 -key-threshold=1
Unseal Key 1: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Initial Root Token: hvs.xxxxxxxxxxxxxxxxxxxxx
# MAN BEACHTE:
Vault does not store the generated root key. Without at least 1 keys to
reconstruct the root key, Vault will remain permanently sealed!
It is possible to generate new unseal keys, provided you have a quorum of
existing unseal keys shares. See "vault operator rekey" for more information.
Nginx
Wie so häufig wird nginx als Proxy davor gestellt, hierfür ist keine große Konfiguration nötig und es kann eine einfache Proxy-Config genutzt werden:
server {
######################################
# NGINX vhost listen, logs, ssl, ...
######################################
# OPTIONALER Schutz um die Vault mit vorangehendem Basic Auth zu schützen
#auth_basic "Vault realm";
#auth_basic_user_file /etc/nginx/htpasswd/vault.htpasswd;
location / {
proxy_pass http://172.16.3.2:8200;
proxy_set_header X-Real-IP $remote_addr; # pass on real client's IP
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_read_timeout 900;
}
Danach kann die Nginx Config getestet und geladen werden:
nginx -t && nginx -s reload
Konfiguration Vault Adresse im Client
Nachdem die Vault über Nginx erreichbar gemacht wurde, kann die Adresse des Clients angepasst werden:
ohne Basic Auth
export VAULT_ADDR=https://vault.mydomain.com
lokal auf dem Docker Host
Falls man nicht über Nginx gehen möchte …
export VAULT_ADDR=http://172.16.3.2
Mit zusätzlichem Basic Auth
export VAULT_ADDR=https://USERNAME:PASSWORD@vault.mydomain.com
Login & Tests
Um die Anleitung nicht zu kompliziert werden zu lassen, sollen verschiedene Authentifizierungsverfahren hier nicht beleuchtet werden.
Der Standardlogin mit gerade erzeugtem Root-Token funktioniert wie folgt:
vault login
Token (will be hidden): <HIER TOKEN (beginnt mit hvs.) EINGEBEN>
Vault Status prüfen
Die Vault ist aktuell noch verschlossen (Sealed)
vault status
Key Value
--- -----
Initialized true
Sealed true
Total Shares 1
Threshold 1
Unseal Progress 0/1
Unseal Nonce n/a
Vault öffnen
Öffnen der Vault mit X Schlüsseln
vault operator unseal
Unseal Key (will be hidden): <UNSEAL TOKEN EINGEBEN>
Key Value
--- -----
Sealed false
Let's Encrypt Zertifikate mit Vault synchronisieren
Die Scripts benötigen einen gültigen Vault Token (siehe Vorbereitung)! Außerdem muss ein KV-Secret Store eingerichtet sein. In diesem Beispiel lautet der Secret-Store certbot.
Vorbereitung
Installation Komponenten
Für das Script werden JQ und Vault (CLI Client) benötigt:
pacman -Syu vault jq
Offizielle Anleitung: https://developer.hashicorp.com/vault/tutorials/getting-started/getting-started-install
wget -O- https://apt.releases.hashicorp.com/gpg | sudo gpg --dearmor -o /usr/share/keyrings/hashicorp-archive-keyring.gpg
gpg --no-default-keyring --keyring /usr/share/keyrings/hashicorp-archive-keyring.gpg --fingerprint
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] https://apt.releases.hashicorp.com $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/hashicorp.list
sudo apt update
sudo apt install vault jq
Vault Login
Durch den Login wird der Token in die Datei /root/.vault-token
geschrieben. Dieser wird von den Scripts und den weiteren Vorbereitungen benötigt.
export VAULT_ADDR=https://vault.domain.com
vault login
KV Store
Vault muss einen KV-Store bereitstellen, dieser lautet in den Scripts certbot. Ist ein anderer Namen gewünscht, müssen alle Pfade angepasst werden.
Nun wird der Secret Store angelegt und die Rotation von maximal 7 Versionen eingestellt, wodurch nur insgesamt 7 Versionen behalten werden und ältere automatisch gelöscht werden.
vault secrets enable -path=certbot kv
vault write certbot/config max_versions=7
Cert Download
Script zum downloaden der Zertifikate
Script auf dem Server anlegen /usr/local/sbin/update_cert.sh
:
#!/bin/bash
# cancel if any error occurs
set -e
if [[ $# -ne 1 ]];then
echo "usage: $0 DOMAIN"
exit 1
fi
DOMAIN="${1}"
CERT_DIR="/etc/nginx/ssl/${DOMAIN}/"
export VAULT_ADDR
# load certificate, chain + key from vault
json=$(vault kv get -format=json certbot/certificates/${DOMAIN})
mkdir -p ${CERT_DIR}
# join cert + chain -> (fullchain)
jq -r '.data.data.cert + .data.data.chain' <(echo $json) > ${CERT_DIR}/fullchain.pem
jq -r '.data.data.privkey' <(echo $json) > ${CERT_DIR}/privkey.pem
# OPTIONAL: reload nginx if config is okay
#nginx -t && nginx -s reload
Berechtigung korrigieren, damit das Script ausführbar ist
chown -R root:root /usr/local/sbin/update_cert.sh
chmod 700 /usr/local/sbin/update_cert.sh
Cronjob / Systemd Timer
Unbedingt den Hostname vault.domain.com anpassen!
Cronjob
crontab -e
0 2 * * * VAULT_ADDR=https://vault.domain.com /usr/local/sbin/update_cert.sh cert.mydomain.com
1 2 * * * VAULT_ADDR=https://vault.domain.com /usr/local/sbin/update_cert.sh mydomain.com
Systemd Unit / Timer
Service Unit /etc/systemd/system/cert_update@.service
[Unit]
Description=Download Certbot files
Requires=network-online.target
After=network-online.target
[Service]
Type=oneshot
ExecStart=/usr/local/sbin/update_cert.sh %i
Environment="VAULT_ADDR=https://vault.domain.com"
Service Timer /etc/systemd/system/cert_update@.timer
[Unit]
Description=Daily renewal of cert
[Timer]
OnCalendar=01:00:00
Persistent=true
RandomizedDelaySec=60
[Install]
WantedBy=timers.target
Danach kann der Timer für jedes Zertifikat aktiviert werden:
systemctl daemon-reload
systemctl enable --now cert_update@my-domain.com.timer
systemctl enable --now cert_update@cert2.my-domain.com.timer
Cert Upload
Script zum uploaden der Zertifikate
Das Script kann mehrere Zertifikate mit einem Aufruf verarbeiten, diese müssen lediglich als Parameter übergeben werden.
In diesem Beispiel wird allerdings für jedes Zertifikat ein eigener Cronjob / Systemd-Timer angelegt, geht aber auch anders …
Das Script unter /usr/local/sbin/push_certs_to_vault.sh
anlegen:
#!/bin/bash
export VAULT_ADDR
# script supports multiple domains via parameter
DOMAINS="$*"
for domain in ${DOMAINS};do
vault kv put "certbot/certificates/${domain}" "cert=@/etc/letsencrypt/live/${domain}/cert.pem" "chain=@/etc/letsencrypt/live/${domain}/chain.pem" "privkey=@/etc/letsencrypt/live/${domain}/privkey.pem"
done
Berechtigung korrigieren, damit das Scripts ausführbar ist:
chown -R root:root /usr/local/sbin/push_certs_to_vault.sh
chmod 700 /usr/local/sbin/push_certs_to_vault.sh
Cronjob
Unbedingt den Hostname vault.domain.com anpassen!
crontab -e
0 2 * * * VAULT_ADDR=https://vault.domain.com /usr/local/sbin/update_cert.sh cert.mydomain.com
1 2 * * * VAULT_ADDR=https://vault.domain.com /usr/local/sbin/update_cert.sh mydomain.com
Systemd Unit / Timer
Service Unit /etc/systemd/system/push_cert@.service
[Unit]
Description=Upload Certbot files
Requires=network-online.target
After=network-online.target
[Service]
Type=oneshot
ExecStart=/usr/local/sbin/push_certs_to_vault.sh %i
Environment="VAULT_ADDR=https://vault.domain.com"
Service Timer /etc/systemd/system/push_cert@.timer
[Unit]
Description=Daily upload of certs
[Timer]
OnCalendar=00:00:00
Persistent=true
RandomizedDelaySec=30
[Install]
WantedBy=timers.target
Danach kann der Timer für jedes Zertifikat aktiviert werden:
systemctl daemon-reload
systemctl enable --now push_cert@my-domain.com.timer
systemctl enable --now push_cert@cert2.my-domain.com.timer
Anmerkung
Wieso das Script nicht als Certbot Hook eingebunden wird
Während des Cert Renewals muss die Vault verfügbar sein. Ist dies nicht gegeben, würde der Upload nicht funktionieren und auch nicht nachgeholt.
Deshalb habe ich mich dazu entschieden diese einfach täglich hochzuladen.
In Vault werden inerhalb der Secret Engine rotierte Inhalte gelöscht, sodass der tägliche Upload nicht problematisch ist und man Gefahr läuft die Vault vollzumüllen.
Wenn die Vault zusätzlich mit Basic Auth geschützt ist
Wie in der Anleitung zur Einrichtung beschrieben, muss hier nur die URL ergänzt werden.
Aus VAULT_ADDR=https://vault.domain.com
wird dann VAULT_ADDR=https://USERNAME:PASSWORD@vault.domain.com
.
Mailu
Subsections of Mailu
Mails über Relayhost versenden
Falsche Konfigurationen, oder die Wahl eines unpassenden Relayhosts können zu Problemen beim Versand, oder Empfang durch Dritte führen!
Vorwort
Um E-Mails über einen externen Server zu versenden, bietet Mailu native Umgebungsvariablen für den SMTP Docker-Container an.
Mit diesen kann festgelegt werden über welchen Server versendet werden soll und ob eine Authentifizierung erfolgen muss.
allgemeiner Versand via Relayhost
# File: mailu.env
# Adresse / IP des Relay Hosts
RELAYHOST=1.2.3.4
# user:password
RELAYUSER=user@my-domain.de:mein_passwort
Manche Hoster nutzen auch keine E-Mailadresse zum Authentifizieren
RELAYUSER=postfach1234:mein_passwort
In diesem Beispiel werden alle E-Mails die nicht lokal zugetellt werden können über den Relay-Host versendet!
bestimmte Ziele per Relay
In manchen Fällen möchte man nur bestimmte Domains über einen Relay versenden, weil ein Hoster E-Mails von der eigenen IP-Adresse nicht akzeptiert. In diesem Fall funktioniert die Anleitung oben nicht, sondern man muss etwas mehr konfigurieren.
Folgende Schritte sind notwendig
- Postfix Map mit Transport pro Domain und Relay Host konfigurieren
- Postfix zum nutzen der Map konfigurieren
- Authentifizierung hinterlegen (falls notwendig)
#File: postfix.cf
# Transport-Ziele in transport.map hinterlegen
transport_maps = hash:/etc/postfix/transport.map
# Passwort pro Relay-Server
smtp_sasl_password_maps = hash:/etc/postfix/sasl_passwd.map
# Authentifizierung aktivieren
smtp_sasl_auth_enable = yes
# deaktivieren von verschlüsselten Passwörtern, die Transport-Verschlüsselung ist aktiviert, Passwörter müssen oft im Klartext genutzt werden
smtp_sasl_security_options = noanonymous
# File: transport.map
t-online.de smtp:irgend.ein.smtp-server.com
test.de smtp:1.2.3.4
# File: sasl_passwd.map
irgend.ein.smtp-server.com myuser@smtp-xy.com:mein_p4ssword
1.2.3.4 postfachx:super_sicher!
Nach der Konfiguration
Im Anschluss muss der SMTP Container neugestartet werden, oder sogar neu erstellt werden, falls die Option mit Docker Variablen genutzt wurde:
# Fall: Docker Variablen, Neubau des Containers
docker-compose up -d
# Fall: postfix.cf und Maps
docker-compose restart smtp
Mailu individuell konfigurieren
Falsche Konfigurationen können zu Problemen beim Versand, oder Empfang durch Dritte führen!
Diese Anleitung wurde ursprünglich für Version 1.9 geschrieben und für 2.0 aktualisiert.
Ich möchte aber nicht ausschließen, dass ich etwas vergessen habe. Augen auf bei der Konfiguration!
Die Anleitung geht davon aus, dass Mailus Daten unter /srv/docker/mailu/data/ liegen.
Das ist kein muss, sollte nur bei Abweichungen berücksichtigt werden!
Vorwort
Mailu ist eine komplette E-Mailserver-Lösung basierend auf Docker. Hierbei werden alle nötigen Funktionen wie zum Beispiel SMTP (Postfix) , IMAP / POP3 (Dovecot), Filterregeln (Sieve), Antispam (Rspamd), Antivirus (ClamAV), Webmail (Roundcube), Fetchmail und auch ein modernes Verwaltungstool (Mailu Admin) bereitgestellt.
Alle Dienste laufen in eigenen Containern und kommunizieren über das Netzwerk miteinander. Weitere Details werden auf der Projektseite aufgezeigt.
Im Standardfall wird Mailu bereits durchaus gut und sinnvoll konfiguriert, womit die größten Anwendungsfälle bereits im Vorfeld abgedeckt sind. Allerdings ist der Wille nach nach individueller Konfiguration durchaus gerechtfertigt und darum soll es in diesem Artikel gehen.
Wie werden einzelne Dienste von Mailu konfiguriert, welche Möglichkeiten gibt es und was gibt es zu beachten.
Allgemeines zur Konfiguration
Die meisten Einstellungen die Mailu bereits unterstützt werden durch Umgebungsvariablen gesetzt und können hier eingesehen werden:
https://mailu.io/2.0/configuration.html
Für weitergehende Einstellungen die hierdurch nicht abgedeckt sind, bietet Mailu ein sehr gut nutzbares Verfahren von Überschreibungen an. Hierfür wird in den Docker-Containern ein Overrides Ordner angezogen. Je nach Containerart kann dies ggf. ein unterschiedlicher Pfad sein. Hierzu möchte ich im folgenden die einzelnen Container und Möglichkeiten der Konfiguration kurz und bündig beschreiben.
Grundsätzlich finden sich alle override Verzeichnisse unterhalb von data.
Liegt das Data Verzeichnis zum Beispiel unter /srv/docker/mailu/data/
, finden sich die override hier: /srv/docker/mailu/data/overrides
.
Die Container mit override Support sind im docker-compose.yml File mit einem Mountpoint versehen…
Konfiguration der Komponenten
Redis
Weil es sich um das Standard Alpine Docker Image von Redis handelt, kann eine individuelle Konfigurationsdatei als Startparameter angegeben werden. Der Pfad ist hierbei frei zu wählen und die Datei muss noch eingehängt werden.
Dovecot (IMAP / POP3)
Pfad: /srv/docker/mailu/data/overrides/dovecot/dovecot.conf
Über die Datei /overrides/dovecot.conf können Anpassungen vorgenommen werden.
In der Standard-Konfigurationsdatei /etc/dovecot/dovecot.conf
wird durch die unten stehende Zeile versucht die override Datei einzulesen. Existiert diese nicht, gibt es auch keinen Fehler.
!include_try /overrides/dovecot.conf
Postfix (SMTP)
Pfad: /srv/docker/mailu/data/overrides/postfix/
In Postfix können alle Einstellungen über den Ordner /overrides vorgenommen werden. Hierbei werden Einstellungen aus den Dateien /overrides/postfix.cf und /overrides/postfix.cf gelesen und mittels “postconf -e” gesetzt:
# File: /start.py
if os.path.exists("/overrides/postfix.cf"):
for line in open("/overrides/postfix.cf").read().strip().split("\n"):
os.system('postconf -e "{}"'.format(line))
if os.path.exists("/overrides/postfix.master"):
for line in open("/overrides/postfix.master").read().strip().split("\n"):
os.system('postconf -e "{}"'.format(line))
Zusätzlich werden im /overrides Ordner alle Maps mittels postmap kompiliert. Somit können Maps ebenfalls dort abgelegt werden, müssen lediglich die Endung .map im Dateinamen haben. Beim Start werden diese dann verarbeitet:
for map_file in glob.glob("/overrides/*.map"):
destination = os.path.join("/etc/postfix", os.path.basename(map_file))
shutil.copyfile(map_file, destination)
os.system("postmap {}".format(destination))
os.remove(destination)
Alle Maps bekommen nach der Bearbeitung eine .db-Endung, allerdings müssen die Dateien in der postfix.cf ohne die .db-Endung konfiguriert werden. Hier ein Beispiel:
# File: postfix.cf
transport_maps = hash:/etc/postfix/transport.map
smtp_sasl_password_maps = hash:/etc/postfix/sasl_passwd.map
smtp_sasl_auth_enable = yes
smtp_sasl_security_options = noanonymous
Rspamd
Pfad: /srv/docker/mailu/data/overrides/rspamd
Im override Ordner können Dateien abgelegt werden, welche zum Containerstart vom start.py Script nach /etc/rspamd/local.d
kopiert werden.
Clamav
Hier gibt es keine Überschreibungsoptionen, allerdings können die Dateien freshclam.conf und clamd.conf des Ordners /etc/clamav überschrieben (gemountet) werden. Hier die Schritte wie es zum Beispiel gemacht werden kann:
Datei laden und editieren
# Datei aus dem Container laden
mkdir -p /srv/mailu/override/antivirus/clamd.conf
docker cp mailu_antivirus_1:/etc/clamav/clamd.conf /srv/mailu/override/antivirus/
# Anpassungen vornehmen
vim clamd.conf
In der docker-compose.yml muss die Datei nun hinterlegt werden:
antivirus:
...
volumes:
- "/srv/mailu/data/filter:/data"
- "/srv/mailu/override/antivirus/clamd.conf:/etc/clamav/clamd.conf"
Ein Neuerstellen des Containers ist notwendig!
Webmail (Roundcube)
Pfad: /srv/docker/mailu/data/overrides/roundcube
Roundcube ist nicht verpflichtend, aber hat man diesen Webmailer gewählt, können Einstellungen vorgenommen werden, indem die folgenden Dateien kopiert, editiert und eingehangen (mounted) werden:
/php.ini
/var/www/html/config/config.inc.php
Nachwort zur Konfiguration
Wenn man sich dazu entscheidet, Konfigurationsdateien aus den Containern zu kopieren und im Anschluss zu überlagern (mounten), kann dies zu Fehlern, oder nachgelagerten Problemen führen, weil sich das Verhalten der Container ändert.
Anpassungen die im Upstream Image passieren, können dann in den eigenen Dateien nicht mehr enthalten sein. Dies betrifft vor allem wichtige Sicherheitseinstellungen. Deshalb hier ein möglicher Weg damit umzugehen, der bei jedem Update (Image-Update) durchgeführt werden sollte:
- Update der Container (docker-compose pull && docker-compose up -d)
- Kopieren der Dateien aus den Containern
- Anhängen neuer Einstellungen oder Überschreiben der Optionen
- Einhängen der neuen Dateien durch Neustart der Docker-Container
Docker Compose anpassen
Mit einer docker-compose.override.yml
können alle Werte des originalen Compose Files überschrieben / angepasst werden.
Dies ist dann sinnvoll, wenn man beispielsweise die Ports vom Front Container entfernen, Volumes an Container hängen möchte oder die IP-Adressen / Netze der Container statisch setzen.
Ein denkbares Beispiel könnte sein, dass der Front Container eine feste IP-Adresse erhalten soll, damit ein vorgelagerter Nginx eine feste IP-Adresse zum verbinden kennt.
Deshalb hier ein Beispiel Override File. Das Subnetz des default Netzes steht bereits im docker-compose.yml File (10.33.0.0/24), aber die anderen bridges (webmail, noinet sollen erweitert werden)
version: "3.4"
services:
front:
# remove uneeded port bindings
ports: !reset []
networks:
default:
ipv4_address: 10.33.0.253
# set static ip addresses / static bridge names
networks:
default:
driver_opts:
com.docker.network.bridge.name: br_mailu
webmail:
driver: bridge
ipam:
driver: default
config:
- subnet: 10.33.1.0/24
driver_opts:
com.docker.network.bridge.name: br_mailu_web
noinet:
driver: bridge
internal: true
ipam:
driver: default
config:
- subnet: 10.33.2.0/24
driver_opts:
com.docker.network.bridge.name: br_mailu_noinet
Beim Hochfahren werden die docker-compose.yml und docker-compose.override.yml zusammengeführt (merged).
Da Listenelemente wie die Ports nur ergänzt, aber nicht entfernt werden können, gibt es in neueren Compose versionen die !reset
Funktion. Nähere Informationen finden sich hier: https://docs.docker.com/compose/compose-file/13-merge/#reset-value
Links
Setup-Tool zum genrieren eines Docker-Compose Files für Mailu https://setup.mailu.io/2.0/setup/
Dokumentation von Mailu https://mailu.io/2.0/
Github Projekt https://github.com/Mailu/Mailu
Minio
Subsections of Minio
Minio Single Node Installation
Vorwort
Die hier gezeigte Anleitung ist rudimentär und kann nicht den kompletten Funktionsumfang von Minio abdecken.
Ich werde nach und nach weitere Beispiele hinzufügen, gerade im Hinblick auf Policies für Backups, oder Bereitstellung statischer Dateien / Webseiten.
Docker / Compose solle bereits installiert sein!
Zusätzlich wird in dieser Anleitung der Traffic durch einen Nginx geleitet, dies ist aber kein muss.
Wird Minio direkt erreichbar gemacht werden (ohne Proxy davor), sollte die Transport-Verschlüsselung von Minio bereitgestellt werden. Hierfür muss Minio Zertifikate mitgeteilt bekommen (nicht Bestandteil der Anleitung)
Docker Compose
Der Pfad /mnt/data/minio/data
muss noch den lokalen Gegebenheiten angepasst werden!
version: "3"
services:
minio:
user: "1000:1000"
image: quay.io/minio/minio
container_name: minio
environment:
MINIO_ROOT_USER_FILE: "/run/secrets/minio_root_user"
MINIO_ROOT_PASSWORD_FILE: "/run/secrets/minio_root_password"
volumes:
- /mnt/data/minio/data:/data
restart: unless-stopped
command: "server /data --console-address :9001"
secrets:
- minio_root_user
- minio_root_password
networks:
default:
ipv4_address: 172.55.1.2
# optionales Port Forwarding, FALLS kein Firewall Forwarding konfiguriert werden soll
ports:
- 9001:9001
- 9000:9000
networks:
default:
driver: bridge
ipam:
driver: default
config:
- subnet: 172.55.1.0/24
driver_opts:
com.docker.network.bridge.name: br_minio
secrets:
minio_root_user:
file: ./secrets/minio_root_user
minio_root_password:
file: ./secrets/minio_root_password
Nun werden die Secrets in Dateien abgelegt, das Passwort sollte angepasst werden und sicher sein!
USERNAME=minio
PASSWORD=$(pwgen -s 32 1)
mkdir -p secrets
cat - > secrets/minio_root_user <<EOF
${USERNAME}
EOF
cat - > secrets/minio_root_password <<EOF
${PASSWORD}
EOF
Nginx Konfiguration
Die offizielle Dokumentation ist an manchen stellen etwas wild und nach verschiedenen Loginproblemen habe ich für mich entschieden, die Konfiguration etwas abweichend vom Standard vorzunehmen.
Die Dokumentation schlägt vor, eine pfadbasierte Unterscheidung zwischen S3 und Minio Admin Panel zu konfigurieren.
Dies hat bei mir und vielen anderen Benutzern auch nicht geklappt und sorgte für Probleme, weshalb ich 2 vhosts in nginx konfiguriere:
Die unten stehenden Snippets können nicht ohne Anpassung vorgenommen werden!
Es fehlen Einstellungen wie listen, SSL Zertifikat, Logs und andere sicherheitsrelevante Einstellungen.
Einfaches kopieren wäre copy&waste…
S3 vhost
server {
server_name s3.domain.com
#######################
# listen, ssl, logs, etc pp
#######################
# Allow special characters in headers
ignore_invalid_headers off;
# Allow any size file to be uploaded.
# Set to a value such as 1000m; to restrict file size to a specific value
client_max_body_size 0;
# Disable buffering
proxy_buffering off;
proxy_request_buffering off;
# bestimmte IPs zulassen
# allow 192.168.201.1;
# deny all;
location / {
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_connect_timeout 300;
# Default is HTTP/1, keepalive is only enabled in HTTP/1.1
proxy_http_version 1.1;
proxy_set_header Connection "";
chunked_transfer_encoding off;
proxy_pass http://172.55.1.2:9000; # This uses the upstream directive definition to load balance
}
}
Minio Admin Panel
server {
server_name minio.domain.com
#######################
# listen, ssl, logs, etc pp
#######################
location / {
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-NginX-Proxy true;
# This is necessary to pass the correct IP to be hashed
real_ip_header X-Real-IP;
proxy_connect_timeout 300;
# To support websocket
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
chunked_transfer_encoding off;
proxy_pass http://172.55.1.2:9001; # This uses the upstream directive definition to load balance
}
}
Nach einem Nginx Reload / Restart kann Minio aufgerufen werden:
- https://minio.domaion.com
- http://ip-des-servers:9001
Policies
Vollzugriff auf Bucket-Namen wie Username
Hier kann der Benutzer nur auf Buckets zugreifen die wie sein Username lautet ${aws:username}
.
Dieses Muster kann grundsätzlich auf verschiedene Weise genutzt werden, ist hier aber stark vereinfacht dargestellt:
Append Only (Restic)
Diese Regeln sorgen dafür, dass Restic (Backup Software) nur Daten anhängen und lesen darf.
Der Bucket muss dem Usernamen entsprechen:
Nextcloud
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
Unifi
Subsections of Unifi
LinuxServer.io Migration von unifi-controller zu unifi-network-application
Bevor etwas umgestellt wird, solltest du unbedingt ein Backup anlegen.
Dieses sollte über das Unifi Webinterface erstellt und heruntergeladen werden, weil es zum Ende benötigt wird!
Vorwort
Linuxserver.io hat den Support für das Image linuxserver/unifi-controller eingestellt, wie auf der offiziellen Webseite gezeigt wird: https://docs.linuxserver.io/images/docker-unifi-controller/
Deswegen sollten bestehende Installationen auf das neue Image migriert werden. Weil dieses aber keine MongoDB eingebaut hat, muss ein externer Datenbank-Server erstellt werden.
Die Anleitung besteht aus folgenden Schritten:
- Backup über das Webinterface erstellen
- MongoDB Container erstellen
- alten Unifi Container stoppen
- neuen Unifi Container erstellen
- Backup über Webinterace einspielen
Netzwerk
In dieser Anleitung wird ein eigenes Netzwerk für die MongoDB und den Unifi Controller erstellt.
Durch die festen IP-Adressen kann ein davorgestellter nginx und andere Dienste einfacher konfiguriert werden.
Container | IP-Adresse |
---|---|
MongoDB | 172.25.0.2 |
Unifi Controller | 172.25.0.3 |
Die Ports der Anwendungen können entweder in der docker-compose.yml freigegeben werden, oder deine Firewall übernimmt dies, je nach persönlicher Präferenz.
Werden die Port-Freigaben von Docker nicht benötigt, kannst du diese in den docker-compose.yml Dateien einfach entfernen.
Backup erstellen & herunterladen
Das Erstellen des Backups kann je nach Datenmenge etwas dauern
- Einloggen
- Einstellung
- Backup
- “Backup / Wiederherstellen”
- Backup herunterladen (& den gewünschten Zeitraum der migriert werden soll wählen)
- Datei herunterladen
Konfiguration
Beim Unifi-Controller und Init Skript muss noch das Kennwort gesetzt werden, also das “xxxxx” ersetzen!
Docker-Compose
version: "3"
services:
unifi-db:
# aktuell ist offiziell Mongo <= 4.4 supported!
image: docker.io/mongo:4.4
container_name: unifi-db
volumes:
- /srv/docker/containers/unifi-db/db:/data/db
- /srv/docker/containers/unifi-db/init-mongo.js:/docker-entrypoint-initdb.d/init-mongo.js:ro
restart: unless-stopped
networks:
default:
ipv4_address: 172.25.1.2
ports:
- 27017:27017
unifi-controller:
image: lscr.io/linuxserver/unifi-network-application:latest
container_name: unifi-controller
environment:
PUID: 1000
PGID: 1000
MONGO_USER: "unifi"
MONGO_PASS: "xxxxxx"
MONGO_HOST: "172.25.1.2"
MONGO_PORT: "27017"
MONGO_DBNAME: "unifi"
MEM_LIMIT: "1024"
MEM_STARTUP: "1024"
volumes:
- /srv/docker/containers/unifi-controller/config/:/config
restart: unless-stopped
networks:
default:
ipv4_address: 172.25.1.3
ports:
- 8443:8443
- 3478:3478/udp
- 10001:10001/udp
- 8080:8080
- 1900:1900/udp #optional
- 8843:8843 #optional
- 8880:8880 #optional
- 6789:6789 #optional
- 5514:5514/udp #optional
networks:
default:
driver: bridge
ipam:
driver: default
config:
- subnet: 172.25.1.0/24
driver_opts:
com.docker.network.bridge.name: br_unifi
MongoDB Init Skript
Erst legen wir den Container Ordner an, damit im Anschluss das Init-Skript angelegt werden kann.
Das Skript legt beim ersten Start die Logindaten des Unifi Controllers an.
PW="xxxxx"
mkdir -p /srv/docker/containers/unifi-db/
cat - > /srv/docker/containers/unifi-db/init-mongo.js <<EOF
db.getSiblingDB("unifi").createUser({user: "unifi", pwd: "${PW}", roles: [{role: "dbOwner", db: "unifi"}]});
db.getSiblingDB("unifi_stat").createUser({user: "unifi", pwd: "${PW}", roles: [{role: "dbOwner", db: "unifi_stat"}]});
EOF
Docker stop / start
Nun muss der alte Unifi Controller gestoppt werden, damit die Ports wieder freigegeben werden.
Danach kann die neue Umgebung hochgefahren werden.
docker stop <unifi-controller>
docker-compose up -d
optional: Reverse Proxy
Sollte ein reverse Proxy wie (nginx, Traefik, …) vor dem Controller stehen, muss das Backend angepasst werden auf 172.25.0.3:8443.
Abweichungen sind allerdings je nach deiner Umgebung durchaus möglich…
Backup einspielen
Nach kurzer Zeit sollte das Webinterface wieder verfügbar sein. Entweder über den Port 8443, oder den Reverse Proxy mit gewohnter URL. Das Portal bietet das Hochladen und Einspielen des Backups an. Hier muss die *.unf Datei, die zu Beginn heruntergeladen wurde, gewählt werden.
Je nach Größe des Backups, kann dies mehrere Minuten dauern.
Die Webseite lädt automatisch neu, wenn die Wiederherstellung durch ist.
Links
LinuxServer.IO - Unifi Controller Image
https://docs.linuxserver.io/images/docker-unifi-network-application/#docker-compose-recommended-click-here-for-more-info