Journal Implementer un serveur Webdav - qui fonctionne - sous Linux.

Posté par  (site web personnel) . Licence CC By‑SA.
Étiquettes :
20
6
juil.
2023

Sommaire

Vu dans un journal précédent (https://linuxfr.org/users/ploum/journaux/vos-services-pour-mail-calendrier-et-synchro-de-dossiers), voici un peu de contenu utile, enfin, j'espère.

Ce sont des exemples / modèles de configuration que j'ai utilisé sur Homebox, mais que vous devriez normalement être en mesure d'utiliser, en tout cas, l'idée.

Si vous voulez une solution clé en main, désolé, ce n'est pas le but du journal.

Il semble que lorsque l'on utilise nginx en lieu et place d'Apache, l’implémentation soit plus difficile que prévu.

Mes contraintes de départ

  • Comme j'ai déjà nginx, je ne veux pas en plus utiliser Apache.
  • Je veux utiliser un serveur pour chaque utilisateur, qui s’exécute avec ses droits.
  • Je veux utiliser l'authentification de la base LDAP de mon serveur.
  • Je veux utiliser des permissions strictes, c'est à dire restreindre restreindre l'accès des fichiers crées à l'utilisateur qui les a envoyé.
  • Je veux pouvoir renommer et déplacer des dossiers et des fichiers sans erreurs.

Je vous laisse deviner le sens des macros Jinja2, mais cela ne devrait pas vous empêcher de comprendre la logique.

Le serveur en tant qu'utilisateur

Le premier serveur, tourne comme un service systemd utilisateur, dont voici la configuration:

error_log /home/archives/{{ user.uid }}/webdav/webdav-error.log;

pid /home/archives/{{ user.uid }}/webdav/nginx.pid;

# Modules to load
load_module modules/ndk_http_module.so;
load_module modules/ngx_http_lua_module.so;
load_module modules/ngx_http_dav_ext_module.so;

events {
    worker_connections {{ workers_connections }};
}

http {

    sendfile on;
    tcp_nopush on;
    types_hash_max_size 2048;

    include /etc/nginx/mime.types;
    default_type application/octet-stream;

    # log files per virtual host
    access_log /var/tmp/webdav-user/{{ user.uid }}/webdav-access.log combined;
    error_log /var/tmp/webdav-user/{{ user.uid }}/webdav-error.log;

    # Define lua functions
    init_by_lua_file  /var/www/webdav/functions/init.lua;

    # DAV parameters
    dav_ext_lock_zone zone={{ network.domain }}:10m;

    server {

        # Listen on a socket file
        listen unix:/var/tmp/webdav-user/{{ user.uid }}/socket;

        # Default webdav root, although this should never be accessed
        root /home/archives/{{ user.uid }}/files;
        autoindex on;

        location / {
            dav_methods PUT DELETE MKCOL COPY MOVE;
            dav_ext_methods PROPFIND OPTIONS LOCK UNLOCK;
            dav_ext_lock zone={{ network.domain }};

            # Upload parameters
            client_body_temp_path /var/tmp/webdav-user/{{ user.uid }}/tmp/;

            # Creation parameters
            dav_access user:rw;
            create_full_put_path on;

            # Handle complex DAV functions
            rewrite_by_lua_file /var/www/webdav/functions/rewrite.lua;

            # Optimisations for file transfer
            send_timeout 3600;
            client_body_timeout 3600;
            keepalive_timeout 3600;
            lingering_timeout 3600;
            client_max_body_size 10G;

            root /home/archives/{{ user.uid }}/files/;
            autoindex on;
        }

        # Do not use a favicon
        location ~ ^/favicon.ico$ {
            return 204;
            log_not_found off;
            access_log off;
            expires max;
        }

    }

}

Comme on peut le voir, il tourne sur un socket unix, et non sur un port. Cela permet, par exemple avec des ACL posix, de compartimenter chaque serveur plus facilement.

Le service systemd est assez simple:

[Unit]
Description=User webdav server
ConditionDirectoryNotEmpty=%h/.config/webdav/

[Service]
Type=forking
ExecStart=/usr/sbin/nginx -c %h/.config/webdav/nginx.conf
Restart=on-failure

[Install]
WantedBy=default.target

Comme on peut le voir plus haut, la magie est dans les fonctions Lua:

Initialisation

local lfs = require "lfs"

function is_dir(path)

   local success, attr = pcall(lfs.attributes, path)

   if not success then
      return false
   end

   if type(attr) ~= "table" then
      return false
   end

   return attr.mode == "directory"

end

Réecriture des requêtes

C'est la fonction qui permet de ne pas avoir d'erreur lorsque l'on déplace ou renomme un dossier.

local dir_requested = is_dir(ngx.var.request_filename)

if ngx.req.get_method() == "MKCOL" and not ngx.re.match(ngx.var.uri, "^.*/$") then

    -- When creating a collection, ensure the path ends with '/'
    local uri = ngx.re.sub(ngx.var.uri, "^(.*?)/?$", "$1/")
    ngx.req.set_uri(uri, true)

elseif dir_requested and not ngx.re.match(ngx.var.uri, "^.*/$") then

    -- URL should end with "/" if directory requested
    local uri = ngx.re.sub(ngx.var.uri, "^(.*?)/?$", "$1/")
    ngx.req.set_uri(uri, true)

end

local dst = ngx.req.get_headers()["Destination"]

if dst then
    -- Remove hostname from destination
    dst = ngx.re.sub(dst, "^(https?://.+?)?(/.*)$", "$2")

    -- Rename the folder Destination does not end with a /,
    -- it is necessary headers-more-nginx-module
    if dir_requested then
        dst = ngx.re.sub(dst, "^(.*?)/?$", "$1/")
    end

    ngx.req.set_header("Destination", dst)
end

-- PROPPATCH no instruction processing PROPFIND.
if ngx.req.get_method() == "PROPPATCH" then
    ngx.req.set_method(ngx.HTTP_PROPFIND)
end

Serveur parent

Le serveur parent est un proxy vers chaque serveur utilisateur. Lui, s'occupe de l'authentification (avec nginx_pam), et de passer les requêtes au serveur sous-jacent.

Service nginx / pam

# Deployed by {{ role_name }} role
# This allows other web sites to use nginx authentication
auth    required     pam_ldap.so
account required     pam_ldap.so

Configuration nginx

dav_ext_lock_zone zone={{ network.domain }}:10m;

server {

    # webdav FQDN
    server_name webdav.{{ network.domain }};

    # Listen on both IPv4 and IPv6
    listen 80 http2;
    listen 443 ssl http2;
    listen [::]:443 ssl http2;

    # Add security headers
    {% for sh in nginx_sec_headers -%}
    add_header {{ sh.id }} {{ sh.value | quote }};
    {% endfor %}

    # Add Content security policy
    add_header Content-Security-Policy "...";

    # Features policy
    add_header Feature-Policy "...";

    # Enforce https
    if ($https != "on") {
        return 301 https://$host$request_uri;
    }

    # SSL configuration
    ssl_certificate /etc/ssl/certs/webdav.{{ network.domain }}.crt;
    ssl_certificate_key /etc/ssl/private/webdav.{{ network.domain }}.key;
    ssl_trusted_certificate /etc/ssl/certs/webdav.{{ network.domain }}.issuer.crt;

    ssl_protocols {{ security.tls.versions | join(" ") }};
    ssl_ciphers {{ security.tls.openssl_ciphers | join(":") }};
    ssl_prefer_server_ciphers off;

    # OCSP stapling
    ssl_stapling on;
    ssl_stapling_verify on;

    # Remove useless tokens for better security feelings ;-)
    server_tokens off;

    # pam authentication
    auth_pam {{ network.domain }};
    auth_pam_service_name "nginx";
    allow 127.0.0.1;
    allow ::1;
    satisfy any;
    deny all;

    # Default webdav root, although this should never be accessed
    root /var/www/webdav/default;
    index index.html;

    # This might help for office files
    gzip on;

    location / {

        # pam authentication
        auth_pam {{ network.domain }};
        auth_pam_service_name "nginx";
        allow 127.0.0.1;
        allow ::1;
        satisfy any;
        deny all;

        # Dynamically forward to user socket
        proxy_pass http://unix:/var/tmp/webdav-user/$remote_user/socket;

        root /home/archives/$remote_user/files;
        autoindex on;
    }

    # Do not use a favicon
    location ~ ^/favicon.ico$ {
        return 204;
        log_not_found off;
        access_log off;
        expires max;
    }

    # log files per virtual host
    access_log /var/log/nginx/webdav-access.log combined if=$loggable;
    error_log /var/log/nginx/webdav-error.log;
}

Tests de clients WebDAV

J'ai testé cette configuration avec le client Gnome, et des clients Android.

Pour le client Android, DavX marche à merveille.

J'utilise aussi Cryptomator, un excellent client WebDAV avec le chiffrage côté client. À noter que ce dernier est aussi disponible sous Linux.

Cela permet donc de stocker des fichers "dans un cloud", sans devoir faire une confiance aveugle au fournisseur.

Questions

  • Idéalement, démarrer le service utilisateur systemd à la demande.
  • Si vous avez des questions ou des remarques, mettez les en commentaire, je répondrai si possible.
  • Si vous avez des remarques sur d'éventuels problème de sécurité, envoyez les-moi par email ou Jabber.

Quelques liens

Voici quelques liens, notamment sur des blogs externes, dont je me suis largement inspiré.

  • # Mauvais souvenirs pour moi

    Posté par  . Évalué à 1.

    C'était dans un contexte épicé à l'époque mais en 2023, le webdav n'est
    clairement pas recommandé pour de nombreuses raisons.
    Cela correspond sans doute à ton cas d'usage et ton journal pourra sans doute
    aider du monde mais je ne peux que prévenir les bidouilleurs qui voudraient le
    déployer en loucedé dans un contexte pro : "Etudiez bien les autres options et faites valider par des vieux barbus genre des archi sécus ou des admins portant des tshirts de death metal."

    • [^] # Re: Mauvais souvenirs pour moi

      Posté par  (site web personnel) . Évalué à 7.

      Il y a un gar avec un pingouin en peluche sur son bureau, tu crois que je peux lui demander?
      Plus sérieusement, je ne trouve rien de très concluant sur le fait qu'il ne faudrait plus utiliser ce protocole (en cherchant 2 minutes).

      Un LUG en Lorraine : https://enunclic-cappel.fr

    • [^] # Re: Mauvais souvenirs pour moi

      Posté par  (site web personnel, Mastodon) . Évalué à 7.

      Et la raison c'est ?

      J'ai développé un serveur et un client WebDAV récemment, et c'est un protocole relativement simple et solide, je ne sais pas d'où vient cette mauvaise réputation, à part la qualité nullissime des clients Windows et OSX (davfs sur Linux n'est pas génial non plus…).

      « Je vois bien à quels excès peut conduire une démocratie d'opinion débridée, je le vis tous les jours. » (Nicolas Sarkozy)

      • [^] # Re: Mauvais souvenirs pour moi

        Posté par  (Mastodon) . Évalué à 4.

        Du coup, tu pourrais nous donner une liste de clients Linux de qualitay ?

        En théorie, la théorie et la pratique c'est pareil. En pratique c'est pas vrai.

        • [^] # Re: Mauvais souvenirs pour moi

          Posté par  (site web personnel) . Évalué à 6.

          AMHA, je pense que comme le protocole est approprié pour des échanges non continu. Je pense par exemple à la sauvegarde et la récupération de fichiers personnels, ou même la lecture de média à distance (audio ou vidéos). Depuis un téléphone mobile ou un ordinateur portable, franchement, ça marche très bien.

          Évidemment, si on cherche à utiliser le protocole pour monter un système de fichier en permanence (e.g. /home), on cherche les ennuis. Pour ces cas là, il vaut mieux utiliser ceux qui sont fait pour ça, comme NFS ou Samba.

          WebDAV s'appuie sur un protocole déconnecté (HTTP)

        • [^] # Re: Mauvais souvenirs pour moi

          Posté par  (site web personnel, Mastodon) . Évalué à 3.

          Il y a une liste de clients dans la doc de KaraDAV : https://github.com/kd2org/karadav/#webdav-clients-compatibility

          Mon expérience est que davfs2 sur Linux est une plaie, que l'implémentation de Gnome VFS est plutôt lente, mais que sous KDE ou avec webdavfs c'est plutôt bien :)

          Sous Windows/OSX y'a cyberduck/mountainduck.

          « Je vois bien à quels excès peut conduire une démocratie d'opinion débridée, je le vis tous les jours. » (Nicolas Sarkozy)

      • [^] # Re: Mauvais souvenirs pour moi

        Posté par  . Évalué à 1.

        Il est mauvais parce que lent,qu'il ne gère pas les reprises en cas de perte de connexion et protection contre l'écrasement.

        Il existe des extensions, mais elles ne sont généralement pas mises en place.

  • # Emplacement du fichier socket

    Posté par  . Évalué à 8.

    Bonjour,

    Ne serait-ce pas plus propre d’un point de vue LFS de mettre le socket sous /var/run au lieu de /var/tmp ? Je pense notamment aux systèmes avec SELinux qui ne vont peut être pas aimer car /var/tmp n’a pas les mêmes labels que /var/run.

    • [^] # Re: Emplacement du fichier socket

      Posté par  (site web personnel) . Évalué à 3.

      Oui, la remarque est pertinente.

      Le raison pour laquelle j'utilise /var/tmp, est la présence d'ACL posix. Lorsque le socket est crée, il est automatiquement marqué avec une permission en lecture de l'utilisateur www-data.

      Cependant, peut être pourrais-je utiliser /var/lib en lieu et place.

  • # lighttpd sur raspberry pi et raisons de ne pas choisir webdav

    Posté par  . Évalué à 1. Dernière modification le 07 juillet 2023 à 00:02.

    Hello. Merci pour ce partage de Webdav sous Nginx.

    Pour ma part 2 min ne suffisaient pas il y a quelques années à se faire une idée des risques et potentiels même pour un vulgaire averti comme moi qui ne connaissait pas le sujet.

    Ce que j'en ai retenu, au fil des ans, c'était plus des problèmes de perf que de sécu, mais perso je m'en fichais à partir du moment où je l'auto-hébergeais dans une framboise. Ce qui par contre fait sens dans un cadre pro.

    Il me semble que les arguments se trouvaient dans un article de Framasoft concernant leur nouvelle archi Nextcloud… ou/et dans la doc ou les discussions ayant mené à l'emploi du client Karadav dans un logiciel de gestion déjà apparu dans ces colonnes (me souviens plus du nom). EDIT c'est BohwaZ ci-dessus qui l'a dev (ça je m'en souviens enfin, maintenant que je relis le fil de la discussion… j'etais en train d'écrire aussi)!

    Et justement Karadav a été choisi pour limiter les lourdeurs des clients "natifs", développé ou/et co-maintenu dans cet esprit-là, même si de mémoire il est en berne désormais (repose sur des composants obsolètes ou/et nécessitant du hack).

    Concernant l'article de Framasoft (ou une vidéo), il me semble qu'il indiquait des initiatives ayant pour but d'améliorer des fonctionnalités comme la recherche avec des extensions, autrement pourrie en natif Nextcloud côté serveur.

    J'ai dû marquer les pages web dans mes Signets j'essaierai de les retrouver, mais "Framasoft webdav extension recherche Nextcloud" devrait aider à retrouver l'article.

    Et il y a quelques années, j'ai implémenté assez rapidement sur un Pi2 un Webdav très simple grâce à l'article suivant, à l'époque plus simple même car il ne mettait pas l'accent sur l'optionnel certificat (qui à la date de la web.archive représente plus de la moitié de l'article) :

    https://web.archive.org/web/20220705040728/https://nerd.one/how-to-setup-secure-webdav-on-raspberry-pi/ (le site originel retourne une 500 à l'heure où je vous écrit).

    J'ai utilisé ce Webdav pour augmenter l'espace de stockage de mon Nextcloud Disroot… Le top! A condition de ne pas regarder les perf… ni la sécu d'ailleurs.

  • # errata

    Posté par  . Évalué à 3.

    La page github du projet Karadav lui rend justice car j'ai écorché plusieurs choses à son sujet. Ce n'est pas un client (bien qu'un client web soit fourni), mais bien un serveur. Et sur cette même page il y a aussi des comparaisons de perfs serveur concernant Webdav qui accablent Nextcloud.

    Bohwaz en parlera mieux que moi et on trouve un article et sa discussion sur Karadav dans Linuxfr.org.

  • # WebDAV, Nginx et Windows ...

    Posté par  (site web personnel, Mastodon) . Évalué à 10. Dernière modification le 07 juillet 2023 à 13:07.

    Il y a quelques années, on a implémenté le support de WebDAV dans Tracim. On n'a pas développé un serveur mais on a intégré WsgiDAV avec les mécanismes natifs de Tracim : stockage de certaines données en base de données, versionning, etc.

    On a mis un bon moment à stabiliser le truc car chaque client fonctionne différemment. Au bout d'un moment on avait toujours des problèmes sous Windows, MacOS et impossible d'identifier la source. On a vraiment, vraiment dépensé de l'énergie sur notre code … pour nous rendre compte au final que c'était juste nginx qui proposait juste une implémentation partielle du protocole.

    On est (re)passé sur Apache en frontal et pouf, tout s'est mis à fonctionner. Aujourd'hui je ne sais pas si la situation a évolué, mais je conseillerais plutôt de déployer avec Apache.

    À noter que sous linux, avec les gestionnaires de fichiers natifs, on n'avait aucun problème ; c'était vraiment lié aux clients Windows & MacOS. Sous Windows on avait testé cyberduck qui marchait très bien.

    • [^] # Re: WebDAV, Nginx et Windows ...

      Posté par  (site web personnel) . Évalué à 6.

      Effectivement, l'implémentation WebDAV d'Apache est excellente, je confirme, je l'ai utilisée avec succès pour des environment sous Windows.

      Dans ce cas là, cependant, nginx me convenais mieux. Je voulais pouvoir faire tourner en tant qu'utilisateur, et éviter de mixer les deux serveurs sur le même système.

      Finalement, la compatibilité avec Windows n'était pas ma priorité.

      En plus, les scripts lua, j'ai trouvé ça vraiment intéressant et très souple.

      • [^] # Re: WebDAV, Nginx et Windows ...

        Posté par  . Évalué à 4.

        Je confirme que, même si ce n'est pas ce qu'il y a de plus sexy, souvent du fait de clients moisis; sous apache ça juste marche !

        Comme dans beaucoup de cas de mon expérience, Nginx est très bien pour le b-a-ba : serveur de fichier, proxy simple. Dés qu'on veut des fonctionnalités plus évolués ou spécifiques, Apache et sa complexité qu'il ne faut pas nier est une (la ?) bonne solution.

Suivre le flux des commentaires

Note : les commentaires appartiennent à celles et ceux qui les ont postés. Nous n’en sommes pas responsables.