Site Tools


software:nginx:webdav

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revisionPrevious revision
Next revision
Previous revision
software:nginx:webdav [2022/02/04 18:33]
– removed - external edit (Unknown date) 127.0.0.1
software:nginx:webdav [2022/02/05 04:30] (current)
root
Line 1: Line 1:
 +====== Nginx and Microsoft Windows WebClient (WebDav) ======
 +В Microsoft Windows начиная с 2к встроен Веб Клиент, по сути это средство для монтирования сетевых дисков по протоколу WebDav, который ходит поверх HTTP/HTTPS.\\
 +Клиент писался явно под себя (связка с IIS) и от того работает весьма <del>коряво</del>своеобразно.\\
 +Это единственный способ "малой" кровью примонтировать диск через интернет не настраивая VPN для проброса SAMBA протокола.\\
 +SAMB-у в принципе тоже без VPN можно пробросить, но это из области поиска приключений с последствиями.\\
  
 +Nginx в базовом функционале имеет не полную поддержку [[http://nginx.org/ru/docs/http/ngx_http_dav_module.html|Dav]]: PUT DELETE MKCOL COPY MOVE.\\
 +Расширить его ещё двумя: PROPFIND и OPTIONS можно с помощью модуля: [[https://github.com/arut/nginx-dav-ext-module|dav-ext]]\\
 +
 +
 +Для работы потребуется Nginx собранный с поддержкой [[http://nginx.org/ru/docs/http/ngx_http_dav_module.html|Dav]], [[https://github.com/arut/nginx-dav-ext-module|dav-ext]], [[http://nginx.org/ru/docs/http/ngx_http_rewrite_module.html|rewrite]] и [[http://wiki.nginx.org/HttpHeadersMoreModule|headers_more]].\\
 +<code>[x] HTTP_DAV              Enable http_webdav module
 +[x] HTTP_DAV_EXT          3rd party webdav_ext module
 +[x] HTTP_REWRITE          Enable http_rewrite module
 +[x] HEADERS_MORE          3rd party headers_more module</code>
 +
 +
 +
 +===== Проблема 1 - Майкрософт нарушает стандарты и свои обещания =====
 +{{:software:nginx:webdav1.png|}}
 +
 +Перед тем как создать файл WebDav клиент проверяет наличие файла посылая запрос:
 +<code>PROPFIND /!!!!/test.lnk HTTP/1.1
 +Connection: Keep-Alive
 +User-Agent: Microsoft-WebDAV-MiniRedir/6.1.7601
 +Depth: 0
 +translate: f
 +Content-Length: 0
 +Host: 172.16.0.254:8089</code>
 +
 +В ответ IIS ему выдаёт:
 +<code>HTTP/1.1 404 Resource Not Found
 +Content-Length: 1635
 +Content-Type: text/html
 +Server: Microsoft-IIS/6.0
 +X-Powered-By: ASP.NET
 +Date: Sun, 10 Aug 2014 20:06:08 GMT
 +
 +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
 +...</code>
 +
 +Только это не правильно, должен быть код 207 и xml в котором описан элемент и код для него, те 404 должно быть в xml.\\
 +с точки зрения стандартов: [[http://tools.ietf.org/html/rfc2518#page-24]] \\
 +с точки зрения доков мс: [[http://msdn.microsoft.com/en-us/library/aa142960(v=exchg.65).aspx]] \\
 +У яндекса тоже в примерах xml: [[http://api.yandex.ru/disk/doc/dg/reference/propfind_property-request.xml]] \\
 +
 +Ответ Nginx:
 +<code>HTTP/1.1 207 Multi-Status
 +Server: nginx/1.7.4
 +Date: Sun, 10 Aug 2014 21:17:34 GMT
 +Transfer-Encoding: chunked
 +Connection: keep-alive
 +Keep-Alive: timeout=60
 +
 +47
 +<?xml version="1.0" encoding="utf-8" ?>
 +<D:multistatus xmlns:D="DAV:">
 +
 +cc
 +<D:response>
 +<D:href>/Family/test.lnk</D:href>
 +<D:propstat>
 +<D:prop>
 +</D:prop>
 +<D:status>HTTP/1.1 404 Not Found</D:status>
 +</D:propstat>
 +</D:response>
 +
 +11
 +</D:multistatus>
 +
 +0</code>
 +Такой "неожиданный" ответ сносит голову WebDav клиенту винды.\\
 +
 +**Фикс 1**: в конфиге (возможно это сведёт с ума остальные, порядочные WebDav клиенты, но лично мне нужен был только один не такой как все):
 +<code>error_page 599 = @propfind_handler;
 +if ($request_method = PROPFIND) {
 + return 599;
 +}
 +
 +location @propfind_handler {
 + internal;
 +
 + open_file_cache off;
 + if (!-e $webdav_root/$uri) { # Microsoft specific handle.
 + return 404;
 + }
 + root $webdav_root;
 + dav_ext_methods PROPFIND;
 +}</code>
 +
 +
 +
 +===== Проблема 2 - PROPPATCH отсутствует в nginx =====
 +{{:software:nginx:webdav2.png|}} \\
 +WebDav от мс очень хочет метод PROPPATCH, которого в Nginx и расширениях нет. Совсем нет.\\
 +Я рассматривал два варианта решения:\\
 +1. Написать плагин к Nginx или патч к dav-ext, короче Си код и пересборка Nginx.\\
 +2. Положится на кривость виндовой реализации WebDav и скормить статический ответ.\\
 +
 +Запрос:
 +<code>PROPPATCH /Family/test.lnk HTTP/1.1
 +Cache-Control: no-cache
 +Connection: Keep-Alive
 +Pragma: no-cache
 +Content-Type: text/xml; charset="utf-8"
 +User-Agent: Microsoft-WebDAV-MiniRedir/6.1.7601
 +translate: f
 +Content-Length: 443
 +Host: xxx.xxx.net
 +
 +<?xml version="1.0" encoding="utf-8" ?><D:propertyupdate xmlns:D="DAV:" xmlns:Z="urn:schemas-microsoft-com:"><D:set><D:prop><Z:Win32CreationTime>Sun, 10 Aug 2014 21:30:21 GMT</Z:Win32CreationTime><Z:Win32LastAccessTime>Sun, 10 Aug 2014 21:30:22 GMT</Z:Win32LastAccessTime><Z:Win32LastModifiedTime>Sun, 10 Aug 2014 21:30:22 GMT</Z:Win32LastModifiedTime><Z:Win32FileAttributes>00000020</Z:Win32FileAttributes></D:prop></D:set></D:propertyupdate></code>
 +
 +Ответ IIS:
 +<code>HTTP/1.1 207 Multi-Status
 +Date: Sun, 10 Aug 2014 12:24:47 GMT
 +Server: Microsoft-IIS/6.0
 +X-Powered-By: ASP.NET
 +Content-Type: text/xml
 +Transfer-Encoding: chunked
 +
 +<?xml version="1.0"?><a:multistatus xmlns:a="DAV:" xmlns:b="urn:schemas-microsoft-com:"><a:response><a:href>http://172.16.0.254:8088//!!!!/test.lnk</a:href><a:propstat><a:status>HTTP/1.1 200 OK</a:status><a:prop><b:Win32CreationTime/><b:Win32LastAccessTime/><b:Win32LastModifiedTime/><b:Win32FileAttributes/></a:prop></a:propstat></a:response></a:multistatus>
 +0</code>
 +"Пропритериарщина", - подумал я. Вот кому такое вообще надо?
 +И как эти все атрибуты писать в разных ОС и разных ФС?...
 +
 +**Фикс 2**
 +<code>if ($request_method = PROPPATCH) { # Unsupported, allways return OK.
 + add_header Content-Type 'text/xml';
 + return 207 '<?xml version="1.0"?><a:multistatus xmlns:a="DAV:"><a:response><a:propstat><a:status>HTTP/1.1 200 OK</a:status></a:propstat></a:response></a:multistatus>';
 +}</code>
 +
 +Из ответа IIS я выкинул всё что мне не понравилось и виндовый WebDav это проглотил.\\
 +Теперь одиночные и группы файлов без проблем можно копировать на сетевой диск примонтированный по WebDav.\\
 +Минусом - не выставляется оригинальная дата создания, модификации и атрибуты.\\
 +
 +
 +===== Проблема 3 - создание папок =====
 +{{:software:nginx:webdav3.png|}} \\
 +См п1 :)
 +
 +Приходит запрос:
 +<code>MKCOL /Family/MR3020 HTTP/1.1
 +Connection: Keep-Alive
 +User-Agent: Microsoft-WebDAV-MiniRedir/6.1.7601
 +translate: f
 +Content-Length: 0
 +Host: xxx.xxx.net</code>
 +
 +Nginx на него отвечает (немного странно выбирая код, на мой взгляд, но в принципе правильно):\\
 +<code>HTTP/1.1 409 Conflict
 +Server: nginx/1.7.4
 +Date: Sun, 10 Aug 2014 21:43:15 GMT
 +Content-Type: text/html
 +Content-Length: 166
 +Connection: keep-alive
 +Keep-Alive: timeout=60
 +
 +<html>
 +<head><title>409 Conflict</title></head>
 +<body bgcolor="white">
 +<center><h1>409 Conflict</h1></center>
 +<hr><center>nginx/1.7.4</center>
 +</body>
 +</html></code>
 +
 +IIS отвечает: "HTTP/1.1 201 Created", - смотреть там не на что.\\
 +
 +Как должно быть описано:\\
 +У майкрософта: [[http://msdn.microsoft.com/en-us/library/aa142923(v=exchg.65).aspx]] \\
 +У яндекса: [[http://api.yandex.ru/disk/doc/dg/reference/mkcol.xml]] \\
 +И даже в RFC: [[http://tools.ietf.org/html/rfc2518#page-33]] \\
 +
 +Во всех примерах URL оканчивается слешем. \\
 +Но только не в запросе WebDav клиента от мс. \\
 +
 +Вариантов опять было два: \\
 +1. Поправить файл: [[http://lxr.nginx.org/source/src/http/modules/ngx_http_dav_module.c]] строчки 484 - 493, там как раз проверка наличия слеша и его отрезание. \\
 +2. Пофиксить через конфиг.\\
 +
 +Вариант 1 я оставляю на усмотрение програмеров nginx, может по стандарту оно и должно ругаться.\\
 +
 +**Фикс 3**:
 +<code>if ($request_method = MKCOL) { # Microsoft specific handle: add trailing slash.
 + rewrite ^(.*[^/])$ $1/ break;
 +}</code>
 +Вот для этого пустяка и потребовался REWRITE плагин.\\
 +
 +
 +===== Проблема 4 - удаление папок =====
 +Ноги тут те же что и в п3: отсутствие слеша на конце урла.\\
 +Однако я столкнулся с тем, что nginx тоже ведёт себя несколько странно: если слеш на конце отсутствует, то nginx считает что его просят удалить файл, и получает ошибку: 21: Is a directory при попытке удалить.\\
 +
 +
 +**Фикс 4**:
 +<code>error_page 598 = @delete_handler;
 +if ($request_method = DELETE) {
 + return 598;
 +}
 +
 +location @delete_handler {
 + internal;
 +
 + open_file_cache off;
 + if (-d $webdav_root/$uri) { # Add trailing slash to dirs.
 + rewrite ^(.*[^/])$ $1/ break;
 + }
 + root $webdav_root;
 + dav_methods DELETE;
 +}</code>
 +Если кратко, то переносим обработку DELETE в отдельный локейшин (процедуру), дальше проверяем, если удаляется папка то добавляем слеш.\\
 +
 +
 +===== Проблема 5 - копирование и переименование =====
 +Ноги тут те же что и в п4: отсутствие слеша на конце урла и в Destination заголовке.\\
 +Для решения проблемы с добавлением "/" в заголовок Destinaton нам потребуется модуль headers_more.\\
 +
 +**Фикс 6**:
 +<code>error_page 597 = @copy_move_handler;
 +if ($request_method = COPY) {
 + return 597;
 +}
 +if ($request_method = MOVE) {
 + return 597;
 +}
 +
 +location @copy_move_handler {
 + internal;
 +
 + open_file_cache off;
 + if (-d $webdav_root/$uri) { # Add trailing slash to dirs.
 + more_set_input_headers 'Destination: $http_destination/';
 + rewrite ^(.*[^/])$ $1/ break;
 + }
 + root $webdav_root;
 + dav_methods COPY MOVE;
 +}</code>
 +
 +
 +
 +===== Проблема 6 - OPTIONS корня =====
 +Клиент запрашивает опции не у той папки которую мы хотим чтобы он подключил а у корня сервера.\\
 +
 +**Фикс 6**:
 +<code>location / {
 + if ($request_method = OPTIONS) {
 + add_header Allow 'OPTIONS, GET, HEAD, POST, PUT, MKCOL, MOVE, COPY, DELETE, PROPFIND, PROPPATCH';
 + add_header DAV '1, 2';
 + return 200;
 + }
 +}
 +</code>
 +Возвращаем ему статический список.\\
 +Можно было просто разрешить: dav_ext_methods OPTIONS;\\
 +
 +
 +===== Проблема 7 - виндовый клиент не может подключится =====
 +Соединение происходит но в логах nginx ничего нет, а клиент сообщает об ошибке.\\
 +В логах с включённым дебагом ssl видно что рукопожатие не проходит.\\
 +Проблема у меня проявилась не так давно, после обновления OpenSSL.\\
 +При этом IE и другие браузеры могут авторизоваться по этому URL и отобразить листинг директории.\\
 +
 +**Фикс 7**:
 +Заменить OpenSSL на LibreSSL и пересобрать nginx.
 +Возможно OpenSSL уже исправили.
 +
 +
 +===== Ещё немного о Nginx =====
 +Не трудно заметить, что nginx не добавляет в ответы:\\
 +<code>Content-Type: text/xml</code>
 +и хотя в данном случае это не создаёт проблем, но всё же это не правильно.\\
 +
 +
 +===== Конфиг nginx для WebDav =====
 +WebDav для Nginx:\\
 +<code># WebDAV
 +set $webdav_root "/mnt/WebDav_folder";
 +location ^~ /Family {
 + if ($ssl_protocol = "") { # Block non ssl/tls connections.
 + add_header Strict-Transport-Security 'max-age=600';
 + return 403;
 + }
 + auth_basic "Private site";
 + auth_basic_user_file /usr/local/etc/nginx/secure/authbasic.htpasswd;
 +
 + root $webdav_root;
 + error_page 599 = @propfind_handler;
 + error_page 598 = @delete_handler;
 + error_page 597 = @copy_move_handler;
 + open_file_cache off;
 + client_max_body_size 50m;
 +
 + if ($request_method = PROPFIND) {
 + return 599;
 + }
 + if ($request_method = PROPPATCH) { # Unsupported, allways return OK.
 + add_header Content-Type 'text/xml';
 + return 207 '<?xml version="1.0"?><a:multistatus xmlns:a="DAV:"><a:response><a:propstat><a:status>HTTP/1.1 200 OK</a:status></a:propstat></a:response></a:multistatus>';
 + }
 + if ($request_method = MKCOL) { # Microsoft specific handle: add trailing slash.
 + rewrite ^(.*[^/])$ $1/ break;
 + }
 + if ($request_method = DELETE) {
 + return 598;
 + }
 + if ($request_method = COPY) {
 + return 597;
 + }
 + if ($request_method = MOVE) {
 + return 597;
 + }
 +
 + dav_methods PUT MKCOL;
 + dav_ext_methods OPTIONS;
 + create_full_put_path on;
 + min_delete_depth 0;
 + dav_access user:rw group:rw all:rw;
 +
 + autoindex on;
 + autoindex_exact_size on;
 + autoindex_localtime on;
 +}
 +location @propfind_handler {
 + internal;
 +
 + auth_basic "Private site";
 + auth_basic_user_file /usr/local/etc/nginx/secure/authbasic.htpasswd;
 +
 + open_file_cache off;
 + if (!-e $webdav_root/$uri) { # Microsoft specific handle.
 + return 404;
 + }
 + root $webdav_root;
 + dav_ext_methods PROPFIND;
 +}
 +location @delete_handler {
 + internal;
 +
 + auth_basic "Private site";
 + auth_basic_user_file /usr/local/etc/nginx/secure/authbasic.htpasswd;
 +
 + open_file_cache off;
 + if (-d $webdav_root/$uri) { # Microsoft specific handle: Add trailing slash to dirs.
 + rewrite ^(.*[^/])$ $1/ break;
 + }
 + root $webdav_root;
 + dav_methods DELETE;
 +}
 +location @copy_move_handler {
 + internal;
 +
 + auth_basic "Private site";
 + auth_basic_user_file /usr/local/etc/nginx/secure/authbasic.htpasswd;
 +
 + open_file_cache off;
 + if (-d $webdav_root/$uri) { # Microsoft specific handle: Add trailing slash to dirs.
 + more_set_input_headers 'Destination: $http_destination/';
 + rewrite ^(.*[^/])$ $1/ break;
 + }
 + root $webdav_root;
 + dav_methods COPY MOVE;
 +}
 +location / {
 + if ($request_method = OPTIONS) {
 + add_header Allow 'OPTIONS, GET, HEAD, POST, PUT, MKCOL, MOVE, COPY, DELETE, PROPFIND, PROPPATCH';
 + add_header DAV '1, 2';
 + return 200;
 + }
 +}
 +</code>
 +
 +open_file_cache off; - нужно потому что мы меняем файлы, если кеширование включено то можно будет долго гадать почему не видно файлы которые мы только что закинули на сервер.\\
 +Также в самом начале локейшена есть требование использовать SSL и если SSL используется то запрашивается BASIC авторизация.
 +==== Важно ====
 +  - set $webdav_root "/mnt/WebDav_folder"; - **полный путь** на диске к папке которую мы расшариваем. Папка должна существовать и на неё должны быть выставлены права доступа которые позволят nginx получить доступ к содержимому, читать и записывать файлы.
 +  - в папке "/mnt/WebDav_folder" должна существовать папка Family
 +  - location ^~ **/Family** - означает что клиент должен использовать URL: https://SERVER_ADDRESS**/Family**
 +  - директивы **auth_basic** и **auth_basic_user_file** должны быть в каждом **location** который осуществляет обработку запросов, см [[https://github.com/dgraziotin/docker-nginx-webdav-nononsense/issues/15|DELETE requests work unauthenticated]]
 +
 +
 +===== Немного о настройке клиента =====
 +Он как капризный ребёнок, из коробки ему подавай SSL и никакой Basic аутентификации, файлы не больше 50 мегабайт, в папках не более 500-1000 файлов.\\
 +Увы, но даже после всех проделанных настроек файлы более 4гб передавать не получится. (Скорее всего из за кривой реализации, которая файлы при открытии скачивает в память/на диск чтобы получить более менее стандартный файловый дискриптор, это мои домыслы.)\\
 +
 +Вот здесь собраны все настройки с описанием: [[http://blogs.msdn.com/b/robert_mcmurray/archive/2008/01/17/webdav-redirector-registry-settings.aspx]] \\
 +
 +На данный момент мои настройки WebClient выглядят так:\\
 +<code>Windows Registry Editor Version 5.00
 +
 +[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\services\WebClient\Parameters]
 +"SupportLocking"=dword:00000000
 +"InternetServerTimeoutInSec"=dword:0000001e
 +"ServiceDllUnloadOnStop"=dword:00000001
 +"ServerNotFoundCacheLifeTimeInSec"=dword:0000000a
 +"ClientDebug"=dword:00000000
 +"FileSizeLimitInBytes"=dword:ffffffff
 +"SendReceiveTimeoutInSec"=dword:0000003c
 +"LocalServerTimeoutInSec"=dword:0000000f
 +"FileAttributesLimitInBytes"=dword:00989680
 +"AcceptOfficeAndTahoeServers"=dword:00000001
 +"ServiceDebug"=dword:00000000
 +"BasicAuthLevel"=dword:00000002</code>
 +
 +
 +===== Полезное =====
 +  * Дальнейшая разработка решения происходит тут: [[https://github.com/dgraziotin/docker-nginx-webdav-nononsense|docker-nginx-webdav-nononsense]] - настоятельно рекомендую хотя бы посмотреть все решения которые там применены
 +  * Патч [[https://github.com/arut/nginx-dav-ext-module/pull/56|Fix PROPFIND fail with 500 on simlinks to non exist file/dir.]]
 +
 +
 +
 +{{tag>software howto windows net nginx WebDAV}}
software/nginx/webdav.txt · Last modified: 2022/02/05 04:30 by root