Паста на своих: rustypaste вместо капризного CDN
Почему пришлось брать метлу в лапки
Мы хостили иллюстрации для постов на catbox.moe. В какой‑то момент их CDN (files.catbox.moe) из РФ стал отвечать “Connection reset by peer”: TLS обрывался, картинки не открывались. Наш робот‑дворник посмотрел на этот сугроб разрывов, вздохнул, махнул метлой и сказал: «Хватит, делаем своё».
Что за rustypaste и почему он
rustypaste (v0.17.0) — минималистичный paste/file‑host на одном бинарнике Rust. Базы данных нет, всё хранится в файловой системе, конфиг — один config.toml. Из коробки:
– загрузка файлов и текста, в том числе из удалённого URL;
– угадывает MIME и позволяет его переопределять/блокировать;
– одноразовые ссылки, срок жизни, удаление по токену;
– аутентификация простым Authorization токеном или списком токенов в конфиге.
Нам нужен был быстрый и автономный хост для картинок канала — без лишней магии и с контролем загрузки. Rustypaste закрыл все пункты.
Как подняли: LXC + systemd + Caddy
- Отдельный контейнер (LXC) во внутренней сети сервисов
198.51.100.0/24, порт8000. - Снаружи — поддомен
paste.exampleчерез Caddyreverse_proxy. Сертификат Let’s Encrypt выпустился автоматически (HTTP‑01 challenge). - Загрузка разрешена только с токеном в заголовке
Authorization. Отдача — публичная, с корректнымContent-Type(например,image/png).
systemd-юнит
Создаём пользователя и юнит:
# один раз

useradd -r -s /usr/sbin/nologin -m -d /var/lib/rustypaste rustypaste || true
install -d -o rustypaste -g rustypaste /var/lib/rustypaste/upload /etc/rustypaste
cat >/etc/systemd/system/rustypaste.service <<'UNIT'
[Unit]
Description=Rustypaste service
After=network-online.target
Wants=network-online.target
[Service]
User=rustypaste
Group=rustypaste
WorkingDirectory=/var/lib/rustypaste
Environment=CONFIG=/etc/rustypaste/config.toml
Environment=RUST_LOG=info
ExecStart=/usr/local/bin/rustypaste
Restart=always
[Install]
WantedBy=multi-user.target
UNIT
systemctl daemon-reload
systemctl enable --now rustypaste
Конфиг rustypaste (/etc/rustypaste/config.toml)
Важно: ниже используются документные диапазоны (RFC 5737) и заглушки — подставьте свой поддомен/пути/токены. Для heredoc используйте литеральный режим
<<'TOML'— это спасает от «съедания» бэкслешей.
cat > /etc/rustypaste/config.toml <<'TOML'
[server]
address = "198.51.100.42:8000" # LXC во внутренней сети
url = "https://paste.example" # внешний адрес за Caddy
upload_path = "/var/lib/rustypaste/upload"
auth_tokens = ["<PASTE_UPLOAD_TOKEN>"]
expose_version = false
hardening = true
[paste]
# Расширение по умолчанию для текстов без явного имени
default_extension = "txt"
# Переопределения MIME по regex — используйте литеральные строки (одинарные кавычки)
mime_override = [
{ mime = 'image/png', regex = '^.*\.png$' },
{ mime = 'image/jpeg', regex = '^.*\.(jpg|jpeg)$' },
{ mime = 'image/gif', regex = '^.*\.gif$' }
]
# Что принудительно отдавать как text/plain (для предпросмотра)
text_mime_overrides = ["text/markdown", "application/json"]
TOML
chown -R rustypaste:rustypaste /etc/rustypaste /var/lib/rustypaste
systemctl restart rustypaste
Caddy reverse_proxy
Минимальный сайт‑блок:
paste.example {
encode zstd gzip
reverse_proxy 198.51.100.42:8000
}
Caddy сам пройдёт HTTP‑01 и выпустит сертификат для paste.example.
Загрузка и проверка
# Без токена — 401 Unauthorized
curl -fsS -F "file=@x.png" https://paste.clr58.ru/ || echo "→ 401 as expected"
# С токеном — получаем ссылку вида https://paste.clr58.ru/<имя>.png
curl -fsS -H "Authorization: <PASTE_UPLOAD_TOKEN>" -F "file=@x.png" https://paste.clr58.ru/
Пакетная переливка картинок (пример):
for f in ./images/*; do
curl -s -H "Authorization: <PASTE_UPLOAD_TOKEN>" -F "file=@$f" https://paste.clr58.ru/ >> uploaded_urls.txt
echo >> uploaded_urls.txt
done
Грабли, на которые наступили
1) TOML + regex: «TOML parse error: missing escaped value». Мы сначала писали regex в двойных кавычках и заливали конфиг heredoc’ом без литерального режима — bash скромно «почистил» бэкслеши. Лечение: либо heredoc с <<'TOML', либо TOML‑строки в одинарных кавычках (литеральные), как в примере выше.
2) «config.toml is not found» и цикл рестартов. Первая передача конфига в контейнер банально не долетела — сервис честно падал и перезапускался. Проверяйте, что файл реально на месте и виден пользователю сервиса: ls -l /etc/rustypaste/config.toml и права на каталог.
Итог: картинки вернулись домой
- Свой paste на
rustypasteизбавил нас от капризов чужого CDN. - Загрузка защищена токеном, отдача идёт с правильным MIME.
- Перелили архив картинок и пересобрали посты — читатели снова видят иллюстрации.
Робот‑дворник доволен: меньше мусора из внешних зависимостей — больше чистых дорожек в контент‑плане.
Ссылки
- Репозиторий: https://github.com/orhun/rustypaste
- Релизы: https://github.com/orhun/rustypaste/releases