This shows you the differences between two versions of the page.
Both sides previous revisionPrevious revisionNext 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к встроен Веб Клиент, | ||
+ | Клиент писался явно под себя (связка с IIS) и от того работает весьма < | ||
+ | Это единственный способ " | ||
+ | SAMB-у в принципе тоже без VPN можно пробросить, | ||
+ | Nginx в базовом функционале имеет не полную поддержку [[http:// | ||
+ | Расширить его ещё двумя: PROPFIND и OPTIONS можно с помощью модуля: | ||
+ | |||
+ | |||
+ | Для работы потребуется Nginx собранный с поддержкой [[http:// | ||
+ | < | ||
+ | [x] HTTP_DAV_EXT | ||
+ | [x] HTTP_REWRITE | ||
+ | [x] HEADERS_MORE | ||
+ | |||
+ | |||
+ | |||
+ | ===== Проблема 1 - Майкрософт нарушает стандарты и свои обещания ===== | ||
+ | {{: | ||
+ | |||
+ | Перед тем как создать файл WebDav клиент проверяет наличие файла посылая запрос: | ||
+ | < | ||
+ | Connection: Keep-Alive | ||
+ | User-Agent: Microsoft-WebDAV-MiniRedir/ | ||
+ | Depth: 0 | ||
+ | translate: f | ||
+ | Content-Length: | ||
+ | Host: 172.16.0.254: | ||
+ | |||
+ | В ответ IIS ему выдаёт: | ||
+ | < | ||
+ | Content-Length: | ||
+ | Content-Type: | ||
+ | Server: Microsoft-IIS/ | ||
+ | X-Powered-By: | ||
+ | Date: Sun, 10 Aug 2014 20:06:08 GMT | ||
+ | |||
+ | < | ||
+ | ...</ | ||
+ | |||
+ | Только это не правильно, | ||
+ | с точки зрения стандартов: | ||
+ | с точки зрения доков мс: [[http:// | ||
+ | У яндекса тоже в примерах xml: [[http:// | ||
+ | |||
+ | Ответ Nginx: | ||
+ | < | ||
+ | Server: nginx/1.7.4 | ||
+ | Date: Sun, 10 Aug 2014 21:17:34 GMT | ||
+ | Transfer-Encoding: | ||
+ | Connection: keep-alive | ||
+ | Keep-Alive: timeout=60 | ||
+ | |||
+ | 47 | ||
+ | <?xml version=" | ||
+ | < | ||
+ | |||
+ | cc | ||
+ | < | ||
+ | < | ||
+ | < | ||
+ | < | ||
+ | </ | ||
+ | < | ||
+ | </ | ||
+ | </ | ||
+ | |||
+ | 11 | ||
+ | </ | ||
+ | |||
+ | 0</ | ||
+ | Такой " | ||
+ | |||
+ | **Фикс 1**: в конфиге (возможно это сведёт с ума остальные, | ||
+ | < | ||
+ | if ($request_method = PROPFIND) { | ||
+ | return 599; | ||
+ | } | ||
+ | |||
+ | location @propfind_handler { | ||
+ | internal; | ||
+ | |||
+ | open_file_cache off; | ||
+ | if (!-e $webdav_root/ | ||
+ | return 404; | ||
+ | } | ||
+ | root $webdav_root; | ||
+ | dav_ext_methods PROPFIND; | ||
+ | }</ | ||
+ | |||
+ | |||
+ | |||
+ | ===== Проблема 2 - PROPPATCH отсутствует в nginx ===== | ||
+ | {{: | ||
+ | WebDav от мс очень хочет метод PROPPATCH, которого в Nginx и расширениях нет. Совсем нет.\\ | ||
+ | Я рассматривал два варианта решения: | ||
+ | 1. Написать плагин к Nginx или патч к dav-ext, короче Си код и пересборка Nginx.\\ | ||
+ | 2. Положится на кривость виндовой реализации WebDav и скормить статический ответ.\\ | ||
+ | |||
+ | Запрос: | ||
+ | < | ||
+ | Cache-Control: | ||
+ | Connection: Keep-Alive | ||
+ | Pragma: no-cache | ||
+ | Content-Type: | ||
+ | User-Agent: Microsoft-WebDAV-MiniRedir/ | ||
+ | translate: f | ||
+ | Content-Length: | ||
+ | Host: xxx.xxx.net | ||
+ | |||
+ | <?xml version=" | ||
+ | |||
+ | Ответ IIS: | ||
+ | < | ||
+ | Date: Sun, 10 Aug 2014 12:24:47 GMT | ||
+ | Server: Microsoft-IIS/ | ||
+ | X-Powered-By: | ||
+ | Content-Type: | ||
+ | Transfer-Encoding: | ||
+ | |||
+ | <?xml version=" | ||
+ | 0</ | ||
+ | " | ||
+ | И как эти все атрибуты писать в разных ОС и разных ФС?... | ||
+ | |||
+ | **Фикс 2** | ||
+ | < | ||
+ | add_header Content-Type ' | ||
+ | return 207 '<? | ||
+ | }</ | ||
+ | |||
+ | Из ответа IIS я выкинул всё что мне не понравилось и виндовый WebDav это проглотил.\\ | ||
+ | Теперь одиночные и группы файлов без проблем можно копировать на сетевой диск примонтированный по WebDav.\\ | ||
+ | Минусом - не выставляется оригинальная дата создания, | ||
+ | |||
+ | |||
+ | ===== Проблема 3 - создание папок ===== | ||
+ | {{: | ||
+ | См п1 :) | ||
+ | |||
+ | Приходит запрос: | ||
+ | < | ||
+ | Connection: Keep-Alive | ||
+ | User-Agent: Microsoft-WebDAV-MiniRedir/ | ||
+ | translate: f | ||
+ | Content-Length: | ||
+ | Host: xxx.xxx.net</ | ||
+ | |||
+ | Nginx на него отвечает (немного странно выбирая код, на мой взгляд, | ||
+ | < | ||
+ | Server: nginx/1.7.4 | ||
+ | Date: Sun, 10 Aug 2014 21:43:15 GMT | ||
+ | Content-Type: | ||
+ | Content-Length: | ||
+ | Connection: keep-alive | ||
+ | Keep-Alive: timeout=60 | ||
+ | |||
+ | < | ||
+ | < | ||
+ | <body bgcolor=" | ||
+ | < | ||
+ | < | ||
+ | </ | ||
+ | </ | ||
+ | |||
+ | IIS отвечает: | ||
+ | |||
+ | Как должно быть описано: | ||
+ | У майкрософта: | ||
+ | У яндекса: | ||
+ | И даже в RFC: [[http:// | ||
+ | |||
+ | Во всех примерах URL оканчивается слешем. \\ | ||
+ | Но только не в запросе WebDav клиента от мс. \\ | ||
+ | |||
+ | Вариантов опять было два: \\ | ||
+ | 1. Поправить файл: [[http:// | ||
+ | 2. Пофиксить через конфиг.\\ | ||
+ | |||
+ | Вариант 1 я оставляю на усмотрение програмеров nginx, может по стандарту оно и должно ругаться.\\ | ||
+ | |||
+ | **Фикс 3**: | ||
+ | < | ||
+ | rewrite ^(.*[^/])$ $1/ break; | ||
+ | }</ | ||
+ | Вот для этого пустяка и потребовался REWRITE плагин.\\ | ||
+ | |||
+ | |||
+ | ===== Проблема 4 - удаление папок ===== | ||
+ | Ноги тут те же что и в п3: отсутствие слеша на конце урла.\\ | ||
+ | Однако я столкнулся с тем, что nginx тоже ведёт себя несколько странно: | ||
+ | |||
+ | |||
+ | **Фикс 4**: | ||
+ | < | ||
+ | if ($request_method = DELETE) { | ||
+ | return 598; | ||
+ | } | ||
+ | |||
+ | location @delete_handler { | ||
+ | internal; | ||
+ | |||
+ | open_file_cache off; | ||
+ | if (-d $webdav_root/ | ||
+ | rewrite ^(.*[^/])$ $1/ break; | ||
+ | } | ||
+ | root $webdav_root; | ||
+ | dav_methods DELETE; | ||
+ | }</ | ||
+ | Если кратко, | ||
+ | |||
+ | |||
+ | ===== Проблема 5 - копирование и переименование ===== | ||
+ | Ноги тут те же что и в п4: отсутствие слеша на конце урла и в Destination заголовке.\\ | ||
+ | Для решения проблемы с добавлением "/" | ||
+ | |||
+ | **Фикс 6**: | ||
+ | < | ||
+ | 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/ | ||
+ | more_set_input_headers ' | ||
+ | rewrite ^(.*[^/])$ $1/ break; | ||
+ | } | ||
+ | root $webdav_root; | ||
+ | dav_methods COPY MOVE; | ||
+ | }</ | ||
+ | |||
+ | |||
+ | |||
+ | ===== Проблема 6 - OPTIONS корня ===== | ||
+ | Клиент запрашивает опции не у той папки которую мы хотим чтобы он подключил а у корня сервера.\\ | ||
+ | |||
+ | **Фикс 6**: | ||
+ | < | ||
+ | if ($request_method = OPTIONS) { | ||
+ | add_header Allow ' | ||
+ | add_header DAV '1, 2'; | ||
+ | return 200; | ||
+ | } | ||
+ | } | ||
+ | </ | ||
+ | Возвращаем ему статический список.\\ | ||
+ | Можно было просто разрешить: | ||
+ | |||
+ | |||
+ | ===== Проблема 7 - виндовый клиент не может подключится ===== | ||
+ | Соединение происходит но в логах nginx ничего нет, а клиент сообщает об ошибке.\\ | ||
+ | В логах с включённым дебагом ssl видно что рукопожатие не проходит.\\ | ||
+ | Проблема у меня проявилась не так давно, после обновления OpenSSL.\\ | ||
+ | При этом IE и другие браузеры могут авторизоваться по этому URL и отобразить листинг директории.\\ | ||
+ | |||
+ | **Фикс 7**: | ||
+ | Заменить OpenSSL на LibreSSL и пересобрать nginx. | ||
+ | Возможно OpenSSL уже исправили. | ||
+ | |||
+ | |||
+ | ===== Ещё немного о Nginx ===== | ||
+ | Не трудно заметить, | ||
+ | < | ||
+ | и хотя в данном случае это не создаёт проблем, | ||
+ | |||
+ | |||
+ | ===== Конфиг nginx для WebDav ===== | ||
+ | WebDav для Nginx:\\ | ||
+ | < | ||
+ | set $webdav_root "/ | ||
+ | location ^~ /Family { | ||
+ | if ($ssl_protocol = "" | ||
+ | add_header Strict-Transport-Security ' | ||
+ | return 403; | ||
+ | } | ||
+ | auth_basic " | ||
+ | auth_basic_user_file / | ||
+ | |||
+ | 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, | ||
+ | add_header Content-Type ' | ||
+ | return 207 '<? | ||
+ | } | ||
+ | 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: | ||
+ | |||
+ | autoindex on; | ||
+ | autoindex_exact_size on; | ||
+ | autoindex_localtime on; | ||
+ | } | ||
+ | location @propfind_handler { | ||
+ | internal; | ||
+ | |||
+ | auth_basic " | ||
+ | auth_basic_user_file / | ||
+ | |||
+ | open_file_cache off; | ||
+ | if (!-e $webdav_root/ | ||
+ | return 404; | ||
+ | } | ||
+ | root $webdav_root; | ||
+ | dav_ext_methods PROPFIND; | ||
+ | } | ||
+ | location @delete_handler { | ||
+ | internal; | ||
+ | |||
+ | auth_basic " | ||
+ | auth_basic_user_file / | ||
+ | |||
+ | open_file_cache off; | ||
+ | if (-d $webdav_root/ | ||
+ | rewrite ^(.*[^/])$ $1/ break; | ||
+ | } | ||
+ | root $webdav_root; | ||
+ | dav_methods DELETE; | ||
+ | } | ||
+ | location @copy_move_handler { | ||
+ | internal; | ||
+ | |||
+ | auth_basic " | ||
+ | auth_basic_user_file / | ||
+ | |||
+ | open_file_cache off; | ||
+ | if (-d $webdav_root/ | ||
+ | more_set_input_headers ' | ||
+ | rewrite ^(.*[^/])$ $1/ break; | ||
+ | } | ||
+ | root $webdav_root; | ||
+ | dav_methods COPY MOVE; | ||
+ | } | ||
+ | location / { | ||
+ | if ($request_method = OPTIONS) { | ||
+ | add_header Allow ' | ||
+ | add_header DAV '1, 2'; | ||
+ | return 200; | ||
+ | } | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | open_file_cache off; | ||
+ | Также в самом начале локейшена есть требование использовать SSL и если SSL используется то запрашивается BASIC авторизация. | ||
+ | ==== Важно ==== | ||
+ | - set $webdav_root "/ | ||
+ | - в папке "/ | ||
+ | - location ^~ **/Family** - означает что клиент должен использовать URL: https:// | ||
+ | - директивы **auth_basic** и **auth_basic_user_file** должны быть в каждом **location** который осуществляет обработку запросов, | ||
+ | |||
+ | |||
+ | ===== Немного о настройке клиента ===== | ||
+ | Он как капризный ребёнок, | ||
+ | Увы, но даже после всех проделанных настроек файлы более 4гб передавать не получится. (Скорее всего из за кривой реализации, | ||
+ | |||
+ | Вот здесь собраны все настройки с описанием: | ||
+ | |||
+ | На данный момент мои настройки WebClient выглядят так:\\ | ||
+ | < | ||
+ | |||
+ | [HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\services\WebClient\Parameters] | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | |||
+ | |||
+ | ===== Полезное ===== | ||
+ | * Дальнейшая разработка решения происходит тут: [[https:// | ||
+ | * Патч [[https:// | ||
+ | |||
+ | |||
+ | |||
+ | {{tag> |