Paperless-ngx & Authelia (2024)

Ordner voll mit Dokumenten von Versicherungen, Behörden und Firmen wer kennt das nicht. Obwohl die Menge über die Jahre ein wenig zurück ging, hatte ich immer einige Mühen, alles vernünftig zu digitalisieren.

Bei meiner Suche nach aktuellen Lösungen bin ich über paperless-ngx gestolpert, einer Weiterentwicklung des nicht mehr aktivem paperless-ng Projektes.

Paperless-ngx & Authelia (1)

Paperless-ngx ist eine Open Source Lösung zur Verwaltung von Dokumenten mit einem - meiner Meinung nach gelungenem - Frontend, OCR Support und Integrationen für mobile Anwendungen und E-Mail Konten.

In diesem Post gebe ich einen kurzen Überblick bzgl. der docker-basierten Installation von paperless-ngx und wie man mittels Authelia eine 2FA Lösung integriert.

paperless-ngx - Installation & Konfiguration

Für fast alle meiner Setups verwende ich einen docker-basierten Ansatz, so auch in diesem Fall.

Die Anweisungen beschreiben mehrere Optionen paperless-ngx via Docker zu betreiben, ich entschied mich dafür, die vorhandenen Images zu verwenden und mein docker-compose.yml selbst anzupassen.

Just ein Arbeitsverzeichnis namens paperless-ngx angelegt hatte, und die docker-compose.env und docker-compose.postgres-tika.yml Datei von https://github.com/paperless-ngx/paperless-ngx/tree/main/docker/compose geyogen, ging es gleich an das Anpassen der selbigen.

Unter obiger URL gibt es mehrere compose Files, je nachdem welcher Anwendungesfall abgedeckt werden soll. Da ich eine Lösung brauchte, die Support für MS Office Dokumente bietet, entschiede ich mich für die Option, die Apache Tika nutzt.

Paperless-ngx & Authelia (2)

docker-compose.yml

Die paperless-ngx Dokumentation beschreibt alle vorhanden Konfigurationsmöglichkeiten.

Um zu starten reicht es aber aus, die Variablen für die Postgres Konfiguration (POSTGRES_DB, POSTGRES_USER, POSTGRES_PASSWORD) anzupassen.

version: "3.4"services: broker: image: redis:6.0 restart: unless-stopped volumes: - redisdata:/data db: image: postgres:13 restart: unless-stopped volumes: - /home/docadmin/docker/paperless-ngx/database:/var/lib/postgresql/data environment: POSTGRES_DB: <DB> POSTGRES_USER: <DBUSER> POSTGRES_PASSWORD: <DBPASSWORD> webserver: image: ghcr.io/paperless-ngx/paperless-ngx:latest restart: unless-stopped depends_on: - db - broker - gotenberg - tika ports: - 127.0.0.1:8000:8000 healthcheck: test: ["CMD", "curl", "-fs", "-S", "--max-time", "2", "http://localhost:8000"] interval: 30s timeout: 10s retries: 5 volumes: - ./paperless-ngx/data:/usr/src/paperless/data - ./paperless-ngx/media:/usr/src/paperless/media - ./paperless-ngx/export:/usr/src/paperless/export - ./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 gotenberg: image: gotenberg/gotenberg:7.4 restart: unless-stopped command: - "gotenberg" - "--chromium-disable-routes=true" tika: image: ghcr.io/paperless-ngx/tika:latest restart: unless-stoppedvolumes: redisdata:

docker-compose.env

Das Environment File enthält neben der URL unter der man paperless-ngx anspricht, mit dem PAPERLESS_SECRET_KEY nur einen weiteren variablen Wert. Der Key, welche zur Generierung von Session Tokens verwendet wird, sollte auf jeden Fall angepasst werden.

PAPERLESS_URL=<URL>USERMAP_UID=1010USERMAP_GID=100PAPERLESS_TIME_ZONE=Europe/BerlinPAPERLESS_OCR_LANGUAGE=deuPAPERLESS_SECRET_KEY=<SECRETKEY>

Nach Abschluss der Anpassungen, starten wir den Container mittels

docker-compose up -d

und erzeugen als allererstes das Admin Userkonto

 docker-compose run --rm webserver createsuperuser 

Hat unsere Konfiguration soweit gepasst, so sollten wir unter der angegeben URL die Instanz erreichen

Paperless-ngx & Authelia (3)

SSL Konfiguration

SSL verschlüsselte Kommunikation ist heute Standard und wird von paperless-ngx unterstützt.

Obwohl es möglich ist SSL direkt in der Anwendung zu terminieren, empfiehlt die offizielle Dokumentation, dies vorgelagert am Webserver/Reverse Proxy zu erledigen. In diesem Fall kommt - wie so oft - nginx und letsencrypt zur Rettung.

Wir generieren mittels certbot im ersten Schritt die Zertifikate für unser Domain

sudo certbot --nginx -d <YOURDOMAIN>#oder alternativsudo certbot certonly -d <YOURDOMAIN>

Nun geht es an die Konfiguration des Servers bzw. der Location. Im Beispiel unten ist abgebildet wie man paperless-ngx via https://<YOURDOMAIN>/documents ansprechen kann

server { listen *:443 ssl; server_name <YOURDOMAIN>; ssl_certificate /etc/letsencrypt/live/<YOURDOMAIN>/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/<YOURDOMAIN>/privkey.pem; ssl_session_cache shared:SSL:10m; ssl_session_timeout 5m; ssl_protocols TLSv1.2; ssl_ciphers CDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256; ssl_prefer_server_ciphers on; ssl_stapling on; ssl_stapling_verify on; client_max_body_size 500M; index index.html index.htm index.php; access_log /var/log/nginx/<YOURDOMAIN>.access.log combined; error_log /var/log/nginx/<YOURDOMAIN>.error.log; location /documents { proxy_pass http://localhost:8000/; # These configuration options are required for WebSockets to work. proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; proxy_redirect off; 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-Host $server_name; }}

Hat alles gepasst, dann sollte man die Anwendung nach einem Reload der nginx Konfiguration unter der angegeben URL erreichen und sich als Admin User anmelden können.

Paperless-ngx & Authelia (4)

In meinem Fall hatte soweit alles gepasst und meine Tests (Uploads, Tagging etc) verliefen alle positiv. Ein Punkt trieb mich aber immer noch um, welche Optionen habe ich den Zugriff auf meine Daten weiter abzusichern (Verschlüsselung der Dokumente, 2FA etc).

Beim Durcharbeiten der Dokumentation stellte sich heraus, dass eine Verschlüsselung der Dokumente via GNUPG nicht mehr unterstützt ist (siehe auch https://docs.paperless-ngx.com/administration/) von daher lag mein Hauptaugenmerk auf dem Thema 2FA.

Auch hier stellte sich die Dokumentation des Projektes als hilfreich heraus. Unterstützt die MFA Anwendung das Weiterleiten eines Remote-User Header nach erfolgreicher Authentifizierung so kann man diese mit paperless-ngx integrieren

Um dieses Feature zu aktivieren, muss man PAPERLESS_ENABLE_HTTP_REMOTE_USER und PAPERLESS_LOGOUT_REDIRECT_URL im Environment File mit aufnehmen.

PAPERLESS_URL=<URL>USERMAP_UID=1010USERMAP_GID=100PAPERLESS_TIME_ZONE=Europe/BerlinPAPERLESS_OCR_LANGUAGE=deuPAPERLESS_SECRET_KEY=<SECRETKEY>PAPERLESS_ENABLE_HTTP_REMOTE_USER=truePAPERLESS_LOGOUT_REDIRECT_URL=<AUTHURL>

Authelia - Installation & Konfiguration

Nach dem Befragen der Suchmaschine des Vertrauens und dem Durchforsten etlicher Blogposts, kam ich auf Authelia, einem Open Source Autorisierungs- und Authentifizierungsserver.

Paperless-ngx & Authelia (5)

In Verbindung mit einem NGINX Proxy, bietet Authelia SSO für alle angebunden Anwendungen (insofern dieses das unterstützen) ebenso wie 2FA via, Google Authenticator, Duo, and Yubikey.

Die Dokumentation von Authelia ist sehr ausführlich und es werden von Haus aus einige Deployment Beispiele für ein lokales, leichtgewichtiges oder HA Setup mitgeliefert. Für meine ersten Tests machte ich mir die Lite Option zu Nutze.

Authelia - Basiskonfiguration

Nachdem man sich das Repo via https://github.com/authelia/authelia.git gezogen hat, wirft man am Besten einen Blick in das docker-compose.yml File unter authelia/examples/compose/lite. Für einen ersten Test sind in der Regel keinerlei Anpassungen nötig.

version: '3.3'networks: net: driver: bridgeservices: authelia: image: authelia/authelia container_name: authelia volumes: - ./authelia:/config networks: - net ports: - 9091:9091 restart: unless-stopped environment: - PUID=1000 - PGID=1000 - TZ=Europe/Berlin redis: image: redis:alpine container_name: redis_authelia volumes: - ./redis:/data networks: - net expose: - 6379 restart: unless-stopped environment: - TZ=Europe/Berlin - PUID=1000 - PGID=1000

Authelia - Applikations Konfiguration

Der weitaus spannendere Teil ist der Teil der Konfiguration, in dem man definiert welche URL mittels welchem 2FA Verfahren geschützt werden soll.

Im authelia Verzeichnis findet sich die Datei namens configuration.yml, die man entsprechend Anpassen muss.

server: host: 0.0.0.0 port: 9091log: level: infojwt_secret: <SECRETKEY>default_redirection_url: <DEFAULTURL>authentication_backend: file: path: /config/users_database.ymlaccess_control: default_policy: deny rules: - domain: - "auth.<YOURDOMAIN>" - "<BYPASSDOMAIN>" policy: bypass - domain: - "<PAPERLESSDOMAIN>" policy: two_factorwebauthn: disable: false display_name: Authelia attestation_conveyance_preference: indirect user_verification: preferred timeout: 60ssession: name: authelia_session secret: <SESSIONKEY> expiration: 12h # 12 hours inactivity: 45m # 45 minutes remember_me_duration: 1M # 1 month domain: mobux.de redis: host: redis_authelia port: 6379regulation: max_retries: 3 find_time: 5m ban_time: 15mstorage: encryption_key: <STORAGEKEY> local: path: /config/db.sqlite3notifier: #filesystem: #filename: /config/notification.txt smtp: username: <EMAILUSER> password: <EMAILPASSWORD> host: <MAILHOST> port: <MAILPORT> sender: <MAILSENDER>

Als erstes passt man die variablen Teile ala URL für standarmässige Redirects und die Secret Keys an (Details zu diesen und deren Verwendung finden sich in der Authelia Dokumentation), am wichtigsten ist aber die Konfiguration der Access Controls, sprich welche URL wie geschützt werden soll.

Im Beispiel ist ersichtlich, dass auth.<YOURDOMAIN> sowie <BYPASSDOMAIN> keine weitere Authenifizierung benötigen, wohin gegen <PAPERLESSDOMAIN> ein 2FA erfordert.

access_control: default_policy: deny rules: - domain: - "auth.<YOURDOMAIN>" - "<BYPASSDOMAIN>" policy: bypass - domain: - "<PAPERLESSDOMAIN>" policy: two_factor

Um diese angesprochenen Keysequenzen zu erzeugen, kann man übrigens folgendes Kommando nutzen.

head /dev/urandom | tr -dc A-Za-z0-9 | head -c64

Sobald wir diese Schritte abgeschlossen habe, geht es als letztes daran die Userdatabase zu generieren.

Im config Verzeichnis in dem auch die configuration.yml Datei liegt, legen wir ein neues File namens user_database.yml und fügen unsere Benutzer nach folgdendem Schema ein.

users: <USERNAME>: displayname: "<FULLNAME>" # Generate with docker run authelia/authelia:latest authelia hash-password <your-password> password: "<USERPASSWORD>" email: <USEREMAIL> groups: - admins

<USERNAME> , <FULLNAME> , <USEREMAIL> and <USERPASSWORD> sind mit den jeweiligen Informationen des Accounts zu ersetzen. Ggf. sollte man auch nicht jedem User die Admins Gruppe geben :-)

Während die ersten 3 genannten Attribute, im Klartext eingetragen werden, muss man das Benutzerpasswort erzeugen.

Hierzu nutzt man die im Image vorhandene hash-password Methode.

docker run authelia/authelia:latest authelia hash-password <your-password>

Nach Abschluss der Arbeiten starten wir Authelia mittels

docker-compose up -d

und verifzieren ob wir die Anwendung unter Port 9091 erreichen. Ist alles in Ordnung sollte man Authelia's login prompt zu sehen bekommen.

Authelia - NGINX

Im nächsten Schritt verknüpfen wir Authelia und Nginx.

Paperless-ngx & Authelia (6)

Nachdem wir ein SSL Zertificate für auth.<YOURDOMAIN> benötigen, kommt erneut letsencrypt/certbot zum Einsatz

sudo certbot --nginx -d auth.<YOURDOMAIN> 

Im Anschluss erzeigen wir die Webserver Konfiguration für auth.<YOURDOMAIN>

server { server_name auth.<YOURDOMAIN>; listen 80; return 301 https://$server_name$request_uri;}server { server_name auth.<YOURDOMAIN>; listen 443 ssl http2; ssl_certificate /etc/letsencrypt/live/auth.<YOURDOMAIN>/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/auth.<YOURDOMAIN>/privkey.pem; location / { set $upstream_authelia http://127.0.0.1:9091; proxy_pass $upstream_authelia; client_body_buffer_size 128k; #Timeout if the real server is dead proxy_next_upstream error timeout invalid_header http_500 http_502 http_503; # Advanced Proxy Config send_timeout 5m; proxy_read_timeout 360; proxy_send_timeout 360; proxy_connect_timeout 360; # Basic Proxy Config 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 $http_host; proxy_set_header X-Forwarded-Uri $request_uri; proxy_set_header X-Forwarded-Ssl on; proxy_redirect http:// $scheme://; proxy_http_version 1.1; proxy_set_header Connection ""; proxy_cache_bypass $cookie_session; proxy_no_cache $cookie_session; proxy_buffers 64 256k; # If behind reverse proxy, forwards the correct IP set_real_ip_from 10.0.0.0/8; set_real_ip_from 172.0.0.0/8; set_real_ip_from 192.168.0.0/16; set_real_ip_from fc00::/7; real_ip_header X-Forwarded-For; real_ip_recursive on; }}

Natürlich müssen alle Vorkommen von <YOURDOMAIN> mit der tatsächlichen URL angepasst werden.

Schnell die Seite aktivieren

sudo ln -s /etc/nginx/sites-available/auth.<YOURDOMAIN> /etc/nginx/sites-enabled/auth.<YOURDOMAIN>sudo systemctl reload nginx

und den Zugriff auf auth.<YOURDOMAIN> testen. Wenn alles korrekt angepasst wurde, dann sollte man den Login prompt sehen

Um die Konfiguration modular und wiederverwendbar zu halten, habe ich ein paar Snippets angelegt.

authelia.conf

# Virtual endpoint created by nginx to forward auth requests.location /authelia { internal; set $upstream_authelia http://127.0.0.1:9091/api/verify; proxy_pass_request_body off; proxy_pass $upstream_authelia; proxy_set_header Content-Length ""; # Timeout if the real server is dead proxy_next_upstream error timeout invalid_header http_500 http_502 http_503; # [REQUIRED] Needed by Authelia to check authorizations of the resource. # Provide either X-Original-URL and X-Forwarded-Proto or # X-Forwarded-Proto, X-Forwarded-Host and X-Forwarded-Uri or both. # Those headers will be used by Authelia to deduce the target url of the user. # Basic Proxy Config client_body_buffer_size 128k; proxy_set_header Host $host; proxy_set_header X-Original-URL $scheme://$http_host$request_uri; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $remote_addr; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Forwarded-Host $http_host; proxy_set_header X-Forwarded-Uri $request_uri; proxy_set_header X-Forwarded-Ssl on; proxy_redirect http:// $scheme://; proxy_http_version 1.1; proxy_set_header Connection ""; proxy_cache_bypass $cookie_session; proxy_no_cache $cookie_session; proxy_buffers 4 32k; # Advanced Proxy Config send_timeout 5m; proxy_read_timeout 240; proxy_send_timeout 240; proxy_connect_timeout 240;}

authentication.conf

# Basic Authelia Config# Send a subsequent request to Authelia to verify if the user is authenticated# and has the right permissions to access the resource.auth_request /authelia;# Set the `target_url` variable based on the request. It will be used to build the portal# URL with the correct redirection parameter.auth_request_set $target_url $scheme://$http_host$request_uri;# Set the X-Forwarded-User and X-Forwarded-Groups with the headers# returned by Authelia for the backends which can consume them.# This is not safe, as the backend must make sure that they come from the# proxy. In the future, it's gonna be safe to just use OAuth.auth_request_set $user $upstream_http_remote_user;auth_request_set $groups $upstream_http_remote_groups;auth_request_set $name $upstream_http_remote_name;auth_request_set $email $upstream_http_remote_email;proxy_set_header Remote-User $user;proxy_set_header Remote-Groups $groups;proxy_set_header Remote-Name $name;proxy_set_header Remote-Email $email;# If Authelia returns 401, then nginx redirects the user to the login portal.# If it returns 200, then the request pass through to the backend.# For other type of errors, nginx will handle them as usual.error_page 401 =302 https://auth.<YOURDOMAIN>/?rd=$target_url;

Die snippets verwendend, passen wir unsere Konfiguration wie folgt an

  • authelia.conf wird in den server Block mit aufgenommen
  • authentication.conf wird an jede Domain / URL angefügt, die wir schützen wollen

Beispiel

server { listen *:80; server_name <YOURDOMAIN>; client_max_body_size 500M; return 301 https://$host$request_uri; access_log /var/log/nginx/<YOURDOMAIN>.access.log combined; error_log /var/log/nginx/<YOURDOMAIN>.error.log;} server { listen *:443 ssl; server_name <YOURDOMAIN>; ssl_certificate /etc/letsencrypt/live/<YOURDOMAIN>/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/<YOURDOMAIN>/privkey.pem; ssl_session_cache shared:SSL:10m; ssl_session_timeout 5m; ssl_protocols TLSv1.2; ssl_ciphers CDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256; ssl_prefer_server_ciphers on; ssl_stapling on; ssl_stapling_verify on; client_max_body_size 500M; index index.html index.htm index.php; access_log /var/log/nginx/ssl-<YOURDOMAIN>.access.log combined; error_log /var/log/nginx/ssl-<YOURDOMAIN>.error.log; include snippets/authelia.conf; location /documents { proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header Host $host; proxy_pass http://127.0.0.1:8000; proxy_buffering off; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; proxy_set_header X-Real-IP $remote_addr; include snippets/authentication.conf; # Protect this endpoint }}

Nach Abschluss aller Arbeiten, laden wir die Konfiguration erneut.

sudo nginx -tsudo systemctl reload nginx

Paperless & Nginx

Nach Abschluss aller arbeiten sprechen wir nun unsere paperless URL an.

Paperless-ngx & Authelia (7)

Nach erfolgter Anmeldung mit den Credentials, die wir bei der Konfiguration von authelia hinterlegt haben, werden wir aufgefordert die 2. Authentifizierungsmethode zu verwenden.

Paperless-ngx & Authelia (8)

Falls alles geklappt hat, werden wir ohne weitere Anmeldung nach paperless-ngx geleitet.

Nützliche Resourcen

  • https://www.authelia.com/
  • https://github.com/authelia/authelia
  • https://paperless-ngx.readthedocs.io/en/latest/index.html
  • https://github.com/paperless-ngx/paperless-ngx
Paperless-ngx & Authelia (2024)

References

Top Articles
Latest Posts
Article information

Author: Barbera Armstrong

Last Updated:

Views: 6468

Rating: 4.9 / 5 (59 voted)

Reviews: 82% of readers found this page helpful

Author information

Name: Barbera Armstrong

Birthday: 1992-09-12

Address: Suite 993 99852 Daugherty Causeway, Ritchiehaven, VT 49630

Phone: +5026838435397

Job: National Engineer

Hobby: Listening to music, Board games, Photography, Ice skating, LARPing, Kite flying, Rugby

Introduction: My name is Barbera Armstrong, I am a lovely, delightful, cooperative, funny, enchanting, vivacious, tender person who loves writing and wants to share my knowledge and understanding with you.