Создание своего стримингового сервиса музыки Navidrome

Недавно мне в руки, почти бесплатно, попал вот такой почти что карманный бесшумный маленький терабайтный сервер, Lenovo ThinkCentre.

Думая, что с ним сделать такого интересного, решил, что неплохого было бы создать свой self-hosted аналог сервиса для потокового вещания музыки (что-то типа Spotify или Apple Music).  Первым делом я, конечно же, накатил Linux Debian в качестве ОС для будущего сервера.  В качестве бекенда для музыкального сервера выбор пал на проект Navidrome, из-за его простоты. Процесс установки Navidrome действительно настолько простой, насколько это вообще возможно.

Задача: создать на локальном сервере личный музыкальный стриминговый сервис по типу spotify, который был бы доступен как из локальной сети, так и из внешней, при этом учитывая то, что у нас динамический ip от провайдера.

План:  в качестве бекенда для стримингового сервиса будем использовать navidrome из-за его простоты и надежности. Установим navidrome на локальный сервер с debian linux, протестируем, что все нормально работает в локальной сети. Далее настраиваем возможность работы сервера за пределами локальной сети. Для этого будем использовать VPS с nginx в качестве реверс-прокси. Установим ssh-туннель с VPS и настроим nginx чтобы он обратно проксировал данные на наш локальный сервер с Navidrome.

Что потребуется: локальный сервер с linux-дистрибутивом, vps-сервер на linux со статическим ip-адресом, и, опционально, с доменом.

Реализация:

    ставим ffmpeg и удостоверяемся, что система обновлена:

    sudo apt update
    sudo apt upgrade
    sudo apt install ffmpeg

    создаем нужные директории:

    sudo install -d -o <юзер> -g <группа> /opt/navidrome
    sudo install -d -o <юзер> -g <группа> /var/lib/navidrome

    Заходим на страницу релизов (https://github.com/navidrome/navidrome/releases) navidrome и скачиваем и устанавливаем последнюю версию:

    wget https://github.com/navidrome/navidrome/releases/download/v0.XX.0/navidrome_0.XX.0_Linux_x86_64.tar.gz -O Navidrome.tar.gz
    sudo tar -xvzf Navidrome.tar.gz -C /opt/navidrome/
    sudo chown -R <юзер>:<группа> /opt/navidrome

    Создаем конфигурационный файл navidrome.toml в рабочей директории /var/lib/navidrome и вбиваем туда путь до директории музыкальной библиотеки, которая будет стримиться:

    MusicFolder = "/home/MusicLib"

    Создаем юнит systemd. Для этого создаем файл navidrome.service в директории /etc/systemd/system/с таким содержанием:

    [Unit]
    Description=Navidrome Music Server and Streamer compatible with Subsonic/Airsonic
    After=remote-fs.target network.target
    AssertPathExists=/var/lib/navidrome
    
    [Install]
    WantedBy=multi-user.target
    
    [Service]
    User=<user>
    Group=<group>
    Type=simple
    ExecStart=/opt/navidrome/navidrome --configfile "/var/lib/navidrome/navidrome.toml"
    WorkingDirectory=/var/lib/navidrome
    TimeoutStopSec=20
    KillMode=process
    Restart=on-failure
    
    # See https://www.freedesktop.org/software/systemd/man/systemd.exec.html
    DevicePolicy=closed
    NoNewPrivileges=yes
    PrivateTmp=yes
    PrivateUsers=yes
    ProtectControlGroups=yes
    ProtectKernelModules=yes
    ProtectKernelTunables=yes
    RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6
    RestrictNamespaces=yes
    RestrictRealtime=yes
    SystemCallFilter=~@clock @debug @module @mount @obsolete @reboot @setuid @swap
    ReadWritePaths=/var/lib/navidrome

     

    Запускаем только что созданный сервис:

    sudo systemctl daemon-reload
    sudo systemctl start navidrome.service
    sudo systemctl status navidrome.service
    
    sudo systemctl enable navidrome.service

     

    Заходим на http://localhost:4533 и проверяем, что все нормально работает. Если с сервера зайти посмотреть не удается, то это можно сделать с другого компьютера, если он в одной локальной сети, но localhost нужно будет поменять локальный ip сервера.

    Готово! Теперь у нас есть собственный стриминговый сервис with blackjack and hookers, но есть одно большое НО: работать все будет только в рамках локальной сети. Итак, теперь наша задача сделать так, чтобы сервис был доступен из Интернета. Сразу скажу, что у меня динамический IP и исходить я буду из этого. Вариантов решения этой задачи несколько, но учитывая то, что у меня есть работающая VPS-ка с веб-сервером nginx, статическим IP и доменом, то самым идеальным вариантом для меня стало бы пробросить порты посредством ssh, создав безопасный туннель передачи данных между удаленным VPS и нашим локальным сервером. Дальше нужно будет настроить nginx на удаленном vps чтобы он обратно проксировал данные на наш локальный сервер с Navidrome. Отмечу, что это самый секьюрный вариант, а так же самый удобный в пользовании: в конечном итоге нам остается только зайти на наш домен (например, music.example.com) и пользоваться, с поддержкой SSL. Если нет VPS-ки, то этот вариант будет не самым быстрым. Отмечу вкратце, что нужно сделать:

    1. Приобретаем VPS-ку.
    2. Приобретаем домен.
    3. Настраиваем nginx и ssh доступ через ключ
    4. Регистрируемся в Cloudflare и привязываем туда домен.
    5. Получаем бесплатный SSL-сертификат

    Как все это сделать можно посмотреть в других гайдах (например, здесь).

    Итак, если VPS с настроенным nginx есть, то можем продолжать. В качестве примера будут следующие вводные параметры (взятые “от балды”):

    Внешний IP-адрес VPS: 237.37.96.248

    Порт ssh: 44633

    Домен (а так же URL), на котором будет доступен Navidrome: music.mydomain.com

    Итак, устанавливаем на локальном сервере с Navidrome ssh-туннель из порта 16059 на нашем vps до порта 4533 локального сервера:

     ssh -p 44633 -R 16059:localhost:4533 user@237.37.96.248 -N

    Создаем отдельный конфигурационный файл для music.mydomain.com в nginx такого содержания:

    server {
    listen 80;
    server_name music.mydomain.com;
    location / {
    proxy_pass http://localhost:16059;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
    }
    }

    Здесь мы говорим nginx принимать запросы по адресу music.mydomain.com и переводить их на порт 16059 локалхоста нашего vps, который в свою очередь через туннель передается на наш локальный сервер с установленным Navidrome.

    Теперь, если мы зайдем по адресу music.mydomain.com, то все должно работать. Остался добавить небольшой штрих – подключить ssl-сертификаты. Я предпочитаю это делать через удобнейшую утилиту certbot:

    certbot --nginx -d music.mydomain.com

    Итоговый результат будет выглядеть как-то так на компьютере:

    Navidrome

    На смартфоне можно использовать любое приложение поддерживающее Subsonic API, я предпочитаю Ultrasonic.

    Есть еще один важный момент. Дело в том, что ssh-туннель, установленный выше способом, очень просто, но в то же время неудобен: соединение будет время от времени падать, и нужно будет переподключиться самостоятельно. Можно решить эту проблему несколькими способами (например утилитой autossh), но на мой взгляд лучше всего создать службу systemd, которая будет всегда поддерживать туннельное соединение. Способ авторизации в данном случае только через ключи безопасности.

    Для этого нужно создать файл службы

    sudo vim /etc/systemd/system/ssh-tunnel-persistent.service
    Примерно такого содержания:
    [Unit]
    Description=Persistent SSH Tunnel to from port 9092 on this server to port 9090 on external server (for encrypted traffic)
    After=network.target
    
    [Service]
    Restart=on-failure
    RestartSec=5
    ExecStart=/usr/bin/ssh -i <путь/до/приватного/ключа/ssh> -p 44633-R 16059:localhost:4533 user@237.37.96.248 -NTC -o ServerAliveInterval=60 -o ExitOnForwardFailure=yes
    
    [Install]
    WantedBy=multi-user.target
    Данная конфигурация службы systemd используется для настройки постоянного SSH-туннеля. Этот туннель предназначен для передачи зашифрованного трафика и обеспечивает сохранение активности туннеля и его автоматический перезапуск в случае сбоя. Далее, нужно ввести в терминале:
    sudo /usr/bin/ssh -i <путь/до/приватного/ключа/ssh> -p 44633-R 16059:localhost:4533 user@237.37.96.248 -NTC -o ServerAliveInterval=60 -o ExitOnForwardFailure=yes
    После этого подключение можно будет сразу прервать. Дело в том, что SSH хранит известные ключи хоста и связанные с ними отпечатки пальцев в конфигурационном файле для каждого пользователя, а так как эта служба systemd выполняет команду из-под пользователя root, то у системы для него нет записей в known_hosts.
    Осталось лишь включить и запустить созданную службу:
    sudo systemctl daemon-reload
    sudo systemctl enable ssh-tunnel-persistent.service
    sudo systemctl start ssh-tunnel-persistent.service