mirror of
https://codeberg.org/postscriptum/devzone.org.ua.git
synced 2026-02-18 22:02:41 +00:00
298 lines
No EOL
24 KiB
Markdown
298 lines
No EOL
24 KiB
Markdown
# Налаштування 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` відповідно до типу з'єднання клієнтського резольвера. Так чи інакше, це - вже зовсім інша історія! |