Ein paar irrelevante Tutorials

Paperless fremdgehostet

Einleitung

Was tun, wenn ich keine Server zuhause betreiben aber trotzdem Paperless-NGX nutzen möchte? Nun, es gibt Anbieter, die Paperless NGX hosten und das als Dienstleistung anbieten. Ich möchte aber eine Paperless-NGX-Installation bei einem Hoster aufsetzen und sie ansonsten selbst betreiben. Dabei sind folgende Merkmale gewünscht:

  • Hohe Autonomie bei Konfiguration und Nutzung
  • Hohe Sicherheit
    • Schutz gegenüber unbefugten Zugriffen und Manipulationen
    • Schutz vor Datenverlust
  • Geringe Kosten

Folgende Funktionalitäten sind gewünscht:

  • Nutzung über eine gesicherte Weboberfläche
  • Scannen direkt in Paperless-NGX
  • Ablage von Dokumenten auch über ein dafür vorgesehenes Verzeichnis 

Lösungsdesign

Die Lösung besteht aus einem Server, der bei einem Hoster angemietet wird. Auf diesem läuft zunächst Wireguard, um eine VPN-Verbindung zu Clients im lokalen Netzwerk aufzubauen. Das wird ausschließlich für die Administration des Systems benötigt. Normale Userzugriffe auf Paperless-NGX werden später über die öffentlich zugängliche Domain stattfinden. Dann läuft auf dem Server unter Docker der Nginx Proxy Manager (NPM), Paperless-NGX und die Datensicherungslösung Duplicati. Der NPM und Paperless-NGX laufen im selben Docker-Netzwerk. Das ist ein Sicherheitsdetail, auf das ich später noch eingehen werde. Zum Schutz vor unbefugten Zugriffen und Manipulationen kommen restriktive Firewalleinstellungen zum Einsatz und mit fail2ban wird ein Schutz vor Brute Force Attacken realisiert. Die Datensicherung wird später auf einen Cloud-Speicher durchgeführt; natürlich verschlüsselt.

Zutaten

Man nehme:

  • Einen günstigen virtuellen Server bei einem Hoster
  • Eine (Sub)-Domain, die aus dem Internet erreichbar ist
  • Günstigen Cloudspeicher, der per SFTP angesprochen werden kann

Hosting

Ich werde hier keine Empfehlungen für oder gegen das eine oder andere Hosting-Angebot aussprechen. Die  Angebote der verschiedenen Hoster unterscheiden sich hinsichtlich der Speichermengen, der Systemressourcen, der Bandbreite, mit der die Server ans Internet angebunden sind und auch hinsichtlich der jeweils angebotenen Services (mit oder ohne Firewall vor dem Server, mit oder ohne Snapshots, Nutzung eigener Bootmedien oder ausschließlich vom Anbieter bereit gestellter Medien...). Hier muss man sich überlegen, was man braucht und möchte. Meine Paperless-NGX-Installationen kommen in der Regel mit 2 Prozessorkernen und 4 GB RAM aus. Beim Massenspeicher muss man überlegen, wie viel man vielleicht braucht. Wenn man davon ausgeht, am Anfang nicht sehr viel zu brauchen, dann mögen vielleicht 32 GB Massenspeicher ausreichen. Wenn der aber irgendwann knapp wird, dann ist ein Umzug auf einen besser ausgestatteten Server zumindest nicht unaufwändig. Für den Anfang sollte aber eine Kapazität von etwa 80 GB reichen.

Für diese recht geringen Anforderungen werden von den meisten Hostern (z.B. Hetzner, Ionos, Netcup oder Strato) Produkte in der Preisklasse um die 4 - 5 € pro Monat angeboten. Für die Datensicherung empfehle ich einen Cloudspeicher bei einem anderen Anbieter, als dem bei dem der Server gemietet wird. Da die Sicherungen verschlüsselt übertragen und gespeichert werden, ist das aus Sicherheits- und Datenschutzgesichtspunkten her unbedenklich. Solche Speicherlösungen werden für 3 - 5 € im Monat angeboten (z.B. die Hetzner Storage-Box für 3,20 € für 1 TB oder das Strato HiDrive Start für 3,5 € für 500 GB). Somit summieren sich die monatlichen laufenden Kosten für die vorgesehene Lösung auf 7 - 10 € im Monat. 

Absicherung des Servers

Die Basisinstallation des Servers beschreibe ich hier nicht, weil sich das zum Einen von Hoster zu Hoster geringfügig unterscheidet und zum anderen recht selbst erklärend ist. Die folgenden Schritte basieren auf einem Ubuntu Linux Server oder einem Debian, jeweils in aktueller Long Term Service Version. Ich beschreibe hier die Absicherung des Servers. Zur Basisabsicherung des Servers s. hier: https://tutorials.kernke.koeln/sicherheit/server-absichern.html. Wer seinem Provider seine Daten nicht anvertrauen möchte, kann zusätzlich eine verschlüsselte Partition einrichten. Darauf verzichte ich hier, weil ich Angebote von zertifizierten Providern in Deutschland nutze. Wenn man Bootmedien bzw. entsprechende Templates von Providern für das Betriebssystem des Server nutzt dann, wird bei der Basisinstallation oftmals nur ein Root-User angelegt und kein normaler User ohne Administrationsprovilegien. Die folgenden Anweisungen gehen jeweils davon aus, dass ein normaler User genutzt wird. Wenn der noch nicht existiert, muss er zunächst angelegt werden. Das macht man mit dem Befehl:

adduser --shell /bin/bash {Benutzername}

In den darauf folgenden Dialogen wird man aufgefordert, ein Passwort für den User zu vergeben. Ggf. muss man noch das Tool sudo installieren, mit dem man dem normalen User das Recht einräumen kann, bedarfsweise für einzelne Anweisungen Root-Rechte zu nutzen

apt install sudo

und den Benutzer der Gruppe sudo hinzufügen, damit er das auch darf

usermod -aG sudo {Benutzername}

Danach kann man sich ab- und mit dem neuen Useraccount wieder anmelden.

Installation fail2ban

Zunächst installiere ich fail2ban und schütze das System auf diese Weise vor Brute Force Attacken gegen die SSH-Schnittstelle. Die Installation geht einfach mit dem Befehl

sudo apt install fail2ban

Damit ist bereits die Absicherung der SSH-Schnittstelle standardmäßig aktiv. Hier muss zunächst nichts weiter gemacht werden. Später werde ich fail2ban noch auf die Anmeldung an der Weboberfläche von Paperless-NGX ansetzen.

Konfiguration der Firewall(s)

Wenn der Hoster eine Firewall anbietet, die bereits zwischen dem Server und dem Internet arbeitet, dann sollte diese auf jeden Fall genutzt werden. Alles, was abgefangen wird, bevor es überhaupt den Server erreicht, belastet natürlich nicht die Systemressourcen unseres Servers. Aber auch wenn es eine solche Firewall des Hosters gibt, sollte noch eine Firewall auf dem Server eingesetzt werden. IT-Sicherheit basiert immer auf dem Prinzip der möglichst vielen Schichten. Ich verwende die ufw, die uncomplicated firewall. Soweit sie noch nicht auf dem Server installiert ist, hole ich das mit 

sudo apt install ufw 

nach. Dann schließe ich zunächst alle eingehenden Verbindungen aus

sudo ufw default deny incoming

 und erlaube dann die Verbindungen, die ich unbedingt brauche

sudo ufw allow 51820
sudo ufw allow 22
sudo ufw allow 80
sudo ufw allow 443
sudo ufw allow from 10.11.0.0/24 to any port 22 proto tcp
sudo ufw allow from 10.11.0.0/24 to any port 81 proto tcp
sudo ufw allow from 10.11.0.0/24 to any port 8200 proto tcp
sudo ufw allow from 10.11.0.0/24 to any port 445 proto tcp

Hier nehme ich bereits die Adressen des VPN vorweg. Dieses werde ich für den Adressbereich 10.11.0.1 bis 10.11.0.255 einrichten. Um das VPN überhaupt etablieren zu können, wird ein offener Port 51820 benötigt. Zunächst brauche ich auch einen offenen Port 22. Später soll nur aus dem VPN heraus soll auf die SSH-Shell (Port 22) zugegriffen werden können. Dann schließe ich die allgemeine Freigabe von Port 22 wieder. Ebenso soll auch auf die Weboberfläche des NPM (Port 81) und die Weboberfläche von Duplicati (Port 8200) nur aus dem VPN heraus zugegriffen werden können. Außerdem soll das Consume-Verzeichnis von Paperless NGX später per Samba für das VPN freigegeben werden. Dafür wird Port 445 benötigt. Diese Einstellungen aktiviere ich mit

sudo ufw enable

und nach einer Sicherheitsabfage sind die Firewalleinstellungen aktiv. Wenn ich mich jetzt nicht ausgesperrt habe, dann bleibt die Verbindung bestehen und ich kann weiter arbeiten.

Installation und Konfiguration der Software

Installation und Konfiguration Wireguard

Wireguard ist eine leichtgewichtige und effiziente VPN-Lösung. Ein VPN, also ein virtuelles privates Netzwerk stellt eine Möglichkeit dar, innerhalb eines öffentlichen Netzwerks oder sogar über Netzwerkgrenzen hinweg mittels Verschlüsselungstechnologie ein abgeschottetes Netzwerk zu etablieren. Das ist ein starkes Sicherheitsfeature, um Zugriffe auf sensible Dienste und Daten abzusichern. Zunächst installiere ich wireguard mit

sudo apt install wireguard

Dann wechsele ich in den Root-Modus, weil ich in den geschützten Ordner /etc/wireguard wechseln und außerdem mehrere Befehle mit Adminrechten ausführen muss.

sudo su

und wechsele in den Ordner /etc/wireguard

cd /etc/wireguard

Zunächst erstelle ich ein Schlüsselpaar für die Nutzung von Wireguard

umask 077; wg genkey | tee privatekey | wg pubkey > publickey

Die Schlüssel lasse ich mir anzeigen, um sie über die Zwischenablage wegzusichern:


cat privatekey
cat publickey

Dann schränke ich die Zugriffsrechte auf den privaten Schlüssel auf den Besitzer der Datei ein, sonst arbeitet Wireguard nicht damit:

chmod 600 privatekey

Nun erstelle ich eine Datei zur Konfiguration des VPN-Tunnels. Ich nenne sie wg1011.conf, weil ich das Subnetz 10.11.0.0/24 nutzen möchte.

nano wg1011.conf

mit folgendem Inhalt:

[Interface]
Address = 10.11.0.1/32
SaveConfig = true
PostUp = iptables -A FORWARD -i wg1011 -j ACCEPT; iptables -t nat -A POSTROUTING -o ens18 -j MASQUERADE
PostDown = iptables -D FORWARD -i wg1011 -j ACCEPT; iptables -t nat -D POSTROUTING -o ens18 -j MASQUERADE
ListenPort = 51820
PrivateKey = Private Key des Servers

Der Server wird hier als VPN-Router fungieren und erhält die VPN-Adresse 10.11.0.1. Die Parameter PostUp und PostDown definieren, was beim Start des VPN-Tunnels und bei dessen Beendigung passieren soll. Es wird ein internes Nating, also ein virtuelles Netzwerk generiert. Hier muss die richtige Bezeichnung der Netzwerkschnittstelle eingetragen werden. Bei meinem Beispiel ist es "ens18". Die individuell richtige kann man mit dem Befehl

ip a

ermitteln. Es ist in der Regel die zweite Schnittstelle nach "lo", der Loopback-Schnittstelle. Den ListenPort kann man ändern, wenn man das möchte. Hier nehme ich 51820, was Standard ist für Wireguard. Dass ich unter PrivateKey den soeben kopierten privaten Schlüssel eintrage, ist klar. Nun kann ich mit

wg-quick up wg1011

den Tunnel starten und mit

systemctl enable wg-quick@wg1011

dafür sorgen, dass er bei künftigen Neustarts des Servers direkt mit gestartet wird.

Zur Einrichtung des Wireguard-Tunnels auf dem Client installiere ich auch dort, wenn nicht bereits geschehen Wireguard mit

sudo apt install wireguard

Ich wechsele in den Root-Modus mit

sudo su

und in den Wireguard-Ordner mit

cd /etc/wireguard

Dann erstelle ich ein Schlüsselpaar mit

umask 077; wg genkey | tee privatekey | wg pubkey > publickey
cat privatekey
cat publickey

dann erstelle ich die Konfigurationsdatei für den VPN-Tunnel mit

sudo nano /etc/wireguard/wg1011.conf

mit folgendem Inhalt:

[Interface] 
PrivateKey = {privater Schlüssel des Clients}
Address = 10.11.0.36/32

[Peer]
PublicKey = {öffentlicher Schlüssel des Servers}
Endpoint = {IP-Adresse des Servers}:51820
AllowedIPs = 10.11.0.0/24
PersistentKeepalive = 25

Den neue Tunnel aktiviere ich mit

wg-quick up wg1011

und mache ihn bootfest mit

systemctl enable wg-quick@wg1011

Danach verlasse ich den Root-Modus wieder mit

exit

Nun muss ich auf dem Server noch den Client in das VPN aufnehmen. Ich melde mich also remote auf dem Server an und setze den Befehl

sudo wg set wg1011 peer {öffentlicher Schlüssel des Clients} allowed-ips 10.11.0.36/32

ab. Damit das auch wirksam wird, muss ich den VPN-Tunnel auf dem Server zunächst beenden und wieder starten mit

sudo wg-quick down wg1011 && sudo wg-quick up wg1011

Das teste ich mit

ping 10.11.0.36

auf dem Server und/oder mit

ping 10.11.0.1

auf dem Client. Beides sollte funktionieren, wenn ich keinen Fehler gemacht habe. Wenn das nicht klappt, muss ich die Konfigurationsdateien prüfen. Ein häufiger Fehler besteht darin, privaten und öffentlichen Schlüssel zu verwechseln. Wenn alles klappt, dann kann ich den Port 22 für die große weite Welt schließen, so dass er künftig nur noch aus dem VPN heraus genutzt werden kann. Ich schaue mir die aktuellen Firewalleinstellungen mit 

sudo ufw status numbered 

an und identifiziere die Freigabe für Port 22 für alle. Das ist in meinem Fall die Nummer 2. Ich lösche sie mit

sudo ufw delete 2

und bestätige die Sicherheitsabfrage. Um das wirksam zu machen, muss ich den Befehl

sudo ufw enable

absetzen, weil ein Reload nicht möglich ist. Jetzt fliege ich vermutlich aus der SSH-Session heraus, weil sie über den weltweit offenen Port 22 aufgebaut wurde. Mit

ssh -l andreas 10.11.0.1

kann ich mich aber sofort wieder anmelden. 

Installation Docker

Da die mit den gängigen Linux-Distributionen mitgelieferten Docker-Implementierungen nicht immer ganz kompatibel mit den auf Github bereitgestellten Containern ist, installiere ich Docker und Docker Compose aus den Originalquellen. Dazu habe ich mir ein kleines Script geschrieben:

# Shell-Skript zur Installation von Docker (mit sudo ausführen:

#!/bin/bash

for pkg in docker.io docker-doc docker-compose docker-compose-v2 podman-docker containerd runc; do

apt-get remove $pkg;

done

apt-get update

apt-get install ca-certificates curl

install -m 0755 -d /etc/apt/keyrings

curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc

chmod a+r /etc/apt/keyrings/docker.asc

echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu \
$(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
tee /etc/apt/sources.list.d/docker.list > /dev/null

apt-get update

apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

systemctl start docker

systemctl enable docker

usermod -aG docker "$SUDO_USER"

Wichtig: Wenn man keinen Ubuntu-Server nutzt, sondern ein Debian, dann muss man in dem Script die Zeichenkette ubuntu gegen debian austauschen. Dieses Script muss zunächst gespeichert werden, z.B. unter dem Namen installdocker.sh. Dann muss es ausführbar gemacht werden mit

chmod +x installdocker.sh

Danach muss es mit Rootrechten ausgeführt werden:

sudo ./installdocker.sh

Das Script deinstalliert zunächst ggf. vorhandene Bibliotheken, die Probleme machen könnten und installiert dann die benötigten Pakete. Außerdem fügt es den aufrufenden Benutzer (was trotz "sudo" nicht root ist) der Gruppe docker hinzu, damit ich nachher ohne sudo auch docker-Befehle ausführen kann. Nun muss man sich einmal kurz ab- und wieder anmelden, damit die neue Dockerberechtigung auch greift. Ob das alles funktioniert hat prüfe ich mit dem Befehl:

docker ps

Damit würden laufende Docker-Container aufgelistet. Hier laufen zwar noch keine, aber wenn ich hierauf keine Fehlermeldung erhalte, sondern die Überschriften einer leeren Tabelle, dann ist alles richtig.

Installation Nginx Proxy Manager

Der NPM wird ganz einfach mit docker und docker-compose installiert. Ich erstelle zunächst einen Ordner npm und wechsele in selbigen

mkdir npm

cd npm


dann erstelle ich darin erst eine Konfigurationsdatei namens config.json

nano config.json

mit folgendem Inhalt:

{
"database": {
"engine": "mysql",
"host": "db",
"name": "npm",
"user": "npm",
"password": "npm",
"port": 3306
}
}

und eine Datei docker-compose.yml

nano docker-compose.yml

mit folgendem Inhalt:

services:
app:
image: jc21/nginx-proxy-manager:latest
restart: always
ports:
- 80:80
- 10.11.0.1:81:81
- 443:443
volumes:
- ./config.json:/app/config/production.json
- ./data:/data
- ./letsencrypt:/etc/letsencrypt
networks:
- web_proxy
depends_on:
- db
environment:
# if you want pretty colors in your docker logs:
- FORCE_COLOR=1
db:
image: mariadb:latest
restart: always
environment:
MYSQL_ROOT_PASSWORD: "npm"
MYSQL_DATABASE: "npm"
MYSQL_USER: "npm"
MYSQL_PASSWORD: "npm"
volumes:
- ./data/mysql:/var/lib/mysql
networks:
- web_proxy
networks:
web_proxy:
external: true

Hier sind folgende Besonderheiten zu beachten: Port 81, der für die Weboberfläche des NPM genutzt wird, wird an die IP-Adresse aus dem VPN gebunden. Außerdem werden die Services des NPM in das Netzwerk web_proxy verlegt. In diesem Netzwerk soll später auch der Paperless-Webserver, also die Weboberfläche von Paperless-NGX laufen. So kann dann der Webserver im NPM mit dem Namen des Containers (also webserver) angesprochen werden. Dieses Docker-Netzwerk muss ich mit dem (naheliegenden) Befehl

docker network create web_proxy

erstellen. Dann kann ich den NPM starten mit

docker compose up -d

Wer Docker nicht mit dem oben aufgeführten Script oder sonst wie aus den Originalquellen installiert hat, sondern zum Beispiel aus den Repositories der Linux-Distribution, muss ggf. den Befehl docker-compose (also mit Bindestrich) verwenden. Nun sollte der NPM laufen. Das kann man sich mit 

docker ps

ansehen. Hier sollten nun die Container "npm-app-1" und "npm-db-1" mit dem Status "up" zu sehen sein. Dann hat alles geklappt.

Installation und Konfiguration Paperless-NGX

Auch Paperless-NGX wird mit docker und docker-compose aufgesetzt. Zunächst wechsele ich aus dem Verzeichnis npm wieder zurück in mein Homeshare

cd ..

und erstelle ein Verzeichnis paperless-ngx, in das ich dann auch wechsele

mkdir paperless-ngx
cd paperless-ngx

Darin erstelle ich zunächst eine Datei docker-compose.env

nano docker-compose.env

mit folgendem Inhalt:

PAPERLESS_URL=https://paperless.domain.de
PAPERLESS_TIME_ZONE=Europe/Berlin
PAPERLESS_OCR_LANGUAGE=deu+eng
PAPERLESS_SECRET_KEY='V<_dddxkOW[.l,VF{:%cQAT|$uAIZY)n;MWH/LU?MJ_hBV7HGb%7KhY)+dSethRvWSV'

Dabei ist beim URL idie Domain einzutragen, unter der Paperless-NGX später erreichbar sein soll und für die ich auf meinen Server einen A-Record eingetragen habe. Für den Secret-Key kann eine beliebige, aber möglichst lange Zeichenkette genutzt werden. Die anderen Parameter sind wohl selbst erklärend. Nun brauche ich natürlich noch eine Datei docker-compose.yml

nano docker-compose.yml

mit folgendem Inhalt:

# Docker Compose file for running paperless from the docker container registry.
# This file contains everything paperless needs to run.
# For more extensive installation and update instructions, refer to the
# documentation.

services:
broker:
image: docker.io/library/redis:8
restart: unless-stopped
networks:
- web_proxy
volumes:
- redisdata:/data
db:
image: docker.io/library/postgres:17
restart: unless-stopped
volumes:
- pgdata:/var/lib/postgresql/data
networks:
- web_proxy
environment:
POSTGRES_DB: paperless
POSTGRES_USER: paperless
POSTGRES_PASSWORD: paperless
webserver:
image: ghcr.io/paperless-ngx/paperless-ngx:latest
restart: unless-stopped
depends_on:
- db
- broker
- gotenberg
- tika
networks:
- web_proxy
volumes:
- /home/andreas/paperless-ngx/data:/usr/src/paperless/data
- /home/andreas/paperless-ngx/media:/usr/src/paperless/media
- /home/andreas/paperless-ngx/export:/usr/src/paperless/export
- /home/andreas/paperless-ngx/consume:/usr/src/paperless/consume
env_file: docker-compose.env
environment:
PAPERLESS_REDIS: redis://broker:6379
PAPERLESS_DBHOST: db
PAPERLESS_TIKA_ENABLED: 1
PAPERLESS_TIKA_GOTENBERG_ENDPOINT: http://gotenberg:3000
PAPERLESS_TIKA_ENDPOINT: http://tika:9998
TRUSTED_PROXIES: "172.18.0.0/16" # das Docker-Subnetz, aus dem NPM kommt
USE_X_FORWARDED_FOR: True
PAPERLESS_ALLOWED_HOSTS: '*'
PAPERLESS_LOG_IP_ADDRESS_HEADER: X-Real-IP
PAPERLESS_LOGLEVEL: DEBUG

gotenberg:
image: docker.io/gotenberg/gotenberg:8.20
restart: unless-stopped
networks:
- web_proxy
command:
- "gotenberg"
- "--chromium-disable-javascript=true"
- "--chromium-allow-list=file:///tmp/.*"
tika:
image: docker.io/apache/tika:latest
restart: unless-stopped
networks:
- web_proxy
volumes:
pgdata:
redisdata:

networks:
web_proxy:
external: true

Hier ist jetzt einiges erläuterungsbedürftig. Mit der Angabe networks: - web_proxy sorge ich dafür, dass die Container im selben Docker-Netzwerk laufen, wie der NPM. Die gemounteten Volumes beginnen hier immer mit "/home/andreas/paperless-ngx". Ein symbolischer Pfad auf das Home-Share ("~/) würde im Docker-Container nicht aufgelöst werden können. Wenn der Nutzer, in dessen Kontext das Ganze läuft, nicht "andreas" heißt, dann muss das natürlich angepasst werden. Die Angaben zu Trusted Proxy, der Parameter USE_X_FORWARDED_FOR: True, PAPERLESS_ALLOWED_HOSTS: '*' und PAPERLESS_LOG_IP_ADDRESS_HEADER: X-Real-IP sind erforderlich, damit der Reverseproxy später die IP-Adressen, von denen aus auf das System zuzugreifen versucht wird, übermittelt und Paperless-NGX dem NPM auch vertraut. Das wird benötigt, um fail2ban auf diese Adressen ansetzen zu können. Die restlichen Parameter sind Standard. Diese Konfiguration kann dann ebenfalls mit

docker compose up -d

gestartet werden. Wenn keine Fehler aufgetreten sind, dann starten nun die benötigten Container. Das dauert ein wenig. Mit 

docker ps

kann man sich den aktuellen Status ansehen und mit

docker logs paperless-ngx-webserver-1

kann man sich die Logs des Webservers ansehen. Sollte etwas nicht korrekt sein, dann würden hier Fehlermeldungen oder zumindest Warnungen auftreten. 

Einrichtung des Proxy-Hosts

Nun kann ich die Web-GUI des Reverse Proxys mit dem Webbrowser aufrufen. Sie ist unter der Adresse http://10.11.0.1:81 (nicht https) erreichbar. Die Warnung des Browsers, dass es sich um eine ungesicherte Verbindung handelt, kann ich ignorieren, da ich ja einen VPN-Tunnel nutze. Nach der frischen Installation lauten die Zugangsdaten 

Email address: admin@example.com
Password: changeme

und ich werde natürlich als erstes aufgefordert, das zu ändern. Wichtig ist, hierbei eine echte eMail-Adresse einzugeben, sonst kann man kein Let's Encrypt-Zertifikat beziehen. Als erstes lege ich danach einen neuen Proxy-Host an. 

Hier gebe ich die Domain ein, unter der der Dienst erreichbar sein wird. Diese Domain muss - anders als die hier dargestellte - vom Internet aus erreichbar sein und es muss ein A-Record eingestellt sein, der auf die IP-Adresse des Reverse-Proxy also des Servers zeigt. Das muss natürlich auch eine im Internet erreichbare IP-Adresse sein. Als Scheme ist http auszuwählen. Der Container kann nicht direkt mit https angesprochen werden. Als Forward Hostname ist der interne Name einzutragen, den der Container in der docker-compose.yml zugewiesen bekommen hat. Das ist nicht der Name, der mit docker ps angezeigt wird (also paperless-ngx-webserver-1) sondern hier schlicht "webserver". Da wir nichts anderes eingetragen haben, lauscht der Webserver auf Port 8000. Dann hake ich nur noch an "Cache Assets".

Nun wähle ich die Registerkarte SSL aus und hake die Optionen "Force SSL" und "HSTS Enabled" an, um den Browser zur ausschließlichen Nutzung von HTTPS zu zwingen und Man-in-the-Middle-Angriffe zu verhindern. Dann wähle ich unter "SSL Certificate" aus: "Request a new SSL certificate with Let's Encrypt". Unter Advanced trage ich in das Feld "Custom Nginx Configuration" folgenden Code ein:

# Wichtige Header für den Reverse Proxy
proxy_set_header Host $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-Host $host;

# Schutz vor Clickjacking: Verhindert das Einbetten der Seite in Frames add_header X-Frame-Options "DENY" always;

# Schutz vor MIME-Type Sniffing: Erzwingt die Einhaltung des Content-Type add_header X-Content-Type-Options "nosniff" always;

# Optional, aber empfohlen: Blockiert bekannte XSS-Angriffe add_header X-XSS-Protection "1; mode=block" always;

# Einstellungen für WebSockets (für Live-Updates in Paperless-NGX)
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";

Diese Parameter sind erforderlich, damit die Anwendung hinter dem Proxy richtig funktioniert und gängige Angriffsszenarien abgewehrt werden. Wenn ich das nun mit OK bestätige, dann dauert das eine Weile, weil zunächst das SSL Zertitikat bei Let's Encrypt bezogen wird. Wenn hierbei keine Fehler auftreten, dann wird der neue Proxy Host mit dem Status "online" ausgewiesen und Paperless-NGX ist über die entsprechende Domain mittels https erreichbar. Ich kann nun die Weboberfläche von Paperless-NGX über die dafür ausgewählte Domain ansteuern und den ersten, den administrativen User anlegen. Dann kann ich mit der Nutzung von Paperless NGX beginnen. Aber ich bin noch nicht fertig mit meinem Projekt.

Absicherung mit Fail2Ban

Noch ist der Server nicht vor Brute Force Attacken, also dem massenhaften, ggf. automatisierten Versuch, Benutzernamen und Passworte zu erraten, gesichert. Ganz am Anfang hatte ich ja fail2ban installiert, ohne weitere Konfigurationen daran vorzunehmen. Auf diese Weise sichert fail2ban zunächst die SSH-Schnittstelle gegen Brute Force Attacken ab, aber nicht mehr. Nun setze ich es auf die Anmeldung an Paperless-NGX an. Paperless-NGX speichert Logfiles in der Datei .../data/log/paperless.log. Das data-Verzeichnis habe ich in der docker-compose.yml auf den Pfad /home/andreas/paperless-ngx gemounted. Ich erstelle nun eine Filterdatei, die dieses Logfile auswertet und dort verzeichnete fehlerhafte Anmeldeversuche erkennt. Dazu erstelle ich die Datei /etc/fail2ban/filter.d/paperless-block.conf

sudo nano /etc/fail2ban/filter.d/paperless-block.conf

mit folgendem Inhalt:

[INCLUDES]
before = common.conf

[Definition]
# Paperless-Zeitstempel sind nicht nötig
datepattern =

# Regex für fehlgeschlagene Logins
failregex = \[paperless\.auth\]\s*Login failed for user `.*` from private IP `<HOST>`\.

ignoreregex =

Diese Filterdefinition erkennt nun das Vorkommen der Zeichenkette "Login failed for user... from private IP..." und ermittelt in der Variable <HOST> die IP-Adresse des "Angreifers". Unter ignoregex könnte man noch IP-Adressen erfassen, die von einer Sperre ausgenommen werden sollen. Darauf habe ich hier verzichtet, damit ich das Ganze auch testen kann. Später könnte ich dort die IP-Adressen aus meinem privaten Netzwerk ausschließen. Noch wird diese Filterdefinition aber nicht angewendet. Dazu muss erst ein Jail angelegt werden. Ich erstelle dazu die Datei /etc/fail2ban/jail.d/paperless.conf

sudo nano /etc/fail2ban/jail.d/paperless.conf

mit folgendem Inhalt:

[paperless]
enabled = true
filter = paperless-block

# Verwende die Multiport-Action für Container-Netz
banaction = docker-iptables

# Ports innerhalb des Docker-Netzes blockieren, Host bleibt unberuehrt
port = 80,443

# Logfile, das vom Host erreichbar ist
logpath = /home/andreas/paperless-ngx/data/log/paperless.log
backend = polling

# auch hier IPs vom Bann ausschliessen
ignoreip =

maxretry = 3
findtime = 600
bantime = 3600

Hier wird zunächst der eben definierte Filter verwendet. Dann wird eine banaction bestimmt. Das ist eine Aktion, die zum Aussperren der erkannten Übeltäter verwendet werden soll. Diese muss ich noch definieren. Geblockt werden sollen Zugriffe auf das Docker-Netzwerk. Das ist nun etwas schwer zu verstehen. Endanwender*innen kommen mit einer normalen, öffentliche IP-Adresse an. Diese ist zu sperren. Innerhalb des Docker-Netzwerks gibt es eigene Adressen aus dem Adressraum 172.17.0.0/24 und nicht zuletzt habe ich ja noch ein VPN definiert, mit dem Adressraum 10.11.0.0/24. Hier sollen aber die öffentlichen IPs der zugreifenden Systeme erkannt und ggf. geblockt werden.

Der Parameter logpath ist selbsterklärend. Wichtig ist danach der Parameter "backend = polling". Der bewirkt, dass tatsächlich dieses Logfile ausgewertet wird und nicht irgendwelche Systemprotokolle. Dann könnte ich auch hier noch einmal IP-Adressen vom Blockieren ausschließen. Der Parameter maxretry definiert natürlich die Anzahl der erlaubten Versuche. Findtime definiert den Suchzeitraum in Millisekunden, also den Zeitraum innerhalb dessen die Fehlerversuche gezählt werden. Hier habe ich mit 600 eine Minute definiert. Wenn ich innerhalb einer Minute dreimal versuche, mich mit falschen Credentials anzumelden, werde ich gesperrt. Bots, die durch das Netz schwirren, unternehmen sehr viele Zugriffsversuche in sehr kurzer Zeit und werden schnell erkannt. Wenn ich mich beim Anmelden zweimal vertippt habe, dann warte ich eine Minute und kann gefahrlos einen dritten Versuch unternehmen. Die bantime, also die Zeitspanne einer Sperrung beträgt hier 3600 Millisekunden, also eine Stunde. Diese Werte kann man nach Belieben ändern.

Nun muss ich aber auch noch die Banaction definieren. Das mache ich in der Datei /etc/fail2ban/action.d/docker-iptables.conf

sudo nano /etc/fail2ban/action.d/docker-iptables.conf

mit folgendem Inhalt:

[Definition]
actionstart =
actionstop =
actioncheck =

actionban = iptables -I DOCKER-USER -s <ip> -j DROP
actionunban = iptables -D DOCKER-USER -s <ip> -j DROP

User (bzw. deren IP-Adressen) werden auf der Firewall iptables gesperrt, indem ihre Zugriffsversuche gedropt werden. Da docker die ufw ignoriert, ist dieser Weg über iptables erforderlich. So funktioniert es aber. Wenn ich nun fail2ban mit

sudo systemctl restart fail2ban

neustarte, dann wird der neue Filter aktiv. Ich kann mir jederzeit den aktuellen Stand mit

sudo fail2ban-client status paperless

ansehen.

Scannen

Wenn der Scanner nicht in der Lage ist, an eine eMail-Adresse zu scannnen, dann muss man in den consume-Ordner scannen, wenn man die Digitalisate nicht zu Fuß übertragen möchte. Wenn der Scanner aber nicht in das VPN integriert werden kann, dann muss man einen Umweg gehen. Hierzu gebe ich den consume-Ordner mittels Samba frei. Dann mounte ich den freigegebenen Ordner an meinem Client und gebe diesen wiederum im Netzwerk frei. Das ist zugegebenermaßen kein besonders eleganter Weg. Aber er funktioniert und ein besserer fällt mir nicht ein.

Samba kann ganz einfach mit dem Befehl

sudo apt install samba

installiert werden. Dann muss ich zunächst einen Samba-User anlegen, weil Samba eine eigene Benutzerverwaltung hat. Den Benutzer muss es als User auf dem System aber auch bereits geben. Ich verwende daher meinen üblichen Benutzeraccount und wende den Befehl

sudo smbpasswd -a andreas

an. Dann werde ich nach einem neuen Passwort gefragt (2 mal) und der Benutzer wird angelegt. Nun brauche ich nur noch eine Freigabe einzurichten. Dazu bearbeite ich die Datei /etc/samba/smb.conf mit Adminrechten

sudo nano /etc/samba/smb.conf

und füge am Ende ein:

[scans]
path = /home/andreas/paperless-ngx/consume
read only = no
guest ok = no

Mehr brauche ich hier nicht einzustellen. Ich habe ja den Zugriff auf den Server über die Firewall begrenzt und den Port für Samba ausschließlich für das VPN freigegeben. Mit

sudo systemctl daemon-reload

zwinge ich das System dazu, die Systemdateien neu einzulesen und mit

sudo systemtctl restart smbd.service

starte ich den Samba-Server neu. Nun ist die Freigabe im VPN für den User andreas verfügbar. Wenn ich einen Windows-Client nutzen würde, dann würde ich nun über den Windows-Explorer diese Netzwerkfreigabe als Netzwerklaufwerk verbinden und im Netz freigeben. Unter Linux muss ich zunächst auf dem Client die Freigabe mounten. Ich bearbeite dazu mit root-Rechten (auf dem Client, nicht auf dem Server)  die Datei /etc/fstab

sudo nano /etc/fstab

und trage dort neu ein:

//10.11.0.1/scans /home/andreas/scans cifs _netdev,x-systemd.requires=wg-quick@wg1011.service,credentials=/pfad/zu/deiner/.smbcredentials,uid=1000,gid=1000 0 0

Der Parameter _netdev kennzeichnet die Freigabe als Netzwerk-Gerät. Systemd weiß dadurch, dass es mit dem Mounten warten soll, bis das grundlegende Netzwerk aktiv ist. Mit x-systemd.requires=wg-quick@wg1011.service wird Systemd mitgeteilt, dass die Mount-Unit erst gestartet werden darf, nachdem die Unit wg-quick@wg1011.service erfolgreich gestartet wurde und bereit ist.

Die Credentials für die Samba-Freigabe werden hier in der Datei "/root/.smbcredentials" erwartet. Diese muss ich zunächst mit

sudo nano /root/.smbcredentials

und dem Inhalt

username=andreas

password={smb-password}

erstellen. Dann muss ich die Berechtigungen für den Root-User dafür vergeben mit

sudo chown root:root /root/.smbcredentials

und mit

sudo chmod 600 /root/.smbcredentials

dafür sorgen, dass nur der Root-User diese sensiblen Daten lesen kann. Außerdem muss ich den lokalen Ordner scans ich noch erstellen:

mkdir scans

und dann einmal mounten (beim nächsten Start des Clients wird das automatisch gemountet, falls das VPN verfügbar ist. Wenn ich bisher keine SMB-Freigaben auf diesem Client nutze, muss ich zunächst die cifs-utils installieren

sudo apt install cifs-utils

Danach kann ich das Verzeichnis mounten.

sudo mount scans

oder mit

sudo mount -a

alles mounten, was in /etc/fstab steht und noch nicht gemountet wurde. Nun habe ich den Ordner consume des Paperless-NGX-Servers als Verzeichnis scans lokal verfügbar. Alle Dateien, die ich dort hineinschiebe und die Paperless-NGX verarbeiten kann, werden automatisch verarbeitet. Damit der Scanner auch das Verzeichnis nutzen kann, muss ich auf dem Client nun ebenfalls Samba installieren. Serverdienste auf einem Client sind natürlich nicht im Sinne des Erfinders. Das ist es, was ich mit nicht elegant gemeint habe. Aber nur so funktioniert es. Ich installiere also wie oben beschrieben auch auf dem Client den Samba-Server, füge ebenfalls den Samba-User andreas hinzu und erstelle in der Datei /etc/samba/smb.conf die Freigabe

sudo nano /etc/samba/smb.conf

mit folgendem Inhalt:

[scans] path = /home/andreas/scans
read only = no
guest ok = no

Nach 

sudo systemctl daemon-reload

und

sudo systemctl restart smbd.service

steht der Ordner scans im lokalen Netzwerk unter der lokalen IP-Adresse meines Clients zur Verfügung und kann vom Scanner genutzt werden. Der Scanner greift also über die lokale Netzwerkadresse meines Clients auf die Freigabe zu welche eine Verbindung über das VPN auf den entfernten Server darstellt.

Datensicherung

Wenn ich dem System Dokumente anvertraue, die ich vielleicht ansonsten nicht mehr aufbewahre, dann muss ich mich natürlich um eine zuverlässige Datensicherung kümmern. Hierfür bietet sich das Open Source Tool Duplicati an. Es lässt sich bequem über Docker installieren und bietet verschlüsselte, inkrementelle Backups auf verschiedenste Sicherungsziele an. Die Sicherungen auf dem selben System abzulegen, auf dem auch schon die Daten liegen, ist natürlich komplett sinnfrei. Daher brauche ich ein externes Sicherungsziel. Hier nutze ich, wie eingangs erklärt, Speicherlösungen bei Hosting-Anbietern. Die Einrichtung der Speichers bei externen Hostern führe ich hier nicht aus, weil es viele verschiedene Anbieter und Konfigurationsoberflächen gibt. Im Ergebnis setze ich im Folgenden voraus, dass es einen Speicher gibt, der per SFTP (SSH) angesprochen werden kann.

Export der Inhalte und Metadaten

Paperless-NGX bietet mit dem document-exporter eine komfortable Möglichkeit, alle Dokumente, Datenbankinhalte und Strukturen zu exportieren. Das Ergebnis dieses Exports kann dann gesichert werden und damit ist auch eine Wiederherstellung des Systems jederzeit möglich. Der document-exporter wird mit 

docker exec -it paperless-ngx-webserver-1 document_exporter /usr/src/paperless/export

aufgerufen bzw. genutzt. Hierbei muss der Pfad angegeben werden, den der Export-Ordner innerhalb des Docker-Containers nutzt. Gemountet ist dieser Ordner auf den Pfad /home/andreas/paperless-ngx/export. Dieser Export soll natürlich einmal am Tag ausgeführt werden, also definiere ich einen Cronjob dafür. Da der Job im normalen User-Kontext also ohne Adminitrationsrechte ausgeführt werden soll, kann ich ihn in der Crontab für den Standarduser einrichten. Ich rufe die crontab-Bearbeitung mit

crontab -e

auf. Wenn ich bisher keinen Standard-Editor für die Bearbeitung der Crontab angegeben habe, werde ich nun danach gefragt. Hier wähle ich den vorgeschlagenen Editor nano aus:

no crontab for andreas - using an empty one

Select an editor. To change later, run 'select-editor'.
1. /bin/nano <---- easiest
2. /usr/bin/vim.basic
3. /usr/bin/vim.tiny
4. /bin/ed

Choose 1-4 [1]: 1

An das Ende dieser Datei trage ich dann ein:

0 7 * * * docker exec -it paperless-ngx-webserver-1 document_exporter /usr/src/paperless/export

Am Anfang der Zeile wird definiert, wann der Cronjob ausgeführt werden soll. An erster Stelle werden die Minuten, dann die Stunden, dann der Tag des Monats (1-31), der Monat (1-12), der Wochentag (Sonntag = 0, Samstag = 7) angegeben. Darauf folgt das auszuführende Kommando. In diesem Fall wird jeden Morgen um 7:00 Uhr ein Export ausgeführt. Dabei werden vorhandene Dateien überschrieben, wenn im System eine neuere verfügbar ist. Es werden die Ursprungsdateien, die PDF-Repräsentationen, Web-Vorschauen und in einer Manifest-Datei alle Metadaten und Datenbankinhalte gespeichert. Den Inhalt des Export-Ordner muss ich nun natürlich sichern.

Duplicati installieren

Auch Duplicati installiere ich per Docker und Docker-Compose. Dann erstelle ich ein Verzeichnis duplicati:

mkdir duplicati

Darin erstelle ich dann die Datei docker-compose.yml

nano docker-compose.yml

mit folgendem Inhalt:

services:
duplicati:
image: lscr.io/linuxserver/duplicati:latest
container_name: duplicati
environment:
- PUID=0
- PGID=0
- TZ=Europe/Berlin
- SETTINGS_ENCRYPTION_KEY=strenggeheimerundkomplexerkey
- DUPLICATI__WEBSERVICE_PASSWORD=strenggeheimesundkomplexespasswort
volumes:
- ./config:/config
- /home/andreas/paperless-ngx/export:/paperless-export
- /home/andreas/sicherungsverzeichnis1:/source1
ports:
- 10.11.0.1:8200:8200
networks:
- web_proxy
restart: unless-stopped
networks:
web_proxy:
external: true

Der Encryption-Key wird als Teil der Verschlüsselung der Sicherungsdaten verwendet und das Webservice-Passwort wird benötigt, um sich an der Weboberfläche anzumelden (wer hätte das gedacht?). 

Hier habe ich das Exportverzeichnis von Paperless-NGX als Volume gemountet und zusätzlich ein Verzeichnis namens "sicherungsverzeichnis1". Hier kann man beliebige Verzeichnisse angeben, die dann später von der Weboberfläche aus und für den Dockercontainer erreichbar sind. Das zweite Verzeichnis habe ich angegeben, damit ich später aus duplicati heraus einzelne Files an anderer Stelle zurücksichern kann, als dem Ursprungspfad. Mit der Angabe unter "ports" binde ich die Duplicati-Weboberfläche vor allem an die VPN-Adresse. Duplicati hätte nun nicht unbedingt im selben Docker-Netwzerk laufen müssen, wie der NPM und Paperless-NGX, aber so vermeide ich unnötige Netzwerke auf dem Server.

Duplicati starte ich nun mit

docker compose up -d

und kann mich dann an der Weboberfläche anmelden. Diese rufe ich mit 10.11.0.1:8200 auf. Sie ist nur über das VPN erreichbar.

Sicherung in Duplicati einrichten

Wenn ich mich an der Weboberfläche von Duplicati anmelde, dann moniert mein Browser natürlich die unsichere Verbindung. Das kann ich aber getrost ignorieren, weil ich mich ja innerhalb meines VPN bewege. Der Dialog für das Hinzufügen eines neuen Backup-Jobs ist größtenteils selbst erklärend.

Unter den allgemeinen Sicherungseinstellungen vergebe ich zunächst einen Namen für die Sicherung. Der darf ruhig sprechend sein. Eine Sicherungsbeschreibung kann ich auch noch eingeben. Das bewirkt aber nichts. Interessanter ist da schon die Verschlüsselung. Ich kann hier wählen zwischen AES-256, was bereits integriert ist oder GNU Privacy Guard, was auf den allermeisten Linux-Systemen bereits installiert ist. Ich entscheide mich für die integrierte Variante. Außerdem muss ich mir ein Passwort ausdenken. Hier werden auch schwache Passworte akzeptiert, aber man sollte sich schon etwas Mühe geben.

Bei den Backup-Destinationen wähle ich natürlich SSH aus. Auf der nächsten Seite muss ich dann die Zugangsdaten für den externen Speicher eingeben. Bei einer Storage-Box von Hetzner ist das z.B. accountname.your-storagebox.de, ansonsten könnte das eine IP-Adresse eines entfernten Servers oder auch eine Domain sein. Der Standard-Port für SSH ist 22, bei der Storage-Box von Hetzner ist es 23. Auf der Firewall habe ich ausgehenden Traffik nicht begrenzt. Darum brauche ich mir keine Gedanken zu machen, ob ich einen Port auf der Firewall öffnen muss. Ordnerpfad ist natürlich der Pfad, in den auf dem entfernten System gesichert werden soll. Den hier angegebenen Ordner muss es im Ziel schon geben, sonst läuft die Sicherung auf einen Fehler. Das war in früheren Versionen von Duplicati noch anders. Da wurde man dann gefragt, ob der Ordner angelegt werden soll.. Username ist natürlich der Username in dessen Berechtigungskontext auf dem Zielsystem die Sicherungen abgelegt werden sollen.

Die Autentifizierungsmethode wählt man mit "Erweiterte Option hinzufügen" aus. Hier reicht eine Authentifizierung mit Passwort aus. Duplicati unterstützt auch SSH-Key-Authentifizierung. Allerdings werden nur Schlüssel im PEM-Format erlaubt und die Einbindung ist auch recht kompliziert. Da die Passwortübertragung verschlüsselt erfolgt, mache ich es mir einfach und wähle Passwortauthentifizierung aus. Neben dem Passwort prüft Duplicati den Fingerprint der Gegenseite. Den erwartet es als MD5-Hash. Da es je nach Hosting-Anbieter nicht ganz einfach ist, den Fingerprint der Gegenseite im MD5-Format zu bekommen, wende ich einen kleinen Trick an. Ich füge zunächst unter "Erweiterte Option hinzufügen" "SSH Fingerprint" hinzu und schreibe in das entsprechende Feld irgend etwas hinein. Später, wenn ich mit diesen unsinnigen Daten zu sichern versuche, erhalte ich eine Fehlermeldung, in der mir der korrekte Fingerprint angezeigt wird. Dazu komme ich später noch. Ich klicke also nicht auf "Test destination", sondern erst einmal auf Weiter. Hier wähle ich die zu sichernden Daten aus. In meinem Fall habe ich ja den Export-Ordner von Paperless-NGX in das Verzeichnis "source" gemountet und darum wähle ich das natürlich aus.

Die Parameter auf der Seite Zeitplan sind selbsterklärend. Ich stelle meine Sicherungen auf z.B. 8:00 Uhr morgens ein, weil der Export ja jeden Morgen um 7:00 Uhr läuft. Auf der Seite Optionen brauche ich bei der Zielvolumengröße nichts zu ändern. Interessanter sind die Retention-Regeln, die ich unter "Sicherungsaufbewahrung" einstellen kann. Alle Sicherungen immer aufzubewahren ist natürlich nicht sinnvoll. 

Ich könnte auswählen, dass alle Sicherungen gelöscht werden, die älter sind als n Tage, Wochen, Monate oder Jahre. Ich könnte aber auch vorgeben, dass eine bestimmte Anzahl von Sicherungen aufbewahrt werden und alle anderen (älteren) gelöscht werden. Wenn ich "intelligente Sicherungsaufbewahrung" auswähle, dann werden die Sicherungen der letzten 7 Tage, jeweils eine der letzten 4 Wochen und jeweils eine der letzten 12 Monate aufbewahrt. Alternativ kann ich eine individuelle Aufbewahrungsregel definieren. Die Syntax wird hier ganz gut erklärt. Mir reicht hier für's Erste die intelligente Aufbewahrungsregel. Unter "Optionen für Profis" könnte ich noch sehr viele erweiterte Optionen hinzufügen. Das ist aber wirklich etwas für Profis, darum lasse ich einstweilen die Finger davon. Wenn ich auf "Senden" klicke, dann wird der Sicherungsjob gespeichert. Die Sicherung würde dann um 6:00 Uhr starten und zunächst auf einen Fehler laufen, wegen des falschen SSH-Fingerprints. Das warte ich natürlich nicht ab, sondern klicke auf der Startseite auf "Start". Nun läuft der Job auf den erwarteten Fehler. In der Fehlermeldung wird mir auch ein Link auf das Protokoll angezeigt, den ich anklicke. Dort wird ein Fehler ausgewiesen, der auch den erwarteten SSH-Fingerprint beginnend mit "ssh-ed25519 256" enthält. Den kopiere ich einfach aus der Fehlermeldung heraus. Ich gehe erneut in die Bearbeitung des Sicherungsjobs und trage den korrekten Schlüssel auf der Seite mit der Sicherungszieldefinition ein. Wenn ich nun auf "Test destination" klicke, wird mir angezeigt, dass die Verbindung funktioniert. Ich kann die Bearbeitung der Sicherung abschließen und die erste Sicherung anstoßen. Das sollte nun funktionieren.

Fazit

Nun habe ich also einen Server bei einem Hoster, auf dem Paperless-NGX läuft. Die Weboberfläche von Paperless-NGX ist aus dem Internet über eine (Sub-)Domain erreichbar und mit einem gültigen SSL-Zertifikat von Let's Encrypt abgesichert. Die SSL-Absicherung und die Verbindung mit dem Zertifikat werden durch einen Reverse-Proxy besorgt. Paperless-NGX ist ausschließlich über den Reverse-Proxy ansprechbar. Somit kann die Absicherung mit SSL niemand umgehen. Die Remote-Konsole des Servers sowie die Weboberflächen des Reverse Proxys und von Duplicati sind ausschließlich über das VPN erreichbar. Die Firewall blockiert unerlaubte Zugriffe aus dem Netz und fail2ban sperrt IP-Adressen aus, wenn sie mehr als dreimal versuchen, mit falschen Zugangsdaten auf die Remoteshell oder Paperless-NGX zuzugreifen. Die Datensicherung erfolgt auf ein System bei einem anderen Hoster, also auch in einem anderen Rechenzentrum. Die Sicherungen finden verschlüsselt, komprimiert und inkrementell statt. Auch der Übertragungsweg der Sicherungsdaten ist verschlüsselt.

Damit habe ich ein System geschaffen, das eine sichere und zuverlässige Verarbeitung meiner Dokumente ermöglicht, ohne dass ich zuhause Server dafür betreiben muss. Die laufenden Kosten für die beiden Betreiber belaufen sich auf knapp 10,00 Euro im Monat. Die in der Einleitung genannten Ziele sind alle erreicht.