yt-dlp-gui/deploy/ytdlp-gui.service
codex-pve f1e4cdbe1d Запрет исходящих во внутреннюю сеть заработал: убран IPAddressAllow=any
Главное. Строка IPAddressAllow=any обесценивала весь список запретов ниже,
и заявленная защита от SSRF существовала только на бумаге. Из cgroup
сервиса оставались доступны Proxmox API на 10.20.0.1:8006 и соседняя
виртуальная машина — проверено установлением соединения. Сбивало с толку
то, что адрес метаданных 169.254.169.254 при этом блокировался, поэтому
прежняя проверка выглядела успешной.

Когда список состоит только из запретов, фильтр работает: внутренняя сеть
закрыта, внешняя и DNS продолжают работать. Всё проверено отдельными
запусками через systemd-run до применения на боевом юните.

Петля не закрывается сознательно. Фильтр systemd не различает направление,
и запрет 127.0.0.0/8 обрывает ВХОДЯЩИЕ соединения nginx к этому же
сервису — проверено: слушатель внутри такой cgroup становится недоступен
снаружи, то есть сайт лёг бы. Службы на 127.0.0.1 остаются достижимы;
закрыть их можно, только переведя связку nginx→приложение на unix-сокет.

Дисковый бюджет. Юнит вчетверо поднимал квоту относительно кода (20 ГБ
против 5 ГБ) при 35 ГБ свободного на разделе гипервизора, где живут
виртуальные машины. Приведено к 5 ГБ.

/readyz больше не витрина. Наружу уходит только признак готовности;
свободное место, глубина очереди и версия yt-dlp отдаются лишь локальному
мониторингу — сайт публичный.

Статика кэшируется на месяц: стили и восемь файлов шрифта отдаются этим же
процессом, а потоки здесь дефицитны из-за SSE, и браузер ревалидировал все
девять файлов на каждой навигации.

Согласованы сроки хранения: карточка задачи считается от создания, а файл —
от окончания скачивания, поэтому при равных TTL карточка истекала раньше
файла, и файл оставался недостижимым, занимая квоту. Срок карточки поднят
до 240 минут против 120 у файла.

Также: .gitkeep больше не удаляется уборщиком по TTL (при подметании на
старте он уже исключался), README приведён в соответствие с config.py —
три значения по умолчанию были указаны неверно, тринадцать переменных не
описаны вовсе.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AwY2Cg54RK7WZdvMaLV5Di
2026-09-21 18:19:04 +03:00

95 lines
4.3 KiB
Desktop File
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

[Unit]
Description=yt-grab — веб-GUI для yt-dlp (yt.zapret.moe)
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
User=codex-pve
Group=codex-pve
WorkingDirectory=/home/codex-pve/ytdlp-gui
# Домашняя папка примонтирована только на чтение (ProtectHome),
# а gunicorn пишет туда управляющий сокет — уводим его в приватный /tmp.
Environment=HOME=/tmp
Environment=YTG_HOST=127.0.0.1
Environment=YTG_PORT=8090
Environment=YTG_TRUST_PROXY=1
Environment=YTG_MAX_CONCURRENT=3
Environment=YTG_MAX_FILESIZE_MB=2048
# Квота держится заметно ниже свободного места на разделе: это
# гипервизор, и заполнить его корень — значит остановить виртуальные
# машины, а не просто отдать пользователю ошибку.
Environment=YTG_DISK_QUOTA_MB=5120
Environment=YTG_QUIET_ACCESS_LOG=1
# Один воркер: состояние задач держится в памяти процесса.
# Потоки обслуживают SSE-соединения и параллельные запросы.
# Потоков заметно больше числа одновременных загрузок: каждое SSE-соединение
# занимает поток на всё время загрузки, и при 16 потоках сервис подвешивался
# двумя клиентами. В nginx стоит limit_conn 24 на адрес.
ExecStart=/home/codex-pve/ytdlp-gui/.venv/bin/gunicorn \
--workers 1 \
--threads 64 \
--timeout 0 \
--graceful-timeout 30 \
--bind 127.0.0.1:8090 \
--access-logfile /dev/null \
--error-logfile - \
app:app
Restart=on-failure
RestartSec=5
# --- Изоляция ---
NoNewPrivileges=yes
PrivateTmp=yes
ProtectSystem=strict
ProtectHome=read-only
ReadWritePaths=/home/codex-pve/ytdlp-gui/downloads
ReadWritePaths=/home/codex-pve/ytdlp-gui/data
ProtectKernelTunables=yes
ProtectKernelModules=yes
ProtectControlGroups=yes
RestrictSUIDSGID=yes
RestrictNamespaces=yes
LockPersonality=yes
# --- Запрет исходящих во внутреннюю сеть ---
# Проверка адреса в приложении принципиально не закрывает DNS rebinding
# (yt-dlp резолвит имя заново и сам ходит по редиректам), поэтому запрет
# ставится на уровне ядра. Хост — гипервизор: во внутренней сети живут
# его API и виртуальные машины.
# Здесь НЕТ IPAddressAllow=any, и это существенно: с ним запреты ниже
# фактически не работали — проверено, Proxmox API на 10.20.0.1:8006 и
# соседняя ВМ оставались доступны. Когда список состоит только из
# запретов, systemd пропускает всё остальное, и внешняя сеть с DNS
# продолжают работать (тоже проверено).
#
# Петля (127.0.0.0/8, ::1) сознательно НЕ закрыта: фильтр systemd не
# различает направление, и запрет петли обрывает входящие соединения от
# nginx к этому же сервису — проверено, слушатель становится недоступен
# снаружи и сайт ложится. Поэтому службы на 127.0.0.1 остаются достижимы;
# закрыть их можно, только переведя связку nginx→приложение на
# unix-сокет.
IPAddressDeny=10.0.0.0/8
IPAddressDeny=172.16.0.0/12
IPAddressDeny=192.168.0.0/16
IPAddressDeny=169.254.0.0/16
IPAddressDeny=100.64.0.0/10
IPAddressDeny=fc00::/7
IPAddressDeny=fe80::/10
# --- Ресурсы хоста ---
# Сервис делит гипервизор с виртуальными машинами: ffmpeg при
# перекодировании иначе забирает все ядра у соседей.
CPUWeight=20
IOWeight=20
Nice=10
# --- Лимиты ресурсов ---
MemoryMax=2G
TasksMax=256
[Install]
WantedBy=multi-user.target