Налаштування Fedi-сервера Snac для мережі Yggdrasil

15 хв. читання

Невдовзі, після своїх роздумів про p2p, вирішив спробувати підняти власний експериментальний інстанс Fediverse. При чому, зробити це засобами оверлейної мережі Yggdrasil, оскільки я не планую купувати для цієї іграшки виділений IP чи VPS, натомість буду хоститись з модему, одноплатника або взагалі з ПК, коли буваю онлайн, з динамічною адресою за NAT.

Пишу цю нотатку в першу чергу - для себе, а також, вона може бути корисною для тих, хто як і я тільки починає свої експерименти у сфері адміністрування власного вузла Fediverse і цікавиться альтернативними мережами, в контексті Linux.

Що таке Snac

Snac - це мінімалістична, JS-less, написана мовою C альтернатива серверу Mastodon, яка також не потребує інсталяції PostgreSQL, натомість зберігає усі дані профілю у файлах JSON. Нещодавно, до цього серверу було додано підтримку IPv6, а отже - він буде працювати й з діапазоном Yggdrasil 0200::/7

Оскільки Yggdrasil дозволяє безкоштовно генерувати не обмежену кількість статичних IP (на базі приватного ключа Ed25519), тут немає звичної потреби в DNS. Хоча, можна опціонально прикрутити Alfis, але особисто я цим ділом не користуюсь (зокрема, й через досі не вирішену проблему #364) тому також не хочу нав'язувати його в рамках протоколу ActivityPub - буде просто формат username@IPv6, який мені не потрібно а ні оновлювати, а ні майнити потім.

Встановлення

  1. Точний перелік пакетів для Debian я не знаю, оскільки моя система не нова і вже має встановлені раніше пакунки. Як вказано в README, я тільки встановив libssl-dev і libcurl4-openssl-dev (для Fedora має бути приблизно те само з постфіксом *-devel)

  2. Далі, створюємо окремого системного юзера, щоб ізолюватись від потенційних вразливостей:

useradd -m snac
  1. Змінюємо для зручності середовище на bash у файлі /etc/passwd
  2. Логінимось через su snac і переходимо в домашню директорію цього юзера: cd
  3. Завантажуємо останній вихідний код: git clone https://codeberg.org/grunfink/snac2.git
  4. Заходимо в робочу директорію cd snac2
  5. Компілюємо make && sudo make install і встановлюємо з відповідними правами
  6. Ініціалізуємо серверне сховище: snac init /home/snac/storage
  7. І додамо до нього нашого першого юзера snac adduser /home/snac/storage
  8. Далі продовжуємо від root виконавши команду exit

Налаштування

Я вже маю встановлений і налаштований вузол Yggdrasil, якщо комусь цікавий процес встановлення, скористайтесь попередньою публікацією або офіційною документацією.

Адреса підмережі 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, який займає порт 80, я поки не хочу нічого змінювати, а також не хочу мати публічні адреси Snac з його стандартним портом 8001. Тому, оскільки вже маю виділену адресу підмережі, просто запроксую API на 80 порт через новий віртуальний хост, частково використавши оригінальний приклад конфігурації:

# /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:

location ~ /([^\/]+/admin|oauth) {
    allow ADMIN_IP;
    deny all;
    try_files $uri @proxy;
}

Конфігурація Snac

Відредагуємо раніше згенерований командою snac init файл /home/snac/storage/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

Є готовий офіційний приклад конфігурації, але я його трохи доповнив:

# /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:

@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 є перелік посилань на CSS теми, за допомогою яких можна кастомізувати веб-інтерфейс Snac на власне вподобання.

Спочатку я не зрозумів, як підключати нові теми, і додав посилання на умовний файл /theme.css до /home/snac/storage/server.json, а також створив на нього аліас локального шляху в Nginx (щоб задовольнити браузерну політику CORS):

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, тому якщо користуєтесь цим застосунком, доведеться налаштувати окремий інтерфейс Nginx на порті 443 з використанням сертифікату, хоч у випадку з Yggdrasil - це зайва "капуста" і мабуть поки що лишусь на веб-інтерфейсі або зроблю і викладу потім патч.

UPD 1. розробник виявився супер-оперативним і вже створив гілку з патчем (Flatpak в Артефактах), єдине що - тільки поки не "завезли" валідацію IPv6 у вікно авторизації, тому я тимчасово користуюсь аліасом в /etc/hosts і вказую у якості URL авторизації http://alias. Можливо, у наступних випусках додатка це вже буде не актуально.

UPD 2. Наразі, гілка об'єднана з main, тому замість аліасів для IPv6, можна просто додати при запуску TUBA_SKIP_STRICT_VALIDATION=1, наприклад:

TUBA_SKIP_STRICT_VALIDATION=1 'builddir/dev.geopjr.Tuba'

UPD 3. Особисто я користуюсь окремою гілкою, де ця нубська валідація HTTP випиляна, і жодних додаткових флагів для запуску не потрібно:

Для збірки з форку:

  1. git clone https://github.com/YGGverse/Tuba.git
  2. git checkout multiprotocol-address-support
  3. make && make install
    • або для Flatpak:
flatpak-builder --force-clean build\
                --install-deps-from=flathub\
                --install\
                --repo=repo\
                --user\
                build-aux/dev.geopjr.Tuba.Devel.json

Також, в мене є ще одна, окрема гілка, де я форсовано застосовую українську локаль для постів, адже сервер Snac має певний недопил по синхронізації даного API.

Скачати All-in-One збірку можна тут, виконавши для встановлення кроки вище (з git checkout ps).

Моніторинг трафіку

Оскільки сервер Snac не передбачає користування JS інтерфейсом, а поточна конфігурація використовує Nginx, можу по ходу справи також порадити goaccess - CLI утиліту для зручного моніторингу статистики користувацького трафіку, якщо такий буде:

goaccess /var/log/nginx/access.log

Приватний режим

Yggdrasil дозволяє маскувати реальний IP, якщо ви користуєтесь власним вихідним вузлом. Звісно, таку можливість не варто розглядати в контексті анонімізації окремо без додаткових шарів, оскільки використання протоколу Yggdrasil без наприклад таких проксі, як Shadowsocks - легко виявляється.

Ця тема виходить за рамки матеріалу, але зверну увагу на деякі аспекти, якщо ви плануєте користуватись збіркою Snac + Yggdrasil в режимі "інкогніто". Протокол ActivityPub передбачає "спілкування" між серверами для обміну івентами. Тобто потенційний фоловер може відправити запит підписки на на дозволений у фаєрвол інтерфейс 0200::/7, але вказати в заголовках події ActivityPub - зворотній DNS на вузол в мережі Інтернет. Таким чином, ваша система здійснить транзакцію з білого IP через системний резольвер або без нього, використовуючи локальний Curl API від Snac.

Потенційних сценаріїв витоку можна придумати багато. Я переглянув вихідний код Snac, та не знайшов у ньому жодних фільтрів взаємодії на вихідні підключення. Тому, як і для іншого не спеціалізованого софту, для цієї мети бажано використовувати ізоляцію роутера з контейнера Docker, LXC або віртуалізуватись засобами QEMU.

При використанні клієнтського API, окремого аудиту потребуватиме обробка віддаленого вмісту постів, аватарів та іншого. У цьому напрямку, є перші кроки, зокрема по частині Web UI (PR#394, гілка), але я не впевнений, що буду пиляти це довгий час, адже з цих причин, давно користуюсь протоколом Gemini.

Можна, в принципі, додати правило на "останній рубіж" iptables, наприклад заблокувати вихідні пакети в Інтернет:

ufw default deny outgoing

Якщо користуєтесь Yggdrasil в оверлейному режимі (тобто через публічний пір), важливо додати його до білого списку:

ufw allow out to PUBLIC_PEER_IP
  • якщо вузлів декілька, додаємо їх послідовно
  • перевірити поточні правила ufw можна командою ufw status verbose
  • можливо, для тесту конекту варто перезапустити сервіс systemctl restart yggdrasil
  • також, не забудьте про системний DNS резольвер та інші служби, що залежать від вихідних з'єднань!

На останок, дозволяємо локальні запити для взаємодії між вузлами:

ufw allow out to 0200::/7 from 0200::/7

Багато-мережевий режим

Можливо, згодом (коли розберусь) окремо опишу, як запустити інстанс у різних мережах одночасно, наприклад Інтернет + Yggdrasil, але наскільки бачу по коду Snac, його файлова реалізація сховища дозволяє працювати тільки в рамках однієї мережі / хосту.

Думаю, тут можна буде погратись з проксуванням з авто-заміною, організувати реплікацію або використанням "білого" DNS і записів A/AAAA відповідно до типу з'єднання клієнтського резольвера. Так чи інакше, це - вже зовсім інша історія!

Посилання

Інших вузлів я поки не зустрічав, тому підписуйтесь на мій - для тестів і спілкування:

Можливо, колись сформується нове локальне сузір'я :)

Помітили помилку? Повідомте автору, для цього достатньо виділити текст з помилкою та натиснути Ctrl+Enter
p.s. 767
Приєднався: 1 рік тому
Коментарі (1)
  1. p.s.

    Помітив таку особливість: сервер Snac з коробки видаляє старі об'єкти, тобто чистить історію дописів, не зважаючи на те, що інстанс по суті одноюзерний і не передбачає великих масивів даних.

    Також пофіксив баг #406, який виправляє хардкодне видалення об'єктів та інбоксу через 7 днів.

    Там в конфігах за замовченням є опція timeline_purge_days, вона здається 120 днів стандартно. Мій PR #409 (якщо його приймуть) застосовує це значення замість хардкодного. Якщо опцію перевести в 0, то нічого не повинно більше видалятись. Добре, що в мене був бекап і я руками відновив індекси.

    Якщо цей пул реквест не змержать, ще раз нагадаю про власну гілку, якою користуюсь і тестую апстрім, вона включає цей та деякі інші фікси (наприклад кириличні теги):

    https://codeberg.org/postscriptum/snac2/src/branch/ps

    3 тижні тому ·
    0
Щоб залишити коментар необхідно авторизуватися.

Вхід