Aller au contenu

Paperless-ngx

logo

👋 Présentation

Paperless-ngx est une application libre, gratuite et auto-hébergeable qui permet de numériser et classer ses papiers sans prise de tête afin de pouvoir les retrouver facilement. Ce logiciel GED propose de nombreuses fonctionnalités comme l'apprentissage automatique lui permettant de classer les documents avec le bon correspondant par exemple.

Liste complète des fonctionnalités

Les briques logicielles suivantes seront utilisées pour l'installation de l'application :

  • Docker pour faire tourner la stack (installé dans une VM GNU/Linux Debian 12 sur Proxmox VE).
  • Nginx comme reverse-proxy.
  • Cerbot avec le challenge DNS pour obtenir un certificat Let's Encrypt.

Configuration de Paperless-ngx

Création de l'arborescence
olivier@srv-docker:~$ sudo mkdir -p /srv/paperless-ngx/{redis,db,data,media,export,consume}
olivier@srv-docker:~$ sudo mkdir -p /srv/paperless-ngx/secrets/{paperless-ngx,postgresql}
olivier@srv-docker:~$ sudo chown -R /srv/paperless-ngx
olivier@srv-docker:~$ cd /srv/paperless-ngx
Il faut ensuite créer le fichier docker-compose.yml de notre stack.

fichier docker-compose.yml
networks:
  frontend:
  backend:

secrets:
  pgsql_password:
    file: "./secrets/postgresql/POSTGRES_PASSWORD"
  paperless_secret_key:
    file: "./secrets/paperless-ngx/PAPERLESS_SECRET_KEY"
  paperless_admin_password:
    file: "./secrets/paperless-ngx/PAPERLESS_ADMIN_PASSWORD"
  paperless_email_host_password:
    file: "./secrets/paperless-ngx/PAPERLESS_EMAIL_HOST_PASSWORD"

services:
  broker:
    image: docker.io/library/redis:7
    restart: unless-stopped
    networks:
      - backend
    security_opt:
      - no-new-privileges:true
    volumes:
      - /srv/paperless-ngx/redis:/data:rw
    user: 1000:1000

  db:
    image: docker.io/library/postgres:16
    restart: unless-stopped
    networks:
      - backend
    security_opt:
      - no-new-privileges:true
    volumes:
      - /srv/paperless-ngx/db:/var/lib/postgresql/data
    secrets:
      - "pgsql_password"
    environment:
      POSTGRES_DB: paperless
      POSTGRES_USER: paperless
      POSTGRES_PASSWORD_FILE: /run/secrets/pgsql_password
    user: 1000:1000

  webserver:
    image: ghcr.io/paperless-ngx/paperless-ngx:latest
    restart: unless-stopped
    depends_on:
      - db
      - broker
      - gotenberg
      - tika
    networks:
      - backend
      - frontend
    ports:
      - 8777:8000
    security_opt:
      - no-new-privileges:true
    volumes:
      # Données du conteneurs Docker
      - /srv/paperless-ngx/data:/usr/src/paperless/data
      # Dossier où seront stockés les fichiers
      - /srv/paperless-ngx/media:/usr/src/paperless/media
      # Dossier de destination des sauvegardes
      - /srv/paperless-ngx/export:/usr/src/paperless/export
      # Dossier banette où nous pouvons déposer des fichiers qui seront traités automatiquement par Paperless   
      - /srv/paperless-ngx/consume:/usr/src/paperless/consume
    secrets:
      - "pgsql_password"
      - "paperless_secret_key"
      - "paperless_admin_password"
      - "paperless_socialaccount_providers"
      - "paperless_email_host_password"  
    environment:
      PAPERLESS_REDIS: redis://broker:6379
      PAPERLESS_DBHOST: db
      USERMAP_UID: 1000
      USERMAP_GID: 1000
      PAPERLESS_DBPASS_FILE: /run/secrets/pgsql_password
      PAPERLESS_SECRET_KEY_FILE: /run/secrets/paperless_secret_key
      PAPERLESS_TIME_ZONE: Europe/Paris
      PAPERLESS_ADMIN_USER: ppladmin01
      PAPERLESS_ADMIN_PASSWORD_FILE: /run/secrets/paperless_admin_password
      PAPERLESS_URL: https://ged.mondomaine.tld
      PAPERLESS_CSRF_TRUSTED_ORIGINS: https://ged.mondomaine.tld
      PAPERLESS_ALLOWED_HOSTS: "localhost,ged.mondomaine.tld"
      PAPERLESS_FILENAME_FORMAT: "{created_year}/{correspondent}/{created}"
      # Déclaration de la langue française pour la reconnaissance OCR
      PAPERLESS_OCR_LANGUAGE: fra
      PAPERLESS_TIKA_ENABLED: 1
      PAPERLESS_TIKA_GOTENBERG_ENDPOINT: http://gotenberg:3000
      PAPERLESS_TIKA_ENDPOINT: http://tika:9998
      # Options mail
      PAPERLESS_EMAIL_HOST: mail.mondomaine.tld
      PAPERLESS_EMAIL_PORT: 587
      PAPERLESS_EMAIL_HOST_USER: paperless@mondomaine.tld
      PAPERLESS_EMAIL_FROM: paperless@mondomaine.tld
      PAPERLESS_EMAIL_HOST_PASSWORD_FILE: /run/secrets/paperless_email_host_password
      PAPERLESS_EMAIL_USE_TLS: true

  # Gotenberg est orienté vers la conversion et la génération de fichiers PDF 
  # à partir de divers formats de documents via une API REST.
  gotenberg:
    image: docker.io/gotenberg/gotenberg:7.10
    restart: unless-stopped
    networks:
      - backend
    security_opt:
      - no-new-privileges:true
    command: 
     - "gotenberg" 
     - "--chromium-disable-javascript=true" 
     - "--chromium-allow-list=file:///tmp/.*"  

  # Tika est principalement utilisé pour l'extraction de contenu et 
  # de métadonnées de divers types de fichiers (PDF, docx, HTML,...).
  tika:
    image: docker.io/apache/tika:latest
    restart: unless-stopped
    networks:
      - backend
    security_opt:
      - no-new-privileges:true

On ne met aucun mot de passe dans ce fichier mais dans des fichiers séparés afin de les exploiter avec la fonctionnalité "secrets" de Docker.

Docker secrets

Un secret est toute donnée, telle qu'un mot de passe, un certificat ou une clé API, qui ne doit pas être transmise sur un réseau ou stockée en clair dans un Dockerfile ou dans le code source de votre application.

Docker Compose vous permet d'utiliser des secrets sans avoir à utiliser de variables d'environnement pour stocker des informations. Si vous injectez des mots de passe et des clés API en tant que variables d’environnement, vous risquez une exposition involontaire d’informations. Les services ne peuvent accéder aux secrets que lorsqu'ils sont explicitement accordés par un attribut secrets dans l'élément de niveau supérieur des services.

Les variables d'environnement sont souvent disponibles pour tous les processus et il peut être difficile d'en suivre l'accès. Ils peuvent également être imprimés dans des journaux lors du débogage d’erreurs à votre insu. L'utilisation de secrets atténue ces risques.

Nous allons créer dans un premier temps les secrets suivants :

  • pgsql_password : mot de passe de l'utilisateur "paperless" pour PostgreSQL
  • paperless_secret_key : Paperless l'utilise pour créer des jetons de session. Si vous exposez l'application sur Internet, vous devez changer cela, puisque le secret par défaut est connu.
  • paperless_admin_password : mot de passe du compte admin (ppladmin01 dans cet exemple) créé lors de la première initialisation du conteneur.
  • paperless_email_host_password : mot de passe du compte mail utilisé pour recevoir des notifications.
Création des secrets
olivier@srv-docker:/srv/paperless-ngx$ tr -cd '[:alnum:]' < /dev/urandom | fold -w "64" | head -n 1 > secrets/postgresql/POSTGRES_PASSWORD 
olivier@srv-docker:/srv/paperless-ngx$ tr -cd '[:alnum:]' < /dev/urandom | fold -w "64" | head -n 1 > secrets/paperless-ngx/PAPERLESS_SECRET_KEY

Il vous reste ensuite à renseigner les mots de passe pour secrets/paperless-ngx/PAPERLESS_ADMIN_PASSWORD et secrets/paperless-ngx/PAPERLESS_EMAIL_HOST_PASSWORD. Personnellement j'utilise mon coffre-fort Vaultwarden pour générer et stocker ces mots de passe.

Configuration de Nginx

fichier de configuration du vhost
server {
    listen 80;
    server_name ged.mondomaine.tld;

    if ($host = ged.mondomaine.tld) {
        return 301 https://$host$request_uri;
    } 
}

server {
    listen 443 ssl http2;    
    server_name ged.mondomaine.tld;
    access_log      /var/log/nginx/ged.access.log;
    error_log       /var/log/nginx/ged.error.log;

    ssl_certificate /etc/letsencrypt/live/ged.mondomaine.tld/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/ged.mondomaine.tld/privkey.pem;
    include /etc/nginx/ssl/options-ssl-nginx.conf;
    ssl_dhparam /etc/nginx/ssl/ssl-dhparams.pem;

    location / {
        # Optionnel / Permet de limiter l'accès au réseau local
        # allow 127.0.0.1;
        # allow 192.168.1.0/24;
        # deny all;

        # Important pour uploader des documents
        # Ici la taille des fichiers est limitée à 20 Mo 
        client_max_body_size 20M;

        proxy_pass http://<IP_DU_SERVEUR_DOCKER>:8777/;

        # 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;
        add_header Referrer-Policy "strict-origin-when-cross-origin";
    }
}

Vous pouvez lancer la stack applicative et relancer Nginx.

Utilisation basique de Paperless-ngx

Connectez-vous sur l'application "https://ged.mondomaine.tld" et authentifiez-vous avec le compte admin que vous avez défini plus haut et créé lors de l'instanciation de l'application (dans cet exemple ppladmin01).

Authentification

Une fois authentifié, vous arrivez sur le dashboard vous proposant une visite. Ce dashboard permet d'afficher des vues personnelles filtrées sur des correspondants, des étiquettes, des types de document ou bien des dates.

Dans la partie administration, vous pouvez créer des utilisateurs et des groupes avec différents niveaux d'habiliation (accès complet, vue seulement...).

Avant d'uploader des documents, je vous conseille de créer en amont des correspondants (EDF, Trésor public,...), des étiquettes (État, Fournisseur...) et des types de documents (Facture, Bulletin de paie, Attestation...). Cependant vous pouvez tout de même créer tout cela à la volée lors du traitement d'un document.

Pour uploader un fichier, il vous suffit de vous rendre sur le tableau de bord et de cliquer sur le bouton "Parcourir les fichiers". Vous pouvez également déposer des fichiers dans /srv/paperless-ngx/consume et ils seront automatiquement détectés et traités par Paperless-ngx. Personnellement, j'ai poussé mes fichiers avec la commande "scp", mais on peut très bien créer un partage NFS, SFTP ou encore SMB si vous avez des clients Windows.

Lors des premiers upload de documents, vous allez devoir les classer manuellement. Mais assez rapidement, Paperless-ngx va apprendre et les classer automatiquement sans devoir préciser le correspondant par exemple. Et cela fonctionne aussi bien avec des fichiers générés au format PDF que des fichiers scannés.

Vous retrouverez l'ensemble de vos documents dans la partie Documents. À partir de là, vous pouvez filtrer les fichiers sur les options proposées (étiquettes, correspondant,...). Ces filtres peuvent être ensuite enregistrés dans des vues. Une fois le filtre défini, il suffit de cliquer sur "Vues" en haut à droite puis de l'enregistrer et choisir de l'afficher sur le dashboard par exemple.

vue

On peut définir également des workflows. Pour le moment, j'ai créé un workflow d'assignation des documents au groupe dont ma femme et moi-même faisons parti.

Utilisation de l'option courriel

En plus de pouvoir envoyer directement les fichiers depuis le site web ou en passant par la banette, vous pouvez configurer un collecteur mail (comme sur GLPI pour ceux qui l'utilisent). Pour cela, il vous suffit de vous rendre sur l'option Courriel.

Ajoutez un compte de messagerie sur lequel Paperless-ngx ira chercher des documents par le biais du protocole IMAP. Une fois le compte paramétré et testé, nous allons définir une règle de courriel comme ci-dessous :

Règle courriel

Le compte "Mail perso" est le nom du compte de messagerie créé juste avant. J'ai créé sur ma boite mail le dossier "Paperless" dans lequel je glisse les mails que je souhaite faire traiter par Paperless-ngx.

Le dossier "Paperless" utilisé étant dédié à Paperless-ngx, je n'ai pas précisé d'âge. Cette option peut cependant être utile si vous utilisez par défaut votre boite INBOX.

On peut créer différents filtres notamment sur l'adresse de l'expéditeur avec des règles d'affectation associées, ce qui impliquera de créer plusieurs règles mais vous évitera les erreurs de classement le temps de l'apprentissage.

Dans la partie Action, j'ai choisi l'option de déplacer vers le dossier spécifié "Paperless-archives" que j'ai créé sur ma boite mail et précisé dans la case "Paramètre d'action".

Une fois la règle déterminée, il vous suffit de patienter sachant que Paperless-ngx va consulter la boite mail toutes les 10 minutes.

Utilisation des applications mobiles

Sur iOS, j'utilise l'application gratuite Swift Paperless sous licence MIT qui permet d'accéder aux documents mais aussi de les scanner. L'application est "francisée" et fonctionne très bien. Lors du scan avec l'iphone, il est possible d'attribuer manuellement le correspondant, l'étiquette... avant l'envoi.

Sur Android, j'utilise l'application gratuite Paperless Mobile. Elle présente les mêmes fonctionnalités que Swift Paperless.

Authentification unique avec le protocole OIDC

Il est possible de configurer le SSO. Pour ma part, j'ai fait avec Authelia mais non documenté ici pour éviter d'alourdir cette doc. Cela fonctionne bien malgré un petit bug lors de la première connexion impliquant de se connecter en admin pour finaliser le profil de l'utilisateur connecté en SSO. Il faut dire que la fonctionnalité OIDC est récente. Il manque par exemple l'authentification d'application, ce qui est bloquant pour les applications sur smartphone.

Renforcement de la sécurité avec Crowdsec

Il existe une collection Crowdsec disponible sur le hub de ce dernier : Collection Crowdsec Paperless.

J'ai installé l'agent Crowdsec et la collection sur la machine hébergeant Paperless-ngx qui se connecte à mon serveur Crowdsec. Il suffit ensuite de configurer le fichier /etc/crowdsec/acquis.yaml pour lui indiquer le chemin du fichier de logs de Paperless-ngx.

Dans mon cas, j'ai préféré rendre accessible mon application uniquement à mon réseau local et VPN étant donné que je n'ai aucune raison d'exposer cette application sur Internet.

🏁 Conclusion

Cela faisait longtemps que je voulais me frotter à cette application mais j'avais quelques appréhensions. Elles ont vite été levées. Le logiciel est légé, intuitif et permet une rapide prise en main dans le cadre d'un usage personnel. Elle remplace dorénavant Nextcloud pour la gestion des papiers administratifs.

Elle peut être déployée dans un environnement professionnel mais demandera tout de même de définir en amont quelques règles de gestion et une matrice des droits. Cele demandera également quelques réglages plus fin en cas de traitement de volume important de documents.