WebDav и Nginx
Не так давно в одном из своих проектов мне понадобилось иметь возможность передачи файлов посредством метода PUT, без скрипта-обработчика на принимающей стороне, принять и обработать файл должен был сам сервер. Так же была задача реализовать это не на Apache, а на его собрате — Nginx.
В итоге своих изысканий у меня получилась такая схема — скрипт на PHP получает адрес файла и делает запрос к серверу, а он в свою очередь получает файл и складывает в требуемую папочку.
Приводить дальнейшие примеры по установке, настройке и отработке взаимодействий я буду отталкиваясь от debian-based ОС.
Сторона сервера, Nginx
Скачиваем, компилируем и устанавливаем сервер с требуемым модулем:
wget nginx.org/download/nginx-1.1.1.tar.gz
tar -xvf nginx-1.1.1.tar.gz
cd nginx-1.1.1
./configure —sbin-path=/usr/sbin —conf-path=/etc/nginx/nginx.conf \
—error-log-path=/var/log/nginx/error.log \
—pid-path=/var/run/nginx.pid \
—lock-path=/var/lock/nginx.lock \
—http-log-path=/var/log/nginx/access.log \
—http-client-body-temp-path=/var/lib/nginx/body \
—http-proxy-temp-path=/var/lib/nginx/proxy \
—http-fastcgi-temp-path=/var/lib/nginx/fastcgi \
—with-http_stub_status_module \
—with-http_ssl_module \
—with-http_dav_module
Настраиваем конфигурацию:
При компиляции мы указали что конфигурационный файл будет лежать в /etc/nginx/nginx.conf
Прописываем новую секцию server, так как надо чтобы webdav работал на другом порту — так, и безопасней, и удобней.
server listen 7500; #порт на котором будет слушать nginx
server_name ip-адрес-сервера;
charset utf-8;
location / expires max;
root /путь/до/требуемой/папки; #по этому пути будут складываться полученный PUT’ом файлы
client_max_body_size 20m;
client_body_temp_path /usr/local/nginx/html/;
dav_methods PUT; #разрешенные методы, нам требуется только PUT
create_full_put_path on; #при отсутствии вложенных папок, при включенной директиве, nginx автоматически создаст иерархию
dav_access user:rw group:r all:r; #права на файлы
Хочу так же заметить что с настроенным модулем так же отрабатывают и остальные директивы WebDav указанные в конфигурационном файле Nginx: DELETE, MKCOL, COPY, MOVE.
Сторона «клиента», второго сервера, PHP-скрипт
Оговорим что в переменной $namefile уже содержится имя файла вида file.zip полученное тем или иным способом.
$url = «ip-адрес-сервера:7500/$namefile»;
$file = «/files/$namefile»;
$fp = fopen($file, «r»);
$curl = curl_init();
curl_setopt($curl, CURLOPT_VERBOSE, 1);
curl_setopt($curl, CURLOPT_URL, $url);
curl_setopt($curl, CURLOPT_PUT, 1);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($curl, CURLOPT_INFILE, $fp);
curl_setopt($curl, CURLOPT_INFILESIZE, filesize($file));
Итог
Теперь, когда все установлено и настроено, мы можем обратиться к скрипту, передать путь до файла и скрипт передаст наш файл серверу, который в свою очередь без PHP, Perl или написанного на другом языке обработчика сможет принять и разместить файл на другом сервере.
Надеюсь что написанный текст был вам интересен, за сим откланяюсь.
Делаем файловый сервер из голого nginx. Webdav.
Иногда возникает необходимость заливать файлики на второй сервер и отдавать их оттуда же по http(s). Ну навскидку — отдельные серверы с картинками для вашего проекта. Или с архивами. Да с чем угодно, мне-то какая разница =)
Поднимать ради этого ftp и городить работы с ftp в коде сайта? гм… SFTP… можно, но где взять разработчика, который умеет sftp из php? Джангисты и рельсовики все умеют, ладно. А если nodejs?
А вот curl/libcurl есть везде, PUT/POST запросы можно сделать из любого ЯП/фреймворка. Поэтому приходят 2 логичных решения — либо написать php-код на принимающей стороне, который будет принимать файлы, либо сделать всё это на голом nginx-e. Окей, решили вторым способом, пришли на тостер, спросили… И получили какой-то бред в ответе — https://toster.ru/q/46578
upload-module? big-upload-module? clientbodyonthefly ещё куда ни шло, это всё же штатная фича..
Да к черту. Открываем доку (окей, в моём случае — задаём вопрос в воздух и получаем ответ от соседа-админа) по nginx и листаем до пункта «http webdav module». Всё, можно заливать файлы (ладно-ладно, конфиг я вам покажу). И решаем сразу все проблемы: нужен только дефолтный nginx из репозиториев debian/ubuntu, размер файла ограничен только стабильностью соединения (13 гиговый файл? да легко!), конфиг писать легко (копипастинг работает, да), авторизация рулится через привычный htpasswd.. В общем — быть. Погнали
Допустим nginx у нас уже есть и что-то по http куда-то отвечает. Нам нужно на images.example.com настроить upload файлов через webdav (и научиться заливать файлы курлом) с авторизацией и научиться показывать эти файлы по http без авторизации. Создаём каталог, где будем всё это хранить, например:
Nginx из коробки работает от пользователя www-data (я про деб, в других дистрах это может быть www), от него же он будет и файлы писать, так что пофиксим права:
Теперь пишем конфиг (например, в /etc/nginx/sites-available/00-images.example.com):
server
listen 80;
server_name images.example.com;
# описываем upload-секцию:
location /upload
# максимальный размер файла, который можно залить.
client_max_body_size 15g;
# каталог, куда заливать
root /home/user/data/www/images.example.com;
# chmod для залитых файлов — здесь 777, чтобы user тоже мог удалять файлы
dav_access user:rw group:rw all:rw;
# разрешаем методы webdav-a. Для примера я перечислил все, для аплоада файлов хватит PUT и MKCOL
dav_methods PUT DELETE MKCOL COPY MOVE;
# nginx будет создавать весь путь при аплоаде файлов (можно будет не создавать предварительно вложенные каталоги)
create_full_put_path on;
# включаем autoindex в каталоге upload (чтобы на самом images.example.com листинг не включать).
autoindex on;
autoindex_exact_size off;
autoindex_localtime on;
charset utf-8;
# включаем авторизацию в /upload:
auth_basic «Upload directory»;
auth_basic_user_file /etc/nginx/htpasswd;
>
# теперь описываем раздачу файлов по http из «корня» сайта:
location /
root /home/user/data/www/images.example.com;
>
>
Сгенерим пользователя (если у вас нет утилиты htpasswd, то поставьте пакет apache2-utils — apache2 этот пакет не ставит, если что =)) — команду повторите нужное количество раз, заменяя username, при запуске она запросит пароль для пользователя:
Рестартим nginx, если ещё нет и приступаем к заливанию файлов. Я покажу это на примере консольного curl, с libcurl, думаю, разберетесь сами, если уже дочитали до сюда.
Например, у нас есть файл /tmp/bigfile. Нам его нужно будет показывать по адресу http://images.example.com/archives/bigfile.zip
Запускаем curl с хитрыми параметрами:
user@laptop:~$ curl -T /tmp/bigfile http://username:password@images.example.com/upload/archives/bigfile.zip
Имейте в виду, что пока файл льётся, он льётся в каталог /var/lib/nginx/body/. Когда upload закончится, файл будет перемещен в docroot через mv — так что если у вас /var и каталог для upload на разных файловых системах, то операция будет не атомарной. Если же на одной файловой системе — то файл появится в docroot мнгновенно и целиком (то есть не будет такого, что вы заливаете файл, а в это время его кусок могут скачать другие).
В некоторых местах вы можете услышать мнение, что передавать логин/пароль так, как это сделал я выше небезопасно и нужно обязательно передавать basic-авторизацию через http-заголовок Authorization, потому что так безопаснее. Так вот — не слушайте потом больше никогда этого человека. Заголовок Authorization это всего-лишь base64 (хоть и немного нестандартный) и декодится по первой же ссылке в гугле — https://webnet77.net/cgi-bin/helpers/base-64.pl
Намного лучше будет настроить https (https://debian.pro/581) и настроить ограничение по ip (https://debian.pro/726). Впрочем, если вам удобнее передавать через заголовок — welcome, но не тешьте себя надеждой, что это позволит избежать раскрытие пароля, если к исходникам заливалки получат доступ.
Да, ещё с этим модулем nginx’a не будут работать webdav-клиенты — уж очень он простой (в нём даже нет вебдавного листинга файлов/каталогов). Если вам нужен настоящий webdav — то советую посмотреть в сторону установки OwnCloud.
How to create a webdav server with Nginx
In this tutorial, I will guide you through the step by step instructions to get a webdav server ready in less than 2 minutes. It is structured around 2 parts:
Install nginx
~/$ sudo apt -y update
Get:1 https://mirror.hetzner.com/ubuntu/packages focal InRelease [265 kB]
Get:2 https://mirror.hetzner.com/ubuntu/packages focal-updates InRelease [114 kB]
Get:3 https://mirror.hetzner.com/ubuntu/security focal-security InRelease [114 kB]
.
..
.
Reading state information. Done
~/$ sudo apt -y install nginx nginx-extras libnginx-mod-http-dav-ext libnginx-mod-http-auth-pam
Reading package lists. Done
Building dependency tree
.
..
.
Setting up libnginx-mod-http-image-filter (1.18.0-0ubuntu1.2) .
Setting up nginx-core (1.18.0-0ubuntu1.2) .
Setting up nginx (1.18.0-0ubuntu1.2) .
Processing triggers for ufw (0.36-6ubuntu1) .
Processing triggers for systemd (245.4-4ubuntu3.13) .
Processing triggers for man-db (2.9.1-1) .
Processing triggers for libc-bin (2.31-0ubuntu9.2) .
To make sure everything ran well, let’s make sure nginx is being loaded with the 2 additional modules it needs to run the webdav server: http_dav_module and http-dav-ext:
~/$ if nginx -V 2>&1 | grep -qE «http_dav_module|http-dav-ext»; then echo «good to go :)»; else echo «missing dav module :(«; fi
good to go 🙂
Configure the webdav server
~/$ chown -R www-data:www-data /var/www/html/
~/$ cat > /etc/nginx/sites-available/default server listen 80 default_server;
listen [::]:80 default_server;
root /var/www/html/;
index index.html index.htm index.nginx-debian.html;
server_name _;
location / dav_methods PUT DELETE MKCOL COPY MOVE;
dav_ext_methods PROPFIND OPTIONS;
dav_access user:rw group:rw all:rw;
client_max_body_size 0;
create_full_put_path on;
client_body_temp_path /tmp/;
auth_pam «Restricted»;
auth_pam_service_name «common-auth»;
>
>
EOF
If you want to allow anonymous user, you can remove the 2 nginx directives that starts with auth_pam and won’t have to run this command:
To make sure everything is running fine, you can quickly test your newly created webdav server:
~/$ DOMAIN=example.com
~/$ curl -I http://$DOMAIN/index.nginx-debian.html
HTTP/1.1 401 Unauthorized
Server: nginx/1.18.0 (Ubuntu)
Date: Thu, 09 Dec 2021 04:41:01 GMT
Content-Type: text/html
Content-Length: 188
Connection: keep-alive
WWW-Authenticate: Basic realm=»Restricted»
~/$ curl -I —user username:password http://$DOMAIN/index.nginx-debian.html
HTTP/1.1 200 OK
Server: nginx/1.18.0 (Ubuntu)
Date: Thu, 09 Dec 2021 04:41:58 GMT
Content-Type: text/html
Content-Length: 612
Last-Modified: Thu, 09 Dec 2021 03:35:44 GMT
Connection: keep-alive
ETag: «61b17990-264»
Accept-Ranges: bytes
For a nicer webdav client than curl, check our online webdav client:
- If the server doesn’t have a user already defined, see the user management linux cheat sheet
- Before using this webdav server for anything serious, it is strongly advised to create an SSL certificate. The nginx blog has a great article about that exact topic.
- To make the server readonly, you will need to remove the nginx config line which contains dav_methods PUT DELETE MKCOL COPY MOVE; . For more information about what you can do by removing some of the supported methods in webdav, refer rfc4918.
- if you want to chroot your user onto their home folder, you will need to replace the location block with something like this:
Our Tools