О музыке

2020-08-29

Google Play Music закрывается. Его предлагают заменить на Youtube Music. И даже можно перенести туда всё ранее купленное и закачанное.

Но в Youtube Music отсутствует тот вариант использования, который являлся для меня основным в Play Music. Я не хочу подписку. Я не хочу доступ ко всему. Я не хочу автомиксы и тематические радио. Я хочу покупать конкретные альбомы конкретных любимых исполнителей. В надежде, что самим исполнителям с этого прилетит копеечка.

такое не приемлю

В Youtube Music купить треки или альбомы нельзя. Только подписка. А без подписки никакой работы офлайн. И реклама. Просто ещё одно музыкальное облако. Такое же, как Яндекс.Музыка, Spotify, whatever. Кажется, в iTunes ещё можно покупать альбомы. Но это вражеская территория, я туда не пойду.

Хорошо, что в Play Music можно честно скачать все купленные альбомы. Там будет MP3 с приличным битрейтом в 320 кбит/с. И с правильно выставленными тегами на русском языке. Включая даже такие тонкости, как номер диска, например, на двухдисковом альбоме. Я всё и скачал.

Теперь вся моя музыка лежит в домашней файлопомойке — недо-NAS от Western Digital по имени MyCloud.

Домашняя файлопомойка прекрасно доступна дома. NFS — отличный и быстрый протокол. А с помощью AutoFS можно тихо, прозрачно и незаметно подмонтировать MyCloud в /net, когда он доступен в локальной сети. AutoFS когда-то давно использовался для автомонтирования CD или флэшек, пока эту обязанность не перехватили DE. Ну а на примонтированной файловой системе музыку прекрасно играет тот же DeaDBeeF.

Мне музыка нужнее где-нибудь в пешем пути. То есть в телефоне. Андроидное приложение Google Play Music прекрасно работало как удобный облачный плеер с кэшированием. Я даже в Play Music залил старые альбомы из своей коллекции, чтобы их слушать через это приложение. Так можно было.

Теперь нужно как-то слушать музыку на телефоне, но из домашней файлопомойки.

У WD есть мобильное приложение, и даже веб приложение, для доступа к вашему домашнему облаку. На самом деле домашний MyCloud держит OpenVPN подключение куда-то к недрам WD, а оттуда уже приложения получают доступ к вашим файлам. Вполне здоровый подход, если вы доверяете Western Digital. Но очень проприетарный, к этому подключению никак не влезть сторонним приложением.

А стороннего приложения хочется. Родное приложение My Cloud, конечно, позволяет смотреть файлы, настраивать домашний NAS, даже играть музыку. Но музыку оно играет так себе, плохо помнит позицию проигрывания, не умеет выкачивать следующую композицию заранее (а значит, не будет проигрывания без пауз), не кэширует метаданные музыкальных файлов и, вообще, выглядит и работает не секси.

приложение My Cloud

После недолгих поисков обнаружился чудесный CloudBeats. Он — секси. Выглядит и работает почти как Play Music. Вот эти вот исполнители, альбомы, обложки альбомов, нотификация, сердечки, управление с экрана блокировки. Всё, как оно положено быть нормальному мобильному плееру. Даже больше: неограниченное количество разных плейлистов, если надо.

Нужно только предоставить доступ к своей музыкальной коллекции по WebDAV. Это такое небольшое расширение HTTP, которое превращает веб сервер в почти полноценный файловый сервер. С возможностью заливать и скачивать файлы, а также просматривать каталоги.

WebDAV — вполне родной протокол для домашних облаков. OwnCloud, NextCloud умеют его. И WD MyCloud тоже может.

Вот только не стоит просто брать и пробрасывать доступ к порту 8080 из интернетов. Нам же не нужно выставлять всю нашу файлопомойку голой жопой в опасный и недоброжелательный Интернет.

Нам нужно выставить только музыку, и только нам. В MyCloud доступ можно ограничить только по ресурсам и юзерам. Ресурс — это как бы ещё один сетевой диск, а на деле ещё одна папочка. Я завёл отдельный ресурс Music. И отдельного пользователя music, с длинным паролем. И дал пользователю music права на чтение из Music. Для всех остальных ресурсов, включая и дефолтный Public с, очевидно, публичным доступом, я WebDAV отключил.

Music resource

Файлы с музыкой пришлось скопировать из Public в Music. А чтобы не тратить места на диске, я сделал это командой cp -r -l. Она создаёт жёсткие ссылки, а не копии файлов. Очень удобно. Только при пополнении музыкальной коллекции придётся заходить на MyCloud по ssh. Но не впервой.

Получается, WebDAV доступ оставляем только для внешнего доступа к нашему музыкальному облаку. А другие протоколы, вроде SMB, NFS и DLNA, оставляем для внутреннего домашнего использования.

Доступ должен быть по зашифрованному каналу. Мы же не хотим, чтобы длинный пароль пользователя music кто-то подслушал и скопировал нашу музыкальную коллекцию. Для WebDAV это означает использование HTTPS.

MyCloud умеет HTTPS для WebDAV на порту 4443. Только сертификат там весьма интересный. На некий девайс в домене wd2go.com.

curl -v -k https://10.xxx.xxx.2:4443/Music 
*   Trying 10.xxx.xxx.2:4443...
...
* Server certificate:
*  subject: OU=Domain Control Validated; OU=Hosted by Western Digital Corporation; OU=COMODO SSL Unified Communications; CN=devicexxxxxx-xxxxx.wd2go.com
*  start date: Oct  2 00:00:00 2018 GMT
*  expire date: Oct  1 23:59:59 2020 GMT
*  issuer: C=US; ST=CA; L=Irvine; O=Western Digital Technologies; CN=Western Digital Technologies Certification Authority
*  SSL certificate verify result: unable to get local issuer certificate (20), continuing anyway.
> GET /Music HTTP/1.1
...

Это доменное имя девайса, конечно же, не резолвится. Да и доступа к wd2go.com у нас нет.

Можно было бы попробовать на этом остановиться. Открыть доступ к порту 4443 снаружи и всё. Но что-то мне не хочется экспериментировать с установкой новых доверенных сертификатов в Android. Вкорячить бы Let's Encrypt на MyCloud, но я его ещё не настолько похакал. Там всё ещё стоит родная прошивка со странным недолинуксом на основе Debian. И поэтому просто открытие портов тоже выглядит как выставление голой жопой в интернеты.

Да, наш домашний сервер должен быть доступен из интернетов. Моему домашнему роутеру на OpenWrt уже выдаётся настоящий белый IP адрес. И IPv6 тоже. Только он после каждого переподключения (ну когда свет отрубают, например) меняется. Динамический.

Решение давно известно. Это — Dynamic DNS. Также известный как DynDNS или DDNS. Просто мой маршрутизатор после каждого подключения меняет некоторую DNS запись, чтобы она соответствовала новому полученному IP адресу. В результате IP адрес меняется, но DNS запись всегда (за вычетом времени обновления записи и DNS кэша) указывает на адрес моего домашнего маршрутизатора.

В OpenWrt есть скрипты для разных DDNS провайдеров. Так как мой домен (и этот блог) уже работают через Cloudflare, я себе настроил DDNS посредством ddns-scripts_cloudflare.com-v4.

OpenWrt DDNS

Есть у меня доменное имя, которое всегда указывает на мой домашний роутер. А MyCloud спрятан за роутером. Если на OpenWrt настроить проброс порта на MyCloud, то музыкальное облако будет доступно. Но с кривым сертификатом, как говорилось ранее.

А давайте подымем на OpenWrt reverse proxy. Чтобы он правильным сертификатом светил в интернеты. А внутри домашней сети уже направлял запросы на MyCloud.

В качестве reverse proxy для OpenWrt рекомендуют старый добрый Nginx. Вот только в моём староватом LEDE 17.01 Nginx скомпилирован без поддержки SSL.

Не беда. Я давно подумывал обновить OpenWrt. Всё хорошо, свеженький OpenWrt 19.07.3 для моего старого доброго TP-Link TL-WDR4300 вполне себе существует и без проблем можно до него апгрейдиться.

Вот за это обожаю OpenWrt. Даже вроде серьёзный апгрейд делается быстро, не сносит настройки, и вы сразу получаете работающую сеть в старой конфигурации. Потом только нужно доставить дополнительные пакеты, вроде тех же DDNS скриптов и Nginx, и они тоже подхватят старые конфиги.

С Nginx в OpenWrt не всё так просто. В OpenWrt уже запущен веб сервер uHTTPd. Он держит веб интерфейс управления роутером (который LuCI, написан, кстати, на Lua). Доступ к 80 порту на этот веб интерфейс по умолчанию открыт только из локальной сети. А 443 порт не задействован, потому что по умолчанию поддержка SSL для uHTTPd не установлена.

C SSL в OpenWrt вообще всё сложно. Чтобы оно работало, нужно установить почти мегабайт libopenssl. А корневые доверенные сертификаты для клиента — это ещё сотни килобайт. А диска-то в моём TP-Link, куда можно что-то писать и устанавливать пакеты, — всего лишь 4 мегабайта. Да, 4 мегабайта. Очень компактный Линукс этот OpenWrt.

Так вот, 80 порт занят админским веб интерфейсом. Открывать его наружу негоже. А открыть надо, чтобы получить автоматический красивый сертификат от Let's Encrypt. Либо переносить админский веб интерфейс на другой порт. Тогда в идеале надо бы вообще выкинуть uHTTPd, а запустить LuCI через Nginx. Либо придумать что-нибудь другое.

$ ssh root@10.xxx.xxx.1

BusyBox v1.30.1 () built-in shell (ash)

  _______                     ________        __
 |       |.-----.-----.-----.|  |  |  |.----.|  |_
 |   -   ||  _  |  -__|     ||  |  |  ||   _||   _|
 |_______||   __|_____|__|__||________||__|  |____|
          |__| W I R E L E S S   F R E E D O M
 -----------------------------------------------------
 OpenWrt 19.07.3, r11063-85e04e9f46
 -----------------------------------------------------
root@OpenWrt:~# df -h
Filesystem                Size      Used Available Use% Mounted on
/dev/root                 2.5M      2.5M         0 100% /rom
tmpfs                    60.8M      1.7M     59.1M   3% /tmp
/dev/mtdblock4            3.9M      2.7M      1.2M  70% /overlay
overlayfs:/overlay        3.9M      2.7M      1.2M  70% /
tmpfs                   512.0K         0    512.0K   0% /dev
root@OpenWrt:~# netstat -lnpt
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name    
tcp        0      0 0.0.0.0:80              0.0.0.0:*               LISTEN      1237/uhttpd
tcp        0      0 0.0.0.0:53              0.0.0.0:*               LISTEN      2043/dnsmasq
tcp        0      0 10.xxx.xxx.1:22         0.0.0.0:*               LISTEN      1751/dropbear
tcp        0      0 0.0.0.0:443             0.0.0.0:*               LISTEN      10981/nginx.conf -g
tcp        0      0 :::5000                 :::*                    LISTEN      6038/miniupnpd
tcp        0      0 :::655                  :::*                    LISTEN      5509/tincd
tcp        0      0 :::53                   :::*                    LISTEN      2043/dnsmasq
tcp        0      0 fd9e:ecc8:b68d::1:22    :::*                    LISTEN      1751/dropbear
tcp        0      0 :::443                  :::*                    LISTEN      10981/nginx.conf -g

И тут я вспомнил про Cloudflare.

Мой сайт и блог уже давно работают через Cloudflare. Я туда подключился в тот момент, когда мой блог стал недоступен из-за массированных блокировок IP сетей. Под раздачу попали адреса Blogger, где тогда был блог. А включение проксирования через Cloudflare сменило адрес и сделало блог доступным.

Cloudflare — хитрые жуки. Они берут себе управление DNS вашего домена. И запросы на ваш домен или поддомены (это отдельно настраивается для каждой DNS записи) заруливают на свои сервера. И там же делают HTTPS. Они предоставляют свои сертификаты, где, кстати, раньше было видно, что у них проксируется ещё 100500 других сайтов. А сейчас они перешли на Let's Encrypt.

сертификат Cloudflare

Cloudflare, получается, терминируют HTTPS у себя. И видят весь трафик до вашего сайта. В нешифрованном виде. Cloudflare-in-the-middle. За это их и критикуют.

А дальше, как нормальный reverse proxy, Cloudflare ходит на ваш сайт. По HTTP или HTTPS, как настроите. При этом они (неверно) называют обращение к вашему origin серверу по HTTPS "End-to-end HTTPS".

Cloudflare может не валидировать сертификат вашего сервера (режим "Full"). Тогда на вашем сервере может стоять даже левый дефолтный самоподписанный сертификат.

Либо же Cloudflare будет валидировать сертификат (режим "Full (strict)"). Но доверять при этом он будет не только правильным сертификатам от того же Let's Encrypt, но и своим собственным Cloudflare Origin CA certificates. Это такой специальный, самоподписанный, кстати, сертификат, который вы генерируете на Cloudflare и вставляете в свой сервер. Он не валиден в интернетах. Но он валиден на origin сервере, прикрытом Cloudflare.

$ curl -v -k https://10.xxx.xxx.1:443/Music
*   Trying 10.xxx.xxx.1:443...
...
* Server certificate:
*  subject: O=CloudFlare, Inc.; OU=CloudFlare Origin CA; CN=CloudFlare Origin Certificate
*  start date: Aug 26 09:55:00 2020 GMT
*  expire date: Aug 23 09:55:00 2035 GMT
*  issuer: C=US; O=CloudFlare, Inc.; OU=CloudFlare Origin SSL Certificate Authority; L=San Francisco; ST=California
*  SSL certificate verify result: unable to get local issuer certificate (20), continuing anyway.
> GET /Music HTTP/1.1
...

Между прочим, разбираясь со всем этим, я обнаружил, что Cloudflare ходил на хостинг моего блога (на правах рекламы, sprinthost.ru) голым HTTP. Починил.

В конце концов конфигурация получилась такая.

Для своего музыкального облака я завёл домен в Cloudflare. Домен, который CNAME на тот самый DDNS домен моего домашнего маршрутизатора. Cloudflare проксирует запросы на музыкальный домен, и предоставляет правильный и красивый сертификат для HTTPS на него.

Далее запросы идут на Nginx на моём OpenWrt. По HTTPS. У Nginx прописан тот самый Origin CA сертификат от Cloudflare. Таким образом я избегаю мороки с Let's Encrypt на OpenWrt, хотя это и возможно. Nginx слушает только 443 порт.

Nginx проксирует запрос на MyCloud в локальной сети. На порт 8080, нечего тут SSL приплетать. При этом Nginx проксирует запросы только на /Music. То есть другие ресурсы MyCloud защищены от внешнего доступа. Для надёжности можно в Nginx прирезать лишние HTTP методы, чтобы гарантировать read-only доступ через WebDAV. Можно перенести в Nginx авторизацию, для ещё большей надёжности. Там ведь обычная HTTP Basic аутентификация.

server {
    listen 443 ssl;
    listen [::]:443 ssl;

    ssl_certificate '/etc/nginx/conf.d/cloudflare_origin.crt';
    ssl_certificate_key '/etc/nginx/conf.d/cloudflare_origin.key';
    ssl_session_cache shared:SSL:32k;
    ssl_session_timeout 64m;

    location /Music {
        proxy_pass http://10.xxx.xxx.2:8080;
    }
}

Всё. Проверить можно консольным WebDAV клиентом cadaver.

$ cadaver https://xxx.yyy.zzz/Music
Authentication required for DAV-upload on server `xxx.yyy.zzz':
Username: music
Password: 
dav:/Music/> ls
Listing collection `/Music/': succeeded.
Coll:   Аквариум                       0  Aug 26 12:39
Coll:   Анна Герман                  0  Aug 26 12:39
Coll:   Браво                             0  Aug 26 12:39
...

CloudBeats подключается к моему музыкальному домену на Cloudflare и работает. Можно надеяться, что Cloudflare ещё и будет кэшировать мои музыкальные файлы. Ведь технически получение файлов по WebDAV — это обычный GET запрос. А сами файлы не меняются.

CloudBeats

Ура! У меня теперь есть своё музыкальное облако.

Надеюсь, мои любимые исполнители не перестанут собирать донаты на новые альбомы на planeta.ru. А то альбомы теперь нужно откуда-то скачивать самостоятельно :)