mod: типографика
This commit is contained in:
@@ -1,20 +1,20 @@
|
|||||||
# Прозрачный прокси контейнера через Shadowsocks и tun2socks
|
# Прозрачный прокси контейнера через Shadowsocks и tun2socks
|
||||||
|
|
||||||

|

|
||||||
|
[](https://typograph.cube2.ru/)
|
||||||
|
|
||||||
У меня есть Docker-контейнер Audiobookshelf, в котором я слушаю аудиокниги и подкасты. Он сам скачивает подкасты
|
У меня есть Docker-контейнер Audiobookshelf, в котором я слушаю аудиокниги и подкасты. Он сам скачивает подкасты
|
||||||
из интернета, и это очень удобно (все нужные мне подкасты в одном месте). Работал контейнер на домашнем NAS Synology
|
из интернета, и это очень удобно (все нужные мне подкасты в одном месте). Работал контейнер на домашнем NAS Synology
|
||||||
(в нем есть Docker Station с веб-интерфейсом). Книг у меня в "библиотеке" уже более тысячи, а подкастов и того больше,
|
(в нем есть Docker Station с веб-интерфейсом). Книг у меня в «библиотеке» уже более тысячи, а подкастов и того больше,
|
||||||
так что домашний NAS с доступом из интернета — идеальное решение.
|
так что домашний NAS с доступом из интернета — идеальное решение.
|
||||||
|
|
||||||
Все отлично работало и подкасты обновлялись, пока не настал DPI. Где-то в первой декаде января 2026 года подкасты перестали
|
Все отлично работало и подкасты обновлялись, пока не настал DPI. Где-то в первой декаде января 2026 года подкасты перестали
|
||||||
обновляться. Проверки показали, что это из-за блокировок на уровне провайдера. Mp3-файлы размещены на
|
обновляться. Проверки показали, что это из-за блокировок на уровне провайдера. Mp3-файлы размещены на DigitalOcean, и заблокирован именно он. Audiobookshelf получает ссылки через RSS-фиды, видит новые эпизоды и зависает
|
||||||
DigitalOcean, и заблокирован именно он. Audiobookshelf получает ссылки через RSS-фиды, видит новые эпизоды и зависает
|
при скачивании.
|
||||||
при скачивании.
|
|
||||||
|
|
||||||
## Как решить проблему DPI?
|
## Как решить проблему DPI?
|
||||||
|
|
||||||
Как все работало до блокировок:
|
Как всё работало до блокировок:
|
||||||
|
|
||||||
### Старая схема
|
### Старая схема
|
||||||
|
|
||||||
@@ -35,22 +35,21 @@
|
|||||||
└───────────────────────────┘
|
└───────────────────────────┘
|
||||||
```
|
```
|
||||||
|
|
||||||
Контейнер Audiobookshelf на Synology работает по 8000 порту. Nginx на Orange Pi 5 Plus проксирует на этот порт на
|
Контейнер Audiobookshelf на Synology работает по 8000 порту. Nginx на Orange Pi 5 Plus проксирует на этот порт на IP-адресе Synology, добавляет SSL и обновляет сертификат Let's Encrypt (на самом деле у меня для этой цели используется
|
||||||
IP-адресе Synology, добавляет SSL и обновляет сертификат Let's Encrypt (на самом деле у меня для этой цели используется
|
Traefik на k3s и все еще проходит через VIP-адрес Keepalive, но для простоты я опускаю эти детали). На роутере
|
||||||
Traefik на k3s и все еще проходит через VIP-адрес Keepalive, но для простоты я опускаю эти детали). На роутере
|
настроен проброс 443-порта на Orange Pi. Внешний DNS настроен на IP-адрес роутера (A- и AAAA-записи), и так я могу
|
||||||
настроен проброс 443-порта на Orange Pi. Внешний DNS настроен на IP-адрес роутера (A- и AAAA-записи), и так я могу
|
слушать подкасты из любой точки мира.
|
||||||
слушать подкасты из любой точки мира.
|
|
||||||
|
|
||||||
Скачивание подкастов внутри Audiobookshelf идет напрямую. Контейнер сразу обращается к Роутеру и пытается скачать
|
Скачивание подкастов внутри Audiobookshelf идет напрямую. Контейнер сразу обращается к Роутеру и пытается скачать
|
||||||
mp3-файл. И это ему не удаётся из-за DPI.
|
mp3-файл. И это ему не удаётся из-за DPI.
|
||||||
|
|
||||||
### Новая схема
|
### Новая схема
|
||||||
|
|
||||||
К счастью, проверки показали, что у провайдеров (даже у российских) нет блокировок DigitalOcean. А значит, эпизоды
|
К счастью, проверки показали, что у провайдеров (даже у российских) нет блокировок DigitalOcean. А значит, эпизоды
|
||||||
подкастов можно скачать на VPS/VDS-виртуалке, и нужно только настроить Audiobookshelf так, чтобы он скачивал mp3-файлы
|
подкастов можно скачать на VPS/VDS-виртуалке, и нужно только настроить Audiobookshelf так, чтобы он скачивал mp3-файлы
|
||||||
через прокси. При этом прослушивание книг и подкастов должно работать по старой схеме, без изменений.
|
через прокси. При этом прослушивание книг и подкастов должно работать по старой схеме, без изменений.
|
||||||
|
|
||||||
Как-то так:
|
Как-то так:
|
||||||
```text
|
```text
|
||||||
Интернет для скачивания Интернет для прослушивания
|
Интернет для скачивания Интернет для прослушивания
|
||||||
▲ ▲
|
▲ ▲
|
||||||
@@ -81,20 +80,18 @@ Traefik на k3s и все еще проходит через VIP-адрес Kee
|
|||||||
|
|
||||||
Почему так сложно?
|
Почему так сложно?
|
||||||
|
|
||||||
- **Во-первых**, на VPS/VDS, где я держу свои проекты, уже был настроен Shadowsocks-сервер (Outline), не для обхода
|
- **Во-первых**, на VPS/VDS, где я держу свои проекты, уже был настроен Shadowsocks-сервер (Outline), не для обхода
|
||||||
блокировок, а для безопасного доступа и администрирования (а еще для того, чтобы посещать Госуслуги и другие ресурсы,
|
блокировок, а для безопасного доступа и администрирования (а еще для того, чтобы посещать Госуслуги и другие ресурсы,
|
||||||
находясь в поездках за границей). Поэтому было логично использовать его.
|
находясь в поездках за границей). Поэтому было логично использовать его.
|
||||||
- **Во-вторы**х, для Docker не существует полноценного VPN-клиента Shadowsocks. Есть только прокси-клиенты (ss-local),
|
- **Во-вторы**х, для Docker не существует полноценного VPN-клиента Shadowsocks. Есть только прокси-клиенты (`ss-local`),
|
||||||
которые предоставляют SOCKS5-интерфейс.
|
которые предоставляют SOCKS5-интерфейс.
|
||||||
- При этом — и это **в-третьих** — Audiobookshelf не умеет работать с SOCKS5. Ему нужен настоящий HTTP, причем из-за
|
- При этом — и это **в-третьих** — Audiobookshelf не умеет работать с SOCKS5. Ему нужен настоящий HTTP, причем из-за встроенной в Audiobookshelf защиты от SSRF-атак он не может работать даже с HTTP-прокси, который находится
|
||||||
встроенной в Audiobookshelf защиты от SSRF-атак он не может работать даже с HTTP-прокси, который находится
|
в Docker-сети (например, Privoxy). Ему нужно сделать прозрачную сеть через TUN-интерфейс — тогда он будет
|
||||||
в Docker-сети (например, Privoxy). Ему нужно сделать прозрачную сеть через TUN-интерфейс — тогда он будет
|
|
||||||
доволен.
|
доволен.
|
||||||
- Для этого, **в-четвертых**, нужно сделать так, чтобы весь трафик контейнера Audiobookshelf автоматически
|
- Для этого, **в-четвертых**, нужно сделать так, чтобы весь трафик контейнера `Audiobookshelf` автоматически
|
||||||
шел через единственный контейнер (tun2socks), который будет форвардить и http, и https через Shadowsocks (для
|
шел через единственный контейнер (`tun2socks`), который будет форвардить и http, и https через Shadowsocks (для скачивания подкастов) и отдавать http-трафик на порт 8000 для прослушивания через Nginx. Именно такая схема
|
||||||
скачивания подкастов) и отдавать http-трафик на порт 8000 для прослушивания через Nginx. Именно такая схема
|
не будет ломать работу SSRF-фильтра и защитит от этого типа атаки (_можно было, конечно, откатить Audiobookshelf
|
||||||
не будет ломать работу SSRF-фильтра и защитит от этого типа атаки (_можно было, конечно, откатить Audiobookshelf
|
до древней версии 2.6.0, где дыра SSRF еще была, но это не самое безопасное решение, да и на DockerHub уже нет
|
||||||
до древней версии 2.6.0, где дыра SSRF еще была, но это не самое безопасное решение, да и на DockerHub уже нет
|
|
||||||
этой версии_).
|
этой версии_).
|
||||||
|
|
||||||
### Старый манифест Docker
|
### Старый манифест Docker
|
||||||
@@ -116,18 +113,18 @@ services: audiobookshelf:
|
|||||||
- TZ=Europe/Moscow
|
- TZ=Europe/Moscow
|
||||||
```
|
```
|
||||||
|
|
||||||
Тут, вроде, всё просто. На Synology есть папка `/volume1/music/` уже с завода. В ней я создал папки `_podcasts`
|
Тут, вроде, всё просто. На Synology есть папка `/volume1/music/` уже с завода. В ней я создал папки `_podcasts`
|
||||||
и `_audio_books` для хранения книг и подкастов и смонтировал эти папки в контейнер. Также добавил папки `config`
|
и `_audio_books` для хранения книг и подкастов и смонтировал эти папки в контейнер. Также добавил папки `config`
|
||||||
и `metadata` для хранения конфигурации и метаданных. Эти папки создаются при первом запуске контейнера и будут
|
и `metadata` для хранения конфигурации и метаданных. Эти папки создаются при первом запуске контейнера и будут
|
||||||
расположены рядом с `compose.yml` (в каталоге `/volume1/docker/audiobookshelf/`). Папка `/volume1/docker/` тоже
|
расположены рядом с `compose.yml` (в каталоге `/volume1/docker/audiobookshelf/`). Папка `/volume1/docker/` тоже
|
||||||
будет создана при установке приложения Docker Station на Synology; нам нужно только создать каталог `audiobookshelf/`
|
будет создана при установке приложения Docker Station на Synology; нам нужно только создать каталог `audiobookshelf/`
|
||||||
и выбрать его при создании проекта в Docker Station.
|
и выбрать его при создании проекта в Docker Station.
|
||||||
|
|
||||||
Порт 8000 проброшен наружу, и Nginx на хосте Orange Pi 5 Plus проксирует на этот порт, добавляет SSL и так далее.
|
Порт 8000 проброшен наружу, и Nginx на хосте Orange Pi 5 Plus проксирует на этот порт, добавляет SSL и так далее.
|
||||||
|
|
||||||
### Новая схема и манифест для Docker Station
|
### Новая схема и манифест для Docker Station
|
||||||
|
|
||||||
Теперь у нас будет три контейнера (и определённая зависимость между ними):
|
Теперь у нас будет три контейнера (и определённая зависимость между ними):
|
||||||
|
|
||||||
```yml
|
```yml
|
||||||
version: "3.8"
|
version: "3.8"
|
||||||
@@ -184,28 +181,27 @@ services:
|
|||||||
|
|
||||||
Что тут происходит:
|
Что тут происходит:
|
||||||
|
|
||||||
**Первым стартует** контейнер `ss-ru`. Он ни от кого не зависит. Он подключается к моему Outline-серверу и предоставляет
|
**Первым стартует** контейнер `ss-ru`. Он ни от кого не зависит. Он подключается к моему Outline-серверу и предоставляет
|
||||||
SOCKS5-прокси на порту `1080`.
|
SOCKS5-прокси на порту `1080`.
|
||||||
|
|
||||||
Где брать параметры для подключения к Outline/VPS? В панели Outline Manager "Add new Key". Он выдаст строку вида:
|
Где брать параметры для подключения к Outline/VPS? В панели Outline Manager «Add new Key». Он выдаст строку вида:
|
||||||
`ss://[зашифрованный-base64-ключ--метод-шифрования:пароль]@[IP-адрес Outline/VPS]:[порт Outline/VPS]/?outline=1`.
|
`ss://[зашифрованный-base64-ключ–метод-шифрования:пароль]@[IP-адрес Outline/VPS]:[порт Outline/VPS]/?outline=1`.
|
||||||
Параметры `[IP-адрес Outline/VPS]` и `[порт Outline/VPS]` нужно взять непосредственно из этой строки. Метод шифрования
|
Параметры `[IP-адрес Outline/VPS]` и `[порт Outline/VPS]` нужно взять непосредственно из этой строки. Метод шифрования
|
||||||
и пароль нужно расшифровать из первой части строки (часть строки между `ss://` и `@`). Например, командой
|
и пароль нужно расшифровать из первой части строки (часть строки между `ss://` и `@`). Например, командой
|
||||||
в терминале:
|
в терминале:
|
||||||
```shell
|
```shell
|
||||||
echo "Y2hhY2hhMjAtaWV0Zi1wb2x5MTMwNTpDYVF6OERMRDVHd09meTRvVVk1TmxK" | base64 -d
|
echo "Y2hhY2hhMjAtaWV0Zi1wb2x5MTMwNTpDYVF6OERMRDVHd09meTRvVVk1TmxK" | base64 -d
|
||||||
```
|
```
|
||||||
|
|
||||||
Получим строку вида `chacha20-ietf-poly1305:пароль`. Таким образом, `[метод шифрования]` — это `chacha20-ietf-poly1305`,
|
Получим строку вида `chacha20-ietf-poly1305:пароль`. Таким образом, `[метод шифрования]` — это `chacha20-ietf-poly1305`,
|
||||||
а `[пароль Outline/VPS]` — это `пароль`.
|
а `[пароль Outline/VPS]` — это `пароль`.
|
||||||
|
|
||||||
**Вторым стартует** контейнер `tun2socks`. Он зависит от `ss-ru`, так что гарантированно запустится после него. Он
|
**Вторым стартует** контейнер `tun2socks`. Он зависит от `ss-ru`, так что гарантированно запустится после него. Он использует `ss-ru` как SOCKS5-прокси и создает TUN-интерфейс `tun0` внутри контейнера. Также он предоставляет
|
||||||
использует `ss-ru` как SOCKS5-прокси и создает TUN-интерфейс `tun0` внутри контейнера. Также он предоставляет
|
HTTP-интерфейс на порту 80 внутри нашего проекта Docker Station (с него будет отвечать Audiobookshelf), который
|
||||||
HTTP-интерфейс на порту 80 внутри нашего проекта Docker Station (с него будет отвечать Audiobookshelf), который
|
проброшен наружу как 8000 (его будет проксировать Nginx на хосте Orange Pi 5 Plus).
|
||||||
проброшен наружу как 8000 (его будет проксировать Nginx на хосте Orange Pi 5 Plus).
|
|
||||||
|
|
||||||
Контейнер tun2socks использует системный TUN-интерфейс Synology (`/dev/net/tun`). Поэтому контейнеру нужны права
|
Контейнер tun2socks использует системный TUN-интерфейс Synology (`/dev/net/tun`). Поэтому контейнеру нужны права
|
||||||
и доступ к этому устройству. Это достигается через:
|
и доступ к этому устройству. Это достигается через:
|
||||||
```yml
|
```yml
|
||||||
cap_add:
|
cap_add:
|
||||||
- NET_ADMIN
|
- NET_ADMIN
|
||||||
@@ -213,7 +209,8 @@ services:
|
|||||||
- /dev/net/tun
|
- /dev/net/tun
|
||||||
```
|
```
|
||||||
|
|
||||||
На Synology (да и на многих Linux-системах) TUN-интерфейс `/dev/net/tun` обычно закрыт. Нужно будет разрешить доступ к нему:
|
На Synology (да и на многих Linux-системах) TUN-интерфейс `/dev/net/tun` обычно закрыт. Нужно будет
|
||||||
|
разрешить доступ к нему:
|
||||||
```bash
|
```bash
|
||||||
# Проверяем, что TUN доступен
|
# Проверяем, что TUN доступен
|
||||||
ls /dev/net/tun
|
ls /dev/net/tun
|
||||||
@@ -226,18 +223,17 @@ ls /dev/net/tun
|
|||||||
|
|
||||||
| Заметим |
|
| Заметим |
|
||||||
| ------- |
|
| ------- |
|
||||||
| Я пробовал другие решения: (1) `Privoxy` — Проблемы: axios внутри Audiobookshelf плохо работает с прокси, ломаются редиректы, SSRF-фильтр; сами RSS загружаются, но SSRF будет блокировать запросы к iTunes, а значит, не будут получаться метаданные, а без метаданных и mp3-файлы не скачиваются. (2) `redsocks` — Проблемы: нестабильный iptables redirect (на Synology это вообще не работает, похоже), падение самого redsocks, сложная настройка NAT внутри Docker.
|
| Я пробовал другие решения: (1) `Privoxy` — Проблемы: axios внутри Audiobookshelf плохо работает с прокси, ломаются редиректы, SSRF-фильтр; сами RSS загружаются, но SSRF будет блокировать запросы к iTunes, а значит, не будут получаться метаданные, а без метаданных и mp3-файлы не скачиваются. (2) `redsocks` — Проблемы: нестабильный iptables redirect (на Synology это вообще не работает, похоже), падение самого redsocks, сложная настройка NAT внутри Docker. |
|
||||||
|
|
||||||
**Третьим стартует** контейнер `audiobookshelf`. В нём почти всё по-старому. Но теперь он зависит от `tun2socks`,
|
**Третьим стартует** контейнер `audiobookshelf`. В нём почти всё по-старому. Но теперь он зависит от `tun2socks`,
|
||||||
так что гарантированно запустится после него. Кроме того, у него нет проброса портов `8000:80` (они у нас теперь
|
так что гарантированно запустится после него. Кроме того, у него нет проброса портов `8000:80` (они у нас теперь
|
||||||
в контейнере tun2socks). Вместо этого у него `network_mode: "service:tun2socks"`. В целом, `network_mode:
|
в контейнере tun2socks). Вместо этого у него `network_mode: «service:tun2socks"`. В целом, `network_mode:
|
||||||
"service:..."` — это мощная, но специфичная функция Docker, которая «склеивает» сетевые стеки контейнеров в один,
|
«service:…"` — это мощная, но специфичная функция Docker, которая «склеивает» сетевые стеки контейнеров в один,
|
||||||
а это значит, что контейнер `audiobookshelf` будет использовать сетевой стек контейнера `tun2socks` и в результате
|
а это значит, что контейнер `audiobookshelf` будет использовать сетевой стек контейнера `tun2socks` и в результате
|
||||||
весь трафик будет идти через TUN-интерфейс. Дальше уже сам `tun2socks` определит, что с ним делать. Если трафик пришел снаружи, с порта 8000, то он попадёт в Audiobookshelf, как и раньше, а если же сам Audiobookshelf начнет что-то
|
весь трафик будет идти через TUN-интерфейс. Дальше уже сам `tun2socks` определит, что с ним делать. Если трафик пришел снаружи, с порта 8000, то он попадёт в Audiobookshelf, как и раньше, а если же сам Audiobookshelf начнет что-то скачивать, то запрос уйдет через TUN-интерфейс, попадет в `tun2socks`, который перенаправит его через SOCKS5-прокси
|
||||||
скачивать, то запрос уйдет через TUN-интерфейс, попадет в `tun2socks`, который перенаправит его через SOCKS5-прокси
|
`ss-ru` на Outline/VPS (на внешнем хостинге).
|
||||||
`ss-ru` на Outline/VPS (на внешнем хостинге).
|
|
||||||
|
|
||||||
## Запуск и проверка
|
## Запуск и проверка
|
||||||
|
|
||||||
Конечно, первым делом нужно проверить в интерфейсе Docker Station, что проект «зелененький» и все контейнеры тоже
|
Конечно, первым делом нужно проверить в интерфейсе Docker Station, что проект «зелененький» и все контейнеры тоже
|
||||||
«зелененькие». Ну или выполнить:
|
«зелененькие». Ну или выполнить:
|
||||||
@@ -253,71 +249,71 @@ CONTAINER ID IMAGE COMMAND CREATED
|
|||||||
3bdb3269ed77 shadowsocks/shadowsocks-libev "ss-local -s 90.156.…" 16s ago Up 16s ss-ru
|
3bdb3269ed77 shadowsocks/shadowsocks-libev "ss-local -s 90.156.…" 16s ago Up 16s ss-ru
|
||||||
```
|
```
|
||||||
|
|
||||||
Для проверки того, что тоннель работает и подкасты скачиваются, выполним в терминале на Synology:
|
Для проверки того, что тоннель работает и подкасты скачиваются, выполним в терминале на Synology:
|
||||||
```bash
|
```bash
|
||||||
sudo docker exec -it audiobookshelf sh -c " node -e \"const https=require('https');const fs=require('fs');const file=fs.createWriteStream('/podcasts/test.mp3');https.get('https://aerostats.getmobileup.com/music/1074.mp3',res=>{res.pipe(file);file.on('finish',()=>console.log('Done'))})\""
|
sudo docker exec -it audiobookshelf sh -c " node -e \"const https=require('https');const fs=require('fs');const file=fs.createWriteStream('/podcasts/test.mp3');https.get('https://aerostats.getmobileup.com/music/1074.mp3',res=>{res.pipe(file);file.on('finish',()=>console.log('Done'))})\""
|
||||||
```
|
```
|
||||||
|
|
||||||
Audiobookshelf работает на Node.js, а внутри контейнера нет ни `curl`, ни `wget`. Так что придется запустить такой
|
Audiobookshelf работает на Node.js, а внутри контейнера нет ни `curl`, ни `wget`. Так что придется запустить такой
|
||||||
заковыристый js-скрипт для проверки скачивания файла.
|
заковыристый js-скрипт для проверки скачивания файла.
|
||||||
|
|
||||||
В результате мы получим `test.mp3` в папке `/podcasts/` (а она у нас снаружи контейнера
|
В результате мы получим `test.mp3` в папке `/podcasts/` (а она у нас снаружи контейнера
|
||||||
в `/volume1/music/_podcasts`). Это выпуск №1074 подкаста «Аэростат» с альбомом Аквариума «Странные Новости
|
в `/volume1/music/_podcasts`). Это выпуск №1074 подкаста «Аэростат» с альбомом Аквариума «Странные Новости
|
||||||
с Далёкой Звезды» (2026 год, очень рекомендую послушать). Этот подкаст размещен на DigitalOcean, так что если
|
с Далёкой Звезды» (2026 год, очень рекомендую послушать). Этот подкаст размещен на DigitalOcean, так что если
|
||||||
он скачался, значит туннель работает и обход блокировок провайдера сработал.
|
он скачался, значит туннель работает и обход блокировок провайдера сработал.
|
||||||
|
|
||||||
Проверить, что метаданные из iTunes тоже подгружаются, можно, только настроив автообновление подкастов
|
Проверить, что метаданные из iTunes тоже подгружаются, можно, только настроив автообновление подкастов
|
||||||
в интерфейсе Audiobookshelf (например, через пять минут). Но получение метаданных происходит "под капотом",
|
в интерфейсе Audiobookshelf (например, через пять минут). Но получение метаданных происходит «под капотом»,
|
||||||
и иначе это никак не диагностировать.
|
и иначе это никак не диагностировать.
|
||||||
|
|
||||||
Должно всё работать. Но если не работает, то "курите логи" контейнера `audiobookshelf`:
|
Должно всё работать. Но если не работает, то «курите логи» контейнера `audiobookshelf`:
|
||||||
```bash
|
```bash
|
||||||
sudo docker logs audiobookshelf --tail 100
|
sudo docker logs audiobookshelf --tail 100
|
||||||
```
|
```
|
||||||
|
|
||||||
...и Google и ИИ-ассистенты в помощь. Node.js — это не самая простая среда для отладки.
|
...и Google и ИИ-ассистенты в помощь. Node.js — это не самая простая среда для отладки.
|
||||||
|
|
||||||
|
|
||||||
## P.S. Важное замечание о защите Audiobookshelf от SSRF
|
## P. S. Важное замечание о защите Audiobookshelf от SSRF
|
||||||
|
|
||||||
Во время настройки в логах можно столкнуться с ошибкой вида: `Call to 172.25.0.3 is blocked` или `invalid feed payload`.
|
Во время настройки в логах можно столкнуться с ошибкой вида: `Call to 172.25.0.3 is blocked` или `invalid feed payload`.
|
||||||
Это не ошибка сети и не проблема RSS. Это встроенная защита Audiobookshelf от SSRF-атак.
|
Это не ошибка сети и не проблема RSS. Это встроенная защита Audiobookshelf от SSRF-атак.
|
||||||
|
|
||||||
### Что такое SSRF
|
### Что такое SSRF
|
||||||
|
|
||||||
SSRF (Server Side Request Forgery) — это тип атаки, когда сервер заставляют делать запросы в свою внутреннюю сеть.
|
SSRF (Server Side Request Forgery) — это тип атаки, когда сервер заставляют делать запросы в свою внутреннюю сеть.
|
||||||
Например, злоумышленник может подложить RSS-фид со ссылкой на внутренний сервис. Например: `http://127.0.0.1:8080/admin`
|
Например, злоумышленник может подложить RSS-фид со ссылкой на внутренний сервис. Например: `http://127.0.0.1:8080/admin`
|
||||||
или `http://172.25.0.3:5000`.
|
или `http://172.25.0.3:5000`.
|
||||||
|
|
||||||
Если сервер загрузит такой URL, атакующий потенциально сможет:
|
Если сервер загрузит такой URL, атакующий потенциально сможет:
|
||||||
- читать данные внутренних сервисов
|
- читать данные внутренних сервисов
|
||||||
- получать внутренние токены авторизации
|
- получать внутренние токены авторизации
|
||||||
- обращаться к Docker API
|
- обращаться к Docker API
|
||||||
- сканировать внутреннюю сеть
|
- сканировать внутреннюю сеть
|
||||||
|
|
||||||
Поэтому многие современные приложения блокируют такие запросы. И Audiobookshelf не исключение.
|
Поэтому многие современные приложения блокируют такие запросы. И Audiobookshelf не исключение.
|
||||||
|
|
||||||
Audiobookshelf использует библиотеку `ssrf-req-filter`. Она проверяет IP-адрес, в который разрешается (резолвится) домен, и блокирует некоторые диапазоны:
|
Audiobookshelf использует библиотеку `ssrf-req-filter`. Она проверяет IP-адрес, в который разрешается (резолвится) домен, и блокирует некоторые диапазоны:
|
||||||
- `127.0.0.0/8`
|
- `127.0.0.0/8`
|
||||||
- `10.0.0.0/8`
|
- `10.0.0.0/8`
|
||||||
- `172.16.0.0/12`
|
- `172.16.0.0/12`
|
||||||
- `192.168.0.0/16`
|
- `192.168.0.0/16`
|
||||||
- `169.254.0.0/16`
|
- `169.254.0.0/16`
|
||||||
|
|
||||||
То есть любые внутренние адреса, включая адреса Docker-сети.
|
То есть любые внутренние адреса, включая адреса Docker-сети.
|
||||||
|
|
||||||
Когда использовалась схема `Audiobookshelf → HTTP Proxy → Shadowsocks`, то HTTP-прокси находился в Docker-сети. Например:
|
Когда использовалась схема `Audiobookshelf → HTTP Proxy → Shadowsocks`, то HTTP-прокси находился в Docker-сети. Например:
|
||||||
`privoxy:8118` или `redsocks:12345`.
|
`privoxy:8118` или `redsocks:12345`.
|
||||||
|
|
||||||
После DNS-разрешения (резолва) запрос фактически отправлялся на адрес контейнера прокси внутри Docker-сети. Например, `172.25.0.3`. И SSRF-фильтр Audiobookshelf это видел и блокировал запрос. Именно поэтому в логах появлялось: `Call to 172.25.0.3 is blocked`.
|
После DNS-разрешения (резолва) запрос фактически отправлялся на адрес контейнера прокси внутри Docker-сети. Например, `172.25.0.3`. И SSRF-фильтр Audiobookshelf это видел и блокировал запрос. Именно поэтому в логах появлялось: `Call to 172.25.0.3 is blocked`.
|
||||||
|
|
||||||
### Почему HTTP_PROXY часто ломает Audiobookshelf
|
### Почему HTTP_PROXY часто ломает Audiobookshelf
|
||||||
|
|
||||||
Audiobookshelf использует библиотеку axios. А он:
|
Audiobookshelf использует библиотеку axios. А он:
|
||||||
- плохо работает с редиректами через прокси
|
- плохо работает с редиректами через прокси
|
||||||
- иногда разрешает (резолвит) DNS на стороне прокси
|
- иногда разрешает (резолвит) DNS на стороне прокси
|
||||||
- иногда разрешает (резолвит) DNS локально
|
- иногда разрешает (резолвит) DNS локально
|
||||||
|
|
||||||
В результате: часть запросов проходит, часть падает, RSS может читаться, но mp3 не скачиваются и так далее.
|
В результате: часть запросов проходит, часть падает, RSS может читаться, но mp3 не скачиваются и так далее.
|
||||||
|
|
||||||
Опытным путем я определил, что наиболее надёжный способ — сетевой туннель (`tun2socks`), при котором приложение не знает о проксировании вообще.
|
Опытным путем я определил, что наиболее надёжный способ — сетевой туннель (`tun2socks`), при котором приложение не знает о проксировании вообще.
|
||||||
|
|||||||
Reference in New Issue
Block a user