URL: https://linuxfr.org/users/arodier/journaux/implementer-un-serveur-webdav-qui-fonctionne-sous-linux Title: Implementer un serveur Webdav - qui fonctionne - sous Linux. Authors: Andre Rodier Date: 2023年07月06日T20:18:43+02:00 License: CC By-SA Tags: webdav, selfhost et nginx Score: 20 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: ```conf 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: ```ini [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 ```lua 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. ```lua 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 ```conf # 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 ```conf 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é. - https://nginx.org/en/docs/http/ngx_http_dav_module.html - https://www.robpeck.com/2020/06/making-webdav-actually-work-on-nginx/ - https://blog.learn-or-die.com/buildAWebDavServerWithNginx/ - https://gist.github.com/akhilman/c3542309d07081226de455ff6160ab50 - [cryptomator](https://cryptomator.org/) - [homebox / role webdav](https://github.com/progmaticltd/homebox/tree/dev/roles/webdav)

AltStyle によって変換されたページ (->オリジナル) /