gemlog/public/uk/radicle-web-service-deployment.gmi
2026-02-14 09:10:18 +02:00

453 lines
No EOL
23 KiB
Text
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.

# Розгортання Веб-інфраструктури Radicle на прикладі оверлейних мереж
У попередньому гайді серії про децентралізований Git-хостинг Radicle, було розглянуто приклад налаштування публічного сіда для поширення персональних репозиторіїв в оверлейному режимі з політикою "Selective":
=> radicle-multi-network-seed-deployment.gmi Розгортання сіда Radicle в мульти-мережному середовищі
Цього разу, опишу особистий досвід розгортання публічного Веб-інтерфейсу на його основі, для користувачів оверлейних IPv6 мереж Yggdrasil і Mycelium. Мотивація - зробити сідуючий сервер доступним для локальних користувачів, які бажають переглядати репозиторії "всередині" оверлейної мережі та мати можливість забирати код як з `rad clone` так і через шлюз HTTP звичною командою `git clone`.
Веб-інфраструктура Radicle ділиться на дві основні частини: сервер JSON/API (за яку відповідає пакунок `radicle-httpd`) і статичний асинхронний клієнт (на базі технологій HTML і JavaScript). Обидва рішення є частиною репозиторію `radicle-explorer`.
## Отримання початкового коду
У попередньому гайді, на сервері вже було створено користувача `radicle`, тож спочатку залогінимось від нього:
``` bash
su radicle
```
Оскільки на моєму сервері немає Інтернет-інтерфейсу як такого, але вже є підключений до глобальної мережі (засобами Tor over Yggdrasil) `radicle-node`, я буду тягнути вихідний код засобами команди `rad` а не `git` (для якого в моєму випадку знадобився б вихідний проксі). Це за одно дозволить звикнути до нової обгортки і скористатись перевагами пірингового обміну без прив'язки до конкретної мережі:
``` bash
rad clone rad:z4V1sjrXqjvFdnCUbxPFqd5p4DtH5 radicle-explorer
```
Якщо таки збираєте клієнт без локального вузла Radicle:
``` bash
git clone https://iris.radicle.xyz/z4V1sjrXqjvFdnCUbxPFqd5p4DtH5.git radicle-explorer
```
## Сервер JSON/API (radicle-httpd)
Інструкції з розгортання також описані в офіційній документації:
=> https://radicle.xyz/guides/seeder#running-the-http-daemon Radicle Seeder Guide: Running the HTTP Daemon
Якщо коротко, то робимо наступне:
``` bash
cd radicle-explorer/radicle-httpd
cargo build --release
```
Копіюємо отриманий бінарник `target/release/radicle-httpd` до `/usr/local/bin` на сервері і переконуємось що користувач `radicle` має відповідні права на його виконання:
``` bash
sudo chown radicle:radicle /usr/local/bin/radicle-httpd
sudo chmod +x /usr/local/bin/radicle-httpd
```
### Системний сервіс
Сервіс я оголосив на локальному інтерфейсі `[::1]` (для IPv4 - це може бути `127.0.0.1`) з портом `8788` і подальшим проксуванням через Nginx з публічних IP відповідних мереж.
``` /etc/systemd/system/radicle-httpd.service
[Unit]
Description=Radicle HTTP Daemon
After=network.target network-online.target
Requires=network-online.target
[Service]
User=radicle
Group=radicle
ExecStart=/usr/local/bin/radicle-httpd --listen [::1]:8788
Environment=RAD_HOME=/home/radicle/.radicle RUST_BACKTRACE=1 RUST_LOG=info NO_COLOR=1
KillMode=process
Restart=always
RestartSec=1
StandardOutput=file:///home/radicle/httpd-debug.log
StandardError=file:///home/radicle/httpd-error.log
[Install]
WantedBy=multi-user.target
```
Додаємо сервіс до авто-запуску зі стартом системи і запускаємо сервер:
``` bash
sudo systemctl enable --now radicle-httpd
```
=> https://seed.radicle.xyz/raw/rad:z3gqcJUoA1n9HaHKufZs5FCSGazv5/570a7eb141b6ba001713c46345d79b6fead1ca15/systemd/radicle-httpd.service Приклад конфігурації systemd в офіційному репозиторії
### Nginx
``` /etc/nginx/sites-available/default
server {
listen [202:68d0:f0d5:b88d:1d1a:555e:2f6b:3148]:8788;
listen [505:6847:c778:61a1:5c6d:e802:d291:8191]:8788;
# Поки не визначився
# server_name _;
access_log /var/log/nginx/radicle.access.log;
location / {
proxy_pass http://[::1]:8788;
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
```
* для мереж Yggdrasil і Mycelium - HTTPs звичайно не використовується, тому приклад налаштування спрощено
Застосовуємо оновлення конфігурації:
``` bash
systemctl reload nginx
```
### Фаєрвол
```
sudo ufw allow from 0200::/7 to 202:68d0:f0d5:b88d:1d1a:555e:2f6b:3148 port 8788 proto tcp comment 'radicle-httpd'
sudo ufw allow from 0400::/7 to 505:6847:c778:61a1:5c6d:e802:d291:8191 port 8788 proto tcp comment 'radicle-httpd'
```
### Тестування бекенду
В рамках цього прикладу, перевірити роботу JSON/API, можна за адресами:
=> http://[202:68d0:f0d5:b88d:1d1a:555e:2f6b:3148]:8788
=> http://[505:6847:c778:61a1:5c6d:e802:d291:8191]:8788
Якщо встановлено Alfis DNS:
=> http://ygg.ua.srv:8788
=> http://myc.ua.srv:8788
По аналогії, на бекенд можна легко причепити тунелі I2P і Tor.
## Клієнт (radicle-explorer)
По суті, це статичний Веб-компонент на базі HTML/JavaScript, що звертається до вказаного в його налаштуваннях сервера. Оптимізована статика збирається засобами пакетного менеджера `npm`, після чого отримані файли з теки `build` копіюються до публічного простору, наприклад Nginx: `/var/www/radicle`.
### Налаштування підключення до бекенду
Перед тим, як збирати оптимізований білд, важливо спочатку вказати актуальні налаштування підключення до серверів JSON/API - власних або сторонніх. Робиться це у файлі `config/default.json`. Після збірки, ці налаштування будуть "вбудовані" в компонент `build/assets/components-xxx.js` і при наступних оновленнях конфігурації, потрібно буде перезбиратись. Щоб уникнути цієї незручності, можна використовувати опцію динамічних налаштувань `VITE_RUNTIME_CONFIG=true`, про яку детальніше описано у розділі "Компіляція".
У своїй конфігурації, поки використовую два інтерфейси: Yggdrasil і Mycelium свого сіда. По аналогії, до масиву об'єктів `preferredSeeds` додаються й альтернативні DNS, тунелі I2P або приховані сервіси Tor.
Важливим є той факт, що поточна версія `radicle-explorer` ніяк не проксує через бекенд `preferredSeeds` і тому якщо в клієнта не встановлено роутер Mycelium, то перебуваючи на хості Yggdrasil - він не зможе з меню `0200::/7` відкрити сід `0400::/7`. Навожу приклад міксованого з'єднання, але рекомендую користуватись одним сімейством адрес для одного фронтенду.
``` config/default.json
{
"nodes": {
"fallbackPublicExplorer": "https://app.radicle.xyz/nodes/$host/$rid$path",
"requiredApiVersion": "~0.18.0",
"defaultHttpdPort": 443,
"defaultLocalHttpdPort": 8080,
"defaultHttpdScheme": "http"
},
"source": {
"commitsPerPage": 30
},
"supportWebsite": "https://radicle.zulipchat.com",
"deploymentId": null,
"preferredSeeds": [
{
"hostname": "[202:68d0:f0d5:b88d:1d1a:555e:2f6b:3148]",
"port": 8788,
"scheme": "http"
},
{
"hostname": "ygg.ua.srv",
"port": 8788,
"scheme": "http"
},
{
"hostname": "[505:6847:c778:61a1:5c6d:e802:d291:8191]",
"port": 8788,
"scheme": "http"
},
{
"hostname": "myc.ua.srv",
"port": 8788,
"scheme": "http"
}
]
}
```
Стандартні поля секції `nodes` - свідомо не змінюю (окрім встановлення стандартної схеми `http`), бо в коді клієнта є такі упороті моменти:
``` radicle-explorer/src/views/repos/Header/CloneButton.svelte
$: portFragment =
baseUrl.scheme === config.nodes.defaultHttpdScheme &&
baseUrl.port === config.nodes.defaultHttpdPort
? ""
: `:${baseUrl.port}`;
```
* через що може не працювати внутрішня навігація по сідам а також приклади команд типу `git clone`, виключаючи з URL не типовий для HTTP/80 порт (`8788`) якщо той використовується
### Зауваження щодо HTTP
Якщо відкрити Веб-інтерфейс Radicle на віддаленому (не `localhost`) сервері з протоколом HTTP, то при копіюванні адрес репозиторію до буферу - в консолі браузера буде помилка:
> Uncaught (in promise) TypeError: can't access property "writeText", navigator.clipboard is undefined
Вона означає, що сучасна політика браузера блокує функціональність буферу копіювання для не захищеного протоколу HTTP, за виключенням `localhost` або при ручному додаванні такого виключення через `about:config`. Звісно, для продакшну це не варіант, бо переважна більшість користувачів через захищену природу оверлейних мереж, не користуються HTTPs і вважають таке явище - швидше не правильно налаштованим Nginx, аніж фічею.
Отже, якщо не плануєте "силою" заганяти юзерів на HTTPs (з само-підписаним сертифікатом) то рішення я знайшов тільки у застосуванні патчу `src/lib/utils.ts`, із заміною його функції `toClipboard` на "legacy-fallback" через `copyToClipboard`:
``` src/lib/utils.ts
async function copyToClipboard(text: string) {
if (navigator.clipboard && window.isSecureContext) {
await navigator.clipboard.writeText(text);
} else {
const textArea = document.createElement("textarea");
textArea.value = text;
document.body.appendChild(textArea);
textArea.focus();
textArea.select();
try {
document.execCommand('copy');
} catch (err) {
console.error('Unable to copy', err);
}
document.body.removeChild(textArea);
}
}
export async function toClipboard(text: string): Promise<void> {
await copyToClipboard(text);
}
```
* Firefox 148 - все ще підтримує `document.execCommand('copy')`
### Компіляція
Після завершення конфігурації, на локальній машині збираємо оптимізовану статику і для зручності передачі на сервер, запаковуємо вміст `build` до архіву `radicle-explorer.tar.gz`:
``` bash
cd radicle-explorer
npm install
VITE_RUNTIME_CONFIG=true npm run build
tar -czvf radicle-explorer.tar.gz -C build .
```
* рекомендую збирати з підтримкою динамічного `config.json` (`VITE_RUNTIME_CONFIG=true`), інакше після кожної зміни конфігурації, доведеться заново перезбирати весь білд і перезаливати його на сервер; детальніше про це написано [тут](https://app.radicle.xyz/nodes/iris.radicle.xyz/rad%3Az4V1sjrXqjvFdnCUbxPFqd5p4DtH5/tree/README.md#run-time-configuration).
### Встановлення
Отриманий архів `radicle.tar.gz` копіюємо на сервер до якоїсь тимчасової теки і розпаковуємо вміст архіву за призначенням `/var/www/radicle`, оновивши за одно права для доступу служби Nginx:
``` bash
sudo mkdir -p /var/www/radicle
sudo tar -xzvf /tmp/radicle-explorer.tar.gz -C /var/www/radicle
sudo rm /tmp/radicle-explorer.tar.gz
sudo chown www-data:www-data /var/www/radicle
```
### Nginx
Тут все просто: вказуємо актуальний шлях до статики `/var/www/radicle` з адресацією усіх запитів на `index.html`:
``` /etc/nginx/sites-available/default
server {
listen [202:68d0:f0d5:b88d:1d1a:555e:2f6b:3148]:8780;
listen [505:6847:c778:61a1:5c6d:e802:d291:8191]:8780;
# Ще не визначився
# server_name _;
access_log /var/log/nginx/radicle.access.log;
root /var/www/radicle;
location / {
try_files $uri $uri/ /index.html;
}
# Якщо збірка з `VITE_RUNTIME_CONFIG=true`
#
# location = /config.json {
# root /;
# try_files /etc/radicle-explorer/config.json /usr/share/radicle-explorer/config.json =404;
# }
# Цей блок є опціональним і дозволяє публікувати `radicle-httpd` і `radicle-explorer`
# на одному інтерфейсі одночасно (на прикладі це може спільний порт `8780`)
# таким чином, індексна сторінка API буде заміщена користувацьким Веб-інтерфейсом
#
# location ~ "^/(raw|api|[a-zA-Z0-9]{28,29}(\.git)?)(/|$)" {
# proxy_pass http://[::1]:8788;
#
# proxy_http_version 1.1;
# proxy_set_header Connection "";
#
# proxy_set_header Host $host;
# proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# proxy_set_header X-Forwarded-Proto $scheme;
# }
}
```
Застосовуємо оновлення конфігурації:
``` bash
systemctl reload nginx
```
### Фаєрвол
``` bash
sudo ufw allow from 0200::/7 to 202:68d0:f0d5:b88d:1d1a:555e:2f6b:3148 port 8780 proto tcp comment 'radicle-explorer'
sudo ufw allow from 0400::/7 to 505:6847:c778:61a1:5c6d:e802:d291:8191 port 8780 proto tcp comment 'radicle-explorer'
```
### Тестування фронтенду
В рамках цього прикладу, перевірити роботу можна за адресами:
=> http://[202:68d0:f0d5:b88d:1d1a:555e:2f6b:3148]:8780
=> http://[505:6847:c778:61a1:5c6d:e802:d291:8191]:8780
Якщо встановлено Alfis DNS:
=> http://ygg.ua.srv:8780
=> http://myc.ua.srv:8780
## Анонімні мережі
Веб сервіс Radicle є цілком сумісним з мережами на базі анонімних проксі I2P і Tor. Нюансом є лише обмеження політики CORS у випадку, якщо спробуєте відвідати сіди I2P/Tor перебуваючи на сервері наприклад IPv6. Але якщо підняти окрему копію експлорера без "солянки", то користувачі зможуть переглядати та клонувати репозиторії анонімно.
Особисто я використовую наступну структуру файлової системи з релевантними для кожної теки файлами `config.json`:
``` bash
mkdir -p /var/www/radicle/yggdrasil \
/var/www/radicle/i2p \
/var/www/radicle/mycelium \
/var/www/radicle/tor
```
* з відповідними налаштуваннями `root` в Nginx
### I2P
На прикладі i2pd, до конфігурації тунелів додаються два HTTP сервери:
``` /var/lib/i2pd/tunnels.conf
[radicle-explorer]
type = http
host = ::1
port = 8780
inport = 80
keys = radicle-explorer.dat
[radicle-api]
type = http
host = 202:68d0:f0d5:b88d:1d1a:555e:2f6b:3148
port = 8788
inport = 8788
keys = radicle-api.dat
```
* в Nginx, хост `::1` лінкується на корінь `/var/www/radicle/i2p`
* для IPv4 можна замість `::1` використовувати `127.0.0.1`
Після перезапуску роутера i2pd, засобами i2pd-tools отримуються згенеровані адреси B32:
``` bash
$ keyinfo /var/lib/i2pd/radicle-explorer.dat
k7cfad745uretan7iihkwo6x24ut6mgbhq4ccxjqkzetgbtfknbq.b32.i2p
$ keyinfo /var/lib/i2pd/radicle-explorer.dat
cfwfe2k6dropbymtddz225mbugzs5tfsmvng23zsebf6iw3cj2xa.b32.i2p
```
Додаємо адресу `radicle-api` до `preferredSeeds`:
``` /var/www/radicle/i2p/config.json
"preferredSeeds": [
{
"hostname": "cfwfe2k6dropbymtddz225mbugzs5tfsmvng23zsebf6iw3cj2xa.b32.i2p",
"port": 8788,
"scheme": "http"
}
]
```
* зверніть увагу на порт `8788`: в I2P він може не використовуватись через наявність окремого "домену" і його можна приховати з URL, вказавши значення аналогічне `defaultLocalHttpdPort`
Перевіряємо:
=> http://k7cfad745uretan7iihkwo6x24ut6mgbhq4ccxjqkzetgbtfknbq.b32.i2p
### Tor
Я користуюсь більш сучасною реалізацією роутера - Arti, детальніше про нього було написано тут:
=> arti-onion-router-with-tor-connection-over-yggdrasil.gmi Встановлення Onion-роутера Arti з підключенням до мережі Tor через Yggdrasil
Підходів налаштування "прихованого сервісу" для Radicle може буде декілька. Можна створити окремі `onion_services` на `80` порт, але я поки використовую підхід зі спільним доменом на різних портах:
``` /path/to/config.toml
[onion_services."radicle"]
#enabled = true
proxy_ports = [
# radicle-explorer (WebUI)
["80", "[::1]:8781"],
# radicle-httpd (JSON/API)
["8788", "[202:68d0:f0d5:b88d:1d1a:555e:2f6b:3148]:8788"],
# radicle-node (public seed)
# ["8776", "[202:68d0:f0d5:b88d:1d1a:555e:2f6b:3148]:8776"],
["*", "destroy"]
]
```
* Веб-інтерфейс оголошую на тому ж локальному IP `::1`, як і I2P, через що змінюю порт на будь-який вільний типу `8781` (можна також засетапити окремий локальний IP `::2` але це вимагатиме додаткових кроків з перманентними правилами `ip`)
* `radicle-node` - в контексті гайду не розглядається, утім ви можете його розкоментувати, користуючись гайдом:
=> radicle-multi-network-seed-deployment.gmi Розгортання сіда Radicle в мульти-мережному середовищі
Після збереження налаштувань і перезапуску роутера, для отримання адреси `.onion` виконуємо команду:
``` bash
$ su arti -s /bin/bash \
-c 'arti hss -c /path/to/config.toml --nickname radicle onion-address'
...
tus2sol3kcykzh6dw4adgma3yzvzex7nsizlbdjmizgeines74chk7yd.onion
```
І додаємо результат до конфігурації експлорера у відповідному неймспейсі Nginx:
``` /var/www/radicle/tor/config.json
"preferredSeeds": [
{
"hostname": "tus2sol3kcykzh6dw4adgma3yzvzex7nsizlbdjmizgeines74chk7yd.onion",
"port": 8788,
"scheme": "http"
}
]
```
Пробуємо підключитись і отримати приклади команд на клонування репозиторіїв по HTTP:
=> http://tus2sol3kcykzh6dw4adgma3yzvzex7nsizlbdjmizgeines74chk7yd.onion
### Проксі для клієнта Git
Використання I2P і Tor - передбачає підключення клієнтських застосунків через проксі локального або віддаленого роутера. Це стосується й команди `git`. Щоб клонувати репозиторії Radicle засобами HTTP на прикладі I2P, потрібно до профілю Git додати наступний рядок:
``` bash
git config http.proxy http://127.0.0.1:4444
```
* вкажіть `--global` для глобального використання проксі
Таким чином, клонування репозиторію `heartwood` з цього інстансу відбувається командою:
``` bash
git clone http://cfwfe2k6dropbymtddz225mbugzs5tfsmvng23zsebf6iw3cj2xa.b32.i2p:8788/z3gqcJUoA1n9HaHKufZs5FCSGazv5.git heartwood
```
Відповідно, користувачі Tor - оперуватимуть вже адресами `.onion`, використовуючи проксі з портом `9150` або `9050`, в залежності від обраного ними роутера:
``` bash
git clone http://tus2sol3kcykzh6dw4adgma3yzvzex7nsizlbdjmizgeines74chk7yd.onion:8788/z3gqcJUoA1n9HaHKufZs5FCSGazv5.git heartwood
```