devzone.org.ua/post/nalashtuvannia-fediverse-serveru-snac-dlia-roboty-v-merezi-yggdrasil.md
2025-11-01 14:28:20 +02:00

298 lines
No EOL
24 KiB
Markdown
Raw 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.

# Налаштування Fedi-сервера Snac для мережі Yggdrasil
Невдовзі, після своїх [роздумів про p2p](https://devzone.org.ua/post/hrabli-p2p), вирішив спробувати підняти власний експериментальний інстанс [Fediverse](https://uk.wikipedia.org/wiki/Федиверс). При чому, зробити це засобами оверлейної мережі [Yggdrasil](https://devzone.org.ua/post/yggdrasil-mereza-z-detsentralizovanym-routynhom), оскільки я не планую купувати для цієї іграшки виділений IP чи VPS, натомість буду хоститись з модему, одноплатника або взагалі з ПК, коли буваю онлайн, з динамічною адресою за NAT.
Пишу цю нотатку в першу чергу - для себе, а також, вона може бути корисною для тих, хто як і я тільки починає свої експерименти у сфері адміністрування власного вузла Fediverse і цікавиться альтернативними мережами, в контексті Linux.
## Що таке Snac
[Snac](https://codeberg.org/grunfink/snac2) - це мінімалістична, JS-less, написана мовою C альтернатива серверу [Mastodon](https://joinmastodon.org/uk), яка також не потребує інсталяції PostgreSQL, натомість зберігає усі дані профілю у файлах JSON. Нещодавно, до цього серверу було [додано підтримку IPv6](https://codeberg.org/grunfink/snac2/pulls/256), а отже - він буде працювати й з діапазоном Yggdrasil `0200::/7`
Оскільки Yggdrasil дозволяє безкоштовно генерувати не обмежену кількість статичних IP (на базі приватного ключа [Ed25519](https://en.wikipedia.org/wiki/EdDSA#Ed25519)), тут немає звичної потреби в DNS. Хоча, можна опціонально прикрутити [Alfis](https://devzone.org.ua/post/alfis-dns-reyestratsiia-domenu-v-blokcheyn), але особисто я цим ділом не користуюсь (зокрема, й через досі не вирішену проблему [#364](https://github.com/Revertron/Alfis/issues/364)) тому також не хочу нав'язувати його в рамках протоколу ActivityPub - буде просто формат `username@IPv6`, який мені не потрібно а ні оновлювати, а ні майнити потім.
## Встановлення
1. Точний перелік пакетів для Debian я не знаю, оскільки моя система не нова і вже має встановлені раніше пакунки. Як вказано в [README](https://codeberg.org/grunfink/snac2#building-and-installation), я тільки встановив `libssl-dev` і `libcurl4-openssl-dev` (для Fedora має бути приблизно те само з постфіксом `*-devel`)
2. Далі, створюємо окремого системного юзера, щоб ізолюватись від потенційних вразливостей:
``` bash
useradd -m snac
```
3. Змінюємо для зручності середовище на `bash` у файлі `/etc/passwd`
4. Логінимось через `su snac` і переходимо в домашню директорію цього юзера: `cd`
5. Завантажуємо останній вихідний код: `git clone https://codeberg.org/grunfink/snac2.git`
6. Заходимо в робочу директорію `cd snac2`
7. Компілюємо `make && sudo make install` і встановлюємо з відповідними правами
8. Ініціалізуємо серверне сховище: `snac init /home/snac/storage`
9. І додамо до нього нашого першого юзера `snac adduser /home/snac/storage`
10. Далі продовжуємо від `root` виконавши команду `exit`
## Налаштування
Я вже маю встановлений і налаштований вузол Yggdrasil, якщо комусь цікавий процес встановлення, скористайтесь [попередньою публікацією](https://devzone.org.ua/post/yggdrasil-mereza-z-detsentralizovanym-routynhom) або [офіційною документацією](https://yggdrasil-network.github.io/documentation.html).
### Адреса підмережі Yggdrasil
Можна пропустити цей крок і використовувати основну адресу `2*`, якщо порти `80` чи `8001` не зайняті. Але зауважте, що в рамках API протоколу ActivityPub, сервер Snac надаватиме вашу адресу хосту іншим нодам, а ті - її кешуватимуть, як частину ID і оскільки локально адреса хосту зберігається по файлам, а не в БД, потім буде важко її замінити. Тому краще виділити окрему, особливо - якщо це продакшн:
1. `yggdrasilctl getself` - дізнаємось свій айпішник, зокрема вивід `IPv6 subnet`
2. `ifconfig lo inet6 add IP` - замість IP вказуємо довільну адресу для отриманого діапазону, наприклад `3xx:xxxx:xxxx:xxxx::fed/64` , де `fed` - така собі гра слів в рамках "словника" IPv6 (0-9A-F).
* варто зауважити, що дані маршрутизації `ifconfig` не зберігаються після ребуту системи, для цього потрібно додати відповідний запис (команду з пункту 2), наприклад до `/etc/netplan/01-ygglo.yaml`, `/etc/network/interfaces`, або безпосередньо до systemd `yggdrasil.service` (секція `ExecStartPost=`) - залежно від операційної системи.
### Проксі Nginx
На моєму сервері вже встановлено веб-сервер [Nginx](https://nginx.org/), який займає порт `80`, я поки не хочу нічого змінювати, а також не хочу мати публічні адреси Snac з його стандартним портом `8001`. Тому, оскільки вже маю виділену адресу підмережі, просто запроксую API на `80` порт через новий віртуальний хост, частково використавши [оригінальний приклад конфігурації](https://codeberg.org/grunfink/snac2/src/branch/master/examples/nginx-alpine-ssl/default.conf):
``` /etc/nginx/sites-available/default
# /etc/nginx/sites-available/default
server {
listen [3xx:xxxx:xxxx:xxxx::fed]:80;
server_name 3xx:xxxx:xxxx:xxxx::fed;
location @proxy {
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_redirect off;
proxy_connect_timeout 90;
proxy_send_timeout 90;
proxy_read_timeout 90;
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;
proxy_set_header Proxy "";
proxy_pass_header Server;
proxy_buffering on;
tcp_nodelay on;
proxy_pass http://[3xx:xxxx:xxxx:xxxx::fed]:8001;
proxy_set_header Host $http_host;
}
location /.well-known/webfinger {
try_files $uri @proxy;
}
location /.well-known/nodeinfo {
try_files $uri @proxy;
}
location / {
try_files $uri @proxy;
}
location /fedi/ {
try_files $uri @proxy;
}
}
```
* `systemctl reload nginx` - застосовуємо зміни
* можливо, захочете створити окремий конфігураційний файл для Nginx, замість `default` - в мене він один на всі хости.
* опціонально, засобами Nginx, можна закрити окремі локації по IP
Як бачите, на прикладі вище не вказано порт `443`, а також немає сертифікатів SSL. Це зроблено спеціально, оскільки Yggdrasil вже має захищений канал, і я не хочу створювати тут зайвий шар.
Оскільки клієнтські підключення Yggdasil також мають статичну адресу, я вирішив обмежити доступ до адміністративного API (адмінка усіх акаунтів + `oauth`) по IP. Наскільки це ефективно і чи не забув про інші адреси - я не знаю, але додам свій приклад регулярного виразу для `location`:
``` default
location ~ /([^\/]+/admin|oauth) {
allow ADMIN_IP;
deny all;
try_files $uri @proxy;
}
```
### Конфігурація Snac
Відредагуємо раніше згенерований командою `snac init` файл `/home/snac/storage/server.json`:
``` server.json
{
"host": "[3xx:xxxx:xxxx:xxxx::fed]",
"prefix": "",
"address": "3xx:xxxx:xxxx:xxxx::fed",
"port": 8001,
"layout": 2.7,
"dbglevel": 0,
"queue_retry_minutes": 2,
"queue_retry_max": 10,
"queue_timeout": 6,
"queue_timeout_2": 8,
"cssurls": [
""
],
"def_timeline_entries": 50,
"max_timeline_entries": 50,
"timeline_purge_days": 120,
"local_purge_days": 0,
"min_account_age": 0,
"admin_email": "",
"admin_account": "",
"title": "",
"short_description": "",
"short_description_raw": false,
"protocol": "http",
"fastcgi": false
}
```
* звертаю увагу, що протокол у моєму прикладі змінено на `http`
### Доступи iptables
Конфігурація у прикладах не передбачає доступу до ноди з мережі Інтернет, тому я відкрив порт тільки для Yggdrasil, щоб інші вузли в рамках цієї мережі могли взаємодіяти між собою на івентах типу фоловінгу (обидва вузли мають бути онлайн для транзакції):
```
ufw allow from 0200::/7 to any port 80
```
* якщо не користуєтесь Nginx, або сервер має стандартний чи інший порт, просто замість `80` вкажіть актуальний, наприклад `8001`
* якщо обмежуєте трафік по діапазону `0200::/7`, також зверніть увагу на зауваження щодо приватного режиму, про який описано нижче
### Налаштування systemd
Є готовий [офіційний приклад конфігурації](https://codeberg.org/grunfink/snac2/src/branch/master/examples/snac.service), але я його трохи доповнив:
``` /etc/systemd/system/snac.service
# /etc/systemd/system/snac.service
[Unit]
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
User=snac
Group=snac
ExecStart=/usr/local/bin/snac httpd /home/snac/storage
StandardOutput=file:/home/snac/debug.log
StandardError=file:/home/snac/error.log
[Install]
WantedBy=multi-user.target
```
* `systemctl daemon-reload` - оновлюємо конфігурацію `systemd`
* `systemctl enable snac` - автостарт при запуску системи
* `systemctl start snac` - запуск
* `systemctl status snac` - перевіряємо статус
### Резервне копіювання
Оскільки база даних Snac зберігається у файловому форматі, досить просто бекапити профіль по лише одній локації.
Я роблю це засобами `rsync` для різних часових інтервалів наступною командою `crontab -e`:
``` bash
@daily /usr/bin/rsync -av --delete /home/snac/storage /path/to/snac/daily
@weekly /usr/bin/rsync -av --delete /home/snac/storage /path/to/snac/weekly
@monthly /usr/bin/rsync -av --delete /home/snac/storage /path/to/snac/monthly
```
## Користування
Після запуску Snac командою `snac httpd /home/snac/storage` або через сервіс `systemd`, можна спробувати відкрити у браузері `http://[3xx:xxxx:xxxx:xxxx::fed]`.
### Тестування взаємодії (API)
Щоб перевірити взаємодію з іншим вузлом Yggdrasil, повторюємо для нього ті само дії і робимо тестовий фоловінг чи переписку між користувачами через Web UI або підключений зовнішній клієнтський застосунок.
### Тюнінг браузеру
Якщо вперше користуєтесь сайтами Yggdrasil у Firefox, можливо знадобиться оптимізувати обробку "сирих" IPv6 адрес в `about:config`:
* `browser.fixup.fallback-to-https`:`false` - вимкнути редірект `http` -> `https`
* `browser.fixup.alternate.enabled`:`false` - викнути автоматичну обробку префіксу `www`
### Теми Web UI
В [README](https://codeberg.org/grunfink/snac2#incredibly-awesome-css-themes-for-snac) є перелік посилань на CSS теми, за допомогою яких можна кастомізувати веб-інтерфейс Snac на власне вподобання.
Спочатку я не зрозумів, як підключати нові теми, і додав посилання на умовний файл `/theme.css` до `/home/snac/storage/server.json`, а також створив на нього аліас локального шляху в Nginx (щоб задовольнити браузерну політику CORS):
``` default
location /theme.css {
alias /var/www/snac/theme.css;
}
```
* цей приклад я лишаю на випадок, якщо ви захочете підключити додаткові `cssurls`
Але згодом виявилось, що при створенні інстансу, генерується стандартний файл у теці `/home/snac/storage/style.css` який буде конфліктувати з новою темою (адже він підключатиметься окремо від масиву конфігурації `cssurls`).
Таким чином, потрібно просто переписати вміст стандартного файлу `/home/snac/storage/style.css` обраною темою, а масив `cssurls` потрібен тільки для тюнінгу поточної теми, без правки її оригіналу. Щоб повернутись до оригінальної теми - достатньо видалити цей файл, після чого згенерується стандартний файл Snac.
### Специфіка клієнтських застосунків
Особисто, я встиг перевірити тільки [Tuba](https://tuba.geopjr.dev/). Як виявилось, даний клієнт має захардкожену обробку схеми HTTPS, тому якщо користуєтесь цим застосунком, доведеться налаштувати окремий інтерфейс Nginx на порті `443` з використанням сертифікату, хоч у випадку з Yggdrasil - це зайва "капуста" і мабуть поки що лишусь на веб-інтерфейсі або зроблю і викладу потім патч.
UPD 1. розробник виявився супер-оперативним і вже створив [гілку з патчем](https://github.com/GeopJr/Tuba/tree/refs/heads/experiment/ignore-dom-secure) (Flatpak в [Артефактах](https://github.com/GeopJr/Tuba/actions/runs/14965019143)), єдине що - тільки поки не "завезли" валідацію IPv6 у вікно авторизації, тому я тимчасово користуюсь аліасом в `/etc/hosts` і вказую у якості URL авторизації `http://alias`. Можливо, у наступних випусках додатка це вже буде не актуально.
UPD 2. Наразі, гілка об'єднана з `main`, тому замість аліасів для IPv6, можна просто додати при запуску `TUBA_SKIP_STRICT_VALIDATION=1`, наприклад:
``` bash
TUBA_SKIP_STRICT_VALIDATION=1 'builddir/dev.geopjr.Tuba'
```
UPD 3. Особисто я користуюсь [окремою гілкою](https://github.com/YGGverse/Tuba/tree/multiprotocol-address-support), де ця нубська валідація HTTP випиляна, і жодних додаткових флагів для запуску не потрібно:
Для збірки з форку:
1. `git clone https://github.com/YGGverse/Tuba.git`
2. `git checkout multiprotocol-address-support`
3. `make && make install`
* або для Flatpak:
``` bash
flatpak-builder --force-clean build\
--install-deps-from=flathub\
--install\
--repo=repo\
--user\
build-aux/dev.geopjr.Tuba.Devel.json
```
Також, в мене є ще одна, окрема гілка, де я форсовано застосовую українську локаль для постів, адже сервер Snac має [певний недопил](https://codeberg.org/grunfink/snac2/issues/388) по синхронізації даного API.
Скачати All-in-One збірку можна [тут](https://github.com/YGGverse/Tuba/tree/ps), виконавши для встановлення кроки вище (з `git checkout ps`).
### Моніторинг трафіку
Оскільки сервер Snac не передбачає користування JS інтерфейсом, а поточна конфігурація використовує Nginx, можу по ходу справи також порадити [goaccess](https://goaccess.io/) - CLI утиліту для зручного моніторингу статистики користувацького трафіку, якщо такий буде:
``` bash
goaccess /var/log/nginx/access.log
```
### Приватний режим
Yggdrasil дозволяє маскувати реальний IP, якщо ви користуєтесь власним [вихідним вузлом](https://publicpeers.neilalexander.dev/). Звісно, таку можливість не варто розглядати в контексті анонімізації окремо без додаткових шарів, оскільки використання протоколу Yggdrasil без наприклад таких проксі, як [Shadowsocks](https://github.com/shadowsocks/shadowsocks-rust) - легко виявляється.
Ця тема виходить за рамки матеріалу, але зверну увагу на деякі аспекти, якщо ви плануєте користуватись збіркою Snac + Yggdrasil в режимі "інкогніто". Протокол ActivityPub передбачає "спілкування" між серверами для обміну івентами. Тобто потенційний фоловер може відправити запит підписки на на дозволений у фаєрвол інтерфейс `0200::/7`, але вказати в заголовках події ActivityPub - зворотній DNS на вузол в мережі Інтернет. Таким чином, ваша система здійснить транзакцію з білого IP через системний резольвер або без нього, використовуючи локальний Curl API від Snac.
Потенційних сценаріїв витоку можна придумати багато. Я переглянув вихідний код Snac, та не знайшов у ньому жодних фільтрів взаємодії на вихідні підключення. Тому, як і для іншого не спеціалізованого софту, для цієї мети бажано використовувати ізоляцію роутера з контейнера [Docker](https://uk.wikipedia.org/wiki/Docker), [LXC](https://uk.wikipedia.org/wiki/LXC) або віртуалізуватись засобами [QEMU](https://uk.wikipedia.org/wiki/QEMU).
При використанні клієнтського API, окремого аудиту потребуватиме обробка віддаленого вмісту постів, аватарів та іншого. У цьому напрямку, є перші кроки, зокрема по частині Web UI ([PR#394](https://codeberg.org/grunfink/snac2/pulls/394), [гілка](https://codeberg.org/postscriptum/snac2/src/branch/enhanced-webui-privacy)), але я не впевнений, що буду пиляти це довгий час, адже з цих причин, давно користуюсь протоколом [Gemini](https://devzone.org.ua/post/protokol-gemini-iak-alternatyva-http).
Можна, в принципі, додати правило на "останній рубіж" `iptables`, наприклад заблокувати вихідні пакети в Інтернет:
``` bash
ufw default deny outgoing
```
Якщо користуєтесь Yggdrasil в оверлейному режимі (тобто через публічний пір), важливо додати його до білого списку:
``` bash
ufw allow out to PUBLIC_PEER_IP
```
* якщо вузлів декілька, додаємо їх послідовно
* перевірити поточні правила `ufw` можна командою `ufw status verbose`
* можливо, для тесту конекту варто перезапустити сервіс `systemctl restart yggdrasil`
* також, не забудьте про системний DNS резольвер та інші служби, що залежать від вихідних з'єднань!
На останок, дозволяємо локальні запити для взаємодії між вузлами:
``` bash
ufw allow out to 0200::/7 from 0200::/7
```
### Багато-мережевий режим
Можливо, згодом (коли розберусь) окремо опишу, як запустити інстанс у різних мережах одночасно, наприклад Інтернет + Yggdrasil, але наскільки бачу по коду Snac, його файлова реалізація сховища дозволяє працювати тільки в рамках однієї мережі / хосту.
Думаю, тут можна буде погратись з проксуванням з авто-заміною, організувати реплікацію або використанням "білого" DNS і записів `A`/`AAAA` відповідно до типу з'єднання клієнтського резольвера. Так чи інакше, це - вже зовсім інша історія!