commit d4994cde4ca67c977f475057628aa0dd91aefff0 Author: postscriptum Date: Sat Nov 1 14:28:20 2025 +0200 initial commit diff --git a/post/alfis-dns-reyestratsiia-domenu-v-blokcheyn.md b/post/alfis-dns-reyestratsiia-domenu-v-blokcheyn.md new file mode 100644 index 0000000..40c415c --- /dev/null +++ b/post/alfis-dns-reyestratsiia-domenu-v-blokcheyn.md @@ -0,0 +1,68 @@ +# Alfis DNS - реєстрація домену в блокчейн + +[Alfis DNS](https://github.com/Revertron/Alfis) - децентралізована система доменних імен з реєстром в блокчейн. + +Резольвер та клієнтська частина написані на мові Rust. Системою використовуються зони `.anon`, `.btn`, `.conf`, `.index`, `.merch`, `.mirror`, `.mob`, `.screen`, `.srv`, `.ygg`, де `.anon` та `.ygg` - зарезервовані для адрес мережі [Yggdrasil](https://devzone.org.ua/post/yggdrasil-mereznii-protokol-z-decentralizovanim-routingom) що виключає випадковий перехід з таких адрес на хости клірнет. + +Для запобігання кіберсквотингу використовується модель PoW і частково - PoS, генерація нових доменів також обмежена добовим інтервалом, а на один персональний ключ можна згенерувати не більше 10 доменів (що насправді має досить спірне підгрунтя). + +Тим не менше, система досить зручна для локальних мереж, оскільки дозволяє створити безкоштовний домен на один рік без необхідності реєстрації персональних даних та залежності від центрального серверу. А розмір блокчейну при поточній кількості близько 1000 доменів складає приблизно 7 Мб. + +В середньому, на комп'ютері з процесором i5, новий домен генерується від декількох до 12 годин; утім спочатку потрібно згенерувати персональний ключ, до якого потім цей домен буде прив'язаний. До закінчення року, потрібно змайнити домен повторно, використовуючи існуючий ключ, раніше робити це не має сенсу, бо пролонгація рахується від часу створення блоку. + +## Встановлення + +Встановлення відбувається досить просто, є готові бінарні пакети та репозиторії. При збірці, з початкового коду, доведеться встановити останню версію `rustc` що зручно використовуючи пакет `rustup`. + +В іншому, виконується стандартна процедура `cargo`: + +``` +git clone https://github.com/Revertron/Alfis.git +cd Alfis +cargo build --release +``` + +## Налаштування + +Запускаючи клієнт з графічною оболонкою, буде автоматично налаштовано резольвер. Для систем типу Ubuntu, ймовірно доведеться вивільнити зайнятий порт `53`. + +Утім, графічний інтерфейс зручно використовувати виключно для адміністрування доменів та майнінгу, а сам резольвер розмістити десь на VPS та вказати до нього конфігурацію: + +### В системах з systemd-resolved + +``` +#/etc/systemd/resolved.conf +[Resolve] +DNS=IP +Domains=~. +``` + +після змін оновити кеш командою: +`systemctl restart systemd-resolved` + +### В системах без systemd + +``` +#/etc/resolv.conf +nameserver xx.xx.xx.xx +``` + +### FireFox + +Браузер нічого не знає про діапазон імен Alfis DNS, тому при введені таких адрес, буде відправляти користувача в пошук. +Щоб виправити це, потрібно додати в `about:config`, наприклад для зони `ygg` - наступну опцію: +``` +browser.fixup.domainsuffixwhitelist.ygg = true +``` + +### Особистий досвід + +* Alfis DNS хоч і працює з типовими адресами, але вимагає наявності встановленого або підключеного вузла для клієнтів. З цієї ж причини виникає і проблема сертифікації, наприклад засобами Certbot / Let's Encrypt. +* В намірах зменшити розмір блокчейну, автор явно перегнув із захистом від зловживань сквотерами, від чого іноді доводиться чекати випадкові вузли, що підпишуть блок. А сама модель захисту явно грає на руку тим, хто в сквотингу зацікавлені (володіючи відповідним обчислювальним ресурсом). Під час останнього такого "зависання" частина користувачів була обурена бездіяльністю автора у відповідь на повторний інцидент і вірогідно відмовилась від цього і до того непопулярного рішення. +* Внутрішньої монети блокчейн немає, утім потенційно може мати деякий економічний потенціал з використанням бартеру (засобами трансферу) +* Наразі проєкт варто сприймати виключно як експериментальну альтернативу централізованим сервісам для ентузіастів, який фактично виконує ту само роль, що й `/etc/hosts` + +### Посилання + +* [Репозиторій проєкту на GitHub](https://github.com/Revertron/Alfis) +* [Веб-експлорер](https://viewer.alfis.name) \ No newline at end of file diff --git a/post/alternatyvy-standartnomu-vidzetu-vkladok-gtk-notebook.md b/post/alternatyvy-standartnomu-vidzetu-vkladok-gtk-notebook.md new file mode 100644 index 0000000..dcfebba --- /dev/null +++ b/post/alternatyvy-standartnomu-vidzetu-vkladok-gtk-notebook.md @@ -0,0 +1,166 @@ +# HdyTab як альтернатива віджету вкладок GtkNotebook + +*Якщо ви тільки починаєте своє знайомство з фреймворком GTK, можливо, як і в мене, виникне схожа дилема з обмеженою функціональністю класичного віджету для створення вкладок GtkNotebook. В мережі віднайшов матеріал 2021 року "[Reinventing tabs](https://blogs.gnome.org/alicem/2021/03/13/reinventing-tabs/)", в якому розглядається проблема стандартного віджету та пропонується альтернатива - HdyTab.* + +## Переосмислення вкладок + +В робочому столі GNOME 40, браузер Epiphany має нову панель вкладок. Це не просто переробка старої панелі, а повне її переписування з нуля: + +![Вигляд вкладок в Epiphany](https://blogs.gnome.org/alicem/files/2021/03/ephy.png) + +Але для чого це було необхідно? + +## Статичні та динамічні вкладки + +Додатки GNOME використовують вкладки різними способами, їх можна приблизно поділити на дві категорії. + +### Статичні вкладки + +![Вигляд статичних вкладок в різних діалогах](https://blogs.gnome.org/alicem/files/2021/03/dialog-tabs-768x645.png) + +Вони є повністю незмінними, кожна вкладка містить заголовок і зазвичай не прокручуються. Зазвичай їх використовують у діалогових вікнах, але, наприклад, їх також можна використовувати в панелях інструментів. Сучасні додатки часто використовують `GtkStackSwitcher` або `HdyViewSwitcher`, але `GtkNotebook` все ще досить поширений. + +### Динамічні вкладки + +![Вигляд зверху вниз: gedit, GNOME Terminal, Nautilus, GNOME Editor, Devhelp, Sysprof, Epiphany](https://blogs.gnome.org/alicem/files/2021/03/browser-tabs.png) + +Цей тип вкладок використовується в багатовіконних додатках, таких як веб-браузери, текстові редактори, файлові менеджери або емулятори терміналів. Вони відкриваються та закриваються користувачем, їх можна змінювати за порядком, перетягувати між вікнами або в нові вікна, скидаючи їх на робочий стіл. Вони часто реалізують прокрутку. Завжди мають заголовок і кнопку закриття, іноді іконку та/або індикатор завантаження. Вони ховаються, коли присутня лише одна вкладка. Їх можна закривати середнім кліком або через контекстне меню. Іноді, динамічні вкладки можна закріпити або додати іконку, наприклад, індикатор відтворення аудіо чи діалог підтвердження закриття. + +### Чому така різниця? + +Хоча статичні вкладки в GNOME досить однорідні, динамічні вкладки в різних додатках виглядають і відчуваються дуже по-різному. + +Наприклад: + +* в GNOME Text Editor - кнопки закриття є лише в обраних вкладках, або при наведені на вкладку, тоді як в інших додатках - вони присутні на всіх вкладках +* деякі додатки дозволяють вкладкам зменшуватися до малого розміру перед прокруткою, інші починають прокручуватися раніше +* Devhelp - взагалі не прокручує вкладки, а просто виводить їх за межі вікна +* Epiphany та GNOME Terminal - мають спливаючі вікна, що показують усі ці вкладки; GNOME Terminal використовує `GtkMenu`, тоді як Epiphany - використовує `GtkPopover` +* хоча це не видно на скріншоті, лише Epiphany та gedit підтримують перетягування вкладок між вікнами +* в Epiphany та Nautilus - можна перетягувати файли на вкладки. У Nautilus, якщо ви наведете курсор на вкладку під час перетягування файлу, вона автоматично переключиться на цю вкладку, але не в Epiphany +* У gedit та GNOME Terminal - неможливо зосередити фокус на активній вкладці, натискаючи на неї знову +* У GNOME Text Editor та Epiphany - при переході на іншу вкладку, коли відбувся фокус на активну вкладку, її фокус буде здійснено на сторінку а не саму вкладку + +### GtkNotebook + +Усі додатки вище використовують віджет під назвою `GtkNotebook`. Він реалізує загальний вигляд з вкладками та показує вкладки з одного боку, причому кожна вкладка має заголовок, який можна замінити на будь-який інший віджет. + +За замовчуванням, `GtkNotebook` не має засобів прокручування, має простий заголовок на кожній вкладці, не розширює вкладки та не дозволяє жодного виду перетягування. + +Іншими словами, він орієнтований виключно на статичні вкладки. Це не означає, що його неможливо використовувати для динамічних вкладок, але вам доведеться вручну реалізувати наступне: + +* макет вкладок з кнопкою закриття, заголовком, іконкою та, можливо, індикатором беззвучного режиму, а також встановити розумну мінімальну ширину для прокрутки +* гарячі клавіші, такі як `Ctrl+Tab` або `Alt+[1-9]` для перемикання між вкладками +* обробку контекстного меню, включаючи гарячі клавіші та жест довгого натискання для сенсорних екранів +* закриття середнім кліком +* логіку відкриття/закриття вкладок - наприклад, яку вкладку вибрати, коли активна вкладка закрита +* якщо потрібно скинути дані на вкладки, обробник для цього. Також потрібно бути обережним, щоб випадково не зламати тайм-аут наведення в процесі, як це робить Epiphany у версії 3.38.x +* авто-приховування контейнера, коли в ньому присутня лише одна вкладка + +Ці вимоги підкреслюють, що хоча `GtkNotebook` може бути використаний для динамічних вкладок, його базова реалізація не підтримує багато функцій, які користувачі очікують від сучасних динамічних вкладок, і вимагає значних зусиль для налаштування. + +Звісно, кожен додаток реалізує ці функції трохи по-різному. А ще є проблеми, які не можна повністю вирішити з боку додатка, такі як: + +* прокручування вкладок є дискретним, це виглядає заплутано і викликає інші проблеми: + * динамічні вкладки можуть мати велику кількість вкладок, натискання стрілок для прокрутки по одній вкладці за раз не є ефективним у цьому випадку + * пов'язано з цим, неможливо взяти вкладку і перемістити її на великі відстані; вам доведеться перемістити її на кілька вкладок вперед, натиснути кнопки прокрутки кілька разів, знову перемістити тощо + * при відкритті нової вкладки вона може опинитися за межами екрану без очікування такої поведінки + * мінімальна ширина вкладки змінюється в залежності від розміру вікна +* відсутня функціональність закріплених вкладок +* інтерфейс загалом не дуже відшліфований - наприклад, не має анімації при відкритті або закритті вкладок + +Ці проблеми підкреслюють, що, хоча додатки можуть намагатись реалізувати динамічні вкладки засобами статичного віджета `GtkNotebook`, існують обмеження, які ускладнюють створення зручного та інтуїтивно зрозумілого досвіду для користувачів. + +Деякі з таких проблем можна замаскувати: + +* Epiphany імітує закріплені вкладки, примусово переміщуючи їх на початок, використовуючи компактний макет, не розширюючи їх і не дозволяючи закривати. Однак ці вкладки все ще можуть бути прокручені, і те, що ширина вкладок змінюється в залежності від розміру вікна, особливо негативно впливає на зовнішній вигляд: + +![Вигляд компактно закріплених вкладок](https://blogs.gnome.org/alicem/files/2021/03/ephy-pinned1-768x82.png) +![Вигляд закріплених вкладок при зміні ширини вікна](https://blogs.gnome.org/alicem/files/2021/03/ephy-pinned2-768x78.png) + +* Epiphany вручну підсвічує стрілки, коли вкладка була відкрита за межами екрана, також надає спливаюче вікно для швидкого перемикання між вкладками, коли вони не вміщаються на екрані, однак це не допомагає з перестановкою вкладок +* динамічний віджет [DynamicNotebook](https://valadoc.org/granite/Granite.Widgets.DynamicNotebook.html) в Elementary реалізує затримку зміни розміру після закриття вкладки, але це не завжди надійно; наприклад, він стрибає, коли стрілки прокрутки зникають, крім того, він може працювати лише тоді, коли вкладки не розширені +* ці особливості підкреслюють, що, хоча обидва додатки намагаються покращити управління вкладками, існують обмеження, які можуть призвести до непередбачуваного або незручного досвіду для користувачів; підсвічування стрілок може бути корисним, але не вирішує основну проблему з доступом до вкладок, які відкриваються за межами екрана; затримка зміни розміру може бути корисною, але її ненадійність може викликати додаткові труднощі + +Крім того, `GtkNotebook` містить як вміст, так і вкладки, і вони є невід'ємними, тому неможливо мати вкладки у повноекранному режимі, автоматично приховуючи їх з заголовної панелі, або розміщувати їх у панелі заголовку, відокремлено від вмісту. + +Нарешті, `GtkNotebook` не є адаптивним, і Epiphany наразі має вбудовану реалізацію мобільного перемикача вкладок, яку кожен додаток, що прагне адаптивного інтерфейсу з вкладками - повинен буде копіювати. + +Ці обмеження підкреслюють, що `GtkNotebook` не забезпечує гнучкості, необхідної для сучасних інтерфейсів, які можуть адаптуватися до різних розмірів екранів і режимів використання. Відсутність можливості розміщення вкладок окремо від вмісту обмежує можливості дизайну, а необхідність копіювати реалізацію мобільного перемикача вкладок може призвести до дублювання зусиль і несумісності між додатками. + +## HdyTab як рішення + +![Як виглядає HdyTab](https://blogs.gnome.org/alicem/files/2021/03/tab-view-demo-768x128.png) + +Отже, з урахуванням усіх цих обмежень, я вважаю, що найчистішим шляхом вперед є створення абсолютно нового віджета, який реалізує специфічно динамічні вкладки. Таким чином, також зможемо відокремити перегляд вкладок і панель вкладок в окремі віджети, так само як `GtkStack` і `GtkStackSwitcher` - є окремими. + +Реалізацію цього віджета було розпочато незадовго до GUADEC 2020 року, і до кінця вересня, на момент долучення до [Purism](https://puri.sm) - її було майже завершено. У січні ми з Адрієном нарешті знайшли час, щоб закінчити, переглянути та впровадити його. + +Результатом цього стали два віджети під назвою `HdyTabView` і `HdyTabBar`, доступні в бібліотеці [libhandy](https://gnome.pages.gitlab.gnome.org/libhandy), починаючи з версії 1.2 + +`HdyTabView` містить `GtkStack` і надає API, більш придатний для динамічних вкладок: наприклад, сторінки відтепер строго впорядковані, є API для перестановки вкладок тощо. Крім того, кожна сторінка має велику кількість метаданих, вона використовує об'єкти сторінок, схожі на GTK 4 (`HdyTabPage`), навіть у GTK 3, замість властивостей дочірніх елементів, які було б досить незручно використовувати без прив'язок властивостей. + +`HdyTabBar` є панеллю вкладок, яка підключається до `HdyTabView` і відображає його сторінки, реалізуючи цей дизайн: + +![Вигляд різних реалізацій вкладок](https://gitlab.gnome.org/Teams/Design/os-mockups/-/raw/master/tabs/tabs.png) + +### Що він забезпечує + +* вкладки мають іконку, заголовок, підказку, кнопку закриття, контекстне меню, і вони можуть показувати індикатор завантаження замість іконки +* закріплені вкладки: вони компактні, розташовані на початку панелі вкладок і не прокручуються разом з іншими вкладками; вони також впливають на поведінку гарячих клавіш, таких як `Ctrl+Home/End` +* індикатори непрочитаних повідомлень, включаючи випадки, коли непрочитана вкладка прокручується за межі екрану: + +![Вигляд непрочитаних вкладок](https://blogs.gnome.org/alicem/files/2021/03/tabs-unread-1024x156.png) + +* піктограми-індикатори: кожна сторінка може мати одну піктограму-індикатор на вкладці, опціонально доступну для натискання; Epiphany використовує його як індикатор відтворення аудіо/кнопку вимкнення звуку, а індикатори - розташовані зліва від вкладки, щоб не конфліктувати з кнопками закриття: + +![Вигляд індикаторів вкладок](https://blogs.gnome.org/alicem/files/2021/03/tabs-indicators-768x109.png) + +Коли вкладка закріплена, індикатор відображається замість іконки, а не поруч з нею, як це було раніше в Epiphany. Щоб забезпечити легкий вибір вкладки, вона є клікабельною лише тоді, коли вкладка вже вибрана, тому перший клік вибирає вкладку, а другий клік - активує індикатор. + +* автоматичне приховування, коли присутня лише одна вкладка +* при закритті вкладок за допомогою вказівника, вони не заповнюють порожній простір одразу, натомість вони змінюють розмір так, щоб кнопка закриття наступної вкладки перемістилася точно туди, де була остання, що дозволяє закривати кілька вкладок поспіль, не переміщуючи вказівник +* автоматичне позиціонування для нових вкладок, хоча також є API для вставки вкладок у довільні позиції, якщо це потрібно +* повноцінна підтримка перетягування: вкладки можна переставляти, переміщувати між вікнами, скидувати на робочий стіл для створення нового вікна, вкладки можуть приймати довільні дані, які скидаються; якщо панель вкладок має лише одну вкладку, вона автоматично відображається, коли починається перетягування, і приховується, коли воно закінчується, тому все ще можливо скинути іншу вкладку в неї +* вкладки також можна скинути на перегляд вкладок; в цьому випадку вони додаються в кінець панелі вкладок +* підтримка сенсорних екранів: перетягніть панель вкладок, щоб прокрутити її, довге натискання і перетягування для перестановки вкладки, довше натискання для відкриття контекстного меню +* API підтвердження закриття +* відкриття, закриття, перестановка вкладок, перетягування, показування та приховування панелі вкладок - анімовані +* гарячі клавіші: + + - `Ctrl+Tab` і `Shift+Ctrl+Tab` - перемикання між вкладками з обгортанням + - `Ctrl+PageUp/PageDown/Home/End` - те ж саме, але без обгортання + - `Ctrl+Shift+PageUp/PageDown/Home/End` - перестановка вкладок + - `Alt+[1–9]` - перемикання на одну з перших 9 вкладок + +Той факт, що вони є окремими, дозволяє Epiphany зберігати свою панель вкладок у повноекранному режимі в 40 версії: + +![Вигляд вкладок у повноекранному режимі Epiphany](https://blogs.gnome.org/alicem/files/2021/03/ephy-fullscreen-768x51.png) + +### Чого він не забезпечує + +* фіксований макет вкладок: `HdyTabView` не дозволяє встановлювати довільний віджет як макет вкладок; макет є фіксованим і керується виключно через властивості `HdyTabPage` +* відсутність окремого управління перестановкою та від'єднанням: не можна окремо включати або вимикати перестановку та від'єднання для різних вкладок; вкладки завжди можна переставляти та від'єднувати, і немає можливості це вимкнути +* відсутність контролю над розширенням вкладок: аналогічно, немає можливості зробити так, щоб певні вкладки розширювалися або не розширювалися, усі вкладки (за винятком закріплених) за замовчуванням - розширені +* кнопки закриття: у всіх вкладках (за винятком закріплених) є кнопки закриття, їх не можна видалити, хоча можливо затримати та/або відхилити запит на закриття, наприклад, якщо додаток хоче показати діалог підтвердження при закритті вкладки +* видимість кнопок закриття: кнопки закриття видимі лише на вибраних та/або наведені вкладки, немає можливості показувати їх на всіх вкладках +* вертикальні вкладки: `HdyTabBar` є строго горизонтальним, і його макет не має сенсу у вертикальному вигляді, однак він має API для спостереження за своїми сторінками через `GListModel` (`GtkSelectionModel` у GTK 4), тому дуже легко створити вертикальний список замість цього + +Оскільки Epiphany також використовується в Elementary OS, `HdyTabBar` надає API для вимкнення автоматичного приховування та розширення вкладок, а також для зміни місцями кнопки закриття та індикатора. Усі ці параметри все ще працюють так, як і раніше. + +![Вигляд Epiphany в "елементарному режимі"](https://blogs.gnome.org/alicem/files/2021/03/tabs-elementary-768x84.png) + +### Адаптивність + +`HdyTabBar` поки що не є адаптивним. Epiphany все ще постачає той самий мобільний інтерфейс у версії 40. Однак це значно полегшує реалізацію альтернативних комутаторів, наприклад, нижній аркуш на основі `HdyFlap`: + +![Вигляд перемикача вкладок для мобільних пристроїв у стадії розробки](https://blogs.gnome.org/alicem/files/2021/03/tabs-adaptive-gtk3-603x1024.png) + +Однак це дуже базово. Мобільні перемикачі вкладок часто є дуже складними, наприклад, використовують 3D-стеки карток, сітки, каруселі або списки з попередніми переглядами. На жаль, усе це недоступно в GTK 3. Однак у GTK 4 це можливо, і ми можемо робити такі речі: + +![Вигляд вкладок у режимі каруселі](https://blogs.gnome.org/alicem/files/2021/03/tabs-overview-carousel-603x1024.png) +![Вигляд вкладок у режимі сітки](https://blogs.gnome.org/alicem/files/2021/03/tabs-overview-grid-1024x904.png) + +Маючи належний огляд, можна буде нарешті видалити спливаюче вікно вкладок з Epiphany, яке все ще є в 40 версії, але показується лише тоді, коли панель вкладок починає прокручуватися. + +Порт `HdyTabView` і `HdyTabBar` на GTK 4 завершено і повністю працює, і йому просто потрібен код-рев'ю, хоча огляд ще не є повторно використовуваним віджетом. \ No newline at end of file diff --git a/post/anonimne-korystuvannia-bittorrent-z-i2psnark-ta-i2pd.md b/post/anonimne-korystuvannia-bittorrent-z-i2psnark-ta-i2pd.md new file mode 100644 index 0000000..d87fa63 --- /dev/null +++ b/post/anonimne-korystuvannia-bittorrent-z-i2psnark-ta-i2pd.md @@ -0,0 +1,160 @@ +# Анонімний піринг BitTorrent з I2PSnark та i2pd + +Давно цікавлюсь тематикою альтернативних мереж, зокрема - експериментую з [Yggdrasil](https://devzone.org.ua/post/yggdrasil-mereza-z-detsentralizovanym-routynhom) в контексті BitTorrent. Про мережу [I2P](https://uk.wikipedia.org/wiki/I2P) знаю давно, але мені жодного разу не доводилось нею користуватись. Утім, організувавши на прохання деяких користувачів доступ по I2P до [форуму адміністраторів альтернативних мереж](https://devzone.org.ua/topic/ukrayinska-spilnota-administratoriv-alternatyvnykh-merez), [налаштувавши дзеркало свого блогу Gemini](https://devzone.org.ua/post/publikatsiia-kapsuly-gemini-v-i2p-na-prykladi-servera-agate), та [ретрансляцію каналів eQtv і Громадське радіо](https://devzone.org.ua/post/proksuvannia-m3u8-zasobamy-icecast), стало цікаво також спробувати цю мережу і в контексті торентів. + +Для одночасного обміну файлами в Інтернет, Yggdrasil і Mycelium - я користуюсь вільним клієнтом [qBittorrent](https://www.qbittorrent.org), що з коробки працює на всі мережі (Інтернет та Yggdrasil через подвійний стек IPv4/IPv6) і має гнучкі налаштування для керування великими колекціями. Але Yggdrasil не є анонімною мережею, а qBittorrent - допускає в такому випадку непередбачені з'єднання в гібридному режимі, оскільки розроблявся перш за все для максимальної конективності, а не для анонімізації/деперсоналізації. Так, в ньому є експериментальна опція I2P та універсальна реалізація проксі SOCKS5, але я вже помічав за цим клієнтом "сюрпризи" у вигляді вихідних з'єднань на веб-сокет IPv4 при біндінгу на IPv6 та інші нюанси, що в цілому залежать від конкретної версії. + +Ситуація не краще й з віднайденою мною бібліотекою [librqbit](https://docs.rs/librqbit/latest/librqbit/) (на базі якої мав намір створити приватно-орієнтований клієнт GTK для оверлейних мереж) де наприклад не можливо вимкнути [PEX](https://en.wikipedia.org/wiki/Peer_exchange), для чого треба користуватись костилями blacklist. Взагалі, тему приватного користування торентами в контексті оверлейних мереж краще розкрити окремо, бо тут є що розповісти. + +## Інфраструктура I2P + +В своїй конфігурації I2P, я використовую C++ роутер [i2pd](https://github.com/PurpleI2P/i2pd), оскільки він дозволяє з коробки [налаштувати підключення в режимі Yggdrasil-only](https://devzone.org.ua/post/vstanovlennia-routera-i2p-z-pidkliuchenniam-cherez-yggdrasil) (минаючи прямі з'єднання в Інтернет) і може бути корисним в умовах, коли піринговий трафік через Інтернет може бути не бажаним. + +Переглянувши [доступні клієнти](https://i2pd.readthedocs.io/en/latest/tutorials/filesharing/), відкинув вже згаданий qBittorrent і [BiglyBT](https://www.biglybt.com/) через їх універсальність; [XD](https://github.com/majestrate/XD) - як такий, що "крашить" i2pd. Тому з кандидатів лишився I2P-only [I2PSnark](https://gitlab.com/i2pplus/I2P.Plus/-/tree/master/apps/i2psnark). + +### I2PSnark + +Як видно з посилання, клієнт I2PSnark - є частиною Java роутера [I2P.Plus](https://gitlab.com/i2pplus/I2P.Plus), у той час як моя збірка використовує i2pd (C++). На щастя, розробники заморочились і написали скрипт для генерації артефакту "[standalone](https://gitlab.com/i2pplus/I2P.Plus/-/jobs/artifacts/master/raw/i2psnark-standalone.zip?job=Java8)" - тобто цей клієнт можна запустити як окрему програму та під'єднати до наявного демона i2pd через API [I2CP](https://geti2p.net/uk/docs/protocol/i2cp) (один з протоколів комунікації застосунків мережі I2P) + +Для "встановлення" в Linux, достатньо розпакувати архів до потрібної вам теки з програмами користувача і запустити скрипт `launch-i2psnark` (для Windows це наче має бути `launch-i2psnark.bat`). Якщо потрібно зібратись з сорсу - дивіться інструкції генерації артефакту відповідно до актуальної версії, особисто я не бачу сенсу білдитись тут руками. Само собою, для запуску I2PSnark повинна бути встановлена віртуальна машина Java. + +### i2pd + +Пара слів про i2pd. + +По-перше, якщо в налаштуваннях роутера не увімкнено протокол I2CP - вмикаємо: + +``` /etc/i2pd.conf +#/etc/i2pd.conf +[i2cp] +enabled = true +``` +* або запускаємо роутер з аргументом `--i2cp.enabled=true` + +По-друге, цей роутер в мене встановлено на віддаленому сервері, тому якщо запустити стандартну конфігурацію I2PSnark, він не зможе підключитись до локалхосту (`127.0.0.1`). Якщо у вас аналогічна ситуація, відредагуємо додатково: + +``` /etc/i2pd.conf +#/etc/i2pd.conf +[i2cp] +address = a.a.a.a +``` +* `a.a.a.a` - IP адреса віддаленого i2pd роутера, це може бути локальна IPv4 або IPv6 адреса + +Якщо роутер віддалений, не забуваємо відкрити відповідний порт для клієнтського хосту: + +``` bash +ufw allow from b.b.b.b to a.a.a.a port 7654 proto tcp +``` +* `b.b.b.b` - адреса, де встановлено клієнт I2PSnark +* `a.a.a.a` - адреса роутера i2pd +* якщо `a.a.a.a` == `127.0.0.1` або `::1` (тобто I2PSnark і i2pd на одному пристрої) то відкривати порт не потрібно + +## Запуск + +Як згадано вище, запуск I2PSnark відбувається командою: + +``` bash +./launch-i2psnark +``` +* після цього має автоматично відкритись вкладка типового браузера за адресою http://127.0.0.1:8002/i2psnark/ + +### Приклад systemd + +Для зручності запуску, в системах Linux можна створити системний сервіс: + +``` /etc/systemd/system/i2psnark.service +#/etc/systemd/system/i2psnark.service +[Unit] +After=network.target + +[Service] +Type=idle + +# useradd -m i2psnark +User=i2psnark +Group=i2psnark + +# за умови, що I2PSnark розпаковано до теки користувача +WorkingDirectory=/home/i2psnark +ExecStart=/home/i2psnark/i2psnark/launch-i2psnark + +# опціонально, можна вказати як null +# StandardOutput=file:///home/i2psnark/debug.log +# StandardError=file:///home/i2psnark/error.log + +[Install] +WantedBy=multi-user.target +``` +* `systemctl start i2psnark` - старт +* `systemctl enable i2psnark` - додати в авто-запуск +* `systemctl status i2psnark` - перевірка статусу +* `systemctl stop i2psnark` - зупинити + +### Підключення до віддаленого роутера i2pd + +У разі, якщо роутер i2pd віддалений, перше що треба зробити - це зайти до налаштувань WebUI: + +![Кнопка налаштувань I2PSnark в браузері](https://devzone.org.ua/storage/posts/2025/10/24/8ADzR48l2Rnx8cCrDnoVmntGoXAWk7mRK1bIpVBL.png) + +І вказати актуальний його хост (_Configuration_ > _Tunnel Configuration_ > _I2CP Host_) + +![Налаштування хосту I2CP в I2PSnark](https://devzone.org.ua/storage/posts/2025/10/24/KitbzyZ9zJYCQaQQv9dlJKRSm1BKYCDfL347YYIk.png) + +В принципі, можна вказати до запуску, у файлі `/i2psnark/i2psnark.config.d/i2psnark.config`: + +``` /i2psnark/i2psnark.config.d/i2psnark.config +i2psnark.i2cpHost=a.a.a.a +``` + +### Налаштування довжини ланцюжка з'єднань + +На що варто звернути увагу перед початком обміну файлами - це довжина "хопів" або точок ретрансляції. Ці налаштування знаходяться в тому ж розділі (_Configuration_ > _Tunnel Configuration_ > _Inbound Settings_ та _Outbound Settings_) + +![Налаштування довжини тунелів I2PSnark](https://devzone.org.ua/storage/posts/2025/10/24/dsMq2hZyK6EngFe8Vg7X70SSJnQvC00MBgDplMGS.png) + +Тут можна поставити значення "2" або навіть "1", адже заплутаність маршруту - не є останнім засобом анонімізації, яку використовує I2P: зокрема клієнт I2PSnark генерує випадкові PeerID, а трафік в каналі є зашифрованим, до того ж (у моєму випадку) загорнутий в Yggdrasil. Не варто зайвий раз навантажувати транзитні вузли ціною вашого ж часу на завантаження серіалу. + +### Налаштування швидкості + +I2PSnark має "комфортні" обмеження з коробки, тобто такі, що не перевантажують пропускний канал роутера. Тому, якщо ви, як і я, не дуже активно сьорфите мережею I2P, можна збільшити ліміти на вхідний / вихідний трафік в розділі _Configuration_ > _Torrent Options_ > _Bandwidth limit_: + +![Налаштування ліміту швидкості каналу I2PSnark](https://devzone.org.ua/storage/posts/2025/10/29/Fxctgu1wcL02mHyCOLPA86FzDDfDwIlZBteSMmzr.png) + +### Налаштування локального сховища + +Варто звернути увагу на те, де будуть зберігатись завантажені та поширювані файли. Вказати шлях до кореневої теки можна в розділі _Configuration_ > _Data Storage_ > _Data directory_: + +![Налаштування локального сховища I2PSnark](https://devzone.org.ua/storage/posts/2025/10/29/mAouGuTZB8ZXQKT9BtRPqD84JMFpCpGyinRhPczP.png) + +* Якщо в _Data directory_ через файловий менеджер або FTP додати торент-файли, вони будуть автоматично розпізнані клієнтом і з'являться в списку на головній сторінці Веб-панелі, а дані торентів - будуть завантажені відносно її кореня +* I2PSnark підтримує символічні посилання, тому якщо у вас для сховища примонтовані різні диски з файлами, для них в _Data directory_ можна створити вказівники командою `ln -s` + +### Користування + +Стосовно інтерфейсу, хто користувався торентами і здатен поставити описаний вище софт - зорієнтується як додавати через веб-адмінку файли `.torrent` і посилання Magnet. Зі свого першого досвіду, можу сказати, що цей клієнт дуже "дивний" і так би мовити не інтерактивний. Тут потрібен час, щоб вивчити кожний його нюанс, тому не поспішайте сідити з нього великі колекції. + +Декілька порад, що можуть стати в нагоді початківцям: + +* При першому запуску, дочекайтесь індексації каталогу і відбудови усіх роздач у спику. Поки вона не завершиться, не варто додавати нові, бо вони з'являться тільки після завершення усіх попередніх процесів у черзі. Можливо, це пов'язано з реалізацією UI/API, але таке враження що I2PSnark працює в одному потоці і не реагує, коли ви шлете через WebUI нову команду. Це дуже збиває з толку, бо консоль і журнали не реєструють ініціацію нових подій. +* Не додавайте кастомні шляхи при імпорті торентів з WebUI, окрім того що вказано в _Data directory_. Я так робив та після хард-ребуту (зникло світло), в мене відвалились роздачі розміщені за рамками стандартної теки - довелось руками писати символічні посилання. Ці шляхи я згодом віднайшов у профілі I2PSnark (`/path/to/i2psnark.config.d/xx/xx.config`), але їх зміна в мене не працює; можливо там є часові мітки або ще щось мені не відоме. +* Зверніть увагу, що в екосистемі I2P торенти мають не формальний ліміт файлів (inode) 2000 на торент. Таке ж значення вказане в I2PSnark. Хоча ця опція є в налаштуваннях, вона доступна тільки на читання. В принципі, ви можете її змінити в конфігурації профілю (`i2psnark.maxFilesPerTorrent=5000`) і перезапустити клієнт, але інші користувачі I2PSnark не зможуть її обробити, якщо як і ви, не змінили цей стандартний ліміт локально. Тобто ваша роздача буде напів-сумісною з мережею: також, відомі трекери валідують кількість файлів на торент. Якщо ваша роздача містить більше 2000 файлів, їх варто або заархівувати або розбити на окремі торенти, які згодом організувати в рамках так званого "пулу" або "колекції". + +### Трекери + +Щодо трекерів, то в мережі I2P є різні ресурси, найвідоміший і найстаріший з них - це [tracker2.postman.i2p](http://tracker2.postman.i2p) ([b32](http://6a4kxkg5wp33p25qqhgwl6sj4yh4xuf5b3p3qldwgclebchm3eea.b32.i2p)) він вже входить до стандартного пресету I2PSnark. Створюючи новий торент для відвантаження на цей трекер, переконайтесь, що його адресу додано до списку: + +![Додавання трекерів до нової роздачі в I2PSnark](https://devzone.org.ua/storage/posts/2025/10/30/5AVkv7Coxle43iCOQRel68gfEqpTFpNO6I5dux0k.png) +* можна вказати альтернативні трекери (колонка _Alternates_) для кращої конективності DHT; +* поле "_Content Filters_" - наскільки я розумію, дозволяє прибрати з роздачі системні файли; +* поле "_Data to seed_" - назва файлу або теки у файловій системі (відносно _Data directory_) або абсолютний альтернативний шлях, який з досвіду вище я використовувати не раджу. + +Якщо ви користуєтесь іншим трекером і його немає в стандартному списку, додати такий (як і налаштувати власні фільтри) можна в _Configuration_ > _Trackers_ і _Torrent create file filtering_ відповідно: + +![Налаштування користувацьких трекерів та фільтрів в I2PSnark](https://devzone.org.ua/storage/posts/2025/10/30/OALMUSfqWciyaFDP37igNgsM9ZmcZZxKeVQ5spGG.png) + +### Поради з безпеки + +Будь-який роутер I2P реалізує класичну технологію пірингу (P2P), це означає, що ваш пристрій надсилатиме запити на випадкові адреси IP, отримані в процесі роботи роутера (подібно до звичайного клієнта BitTorrent). I2P не маскує ваше користування цією мережею, а лише ідентичність в ній. Тому, якщо такі технології, зокрема що базовані на BitTorrent/DHT - частково або повністю обмежені у вашій країні як такі, підключайтесь до I2P через VPN або локальні мережі типу [Yggdrasil](https://devzone.org.ua/post/vstanovlennia-routera-i2p-z-pidkliuchenniam-cherez-yggdrasil). Проаналізувати якість налаштувань та активні підключення (наявність витоків трафіку), зручно програмою [Etherape](https://etherape.sourceforge.io). + +Якщо у вас є сумніви стосовно якості реалізації i2pd / I2PSnark, скористайтесь додатковими засобами віртуалізації, наприклад без використання мережного інтерфейсу як такого: +* [Ізоляція Linux від прямих Інтернет з'єднань на базі QEMU / Virtual Machine Manager з VSOCK](https://devzone.org.ua/post/izoliatsiia-linux-vid-priamykh-internet-zyednan-na-bazi-qemu-virtual-machine-manager-i-vsock) \ No newline at end of file diff --git a/post/bezpechnyy-perehliad-saytiv-yggdrasil-z-yggstack.md b/post/bezpechnyy-perehliad-saytiv-yggdrasil-z-yggstack.md new file mode 100644 index 0000000..961729a --- /dev/null +++ b/post/bezpechnyy-perehliad-saytiv-yggdrasil-z-yggstack.md @@ -0,0 +1,112 @@ +# Безпечний перегляд сайтів Yggdrasil з Yggstack + +Враховуючи специфіку сучасних Веб-стандартів, при відвідуванні веб-сайтів у мережі [Yggdrasil](https://devzone.org.ua/post/yggdrasil-mereza-z-detsentralizovanym-routynhom), можливі та цілком ймовірні фонові запити до зовнішніх Інтернет вузлів. Таким чином, може не контрольовано здійснюватись підвантаження сторонніх ресурсів сторінки, зокрема - графіки, стилів, скриптів та іншого. Наприклад, це може бути випадкове або спеціальне звернення веб сайту до ресурсів у зоні `.ru` (зокрема Yandex) або ви просто не бажаєте витоків персональної IP адреси до будь-яких вузлів Інтернет, наприклад - користуючись Yggdrasil в режимі інкогніто. + +Нижче наведу невеличку нотатку, про те, як особисто я фільтрую не бажаний вихідний трафік з використанням [SOCKS5](https://uk.wikipedia.org/wiki/SOCKS) проксі [Yggstack](https://github.com/yggdrasil-network/yggstack) і окремим браузером для навігації в цій мережі - [LibreWolf](https://librewolf.net/), на прикладі системи Fedora Linux. + +Звертаю увагу, що дана інструкція не включає кроки з анонімізації - маскування відбитків, оновлення сесій, cookies, вимкнення акселерації та іншого. Це окрема тема, для якої існують спеціалізовані софт, додатки і навички. Тут ми просто контролюємо вихідний трафік в режимі Yggdrasil-only. + +## Yggstack + +Yggstack - це невеличкий проксі сервер на базі Netstack, що містить в собі ізольований екземпляр вузла Yggdrasil (так само, як [Yggmail](https://devzone.org.ua/post/yggmail-mesendzer-z-poshtovym-interfeysom)) і надає локальний інтерфейс для підключення до нього браузеру (чи іншого додатку) для проксування в діапазоні `0200::/7`. + +### Залежності + +Тут достатньо встановити актуальну версію [Go](https://uk.wikipedia.org/wiki/Go_(мова_програмування)). Про це я вже писав у матеріалі [Встановлення останньої версії Go в Debian](https://devzone.org.ua/post/vstanovlennia-ostannyoyi-versiyi-go-v-debian). + +### Встановлення + +Збираємо приблизно так само, як Yggdrasil: + +1. `git clone https://github.com/yggdrasil-network/yggstack.git` +2. `cd yggstack` - переходимо в робочу теку +3. `./build` - компілюємо +4. `sudo ./yggstack -genconf > /etc/yggstack.conf` - генеруємо конфігурацію + * якщо користуєтесь в оверлеї, також не забудьте вказати [екзіт-ноду](https://publicpeers.neilalexander.dev/) в `Peers` +6. `sudo mv yggstack /usr/local/bin/yggstack` - копіюємо в штатну директорію бінарних програм +7. `sudo restorecon -rv /usr/local/bin/yggstack` - корегуємо доступи на виконання + +### Systemd + +Оскільки Yggstack, в принципі, створений щоб запускатись без `tun` інтерфейсу і `root`, створимо системний сервіс з виконанням від поточного системного користувача. Якщо не плануєте його запускати на фоні, можна цей крок пропустити і запуститись командою: + +``` bash +/usr/local/bin/yggstack -useconffile /etc/yggstack.conf -socks 127.0.0.1:1080 +``` + +Або ж створіть файл конфігурації командою `sudo nano /etc/systemd/system/yggstack.service`, додавши наступний вміст: + +``` yggstack.service +[Unit] +After=network-online.target +Wants=network-online.target + +[Service] +Type=simple +User=USER +Group=GROUP +ExecStart=/usr/local/bin/yggstack -useconffile /etc/yggstack.conf -socks 127.0.0.1:1080 +StandardOutput=null +StandardError=null +Restart=on-failure + +[Install] +WantedBy=multi-user.target +``` +* `USER`/`GROUP` змінюємо на актуальні +* `StandardOutput` і `StandardError` можна вказати як `file:///path/to/log`, якщо потрібне ведення журналу у файл +* `sudo systemctl daemon-reload` - потрібно якщо файл вже існує (оновлення) +* `sudo systemctl restart yggstack` - якщо потрібен автозапуск при старті системи +* `sudo systemctl restart yggstack` - запуск (в режимі рестарт) +* `sudo systemctl status yggstack` - перевіряємо чи сервіс працює + +### Браузер + +Як писав вище, для цієї мережі я користуюсь окремим браузером LibreWolf з трохи іншим пресетом, для зручності. В принципі, ті само кроки актуальні й для FireFox. + +1. Переходимо в `Settings` -> `Proxy` / `Network settings` -> `Manual proxy configuration` +2. В `SOCKS Host` пишемо наші локальні хост `127.0.0.1` і порт `1080` +3. Переконуємось, щоб версія була відмічена як `SOCKS v5` + +#### Конфігурація PAC + +Я додатково користуюсь файлом [PAC](https://en.wikipedia.org/wiki/Proxy_auto-config), щоб зароутити всі запити, що не відносяться до мережі Yggdrasil на фіктивний проксі, щоб швидше прискорити відвантаження сторінки. Інакше браузер буде намагатись відрезольвити ресурси з незначною затримкою. + +Цього можна не робити, адже Yggstack і так відфільтрує подібні запити, оскільки він працює виключно в діапазоні `IPv6`/`0200::/7`. Але приклад нижче лишу, якщо ви хочете користуватись цим проксі на обидві мережі, закоментувавши останній `return`: + +``` yggstack.pac +function FindProxyForURL(url, host) +{ + // спрямовуємо трафік Yggdrasil по регулярному виразу на проксі Yggstack + // цей приклад також включає доменні зони .ygg якщо користуєтесь Alfis DNS + if (/^0{0,1}[2-3][a-f0-9]{0,2}:/.test(host) || /\.ygg$/.test(host)) { + return 'SOCKS5 127.0.0.1:1080'; + } + return 'PROXY 127.0.0.1:123'; // видаліть цей рядок, якщо хочете працювати з усіма мережами + // або вкажіть окремий проксі для цих мереж, наприклад Tor +} +``` +* шлях до даного файлу потрібно вказати у полі `Automatic proxy configuration URL` у форматі `file:///path/to/file` (якщо зберігаєте його локально) +* також, в мене працює окремий від Yggstack інтерфейс Yggdrasil для інших служб, таких як Bittorrent і радіо Rhythmbox. Тому для основного браузеру Firefox, я створив інший файл PAC з протилежними умовами, щоб виключити випадкове звернення до ресурсів Yggdrasil з нього. +* щоб не плутатись в обох клонах Firefox, я користуюсь різними темами оформлення. + +#### Тестування підключення + +Відкриваємо будь який сайт з каталогу http://[21e:a51c:885b:7db0:166e:927:98cd:d186], переходимо в режим дебагу `Ctrl+Shift+i` та дивимось заблоковані запити, якщо такі є. + +* щоб вимкнути форсований редірект `HTTP` > `HTTPS`, в `about:config` встановіть опцію `browser.fixup.fallback-to-https`:`false` + +Для аналізу системного трафіку, мені подобається TUI утиліта `iptraf` (`iptraf-ng`), або GUI `etherape`, [Sniffnet](https://sniffnet.net/) та інші. + +### Додаткова фільтрація + +Опціонально, можна додати правила на `iptables`, особливо це зручно, якщо користуєтесь окремим контейнером: + +``` bash +ufw default deny outgoing +ufw allow out to PUBLIC_PEER_IP +ufw allow out to 0200::/7 from any +``` +* на Fedora, замість CLI `ufw`, для `firewalld` мені зручніше налаштовувати потрібні зони через GUI за допомогою `firewall-config` + +Також не зайвим буде використання вихідного роутера, наприклад на базі [OpenWRT](https://uk.wikipedia.org/wiki/OpenWrt), але на мою думку, додаткова фільтрація саме тут - не критична, оскільки вбудованих засобів браузеру, для поставлених вище задач, цілком вистачає. \ No newline at end of file diff --git a/post/chesslablab-shakhy-onlayn-z-vidkrytym-kodom-na-php.md b/post/chesslablab-shakhy-onlayn-z-vidkrytym-kodom-na-php.md new file mode 100644 index 0000000..f9c7320 --- /dev/null +++ b/post/chesslablab-shakhy-onlayn-z-vidkrytym-kodom-na-php.md @@ -0,0 +1,153 @@ +# ChesslaBlab - шахи онлайн з відкритим кодом на PHP + +[ChesslaBlab](https://chesslablab.org) - це проект з [відкритим кодом](https://github.com/chesslablab) на PHP, для розгортання шахового серверу з нуля та клієнтська частина на [Symfony](https://symfony.com) - для вивчення стратегій, гри онлайн з комп'ютером або друзями. + +Являє собою невибагливу до серверних потужностей та більш спрощену альтернативу [Lichess](https://github.com/lichess-org). У якості рушія штучного інтелекту використовується сервер [Stockfish](https://github.com/official-stockfish/Stockfish). + +ChesslaBlab може бути цікавим для розробників, керівників освітніх програм, тренерів шахових клубів та просто бажаючих проводити власні турніри, розгорнувши багатокористувацький сервер для гри в браузері на мінімальному тарифі VPS. + +Варто зауважити, що обчислення сценаріїв виконується в режимі *server-side*, тому такий проект оптимально підійде для ігор з невеликою кількістю одночасних сесій, наприклад в локальній мережі. + +## Сервер + +Для розгортання серверної частини, використовується [chess-server](https://github.com/chesslablab/chess-server), що реалізує бібліотеку [php-chess](https://github.com/chesslablab/php-chess). + +В системах Debian, спочатку потрібно встановити наступні залежності: +``` +apt install git php composer stockfish +``` + +Для безпечного запуску, краще створити окремого користувача: +``` +useradd -m chesslablab +su chesslablab +``` + +Клонуємо репозиторій та оновлюємо залежності Composer: +``` +git clone https://github.com/chesslablab/chess-server.git +cd chess-server +composer update +``` + +Копіюємо приклад конфігурації в продакшн: +``` +cp .env.example .env +``` + +Файл містить налаштування для двох типів з'єднань: + +1. `WSS` - захищене з'єднання через веб-сокет (потребує додатково створення сертифікатів) +2. `WS` - незахищене, в принципі цей варіант буде простішим для початку + +Вказуємо актуальні адреси для обраного способу, після чого відкриваємо відповідні порти в `iptables` наприклад за допомогою утиліти `ufw`: + +``` +ufw allow 8443 +ufw allow 8085 +``` + +Для кожного типу з'єднань, є свій метод (скрипт) запуску, описаний в [документації](https://chess-server.docs.chesslablab.org). +Наприклад, для `WS` я користуюсь реалізацією сокет-сервера на PHP - [Ratchet](https://github.com/ratchetphp/Ratchet) і наступною командою: + +``` +php cli/ratchet/ws.php +``` + +Також, зручно створити сервіс `systemd`: + +``` +# /etc/systemd/system/chesslablab.service +[Unit] +Description=chesslablab +After=network.target + +[Service] +Type=simple +User=chesslablab +ExecStart=/usr/bin/php /home/chesslablab/chess-server/cli/ratchet/ws.php +StandardOutput=file:/home/chesslablab/chess-server-debug.log +StandardError=file:/home/chesslablab/chess-server-error.log +Restart=on-failure + +[Install] +WantedBy=multi-user.target +``` + +Після чого додати його в авто-запуск: +``` +systemctl daemon-reload +systemctl enable chesslablab +systemctl start chesslablab +``` + +Перевірити роботу служби на порті можна командою: + +``` +netstat -tulpn | grep LISTEN +``` + +## Клієнт + +За весь час розробки, проект мав декілька клієнтів, зокрема [реалізацію на React.js](https://github.com/chesslablab/react-chess), яка нещодавно була переведена у стан архівної. + +Наразі, актуальним клієнтом є багатосторінкова реалізація на базі фреймворку Symfony, розробка ведеться в репозиторії [website](https://github.com/chesslablab/website). + +Встановлення досить просте, якщо додані залежності серверу, достатньо виконати наступні команди: + +``` +cd /home/chesslablab +git clone https://github.com/chesslablab/website.git +cd website +composer update +cp assets/env.example.js assets/env.js +php bin/console importmap:install +``` + +Змінюємо у скопійованому файлі `assets/env.js` потрібні нам адреси відповідно до налаштувань сервера і генеруємо оптимізований кеш для веб-застосунку: +``` +php bin/console asset-map:compile +``` + +При змінах в стандартному файлі Symfony `.env` в середовищі `production`, скидаємо кеш командою: +``` +APP_ENV=prod APP_DEBUG=0 php bin/console cache:clear +``` + +Налаштування Nginx - таке само як і для типового проекту на Symfony. Для свого проекту в локальній мережі IPv6 [Yggdrasil](https://devzone.org.ua/post/yggdrasil-mereznii-protokol-z-decentralizovanim-routingom) / [Alfis DNS](https://devzone.org.ua/post/alfis-dns-rejestraciia-domenu-v-blokcein), я користуюсь наступною конструкцією `WS` (достатньо змінити домен на той, що вказано в налаштуваннях клієнта): + +``` +server { + listen [::]:80; + + allow 0200::/7; + deny all; + + server_name chesslablab.ygg chesslablab.ygg.at; + + root /home/chesslablab/website/public; + + location / { + try_files $uri /index.php$is_args$args; + } + + location ~ ^/.+\.php(/|$) { + + # php -v + fastcgi_pass unix:/run/php/php8.2-fpm.sock; + + include fastcgi_params; + fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; + fastcgi_param APP_ENV prod; + } +} +``` + +Якщо все зроблено правильно, то можна відкрити сайт і почати гру з комп'ютером. У разі проблем, першим ділом відкриваємо консоль браузеру `Ctrl+Shift+i` та дивимось стан підключення. Якщо підключення до сокету успішне, тоді потрібно дивитись безпосередньо серверні журнали за шляхами, які вказані в секціях `StandardOutput` та `StandardError` сервісу `systemd`. + +Пишіть ваші враження та питання в коментарях а також долучайтесь до розробки на GitHub - проект має хороший фідбек від розробника! + +## Посилання + +* [Офіційний сайт ChesslaBlab](https://chesslablab.org) +* [Сторінка на GitHub](https://github.com/chesslablab) \ No newline at end of file diff --git a/post/chomu-pryvatnist-ne-mozlyva-u-suchasniy-merezi-internet.md b/post/chomu-pryvatnist-ne-mozlyva-u-suchasniy-merezi-internet.md new file mode 100644 index 0000000..1e7fc6b --- /dev/null +++ b/post/chomu-pryvatnist-ne-mozlyva-u-suchasniy-merezi-internet.md @@ -0,0 +1,81 @@ +# Про конфіденційність в мережі Інтернет + +У кожного своє розуміння конфіденційності та інформаційної безпеки у рамках різних контекстів. Дехто не переймається темою взагалі, нехтуючи поширенням своїх персональних даних, аргументуючи це їх повною "непотрібністю". Наочним історичним прикладом колись "нікому непотрібної" інформації, був виток облікових даних єврейських громад у фашистській Германії - коли водномить, здавалось би побутові дані стали питанням виживання мільйонів людей. З іншого боку, такі сфери, як об'єктивна журналістика та альтернативні технології зазнають тиску, і сусідня диктатура - тому наглядний приклад, де недавно бравий анон сьогодні боїться писнути "проти" в глибинах своїх мешів. + +Цей матеріал не має на меті когось навчати, переконувати чи нав'язувати паранойю. Тут немає практичних рекомендацій для жодного з тлумачень приватної інформації. Це виключно моя суб'єктивна фіналізація отриманого досвіду по темі інформаційної гігієни, з точки зору користувача, а не спеціаліста сфери безпеки: зокрема, до вивчення того, як працюють мережеві технології та після. + +## Передісторія + +Мій шлях в мережі Інтернет почався незадовго до популяризації його в Україні. Не зважаючи на той факт, що технологія вже давно була поширена на заході, в Україні, мережа все ще сприймалась не серйозно і через тодішню швидкість, слугувала більше для пошуку друкованої інформації (смішно задувати, але вже тоді Інтернет був джерелом прибутку для піратського передруку брошур на ринку "Караваєві Дачі") тоді ніхто не міг подумати, що Інтернет стане основою сучасної торгівлі, міжнародного зв'язку, а через вартість послуг провайдерів і обмеження швидкості - колись Youtube замінить телевізор чи ПК. + +В мене відносно пізно з'явився комп'ютер, я закінчував вищу медичну освіту на якому і серед багатьох хобі, у вільний час писав статті та різні замітки в MS Word, зберігаючи їх просто до локальної теки. Але Інтернет змінив все, з його появою, нарешті я міг ділитися своїми роботами з іншими та більше того - не шукати потрібну інформацію в книжкових магазинах, де якість технічної літератури була просто водою у порівнянні з авторськими блогами; дуже скоро почав робити свої сайти і в мене почали з'являтись перші комерційні замовлення. Так, поступово став поглиблюватись в технічну сферу, яка почала займати 100% мого студентського часу, а потім й стала основною роботою. + +Оскільки через тодішню швидкість мережі ринок е-комерсу в Україні розвивався повільно, час від часу мені траплялись замовлення на закордонному рику, де крім того, що фігурували інші суми, попиту було в рази більше. Але була одна проблема: фінансові операції з країнами пост-СНД, зокрема через популярний на заході Paypal, були не доступні. Тоді я вперше зіткнувся з якимось невидимим бар'єром, не справедливістю, що відгороджує мою країну з тими ямами на дорогах від цивілізованого світу. Тоді прийшло розуміння, що Україна перебуває в штучній ізоляції, через корупцію та "договірняки" з російською мафією, з'явився певний дух протесту до системи, а з ним - вивчення технологій, що близькі свободі по духу: зокрема - OpenSource. + +## Децентралізовані технології + +Одного разу, я прочитав новину про реліз Bitcoin - свого роду розрахункову систему накшталт електронної пошти, тільки для грошей. При чому, на відміну від тодішніх централізованих контор типу WebMoney (з яких також на закордонному ринку було мало толку) нова криптовалюта не мала центрального органу та не мала географічних обмежень. Це для мене стало просто з'їздом даху в плані потенційних можливостей. Але була одна проблема: вона була нікому не відомою і щоб отримати 50 доларів, доводилось пояснювати кожному замовникові як встановити гаманець, де обміняти "реал" в його країні та як переказати гроші :) + +Тим не менше, це мало певний результат, а схожі технології, що базувались на Bittorrent - стали дружніми та цікавими, оскільки в ньому не було такого поняття як "модерація" та "злий адмін" взагалі. У ті часи з'являлось багато інших цікавих проектів по типу Twister, ZeroNet, IPFS і т.д., які також мали на меті свободу поширення та доступу до інформації та свободи слова. Відбувся свого роду бум, тоді альтернативні, зокрема соціальні мережі отримали великий імпульс і затягнули мене своєю цікавістю в "діпвеби", де й провів багато часу на ентузіазмі, зокрема ще й тому, що інтелектуальний рівень їх користувачів був у рази вищим за "юзвірів" популярних порталів. Було чому повчитись. + +Але пройшло більше десяти років і можна побачити, що майже всі альтернативні платформи мертві. Bitcoin, замість вільного засобу розрахунків, став чорною дірою для спекуляцій, де лох - не мамонт. Пам'ятаю єдиний магазин в Києві, де колись офлайн за біткоїн можна було купити пончик, вже мабуть закрився. Звісно, сьогодні вам продадуть пончик в іншому місці, але це не те, чим мало стати взагалі. Чому так? Давайте розглянемо технічну сторону цього питання, адже для інших сфер - є свої спеціалісти. + +На мою думку, ахіллесовою п'ятою всіх революційних технологій, є використання ними класичних засобів зв'язку. Так, на прикладі Bitcoin, щоб підключити свій гаманець до спільного реєстру блокчейн, потрібно з'єднатись з певним сервером DNS, який надасть вам IP адреси публічних вузлів Bittorrent, через які вже можна отримати дані поточних блоків. Оскільки весь ланцюг з'єднання не включає принципово нових засобів комунікації, такі канали можуть бути повністю заблоковані, а їх провайдери - притягнуті до відповідальності, як тільки певна "революційна" технологія почне заважати централізованій економіці (будь вона легальною або корумпованою). + +Іншим моментом децентралізованих рішень, є складна архітектура їх роботи. Оскільки децентралізовані засоби зв'язку від початку були створені для військового застосування, вони не передбачали роботи з такими об'ємами даних та кількістю користувачів, яких сьогодні вимагає цивільна сфера. Наприклад, якщо запустити сервер IPFS, можна побачити, що фонове споживання трафіку складає приблизно 1 мегабіт на секунду і це "на холостих" і при відносно малій кількості пірів у мережі - близько 400. Можна уявити, що станеться з каналом та акумулятором мобільного пристрою (де кожен пір являє собою хеш-суму) при підключенні до рою мільйонів користувачів одночасно. По цій причині, користувачі криптовалют часто користуються "легкими" гаманцями, тобто такими, які підключаються до мережі на стороні серверу, а користувачеві видають ті само порожні цифри балансу, які надає по суті звичайний банк. + +З точки зору приватності, на мою думку, децентралізовані рішення є парадоксально найбільш вразливими, оскільки в таких мережах користувач надає свою адресу усім її учасникам. Тому якщо справа стосується публікації "чутливих" даних, то потрібно спочатку приховати адресу і вже після того підключатись. Насправді зробити це не так просто, оскільки звичайний VPN може працювати тільки для певного інтерфейсу програми, у той час як на фоні, вона також може підключатись до сторонніх сервісів, використовуючи інший протокол чи зовнішню бібліотеку службового сервісу, через білий IP. Для рядового користувача, це майже непосильне завдання, оскільки потребує окремої збірки операційної системи на певному залізі. + +## Fediverse + +Таким чином, еволюційно на зміну повній "децентралізації" як такої, приходять "федеративні" рішення: коли багато невеличких серверів формують одну спільну екосистему Fediverse, яку ще називають "сузір'ям". Яскравим і найбільш вдалим прикладом реалізації є соціальна мережа мікро-блогінгу Mastodon. + +Обраний сервер, або "провайдер", тут виступає вже у якості проксі та свого роду, додатковим "фаєрволом" для користувача. Можна збитись в певні кучі по інтересам і тусити у вузьких колах однодумців, де в інших місцях вас за такі само пости виженуть або заблокують, але знову таки до тих пір, допоки адміну це подобається або його чи його хостинг-провайдера разом з вашими персональними даними не візьмуть за дупу в юрисдикції тієї країни, де він знаходиться. + +Недоліком федеративних рішень є також відсутність будь яких гарантій зі сторони їх "диких" провайдерів, ваш контент може бути так само легко втрачено як і додано. Це знову таки, рішення "зробити сервіс нікому не потрібним, для того, щоб його не заблокували". Ну і звісно, приватні кампанії фізичних осіб рідко слідують наданим ними самими угодам обробки персональних даних користувача, на відміну від медіа холдингів США, таких як Twitter, Facebook тощо. + +## Mesh, TOR, Onion + +Існують різні технології тунелювання (TOR), заплутування маршрутів (Onion) та віртуальних мереж (CJDNS, Yggdrasil та інших). Всі їх об'єднує спільна риса: вам потрібно якось до них підключитись. Так, можна орендувати VPN, або ланцюг з таких, але кожен з них буде посилатись на початковий вузол, а маскування трафіку під "443" (через наприклад Shadowsocks) можна легко викрити шляхом перебору протоколів під час рукостискання (handshake) з публічним вузлом. Тому отримання таких даних - це просто "питання потреби" + +Як варіант, можна взагалі не використовувати Інтернет і уявити собі світ, де кожен юзер роздає по Wi-Fi точку Yggdrasil, але цього мабуть ніколи не станеться. У разі гіпотетичної катастрофи, ймовірно будуть так само використовуватись стандартні протоколи IPv4/IPv6 для побудови класичних локальних мереж, допоки вони знову не з'єднаються і не утворять собою нову мережу Інтернет. + +Ще однією стороною проблеми анонімізації, яку обіцяють технології вище - неможливість приховування такими засобами цифрового відбитку. Наразі, вже кожному доступні технології штучного інтелекту, які наочно демонструють можливості конверсії будь яких аналогових об'єктів, у нашому випадку з копірайтингом - це може бути як використання діалекту чи навіть пунктуації. + +## Альтернативні протоколи + +Альтернативний протокол передбачає фундаментально новий підхід в обміні інформацією, який невідомий моніторинговій стороні. Це може бути наприклад аналоговий радіо-зв'язок, що використовується в морі. + +Тут мало що можу сказати, маю досвід перебування в окупації, де не те щоб сховатись серед іншого трафіку, але й випромінювати будь який сигнал - самовбивство. Навіть за відсутності радіо передавання, звичайний дріт чи обладнання знищується тими, хто проводить зачистки. Якщо у вас потім знайдуть взагалі будь який пристрій, якого не мало бути - про наслідки думайте самі. + +Отже колись романтична віра в технології пост-апокаліпсису, на ділі виявилась плацебо. У форс-мажорних обставинах, ви не зможете користуватись зв'язком як таким, без ризику бути виявленим, навіть якщо це ліхтарик та азбука морзе. Тож цивільний формат зв'язку забезпечується виключно екосистемою тієї країни, де ви знаходитесь і ймовірно користуватись іншими засобами, окрім ліцензованих - не зможете. + +### Веб протоколи + +Щодо цивільного середовища, не так давно, відкрив для себе старі-нові Веб-протоколи, зокрема Gemini. Пам'ятаю, як задумувався про щось схоже, але було чим займатись окрім як розвивати цю думку матеріально. Але не так давно, знову наткнувся на концепцію мінімалізму, яку пропонує протокол Gemini у якості інструмента приватності. Тут немає JS, Cookies та всього того, з чим бореться Ublock та не намагається зовсім - жоден із сучасних Веб-браузерів. + +Утім, на практиці, досить рідко відвідую Geminispace, оскільки по-перше мало україномовного контенту, в той час як англомовний мені просто не цікавий (бо спільнота живе там своїм життям, до якої мені просто наразі немає діла). З іншого боку, сторінки Geminispace всі однотипні, не зручно читати великі тексти, а щоб переглянути зображення, потрібно спочатку підвантажити, натиснувши на посилання (приватність, все таки). + +Стосовно конфіденційності користування - це звичайний собі веб з моделлю "клієнт-сервер", з тією лише різницею, що всі колись асинхронні запити та завантаження зображень ви будете робити вручну. Такий собі формат "я знаю, на що йду" (хоча не варто забувати про DNS на шляху таких "знань") + +## Класичний Інтернет + +Колись так обожнюваний мною Інтернет, після вивчення базових принципів його роботи, сьогодні для мене виглядає як технологічне решето: через браузер відбувається купа фонових JavaScript звернень до третіх серверів без мого відому, Cookies, з їх імпотентними банерами на кожному сайті. + +Частина Веб-контенту взагалі еволюціонувала у збереження десь в базах даних месенджерів по типу Телеграм, але тут окрім IP, зливається невідомо кому ще й номер телефону а також, в залежності від застосунку - гео-дані та мета дані пристрою. Я не розумію, яким чином в умовах війни, в Телеграм присутні офіційні державні агентства, зокрема такі, що функціонують на окупованих територіях, адже це просто самовбивство для потенційних громадян що там лишились але за відсутності досвіду в сфері ІТ, користуються даним інструментом за офіційними рекомендаціями. + +## Висновки + +Підсумовуючи сказане, на мою думку, будь який пристрій, що підключається до мережі - як не крути, є потенційною дірою. Навіть користуючись вільною операційною системою з відкритим кодом, не виключається ймовірність наявності неописаних багів, що мовчки використовуються відповідними спеціалістами. Постійно фігурують випадки контрибуцій з вразливостями, які потім спішно видаляють з репозиторіїв, але мільйони користувачів вже встигли встановити такі оновлення. + +Конфіденційності для пристрою, підключеного до мережі Інтернет, на мою думку, просто не існує. Немає такого інструменту, що дозволить бути 100% впевненим. Приховати дані у сучасній мережі Інтернет, можна тільки не маючи жодних зв'язків з цією технологією, зробивши еволюційний крок назад, у часи палеоліту. + +### Переосмислення + +Будь які інструменти анонімізації, ймовірно, суперечать початковим концепціям Інтернет, а використання таких просто створює ізоляційний вакуум та обмежує доступність інформації. + +Щодо інструментів без цензури та вільної журналістики, анонімні джерела мають мінімальну вагу, оскільки не є перевіреними. Публікація ноунейма у хащах діпвебу просто не викличе резонансу. У цій справі, як і в інших сферах - потрібна відповідальність та мужність. + +Свобода вибору, захист персональних даних, авторського права, надання гарантованих послуг (таких як хостинг для VPN), можливість висловлювати точку зору та користування перевагами цивілізованого світу, є результатом злагодженої роботи в багатьох сферах - оборонної, дипломатичної, соціально-ініціативної. Конфіденційність, сама по собі, як функціональна одиниця - не ефективна, особливо в такому середовищі як Інтернет. Мабуть, тут вона і не потрібна. + +Цінуймо справжню свободу, а не імітацію. Допомагаймо тим, хто її боронить! \ No newline at end of file diff --git a/post/dumky-stosovno-tls-v-merezakh-yggdrasil-ta-mycelium.md b/post/dumky-stosovno-tls-v-merezakh-yggdrasil-ta-mycelium.md new file mode 100644 index 0000000..c21d935 --- /dev/null +++ b/post/dumky-stosovno-tls-v-merezakh-yggdrasil-ta-mycelium.md @@ -0,0 +1,31 @@ +# Думки стосовно TLS в мережах Yggdrasil та Mycelium + +*Інтернет-версія моєї публікації для сайту локальної [спільноти адміністраторів альтернативних мереж](https://devzone.org.ua/topic/ukrayinska-spilnota-administratoriv-alternatyvnykh-merez).* + +В середовищі оверлейних мереж чомусь прийнято вважати, що якщо ключі вузлів перманентні, а з'єднання між вузлами захищене TLS, то додатковий шар SSL ніби як і не потрібний. Утім, останнім часом я починаю у цьому сумніватись. + +## Компрометація ключа + +В мережах типу [Yggdrasil](https://devzone.org.ua/post/yggdrasil-mereza-z-detsentralizovanym-routynhom) / [Mycelium](https://github.com/threefoldtech/mycelium) немає рівня складності при генерації приватних ключів, а отже теоретично (хоча і малоймовірно) можлива колізія. З цієї причини, рекомендується використовувати основні адреси замість підмереж, а останні - розробники планують, але ніяк не випиляють. Та й зручні вони в рамках шейред хосту. Все одно це не виключає можливості випадкового видобутку копії, або не випадкового, враховуючи потенційні можливості криптоіндустрії, питання лише доречності використання супер-комп'ютера для цієї мети; скільки ці мережі включатимуть користувачів і якого статку, для потенційних атак на роутинг, що базується на фіксованому алгоритмі побудови дерева з peer ID. + +## Подвійний шар + +Технічно, протокол транспорту Yggdrasil бере на себе роль шифрування трафіку тоді, коли це може бути не потрібно. Наприклад, у випадках: +* заощадження електроенергії та ресурсів CPU при пересиланні великих медіа файлів +* коли вже використовується шар SSL / HTTPS на програмному рівні - з метою уникнення перехоплень логіну/паролю чи просто конфіденційного пересилання запитів GET через проксі + +Практичний приклад: вимога щодо шифрування трафіку [Gemini](https://devzone.org.ua/post/protokol-gemini-iak-alternatyva-http) в рамках Yggdrasil, бо перший хоче бути захищеним для Інтернет, але я використовую його не там, де задумував автор. З цієї причини, певний час користувався альтернативою [Nex](https://devzone.org.ua/post/protokol-nex-lehka-alternatyva-gemini), але згодом усвідомив, що деякі дані потенційно можуть таки потребувати сертифікату, а отже, мені потрібна стара-добра модель HTTP+HTTPS на чутливих формах. + +Якщо від клієнта до сервера передаються чутливі дані, то на мою думку, варто користуватись сертифікатом SSL, що слугуватиме запобіжником, але маршрутизатор вже про все "подбав", створивши зайві проблеми. + +## Сертифікація в локальних мережах + +Через ізоляцію локальних мереж, в Yggdrasil є проблемою налаштувати валідний сертифікат, наприклад з Let's Encrypt. Але у випадку протоколу Gemini - центри сертифікації не використовуються взагалі. Натомість застосовується принцип [TOFU](https://en.wikipedia.org/wiki/Trust_on_first_use), що значно зменшує ризик перехоплення даних у часі - до моменту виявлення витоку. В мене навіть були такі думки стосовно організації внутрішньомережного центру сертифікації, чому б і ні; чому б навіть не зробити такий сервіс платним? + +## Висновки + +Коли і як саме шифрувати дані - має вирішувати користувач / адміністратор мережі, на ті потоки / дані які того потребують. Тоді як Yggdrasil та Mycelium - роблять це "добровільно-примусово" як, власне, й інші новомодні софти з лейблом "абсолютно сек'юрно". Сучасний софт, розробники якого конкурують за право називатись "захищеним" нагадує крипто-капусту з коефіцієнтом безпеки було == стало. + +Чомусь вже починає дратувати, коли за мене щось хтось починає вирішувати там, де того не просять. Маркетинг - маркетингом, слогани - слоганами, але досвідчені юзери йдуть через подібний дискомфорт, а туристи й без того не затримуються. + +А ще висновки такі, що ефективні мережні рішення були винайдені повоєнними спеціалістами пів сторіччя тому, які мусили виживати, а не гратись в комерційні експерименти. Нічого принципово нового за цей час не винайдено. Можливо, наступним проривом стане квантова передача даних, а не подібний нонсенс: прокладати автоматичний маршрут до ймовірно компрометованих вузлів, при цьому шифрувати тони сміття, що ходить ним. \ No newline at end of file diff --git a/post/freetube-perehliad-youtube-bez-reklamy.md b/post/freetube-perehliad-youtube-bez-reklamy.md new file mode 100644 index 0000000..66ca3ac --- /dev/null +++ b/post/freetube-perehliad-youtube-bez-reklamy.md @@ -0,0 +1,14 @@ +# FreeTube - перегляд YouTube без реклами + +[FreeTube](https://freetubeapp.io) - це конфіденційно-орієнтований клієнт з відкритим кодом для перегляду відео на YouTube. + +Написаний на базі Node.js / Electron. В стандартних налаштуваннях використовує випадковий проксі-сервер [Invidious](https://invidious.io), що запобігає відслудковуванню користувача і формуванню персоналізованих списків. Метод підключення та провайдер може бути вибраний вручну, якщо така потреба є. Кожен може стати таким провайдером, якщо сервер відповідає [певним умовам](https://docs.invidious.io/instances/#rules-to-have-your-instance-in-this-list). + +Окрім того, що ретрансляція відео FreeTube не містить реклами, клієнт працює у режимі створення локальних підписок, плейлистів, вподобань, які за потреби можна експортувати в JSON чи XML на інший пристрій без участі третіх сторін. Такий підхід дозволяє переглядати виключно той контент, який подобається саме вам, і ніхто не матиме доступу до ваших персональних колекцій. + +Серед функцій - гнучкі налаштування інтерфейсу, зручне завантаження відео, аудіо та субтитрів локально у різній якості, щоправда максимальна якість такого відео обмежена 1080p / 60fps, чого в принципі достатньо для колекції "в дорогу" де немає Інтернет. Якщо ж є потреба зберігати оригінальні відео, варто звернути увагу на утиліту [yt-dlp](https://github.com/yt-dlp/yt-dlp), можливості якої - тема для окремого матеріалу. + +Клієнт орієнтований в першу чергу на десктоп, може працювати в різних операційних системах та архітектурах. Для встановлення в Linux - доступні готові пакети `deb`, `rpm`, `appimage`, `flatpak` та інші; також є готові збірки останніх версій для Windows і MacOS. + +Завантажити FreeTube можна на офіційному сайті: +https://freetubeapp.io \ No newline at end of file diff --git a/post/gmid-bahatofunktsionalnyy-proksi-server-dlia-gemini.md b/post/gmid-bahatofunktsionalnyy-proksi-server-dlia-gemini.md new file mode 100644 index 0000000..f8154d9 --- /dev/null +++ b/post/gmid-bahatofunktsionalnyy-proksi-server-dlia-gemini.md @@ -0,0 +1,112 @@ +# Gmid - багатофункціональний проксі-сервер для Gemini + +[Gmid](https://gmid.omarpolo.com) - багатофункціональний сервер з відкритим кодом для протоколу [Gemini](https://devzone.org.ua/post/protokol-gemini-iak-legka-alternativa-http). + +Зокрема, його зручно використовувати у якості проксі серверу для віртуальних хостів, аналогічно тому як цю функцію виконує Nginx для HTTP. +Це може бути зручно, коли немає змоги піднімати для кожного серверу маску підмережі, або коли сайт працює на VPS з одним виділеним IP. + +## Встановлення + +Написаний Gmid на `C`, перед його збіркою потрібно додати наступні бібліотеки: +``` +apt install bison byacc +``` + +Буде правильним запускати і відповідно виконувати процес Gmid від окремого користувача, якого спочатку створимо та виконаємо вхід: +``` +useradd -m gmid +su gmid +``` + +Клонуємо останню версію з офіційного дзеркала на GitHub і виконуємо компіляцію: +``` +git clone https://github.com/omar-polo/gmid.git +cd gmid +./configure +make +``` + +## Налаштування reverse-proxy + +Нижче описаний приклад створення двох віртуальних хостів `host1.com` та `host2.com`, які працюють на умовних адресах `1.2.3.4`:`1965` та `4.3.2.1`:`1965` відповідно. + +Таким чином, файл конфігурації виглядатиме так: + +``` +#/home/gmid/gmid.conf +server "host1.com" { + + listen on 0.0.0.0 port 1965 + + cert "/home/gmid/host/host1.com/cert.pem" + key "/home/gmid/host/host1.com/key.rsa" + + proxy { + sni "host1.com" + relay-to 1.2.3.4 port 1965 + verifyname off + } +} + +server "host2.com" { + + listen on 0.0.0.0 port 1965 + + cert "/home/gmid/host/host2.com/cert.pem" + key "/home/gmid/host/host2.com/key.rsa" + + proxy { + sni "host2.com" + relay-to 4.3.2.1 port 1965 + verifyname off + } +} +``` + +## Створення сертифікатів + +На прикладі конфігурації вище, додамо само-підписаний сертифікат на прикладі `host1.com`, необхідний для роботи протоколу Gemini. +Аналогічним способом створюється й сертифікат для `host2.com`, замінивши значення атрибуту `CN`: +``` +mkdir /home/gmid/host/host1.com +cd /home/gmid/host/host1.com +openssl req -x509 -newkey rsa:4096 -keyout key.rsa -out cert.pem -days 3650 -nodes -subj "/CN=host1.com" +``` + +## Автозапуск + +Щоб наш проксі-сервер стартував разом з системою і працював як процес `systemd`, додамо для Gmid юніт: +``` +#/etc/systemd/system/gmid.service +[Unit] +Description=gmid +After=network.target + +[Service] +Type=simple +User=gmid +Group=gmid +WorkingDirectory=/home/gmid/ +ExecStart=/home/gmid/gmid/gmid -c /home/gmid/gmid.conf -f -v +StandardOutput=file:/home/gmid/output.log +StandardError=file:/home/gmid/debug.log +Restart=on-failure + +[Install] +WantedBy=multi-user.target +``` + +* `-f` - запуск в режимі `foreground`, інакше процес стартуватиме у фоновому режимі без журналювання +* `-v` - або `verbose` для звітів відлагодження + +Оновлюємо конфігурацію та запускаємо сервер: + +``` +systemctl daemon-reload +systemctl enable gmid +systemctl start gmid +``` + +## Посилання +* [Офіційна сторінка проекту](https://gmid.omarpolo.com) +* [Дзеркало на GitHub](https://github.com/omar-polo/gmid) \ No newline at end of file diff --git a/post/half-life-v-linux-na-bazi-rushiia-xash3dfwgs.md b/post/half-life-v-linux-na-bazi-rushiia-xash3dfwgs.md new file mode 100644 index 0000000..aacb37f --- /dev/null +++ b/post/half-life-v-linux-na-bazi-rushiia-xash3dfwgs.md @@ -0,0 +1,224 @@ +# Half-Life в Linux на базі рушія Xash3D/FWGS + +Half-Life - чудова гра свого часу, яка досі має чимало шанувальників та високий онлайн. + +Вона стандартно працює в Linux через Steam, утім варто знати й про таку альтернативу рушія з відкритим кодом, як [Xash3D/FWGS](https://github.com/FWGS/xash3d-fwgs). + +## Встановлення + +Для встановлення, знадобиться підтримка архітектури i386: +``` +dpkg --add-architecture i386 +apt update +apt install build-essential gcc-multilib g++-multilib python3 libsdl2-dev:i386 libfontconfig-dev:i386 libfreetype6-dev:i386 +export PKG_CONFIG_PATH=/usr/lib/i386-linux-gnu/pkgconfig +``` +* приклад для Debian, для інших дистрибутивів - дивіться [README](https://github.com/FWGS/xash3d-fwgs#build-instructions) + +Після цього клонуємо репозиторій та збираємо бінарний пакет: +``` +git clone https://github.com/FWGS/xash3d-fwgs.git +cd xash3d-fwgs +git submodule update --init --recursive +./waf configure -T release +./waf build +``` + +Коли рушій буде скомпільовано, його можна перенести у потрібну теку: +``` +./waf install --destdir=/path/to/game/directory +``` + +### ARM + +Рушій Xash3D/FWGS підтримує різні платформи: для ігрового клієнта на смартфоні чи сервера на базі Raspberry Pi. +Для такої конфігурації, потрібно зібрати декілька додаткових бібліотек з репозиторію `hlsdk-portable`: + +``` bash +git clone https://github.com/FWGS/hlsdk-portable.git +cd hlsdk-portable +./waf configure -T release +./waf +``` + +Потім скопіювати наступні файли до теки `valve`: +1. `hlsdk-portable/build/cl_dll/client_armv7hf.so` > `valve/cl_dlls/client_armv7hf.so` +2. `hlsdk-portable/build/dlls/hl_armv7hf.so` > `valve/dlls/hl_armv7hf.so` + +### Ігрові набори + +Наступним кроком, потрібно придбати ігрові асети (до якої і досі виходять оновлення). Купити гру можна безпосередньо у [Steam](https://store.steampowered.com/app/70/HalfLife/). Зробити це найкраще у свята, коли бувають розпродажі, знижки іноді сягають 90% вартості, тому можна купити повний набір Half-Life, включно з другою версією (про запуск якої в Linux поговоримо окремо). + +Після завантаження гри до профілю Steam, переходимо з меню до теки з файлами гри та копіюємо їх до `valve` встановленого рушія Xash3D/FWGS. +Якщо при запуску буде помилка: +``` bash +Host_InitError: Can't initialize cl_dlls/client.so: vgui.so: cannot open shared object file: No such file or directory +``` +копіюємо файл `vgui.so` безпосередньо у кореневу теку, де встановлено Xash3D (тобто рівнем вище `valve`) + +## Запуск гри + +Запускається гра виконанням бінарного файлу `xash3d`: +``` +chmod +x xash3d +xash3d -dev +``` +* опція `-dev` потрібна тільки для відображення консолі (`~`) як пункту головного меню +* для більш детального дебагу, вказуємо рівень, наприклад `-dev 2` + +Якщо все зроблено правильно, з'явиться лаунчер рушія з відповідним меню, де можна грати в одиночну гру або в мережі з друзями. + +## Майстер сервер + +Майстер сервер використовується для пошуку ігрових серверів, відповідно усі сервери, що ви бачитимете в мультиплеєрі, відправляють свій статус майстер серверу, а той - клієнтові при виборі підключень. + +За замовченням, рушій використовує власний майстер `mentality.rip:27010`. +Змінити його можна командою в терміналі `clearmasters` / `addmaster IP:port` а перевірити - командою `listmasters`. Також на етапі збірки, змінити можна у вихідному коді, наприклад, як це зроблено [тут](https://github.com/FWGS/xash3d-fwgs/commit/05d9cf895e180f25e6e0cbe33e3f02cd222de3b0). + +Якщо планується запуск власного майстер сервера, наразі існує два рішення з відкритим кодом: +* [pymaster](https://github.com/YGGverse/pymaster/tree/v2) - не офіційна редакція закинутого майстер сервера на python +* [xash3d-master](https://git.mentality.rip/numas13/xash3d-master) - після релізу редакції pymaster, розробник опублікував офіційну версію на Rust + +### xash3d-master + +Коротка інструкція для збірки з репозиторію: + +``` bash +git clone https://github.com/FWGS/xash3d-master.git && cd xash3d-master +cargo build --release +sudo install target/release/xash3d-master /usr/local/bin/xash3d-master +``` +* для встановлення пакетного менеджера cargo, дивіться інструкції [rustup](https://rustup.rs/). + +Типовий сервіс systemd виглядатиме так: + +``` /etc/systemd/system/xash3d-master.service +[Unit] +After=network.target + +[Service] +Type=simple +User=xash3d-master +Group=xash3d-master +ExecStart=/usr/local/bin/xash3d-master --ip xx:xx:xx:xx +StandardOutput=file:///home/xash3d-master/debug.log +StandardError=file:///home/xash3d-master/error.log +# Restart=on-failure + +[Install] +WantedBy=multi-user.target +``` +* даний приклад передбачає створення окремого користувача `useradd -m xash3d-master` +* стандартний порт у цьому випадку - `27010`, не забуваємо його відкрити у фаєрволі! + +## Ігровий сервер + +Для запуску ігрового серверу, використовується аналогічна збірка Xash3D та файлами з грою, запуск відбувається приблизно так: +``` +xash3d -dedicated -port 27015 -ip xx:xx:xx:xx +maxplayers 8 +map crossfire +``` + +При цьому не забуваємо відкрити порт: +``` +ufw allow 27015 +``` + +Налаштування серверу зберігаються у наступних файлах, по яким треба пройтись окремо + +* `valve/server.cfg` +* `valve/skill.cfg` +* `valve/listip.cfg` +* `valve/banned.cfg` + +Щоб сервер став доступним у списку обраного майстра, потрібно також вказати: +``` +sv_lan 0 +public 1 +``` + +### FastDL + +FastDL - це спосіб швидкої передачі ігрових файлів клієнтові. Звичайно організується на базі веб каталогу за допомогою Nginx. +Структура файлів, що передається, має відповідати стандартному їх розташуванню у `valve`, наприклад: + +* `/maps` +* `/models` +* `/sound` +* `/materials` + +після чого, у файлі `server.cfg` вказується шлях до цільових файлів (де `/fastdl/half-life/` - довільний шлях, якщо корінь зайнятий): +``` +sv_downloadurl "http://xx.xx.xx.xx/fastdl/half-life/" +sv_allowdownload 1 +``` + +### RESGen + +[RESGen](https://github.com/kriswema/resgen) - найпростіший спосіб згенерувати файли залежностей для ігрової мапи. Наприклад, особливі моделі гравців, для подальшої передачі їх через FastDL. + +Для генерації текстового файлу необхідно вказати цільову мапу на сервері: +``` +Linux.64-bit /valve/maps/crossfire.bsp +``` + +після чого буде згенеровано приблизно такий файл: + +``` +// crossfire.res - created with RESGen v2.0.3. +// RESGen is made by Jeroen "ShadowLord" Bogers, +// with serveral improvements and additions by Zero3Cool. +// For more info go to http://resgen.hltools.com + +// .res entries (9): +halflife.wad +sound/ambience/jetflyby1.wav +sound/ambience/siren.wav +sound/debris/beamstart11.wav +sound/weapons/electro5.wav +sound/weapons/mortarhit.wav +sound/weapons/sbarrel1.wav +sprites/muzzleflash1.spr +sprites/steam1.spr +models/player/... // тут додаємо кастомну модель гравця +``` + +Отриманий файл розміщуємо у теці `/valve/maps` поряд з однойменною мапою. +Таким чином (після перезапуску серверу), нові гравці, що підключились, зможуть бачити відповідні моделі замість стандартних. Моделі будуть завантажуватись усім учасникам гри до локальної теки `/valve/downloaded/models/player/` + +### Боти + +Щоб гравцям не було самотньо в очікуванні, можна додати ботів, які наприклад будуть зникати із появою онлайну. + +Рішень насправді існує багато, але найбільш простий спосіб - це [Bot Number 10](http://hpb-bot.bots-united.com/downloads.html). + +Є офіційна [бінарна збірка для Linux](http://hpb-bot.bots-united.com/releases/bot10_linux.tgz), утім, якщо потрібно модифікувати бота, можна зібрати власну версію з початкового коду, яка є частиною [Portable Half-Life SDK](https://github.com/FWGS/hlsdk-portable/tree/bot10): + +``` +git clone --recursive https://github.com/FWGS/hlsdk-portable.git +git checkout bot10 +cmake -B build -S . +cmake --build build +``` + +Існує також альтернативна [збірка YGGverse](https://github.com/YGGverse/hlsdk-portable/tree/bot10-yggverse), яка дозволяє запускати зконфігурованих ботів а не просто рандомних, як це зроблено в оригіналі. + +## Висновки + +В цілому, хоч в Steam більший онлайн, Xash3D - має свою спільноту і можливості для розробки. Гра стабільно працює на стареньких PC, а для запуску власного серверу, згодиться мінімальний тариф VPS (в середньому завантаженість CPU складає 15%). + +Якщо тема є цікавою, можливо окремо буде описано спосіб запуску Half-Life 2 на базі рушія Source, наприклад однієї з його версій у [відкритому доступі](https://github.com/nillerusr/source-engine). + +## Дивіться також + +* [Створення графіті для Half-Life в редакторі GIMP](https://devzone.org.ua/post/stvorennia-grafiti-dlia-half-life-v-gimp) + +## Посилання + +* [Придбати файли гри Half-Life в Steam](https://store.steampowered.com/app/70/HalfLife/) +* [Офіційна сторінка рушія Xash3D / FWGS](https://github.com/FWGS/xash3d-fwgs) +* [Опис параметрів конфігурації сервера](https://www.svencoop.com/manual/server-config.html) +* [Серверні запити](https://developer.valvesoftware.com/wiki/Server_queries) +* [Команди майстер сервера](https://developer.valvesoftware.com/wiki/Master_Server_Query_Protocol) +* [Каталог скінів](https://gamebanana.com/mods/cats/7734) +* [PHP 8 / Composer бібліотека для веб-розробників](https://github.com/YGGverse/hl-php) +* [Веб рушій на Symfony для організації серверу аналітики](https://github.com/YGGverse/hlstate) \ No newline at end of file diff --git a/post/heo-lokatsiia-piriv-dlia-transmission-gtk.md b/post/heo-lokatsiia-piriv-dlia-transmission-gtk.md new file mode 100644 index 0000000..a0dd13f --- /dev/null +++ b/post/heo-lokatsiia-piriv-dlia-transmission-gtk.md @@ -0,0 +1,37 @@ +# Гео-локація пірів для Transmission / GTK + +Для тих, хто цікавиться географією своїх роздач, нагадаю, що для Transmission існує окрема [гілка](https://github.com/365andreas/transmission/tree/country-flags). Вона поки не змержена в апстрім і є частиною [PR#7171](https://github.com/transmission/transmission/pull/7171). + +Що там відбувається - не знаю, але для себе її успішно зібрав і користуюсь вже тривалий час. По суті, вона додає колонку Geo-IP до вікна властивостей роздачі (таб `Peers`). На момент допису, останнє оновлення було внесене 2 місяці тому, отже гілка є актуальною. + +Процес збірки на Fedora 42 / GNOME 48 виглядає наступним чином: + +1. `sudo dnf remove transmission transmission-gtk` - видаляємо, якщо встановлено з `dnf`/`yum` +2. `sudo dnf install gtkmm4.0-devel` - додатково поставив тільки API фреймворку [gtkmm](https://gtkmm.gnome.org/en/) v4 +3. `git clone --recurse-submodules https://github.com/365andreas/transmission.git` +4. `cd transmission` +5. `git checkout country-flags` - обираємо гілку, що включає функціональність Geo-IP +6. `git submodule update --init --recursive` - ініціюємо локальні залежності +7. `cmake -B build -DCMAKE_BUILD_TYPE=RelWithDebInfo -DENABLE_GTK=ON` + * `-DENABLE_TESTS=OFF` - не бажано, але тести можна вимкнути для швидкої компіляції + * `-DENABLE_MAC=OFF` - за що відповідає не знаю, лишив як в сказано в доках + * `-DCMAKE_BUILD_TYPE=Release` - додав, бо не планую нічого дебажити +8. `cd build` +9. `cmake --build .` + * можна додатково вказати `-j N` - де `N` кількість потоків / ядер для збірки + * якщо на цьому етапі у вас помилка з `Glib::FileTest::EXISTS`, то вам [сюди](https://github.com/transmission/transmission/pull/7171#issuecomment-3082171771) +10. `sudo cmake --install .` + +Це приклад для першої збірки, якщо ви оновлюєтесь, в README є окремі [команди](https://github.com/transmission/transmission#building-transmission-from-git-updating). + +Після встановлення має з'явитись значок в меню програм GNOME. Якщо цього не сталося - перевірте наявність лаунчера `/usr/local/share/applications/transmission-gtk.desktop` (це стандартна локація для `CMAKE_INSTALL_PREFIX`) + +Скинути кеш можна командою: +``` +update-desktop-database /usr/local/share/applications +``` +* у вас цей шлях може бути іншим, наприклад `~/.local/share/applications` + +Окремо варто зауважити, що деякі трекери можуть блокувати альфа-збірки з репозиторію по заголовкам. Наскільки це актуально - я не знаю, але такі дані можна змінити різними [способами](https://devzone.org.ua/post/zmina-peerid-user-agent-v-transmission). + +Ну власне й все. Я думаю що дану модифікацію рано чи пізно буде додано то наступної версії, адже в qBittorrent така опція вже давно постачається з коробки, до того ж там є прапорці країн. \ No newline at end of file diff --git a/post/hrabli-p2p.md b/post/hrabli-p2p.md new file mode 100644 index 0000000..5b52cb1 --- /dev/null +++ b/post/hrabli-p2p.md @@ -0,0 +1,45 @@ +# Граблі соціального P2P + +Це мій особистий висновок в контексті соціальних платформ, які у свій час мали на меті створити "вільний інтернет" для вільних людей, зокрема - такі вже сьогодні мертві проекти як [twister](https://devzone.org.ua/post/twister-detsentralizovana-platforma-mikroblohiv), OpenBazaar, ZeroNet та інші. А можливо завтра - не так давно [анонсована](https://radicle.xyz/2024/09/10/radicle-1.0.html) перша стабільна версія децентралізованої Git платформи Radicle. + +## BitTorrent / DHT + +Нічого революційного за останні роки еволюції пірингових технологій не відбулось. Мабуть, єдиним напівживим піринговим напрямком, все ще лишається обмін великими файлами через BitTorrent / DHT засобами UPnP, що дозволяє клієнтові користуватись шириною каналу, а серверу - балансувати трафік на піках, допоки пір не отримає своє та не закриє додаток, видаливши потім і сам файл. + +## Залежність від сервісних вузлів + +Трекери, сіди, ноди ретрансляції... + +Ніхто за дарма не буде обслуговувати серйозний об'єм даних. Звісно, можна знайти "не мамонта", хто вірить в щось, паразитувати на його ноді, деякий час.. + +Підняв віддалену ноду Radicle для експерименту на мотивації стати сідом, а та з'їла все вільне місце на диску. Можливо, є певна опція, щоб лімітувати простір чи зберігати якісь фрагментовані дані (pieces), але тоді для мережі потрібна більша кількість вузлів, що забезпечать її 100% покриття. Тут я вже мовчу про оверхед системних даних, що стосуються актуалізації даних (CRUD) і мережевої роботи самого рою. + +Якщо мені потрібен повноцінний сервер з виділеним IP, то питання: чому просто не поставити собі Gitea? + +## Очікування пірів + +В пірингових мережах завжди доводиться чекати когось на роздачу, яка відбувається, коли вже й не потрібно. Або ж знову таки орендувати сервер VPN, щоб спробувати знайти контент в заблокованих глобальним фаєрволом локаціях. Хоча на практиці, IP "не сервісних" вузлів змінюється так швидко, що й VPN не є панацеєю. В сучасному світі прогрес не стоїть на місці, люди користуються мобільним інтернетом, а той еволюціонує для балансування навантажень між різними станціями, генеруючи тони мертвих через годину IP. + +З цієї ж причини, багато даних просто втрачається. Якщо ви хочете загубити на рандомному і цілком тимчасовому пірі шматок опублікованих даних, p2p - вдалий вибір. + +## BlockChain + +Тут здавалось би, рішенням може стати BlockChain, але це веде у ті само ворота, коли об'єм даних розростається а вартість зберігання зростає. При цьому, сама технологія блокчейн доволі чутлива до консенсусу: будь які зміни останнього вимагають згоди від усіх учасників та оновлення ними локальних копій програмного забезпечення, що не рідко спонукає до фрагментування форками. + +Іноді я навіть не знаю, що витратніше для процесору: періодичний майнинг чи постійна актуалізація DHT. Багато p2p платформ використовують для своєї роботи і BlockChain і окремий сервер(и) DHT, а ще SSL інтерфейс для останнього і сервер UPnP. Відповідно це піднімає вимоги до вашого знову ж таки виділеного серверу, бо на локальному, без добровольців-садомазохістів мережа просто не працюватиме. А ті, на практиці, онлайн не довго, бо знаходять собі нову забаву, що вимагає 100% віддачі ресурсів попередньої. + +## Mesh + +Іншим цікавим напрямком, є оверлейні мережі типу [Yggdrasil](https://devzone.org.ua/post/yggdrasil-mereza-z-detsentralizovanym-routynhom), що використовують піринг для побудови автоматизованого роутингу. Здебільшого вони зручні в ситуаціях, коли з якихось причин, протокол UPnP не доступний або певний порт блокується провайдером. Але й тут, подібно до VPN, потрібні вихідні вузли, тобто є залежніть від когось, а цей хтось - по суті, звичайний сервер. + +На мою думку, потреба в оверлейних мережах відпаде з інтеграцією ISP протоколу IPv6, що дозволить кожному отримати свою виділену адресу і закрити це питання разом з його надлишковою інфраструктурою. + +## Висновки + +Таким чином, поки хтось не оформить 5-10 серверів початковою вартістю хоч в одну акцію GitHub (в контексті Radicle), воно просто не функціональне, при всій моїй лояльності до напрямку p2p. + +Вже яку тисячу разів я стаю на ці граблі: ну немає ніякої свободи у цьому, суцільні обмеження, купа зайвого трафіку, обчислень і власного часу. Воно класно тільки тим, що зручно реплікується по хешу, але знову таки якщо задача децентралізувати то простіше руками чи скриптом розкидати на client/server. + +Все одно розподілені/анархічні ноди потребують сервер у якійсь цілком собі монархічній країні. Ще раз нагадування собі про те, що "втекти" кудись вирішує питання, у кращому випадку, тільки на деякий час. + +Сама пірингова технологія, все ще може бути корисною у спеціалізованих сферах, для яких власне вона і розроблялась. Додаткові можливості з повітря не беруться, а свої громадянські права і свободу - досі потрібно виборювати, на жаль. \ No newline at end of file diff --git a/post/hrybni-chornyla-dlia-kalihrafiyi.md b/post/hrybni-chornyla-dlia-kalihrafiyi.md new file mode 100644 index 0000000..8119a92 --- /dev/null +++ b/post/hrybni-chornyla-dlia-kalihrafiyi.md @@ -0,0 +1,26 @@ +# Грибні чорнила для каліграфії + +Поділюсь з гіками альтернативною опцією для блекаутів, тим паче що зараз сезон і потрібні матеріали доступні кожному у межах міста. + +В літню пору можна повернутись до письма на папері та самостійно приготувати чорнила з диких грибів! + +Готове чорнило - відтінку сепії з характерною фактурою мікро-волокон (не намагайтесь заправити картридж), +подібно водяним знакам, робить письмовий твір унікальним та неповторним! + +Приготувати чорнила просто: + +* збираємо десяток грибів Coprinopsis atramentaria до чистої банки +* залишаємо ємність закритою у темному місці кімнатної температури, допоки гриби не пустять достатньо соку +* відфільтровуємо сік марлею - він і буде пігментом для чорнильної суміші +* далі потрібно зібрати з дерев смолу (камідь) в якості згущувача - це може бути абрикоса або вишня +* смолу попередньо розмочуємо у воді, попередньо перемішуючи паличкою +* співвідношення смоли до пігменту визначається експериментально, залежить від інструменту і техніки письма + +Якщо пропорції підібрані правильно, суміш добре тримається на пері та не розтікається на папері. + +Після висихання, пігмент довго тримає колір, а напис лишається стійким до стирання навіть при потраплянні води. + +Описаний спосіб передбачає роботу з сирим матеріалом без консервації, +тому готовий твір можна вважати й артефактом певного сезону! + +*Матеріал може виходити за рамки основної тематики сайту, утім я бачу це способом на деякий час переключитись замість пошуку павербанку, записати свої думки на папері до їх публікації в мережі. Публікація на розсуд адміністрації :)* \ No newline at end of file diff --git a/post/htcount-knopka-lichylnyk-vidviduvachiv-saytu-na-bazi-access-log.md b/post/htcount-knopka-lichylnyk-vidviduvachiv-saytu-na-bazi-access-log.md new file mode 100644 index 0000000..6f345cd --- /dev/null +++ b/post/htcount-knopka-lichylnyk-vidviduvachiv-saytu-na-bazi-access-log.md @@ -0,0 +1,51 @@ +# htcount: кнопка-лічильник відвідувачів сайту на базі access.log + +[htcount](https://crates.io/crates/htcount) - системна утиліта, написана мовою Rust, що рахує кількість хостів / хітів на основі даних локального журналу `access.log` ([CLF](https://en.wikipedia.org/wiki/Common_Log_Format)) і виводить результати у вигляді дампу JSON/API або у форматі кнопки SVG для вставки на сайт. + +По суті, вона влаштована так само, як відомий [goaccess](https://goaccess.io/) - інструмент CLI, який дозволяє аналізувати Веб-трафік без підключення сторонніх сервісів типу Google Analytics. + +## Встановлення + +`htcount` написано мовою Rust, можливо, вам знадобиться спочатку розгорнути відповідну інфраструктуру для її збірки з початкового коду: +* [Встановлення останньої версії Rust в Linux](https://devzone.org.ua/post/vstanovlennia-ostannyoyi-versiyi-rust-v-linux) + +``` bash +git clone https://github.com/YGGverse/htcount.git && cd htcount +cargo build --release +sudo install target/release/htcount /usr/local/bin/htcount +``` +* встановлення до `/usr/local/bin/htcount` потрібне в контексті системних дозволів `systemd` + +### systemd + +``` /etc/systemd/system/htcount.service +#/etc/systemd/system/htcount.service +[Unit] +After=network-online.target +Wants=network-online.target + +[Service] +Type=simple +ExecStart=/usr/local/bin/htcount\ + # вкажіть актуальний шлях цільового журналу (див. також --ignore-host) + --source /var/log/nginx/access.log\ + # вкажіть актуальний шлях для розміщення кнопки + --export-svg /var/www/path/to/public/counter.svg\ + # я збирав пакунок від root, де в мене лежать оригінали у якості шаблону + --template-svg /root/htcount/default/counter.svg\ + # оновлення кнопки раз на годину (3600 секунд) + --update 3600\ + # оновлення відвідувачів раз на добу + --match-time %%d/%%b/%%Y\ + # дебаг у файл вимкнено для заощадження терміну служби SSD + --debug n +# дебаг вимкнено опцією --debug n, можна вказати шлях або драйвер +StandardOutput=null +# збираємо усі помилки до стандартного розташування системних журналів +StandardError=file:///var/log/htcount-error.log + +[Install] +WantedBy=multi-user.target +``` +* перелік доступних опцій залежить від актуальної версії: `htcount --help` +* для інтеграції кнопки на сайт, додайте відповідний код: `` \ No newline at end of file diff --git a/post/izoliatsiia-linux-vid-priamykh-internet-zyednan-na-bazi-qemu-virtual-machine-manager-i-vsock.md b/post/izoliatsiia-linux-vid-priamykh-internet-zyednan-na-bazi-qemu-virtual-machine-manager-i-vsock.md new file mode 100644 index 0000000..59afd12 --- /dev/null +++ b/post/izoliatsiia-linux-vid-priamykh-internet-zyednan-na-bazi-qemu-virtual-machine-manager-i-vsock.md @@ -0,0 +1,101 @@ +# Ізоляція Linux від прямих Інтернет з'єднань на базі QEMU / Virtual Machine Manager з VSOCK + +В матеріалі наведено приклад ізоляції віртуальної операційної системи Linux (та відповідно - усіх її програм) від вхідних та вихідних з'єднань в Інтернет, без додаткових правил в iptables. Зв'язок передбачається виключно засобами локальної мережі [Yggdrasil](https://devzone.org.ua/post/yggdrasil-mereza-z-detsentralizovanym-routynhom) з використанням технології [VSOCK](https://man7.org/linux/man-pages/man7/vsock.7.html), а Інтернет-інтерфейс - буде відсутній як такий. + +Кому потрібен доступ в Інтернет, це можна зробити окремо - засобами локальних проксі DNS та дзеркал репозиторіїв. Тобто, така система не буде "сліпою" та з певними доопрацюваннями буде здатна оновлюватись і взаємодіяти зі світом, але робити це без витоків через дірки софту та пропущені опції в конфігах. + +Нижче розглядається налаштування засобами GUI [Virtual Machine Manager](https://virt-manager.org), що зручно для десктоп образів. Користувачі CLI, зокрема системні адміністратори - можуть цей допис не читати. + +## VSOCK + +З документації: технологія VSOCK - забезпечує взаємодію між віртуальними машинами та вузлом, на якому вони виконуються. Це адресне сімейство використовується гостьовими агентами та службами супервізора, яким потрібен канал зв'язку, що не залежить від налаштувань мережі віртуальної машини. + +Тобто ми вимикаємо усі мережеві інтерфейси, а для підключення до Yggdrasil - будемо використовувати локальний сокет VSOCK, який розшарено з хосту на гостьову систему (або системи) для побудови його локального дерева маршрутизації. + +## Yggdrasil + +Так як конфігурація передбачає взаємодію виключно з мережею Yggdrasil, нам потрібно спочатку зібрати версію роутера, яка має підтримку протоколу VSOCK. Я віднайшов [форк](https://github.com/nagy/yggdrasil-go/tree/vsock), можливо, він втратить свою актуальність з часом або ж буде включений до апстріму в рамках [PR#1223](https://github.com/yggdrasil-network/yggdrasil-go/pull/1223). Як на хості, так і на гостьовій системах, збирається наступним чином: + +``` bash +git clone https://github.com/nagy/yggdrasil-go.git +cd yggdrasil-go +git checkout vsock +./build +# опціонально встановлюється за призначенням +# install yggdrasil /usr/local/bin/yggdrasil +# install yggdrasilctl /usr/local/bin/yggdrasilctl +``` +* тут передбачається, що у вас вже [встановлена актуальна версія Go](https://devzone.org.ua/post/vstanovlennia-ostannyoyi-versiyi-go-v-debian) + +Налаштування модифікованого роутера майже нічим не відрізняється від базового, за тим лише виключенням, що на хост машині (де запускаються гостьові віртуалки) потрібно додати прослуховування сокету: + +``` /etc/yggdrasil.conf +#/etc/yggdrasil.conf +Listen: [vsock://host:1234] +``` +* `host` - лишаємо саме таким (або можна вказати як ID `2`), порт можете вказати довільний + +Перед продовженням, важливо перезапустити роутер Yggdrasil, оскільки потрібно, щоб він забіндився на вказаному інтерфейсі `vsock://host:1234` + +## Virtual Machine Manager + +Тут вже може бути встановлено операційну систему, тому перед тим як продовжити, запустимо її та встановимо такий само форк Yggdrasil за інструкцією вище. + +Після встановлення роутера, Інтернет більше не потрібен, вимикаємо гостьову систему та повертаємось до налаштувань VM. Сторінка налаштувань (Show virtual hardware details) знаходиться там де значок лампи у вікні образу + +![Virtual Machine Manager: Show virtual hardware details](https://devzone.org.ua/storage/posts/2025/09/25/WVyu1jtyI0DGpwiLEtToTNV0Z8pfDZDd6ImEJbZK.png) + +звідки знаходимо NAT (або Route) інтерфейс і видаляємо через контекстне меню "Remove hardware" правим кліком миші; видаляємо всі "девайси" зі значком дво-направленої стрілки, що мають відношення до мережі + +![Virtual Machine Manager: Remove hardware](https://devzone.org.ua/storage/posts/2025/09/25/hh5UYe8XzhQfJXtwOCQIJb5wGhORuPoyFs6DOO2T.png) + +В низу вікна, натискаємо кнопку "Add hardware" + +![Virtual Machine Manager: Add hardware](https://devzone.org.ua/storage/posts/2025/09/25/p8zH6BBJptDGvc6zRYRhj7rMlomEk3T7hMUPDFBI.png) + +шукаємо там "VirtIO VSOCK" та додаємо його до конфігурації з відміченою галкою "Guest CID: Auto" + +![Virtual Machine Manager: VirtIO VSOCK](https://devzone.org.ua/storage/posts/2025/09/25/ENLOEhm1hupgPkIhSSRVBVEucb8QCZ7CRxK2GOKu.png) + +Тепер можна запустити гостьову систему кнопкою "Power on the virtual machine" і перемкнутись на режим перегляду: + +![Virtual Machine Manager: Power on the virtual machine / Show the graphical console](https://devzone.org.ua/storage/posts/2025/09/25/2jSwECe8bFHWD8loyEQPvfl3fvIzTqiBVYbNEnAV.png) + +**Наступні дії виконуються в середині гостьової машини!** + +Після завантаження, вже має бути відсутній Інтернет - так і треба, але якщо у вас Yggdrasil автоматично запускається з systemd, його інтерфейс (стандартно `tun0`) має бути активним: + +``` bash +ifconfig + +# lo +# tun0 - Yggdrasil +``` +* тут взагалі я оголошую `IfName` як `yggdrasil` явно, замість стандартного `auto`, щоб випадково не переплутати з динамічними VPN що також ймовірно буде на `tun*` + +Нагадаю, форк роутера з підтримкою VSOCK для гостьової системи ми вже встановили раніше, тож додамо наш батьківський `vsock://host:1234`, але цього разу не в `Listen`, а в `Peers`: + +``` /etc/yggdrasil.conf +#/etc/yggdrasil.conf +Peers: [vsock://host:1234] +``` +* якщо у вас там якісь інші сторонні конекти, можна їх закоментувати - вони все одно більше не працюватимуть. + +Перезапускаємо сервіс Yggdrasil та перевіряємо підключення до сокету: + +``` bash +sudo yggdrasilctl getpeers +``` + +вивід має бути приблизно таким: + +``` bash +URI State Dir IP Address Uptime RTT RX TX Down Up Pr Cost Last Error +vsock://host:1234 Up Out 200:xxxx:.. 2m59s 1.61ms 1.8KB 0.6KB - - 0 1 - +``` + +Оверлейний пір батьківського хосту підключено, а отже тепер можна підключатись до всіх вузлів мережі Yggdrasil, без наявності Інтернет в гостьовій системі. Тобто можна безпечно качати торенти з увімкненими DHT, PEX, якщо ви десь в ЄС або просто приватно сьорфити локальну мережу без жодних додаткових [налаштувань софта типу PAC для Firefox](https://devzone.org.ua/post/bezpechnyy-perehliad-saytiv-yggdrasil-z-yggstack). + +Про те, як налаштувати локальний DNS і репозиторії, щоб зробити систему здатною до оновлень (через локальні проксі) - згодом. Стосовно DNS, я частково описував свою конфігурацію в матеріалі [Мій пресет Alfis DNS на роутері Yggdrasil / Mycelium](https://devzone.org.ua/post/miy-preset-alfis-dns-na-routeri-yggdrasilmycelium), звідки можна взяти приклади, замінивши Інтернет резольвери на [локальні](https://yggdrasil-network.github.io/services.html#dns). + +Взагалі думки такі, що було б не погано мати готовий образ системи, що адаптований для роботи виключно засобами певних локальних мереж. Але це вже інша історія. \ No newline at end of file diff --git a/post/keruvannia-pamiattiu-v-gtk.md b/post/keruvannia-pamiattiu-v-gtk.md new file mode 100644 index 0000000..0f3e380 --- /dev/null +++ b/post/keruvannia-pamiattiu-v-gtk.md @@ -0,0 +1,380 @@ +# Керування пам’яттю в програмах GTK + +Доповнений переклад PDF документу [CSci493.70 "Introduction to Memory Management in GTK+"](https://www.cs.hunter.cuny.edu/~sweiss/course_materials/csci493.70/lecture_notes/GTK_memory_mngmt.pdf) Стюарта Вайса - доцента +Кафедри інформатики Гантерського коледжу Міського університету, Нью-Йорк. + +## Нотатки + +* Термін GTK+ було змінено на більш сучасний варіант назви - GTK +* Переклад включає локальну адаптацію: корекцію семантики, мої доповнення та скорочення (де це можливо) також видалені не робочі посилання +* Передбачається, що користувач володіє мінімальним досвідом програмування C++ та розуміє принцип роботи об'єктів, поінтерів та посилань + +## Вступ до керування пам'яттю в GTK + +Метою цієї статті є допомога в розумінні того, як саме керується пам'ять у GTK, щоб ваші програми не мали витоків пам'яті і не завершували роботу аварійно. + +В основному, існують лише два способи, якими ви можете зіпсувати програму стосовно того, як вона працює з пам'яттю: + +* програма не звільняє пам'ять, яка їй більше не потрібна, що призводить до витоку (наприклад, використання `new` без `delete`) +* програма намагається звернутися до об'єкта, який більше не існує за адресою або цю адресу було змінено іншим компонентом програми + +### Витоки пам'яті + +Типовим випадком для витоку пам'яті є створення нових об'єктів на кучі (heap) без подальшого їх видалення після використання: + +``` cpp +MyClass* object = new MyClass; + +// попрацювали... + +// delete object; < забули +``` + +Можна подумати, що сьогодні не так вже й важливо запобігати витокам пам'яті. Адже на сучасних комп'ютерах її в досталь, а операційна система сама очистить виділені ресурси після завершення програми. Але якщо ви намагаєтеся бути професіоналом у своїй роботі, то не повинні використовувати більше пам'яті, ніж потрібно, оскільки це знижує продуктивність самої програми, а також впливає на продуктивність системи та енергоспоживання пристрою. Тим паче, якщо витік буде достатньо масштабним, то аварійно зупиниться не тільки програма, але й операційна система! + +### Недійсні посилання + +Недійсні посилання неминуче призводять до збоїв, і їх однозначно потрібно уникати. + +Звісно, вам не потрібно турбуватися, якщо ваша програма ніколи не виділяє пам'ять динамічно - у разі, якщо кожна змінна зберігається в стеку (під час виконання певної області видимості), або в статичній пам'яті (знаходиться в глобальній області видимості), тоді зв'язок адреси зі змінною є фіксованим. + +Проте в GTK практично все, що створює програма, робиться через вказівники: віджети, списки та дерева, рядки тощо. І це не спроста, тому що такий підхід дозволяє не створювати зайві копії об'єктів та не розподіляти для них нову пам'ять - що значно прискорює швидкість та економічність програми. + +Розглянемо простий приклад створення віджету кнопки: + +``` cpp +GtkWidget *button; +button = gtk_button_new(); +``` + +* тут бачимо, що пам'ять для кнопки виділяється засобами GTK, а функція `gtk_button_new()` лише повертає вказівник на область пам'яті, де було збережено цей об'єкт. Таким чином, ваша програма отримує повний доступ до області пам'яті через цей вказівник, і тому існує ймовірність, що ви можете зіпсувати її роботу, якщо не розумієте, як саме керується пам'ять на рівні фреймворку GTK. + +## Облік посилань + +Якщо ви зрозумієте, що таке облік посилань в C++, у вас не буде жодних проблем з керуванням пам'яттю. Якщо ж ні - то ідея в основному схожа з тим, яким чином UNIX керує дисковим сховищем для файлів для ефективного використання дискового простору. Якщо ви не знайомі з цим, то зараз саме час розібратися. + +### Приклад посилань I-node + +В системах UNIX, файл представляється так званою і-нодою (i-node, або в перекладі - вузли) +По суті, це структура мета-даних, що містить всю важливу інформацію про фізичний файл: + +* хто є його власником +* хто має право робити з ним +* який його розмір +* де розташовані його блоки, а також багато іншого. + +І-нода також містить лічильник посилань для кожного екземпляру імені файлу. Таким чином, одні й ті само фізичні файли можуть мати багато імен, в одному або різних каталогах і навіть файлових системах, при цьому дані самого файлу зберігаються в єдиному екземплярі. + +Уявімо, хтось інший створює ім'я для файлу в іншому місці, лічильник посилань збільшиться. Кожного разу, коли ім'я видаляється, лічильник посилань зменшується. Якщо лічильник посилань досягає нуля, i-нода видаляється, а також звільняється пам'ять файлу. + +Користувачам Windows буде простіше зрозуміти суть посилань на прикладі ярлика програми - який може мати багато екземплярів у різних розташуваннях але при цьому посилатись на один і той само об'єкт - з робочого столу, меню чи будь якої іншої теки. Проте саме приклад і-ноди в UNIX описує, що являють собою посилання в C++ та їх лічильники в GTK. + +Дізнатись більше про і-ноди в UNIX можна [тут](https://uk.wikipedia.org/wiki/Inode) і [тут](https://acode.com.ua/inodes-in-linux). + +### Організація посилань в GTK + +В об'єктах GTK також використовується облік посилань. У той час як в UNIX лічильник посилань - це кількість імен, які має файл, то в GTK це кількість власників об'єкта. + +Таким чином, кожен новий об'єкт GTK створюються з лічильником посилань і спочатку дорівнює одному. Кожного разу, коли створюється нове посилання на об'єкт, його лічильник посилань збільшується. Коли посилання видаляється, лічильник зменшується. Якщо лічильник досягає нуля, пам'ять об'єкта звільняється. Акт звільнення пам'яті, пов'язаної з об'єктом, називається фіналізацією об'єкта. + +Концепція була б досить простою для розуміння, якби не той факт, що існує два різних типи об'єктів: + +* ті, що походять від `GInitiallyUnowned` +* ті, що не походять + +Нагадаємо, що верхня частина ієрархії об'єктів виглядає так: + +``` +GObject + +----GInitiallyUnowned + +----GtkObject + +----GtkWidget +``` + +На прикладі вище можемо бачити, що `GtkWidget` походить від `GtkObject`, який, у свою чергу, походить від `GInitiallyUnowned`. Ц означає, що кожен віджет непрямо походить від `GInitiallyUnowned`. + +Що ж, можливо, ви запитаєте, що ж таке `GObject`, який не є `GInitiallyUnowned`? +Цей список досить довгий і включає загальновживані об'єкти, зокрема такі: + +* GtkAction +* GtkListStore +* GtkPrinter +* GtkStyle +* GtkTextBuffer +* GtkTextMark +* GtkTextTag +* GtkTreeSelection +* GtkTreeStore +* GtkUIManager + +Саме об'єкти, які походять від `GObject`, стануть нашою відправною точкою, оскільки їх легше зрозуміти. + +## Прямі нащадки GObject + +Об'єкти, що напряму походять від `GObject`, створюються з початковим лічильником посилань, рівним одному. +Тож давайте створимо декілька таких об'єктів, щоб продемонструвати їх принцип роботи. + +`GtkListStore` використовується в GTK доволі часто, наприклад з віджетами дерева у форматі рядків і стовпців, подібно до того, як це робить файловий браузер. + +Створити його можна наступним чином: + +``` c +GtkListStore* store = gtk_list_store_new(); +``` + +Функція створює об'єкт та повертає нам вказівник в змінну `store` з лічильником посилань, рівним одному. + +Документація [зазначає](https://docs.gtk.org/gtk4/ctor.ListStore.new.html#return-value): +> The caller of the function takes ownership of the data, and is responsible for freeing it. + +Це значить, що створений таким чином об'єкт - володітиме посиланням, тому лічильник посилань для `store` можна збільшити, викликавши `g_object_ref()`: + +``` c +g_object_ref(G_OBJECT( store ) ); +``` + +відповідно, щоб зменшити лічильник об'єкта `store`, потрібно викликати: + +``` c +g_object_unref( G_OBJECT( store ) ); +``` + +Уявімо, що у нас є певний об'єкт дерева `treeview`, який вже було створено раніше. + +Спробуємо додати до нього наш список `store`: + +``` c +gtk_tree_view_set_model(GTK_TREE_VIEW(treeview), GTK_TREE_MODEL(store)); +``` + +Після того, як функція `gtk_tree_view_set_model()` повернулася, віджет дерева `treeview` відтепер тепер буде володіти посиланням на `store`. + +Такі методи як `gtk_tree_view_set_model()` - набувають право власності на об'єкт і самостійно збільшують лічильник посилань об'єкта, використовуючи `g_object_ref()` у своїх реалізаціях. Якщо б вони цього не робили, то програма коли-небудь викликала `g_object_unref()`, лічильник посилань знизився б до нуля і був би фіналізований. У цей момент з'явилося б посилання на недійсну пам'ять, і при наступній спробі доступу до даних програмою - стався б збій. Отже, після того, як список зберігань був доданий до віджета дерева, його лічильник посилань становитиме два. + + +Спосіб, яким `GtkTreeView` робить `GtkListStore` тим, що він відображає, подібний до того, як віджет `GtkTextView` робить текстовий буфер тим, що він відображає, використовуючи `gtk_text_view_set_buffer()`: + +``` c +gtk_text_view_set_buffer(GTK_TEXT_VIEW(textview), GTK_TEXT_BUFFER(buffer)); +``` + +Оскільки текстовий віджет набуває право власності на `buffer`, `gtk_text_view_set_buffer()` збільшує лічильник посилань `buffer`. Якщо `buffer` був створений якою-небудь іншою частиною коду, яка набула право власності під час створення, лічильник посилань `buffer` становитиме два, що вказує на те, що `buffer` має двох власників (текстові буфери можуть бути спільними для кількох віджетів текстового перегляду) + +Повернемося до обговорення нашого списку зберігань `store`. Уявімо, що нашій програмі більше не потрібно отримувати доступ до списку зберігань, як тільки він буде доданий до віджета дерева. Адже увесь доступ до списку зберігань тепер відбувається через віджет дерева. Тому, як тільки вона викликала `gtk_tree_view_set_model()`, вона повинна відмовитися від свого посилання та зменшити лічильник посилань за допомогою `g_object_unref()` таким чином: + +``` c +gtk_tree_view_set_model(GTK_TREE_VIEW(treeview), GTK_TREE_MODEL(store)); +g_object_unref(G_OBJECT(store)); +``` + +Це забезпечує зниження лічильника посилань до одного. Тому, коли віджет дерева `treeview` буде знищено і він звільнить усі свої дочірні об'єкти, лічильник посилань `store` стане нульовим, і він також буде фіналізований. + +Підсумовуючи, коли ваша програма створює об'єкт, що безпосередньо походить від `GObject`, як тільки вона приєднала його до певного об'єкта або віджета GTK, який припускає право власності, ви повинні викликати `g_object_unref()` на об'єкті, який вам більше не потрібен. + +## Об'єкти, що походять від GInitiallyUnowned + +Ситуація зовсім інша для об'єктів, що походять від `GInitiallyUnowned`. Усі такі об'єкти створюються з плаваючим лічильником посилань, рівним одному. Плаваюче посилання - це посилання, яке не належить нікому. Саме тому клас називається `GInitiallyUnowned`, оскільки спочатку об'єкти цього класу не мають власника. Плаваюче посилання можна розглядати як особливий вид посилання, яке не асоційоване з жодним власником. Технічно це прапорець у структурі `GObject` який вказує, чи є початкове посилання плаваючим чи ні; коли він встановлений, об'єкт має плаваюче посилання, а коли він очищений, посилання є неплаваючим. + +Необхідність плаваючих посилань має дві причини. Одна з причин полягає в тому, що вони є способом зберегти об'єкти після їх створення, але до того, як вони будуть приєднані до батьківських контейнерів. Інша причина полягає у тому, як функції можуть бути викликані в C. + +Розглянемо наступний код: + +``` c +container = create_container(); +container_add_child (container, create_child()); +``` + +Припустимо, що `container` - це якийсь тип контейнерного віджета, а `container_add_child()` додає дочірній віджет до даного контейнера а також додає посилання на даний дочірній об'єкт, який у цьому випадку є об'єктом, створеним викликом `create_child()`. + +Припустимо, що плаваючих посилань не існує і що `create_child()` створює об'єкт з лічильником посилань, рівним одному. Оскільки контейнер набуває посилання на об'єкт і збільшує лічильник посилань, після виклику, дочірній об'єкт матиме лічильник посилань рівний двом. + +Однак, оскільки значення, що повертається `create_child()`, не присвоюється жодному об'єкту, немає об'єкта, на якому можна викликати `g_object_unref()`, щоб зменшити лічильник до одного. Коли контейнер знищується, він може "звільнити" дочірній об'єкт, але лічильник дочірнього об'єкта знизиться до одного, а не до нуля, і це призведе до витоку пам'яті, оскільки пам'ять дочірнього об'єкта не може бути звільнена, поки лічильник посилань не досягне нуля. + +Щоб запобігти витоку пам'яті за відсутності плаваючих посилань, можна використати наступний код: + +``` c +Child *child; +container = create_container(); // container ref-count = 1 +child = create_child(); // child ref-count = 1 +container_add_child (container, child); // child ref-count = 2 +g_object_unref (child); // child ref-count = 1 +``` + +Різниця тут полягає в тому, що результат `create_child()` зберігається в `child`, тому до нього можна застосувати `g_object_unref()`. Однак, оскільки можливо написати код у C і в першому варіанті, бібліотека `GObject` була спроектована так, щоб такий код працював правильно. + +Ідея плаваючих посилань дозволяє наведеному коду бути вільним від витоків пам'яті, але лише якщо контейнер має можливість набути право власності та перетворити плаваюче посилання на стандартне посилання, яке називається звичайними посиланням. + +Відмінність наступна: + +* функція, яка створює віджет, така як `create_child()` вище - створює його з плаваючим лічильником посилань рівним одному. Тобто таким, що спочатку не має власника, а не зі стандартним лічильником посилань, рівним одному. +* функція, яка додає дочірній віджет до контейнера, така як `container_add_child()` вище - не викликає `g_object_ref()` на дочірньому об'єкті, оскільки це не очистить прапорець плаваючого посилання. Натомість вона викликає `g_object_ref_sink()` + +``` c +g_object_ref_sink(object); +``` + +Функція `g_object_ref_sink()` існує для того, щоб, коли віджет додається до батьківського контейнера, контейнер міг зробити дві речі: + +* видалити плаваюче посилання +* набути власне стандартне посилання на віджет + +Вона працює наступним чином: якщо `object` мав плаваюче посилання, то викликаючи її: + +* програма володіє посиланням на `object` +* лічильник посилань залишається не змінним +* прапорець плаваючого посилання очищується + +Якщо `object` не мав плаваючого посилання, то `g_object_ref_sink()` має той само ефект, як і `g_object_ref()`, а саме - збільшує лічильник посилань. + +Функціонально, операцію "Sink" можна розглянути як: + +``` c +if ( was_floating(object) ) + clear( object->floating_flag ); +else + g_object_ref(object); +``` + +Тепер повернемось до прикладу вище: + +``` c +container = create_container(); +container_add_child (container, create_child()); +``` + +`create_child()` створює віджет з плаваючим лічильником посилань, рівним одному, а функція `container_add_child()` викликає `g_object_ref_sink()` на анонімному дочірньому віджеті (як об'єкті, звичайно), набуваючи право власності на нього, що надає йому стандартний лічильник посилань, рівний одному. Коли контейнер знищується, він звільнить дочірній об'єкт, зменшуючи його лічильник до нуля, що призведе до фіналізації дочірнього об'єкта без необхідності для програми викликати `g_object_unref()` на дочірньому об'єкті після `container_add_child()`. + +Усі віджети, за винятком вікон верхнього рівня, починають своє життя з плаваючим посиланням. Вікна верхнього рівня відрізняються, оскільки вони ніколи не поміщаються в контейнери - вони є коренями дерев контейнерів. Коли вікно верхнього рівня створюється, бібліотека GTK відразу ж "зливає" його плаваюче посилання і набуває право власності на нього, тому, коли воно передається вашій програмі, його стандартний лічильник посилань становитиме один. + +Якщо програма створює віджет, який не є вікном верхнього рівня, то в якийсь момент його, ймовірно, буде упаковано в контейнер. Усі функції, які упаковують віджети в контейнери, автоматично "зливає" плаваюче посилання віджета і надають контейнеру право власності на дочірній об'єкт. Таким чином, коли батьківський віджет зрештою отримує сигнал знищення і звільняє свої дочірні об'єкти, їх стандартні лічильники посилань знизяться до нуля, і вони будуть фіналізовані (якщо в програмі, з тієї чи іншої причини, код не викликає `g_object_ref()` на будь-якому з дочірніх об'єктів без відповідного` g_object_unref()`, що призведе до витоку пам'яті!). + +Щоб було зрозуміло, коли ваша програма створює віджет типу `foo`, використовуючи конструктор, такий як `gtk_foo_new()`, і додає цей віджет до контейнера, їй ніколи не потрібно робити нічого іншого для керування пам'яттю віджета; GTK подбає про звільнення пам'яті, коли віджет буде знищено. + +Спробуймо розкрити суть на прикладі: + +``` c +GtkWidget *gadget = gtk_gadget_new (); +gtk_widget_destroy (gadget); +``` + +Можна очікувати, що конструкція просто створить `gadget`, виділяючи пам'ять для нього, а потім відразу ж її звільнить. На перший погляд це здається логічним. Але насправді це призведе до витоку пам'яті! + +Це тому, що коли `gadget` створюється, він має лише плаваюче посилання, а виклик `gtk_widget_destroy()` еквівалентний конструкції: + +``` c +gtk_object_destroy (GTK_OBJECT(gadget)); +``` + +Документація для `gtk_object_destroy` стверджує, що: + +> Пам'ять для самого об'єкта не буде видалена, поки його лічильник посилань фактично не знизиться до нуля; `gtk_object_destroy()` лише просить власників посилань звільнити свої посилання, вона не звільняє об'єкт. + +Іншими словами, `gtk_widget_destroy()` не звільняє об'єкт; вона лише просить власників посилань відпустити свої посилання. Оскільки `gadget` не був упакований у жоден контейнер, а отже `g_object_ref_sink()` - не був викликаний на ньому, він не належить жодному власнику посилань, і тому `gtk_widget_destroy()` не звільнить пам'ять, що утримується `gadget`. В результаті виникає витік пам'яті. + +Звичайно, немає жодної причини писати код таким чином. Але в разі якоїсь причини, коли ви хочете створити віджет, а потім знищити його і звільнити пам'ять, яку він утримує (не додаючи його до контейнера) вам потрібно буде звільнити його самостійно: + +``` c +GtkWidget *gadget = gtk_gadget_new(); +g_object_ref_sink(G_OBJECT(gadget)); // перетворити плаваюче посилання на стандартне +gtk_widget_destroy(gadget); // знищити зовнішні посилання +g_object_unref(G_OBJECT(gadget)); // зменшити лічильник посилань до 0 +``` + +Знову таки, немає особливої причини створювати віджет, який ви не маєте наміру додавати до контейнера, за винятком вікна верхнього рівня. Метою попереднього прикладу лише пояснення, як працює облік посилань стосовно об'єктів, таких як віджети, зокрема які походять від `GInitiallyUnowned`. + +## Типи рядків GLib + +Існують різні типи рядків, які можна використовувати в програмі GTK на мові C: + +``` c +char *string1; // стандартний рядок C +gchar *string2; // ідентичний стандартному рядку C, оскільки gchar є типом даних для char +GString string3; // розширення рядків C, яке може автоматично зростати +``` + +При використанні стандартних функцій рядків, таких як `g_strdup()`, `g_strnfill()`, або `g_strdup_printf()`, загальне правило полягає в тому, якщо документація говорить, що функція повертає новостворений рядок, то повернутий рядок слід звільнити за допомогою `g_free()`. + +Зазвичай документація чітко вказує, що повернутий рядок повинен бути звільнений за допомогою `g_free()`. Якщо жодне з цих тверджень не міститься, то не викликайте `g_free()` на рядок, якщо не хочете, щоб програма зазнала збою. + +### Об'єкти GString + +Об'єкт `GString` по суті є структурою, пам'яттю якої керує бібліотека `GLib`: + +``` c +typedef struct { + gchar *str; + gsize len; + gsize allocated_len; +} GString; +``` + +Внутрішній вказівник `str` є адресою текстового буфера і може переміщатися в міру зростання та зменшення рядка. + +Об'єкт `GString` створюється за допомогою однієї з функцій сімейства `g_string_new()`. Після роботи з об'єктом, для вивільнення пам'яті використовується функція `g_string_free()`. Якщо ви хочете, щоб сама структура та вміст її буфера були звільнені, то виклик буде таким: + +``` c +gchar* buf = g_string_free(string, TRUE); +``` + +* другий аргумент вказує, чи буде звільнено також дані рядка, або лише обгорткова структура, тобто якщо передано `TRUE`, значення, що повертається, буде `NULL` але якщо другий аргумент - `FALSE`, то буфер не буде звільнений, і значення, що повертається - буде вказівником на буфер, який потрібно звільнити, коли код закінчить роботу з ним, використовуючи `g_free()` + +## Структури GdkPixbuf + +Структури `GdkPixbuf` безпосередньо походять від `GObject` і відповідно - реалізують облік посилань. Вони створюються з лічильником посилань, рівним одному. Програма може ділитися одним піксельним буфером `pixbuf` з багатьма частинами учасниками. Коли певна частина програми має утримувати вказівник на піксельний буфер, вона повинна додати посилання на нього, викликавши `g_object_ref()`. Коли вона закінчить з піксельним буфером, їй слід викликати `g_object_unref()`, щоб зменшити лічильник посилань. Піксельний буфер буде знищено, коли його лічильник посилань знизиться до нуля. Лічильник посилань загалом повинен бути рівним кількості різних вказівників на об'єкт. + +* діалоги не додаються до контейнерів, але знищуються за допомогою `gtk_widget_destroy()`, і це відбувається тому, що діалоги є вікнами верхнього рівня, а GTK має посилання на них. Сигнал знищення викликає у GTK виклик `unref` на діалозі, звільняючи його пам'ять. + +Правила, описані вище для рядкових типів C, загалом застосовуються і до `pixbuf`. Деякі функції, які повертають вказівник на `GdkPixbuf`, акцентують у своїй документації той факт, що вони повертають новий об'єкт `pixbuf` з лічильником посилань (що дорівнює одному). Якщо ви використовуєте таку функцію, то ви повинні викликати `g_object_unref()` після того, як закінчите використовувати новозалучений `pixbuf`. Якщо `pixbuf` використовувався як джерело для створення нового `pixbuf` (у випадку з `gdk_pixbuf_add_alpha()`, який створює модифіковану версію `pixbuf`, і оригінал більше не потрібен) то потрібно викликати `g_object_unref()`, щоб відмовитися від володіння і зменшити лічильник посилань. + +Останнє, про що вам потрібно знати щодо `pixbuf`, це те, що якщо ви використовуєте `gdk_pixbuf_new_from_data()` для створення `pixbuf` з даних, які вже зберігаються в пам'яті (наприклад, масив значень пікселів) то вам також може знадобитися створити функцію, яка "знає, як" звільнити пам'ять, що належить цим даним, коли лічильник посилань на `pixbuf` зменшується до нуля. + +## Списки і деревоподібні сховища + +Інтерфейс `GtkTreeModel` визначає загальний інтерфейс дерева для використання віджетом `GtkTreeView`. Це абстрактний інтерфейс, який розроблений для використання з будь-якою структурою даних, яка йому відповідає. + +`GtkTreeStore` та `GtkListStore` - це вже реалізовані моделі дерева GTK. Вони надають структуру даних, а також усі відповідні інтерфейси дерева. Заповнення їх даними вимагає використання або методу `gtk_list_store_set()`, або методу `gtk_tree_store_set()` відповідно. Існують варіації цих двох функцій, але зауваження нижче є вірними незалежно від того, яка варіація використовується. + +Обидві функції `gtk_list_store_set()` і `gtk_tree_store_set()` приймають змінну кількість аргументів, які по суті є впорядкованими парами, як у прикладі нижче: + +``` c +void gtk_tree_store_set (GtkTreeStore *store, + GtkTreeIter *iter, + ..., -1); +``` + +Відсутніми аргументами є пари (`column_id`, `value`), де `column_id` - це ціле число, а `value` може бути будь-якого типу: рядком, цилим числом, `pixbuf` або вказівником на довільні структури. Що потрібно знати, так це те, чи передані цій функції дані копіюються у рядок сховища або вони доступні за посиланням. + +В наступному прикладі, питання полягає в тому, чи фактичні рядкові дані (які зберігаються в змінній `name`) копіюються в рядок, на який вказує ітератор, чи в рядок копіюється саме вказівник на `name`: + +``` c +gchar name[] = "Groucho"; +gtk_tree_store_set (store, &iter, NAME_ID, name, -1); +``` + +Коротка відповідь: вам не потрібно турбуватися про виділення та звільнення пам'яті для даних, які потрібно зберігати, оскільки підсистема GLib/GObject `GType` і `GValue` автоматично займається більшістю керування пам'яттю. Наприклад, якщо ви зберігаєте рядок у стовпці рядка, модель зробить копію рядка і зберігатиме цю копію. Якщо ви пізніше зміните стовпець на новий рядок, модель автоматично звільнить старий рядок і знову зробить копію нового рядка та зберігатиме цю копію. + +Довга відповідь полягає в тому, що коли дані додаються до списку або дерева за допомогою будь-якої з функцій `gtk_*_store_set()` те, як вони обробляються GTK, залежить від того, до якої з трьох категорій даних вони належать: + +1. Якщо дані є `GObject` (тобто об'єктом, що безпосередньо походить від `GObject`), то сховище бере на себе право власності на нього, викликаючи `g_value_dup_object()` для об'єкта та відмовляючись від права власності на раніше утримуваний об'єкт, якщо він замінює старе значення. + +2. Якщо дані є простим скалярним типом даних, таким як числовий, логічний або перерахунковий тип, або вказівником, то сховище робить копію даних. Зверніть увагу, що копіюється вказівник, а не дані, на які він вказує. + +3. Якщо дані є рядком або упакованою структурою, то сховище дублює рядок або упаковану структуру та зберігає вказівник на неї. Якщо дані замінюють існуючі дані, то в цьому випадку рядок або упакована структура спочатку звільняються за допомогою `g_free()` або `g_boxed_free()` відповідно (`GBoxed` - це механізм обгортки для довільних структур C, вони обробляються як непрозорі шматки пам'яті) + +Це може залишити вас з питаннями як GTK обробляє `pixbuf`. Як зазначалося вище, `pixbuf` безпосередньо походить від `GObject` і підпадає під першу категорію. Коли ви отримуєте дані зі сховища за допомогою `gtk_tree_model_get()` або будь-якої з його варіацій, вам потрібно бути обізнаним про те, як GTK обробляє різні типи даних, щоб знати, чи потрібно звільняти пам'ять, коли ви закінчите з ними: + +1. Якщо отримувані дані є `GObject`, функція збільшує його лічильник посилань, оскільки вона надає ще одне посилання на нього для нас. +2. Якщо дані є простим скалярним типом даних, таким як числовий, логічний або перерахунковий тип, або вказівником, функція робить копію і повертає цю копію. +3. Якщо дані є рядком або упакованим типом, функція копіює їх і повертає вказівник на дані. + +Це означає, що потрібно викликати `g_object_unref()` для об'єктів, отриманих зі сховища, коли роботу з ними завершено, а також - звільнити дані, які ми отримали зі сховища, якщо це рядкові або упаковані типи даних (використовуючи `g_free()` або `g_boxed_free()` відповідно). + +Щодо `pixbuf`, то такі дані належать до першої категорії, тому спочатку необхідно відмовитися від права власності, отримане зі сховища за допомогою `gtk_tree_model_get()`, за допомогою `unref`. Усі інші дані не потребують додаткового керування пам'яттю. + +## Висновки + +Коли ви налагоджуєте свою програму, хорошою ідеєю буде увімкнення системного монітора та спостереження за використанням віртуальної та фізичної пам'яті програми. Якщо ви бачите, що використання пам'яті постійно зростає під час роботи програми, це означає, що у вас десь є витік пам'яті. Моя порада - повторювати дії, такі як багаторазове натискання на один і той же пункт меню, щоб виявити, що є винуватцем. + +Якщо ви звільнили пам'ять, яку не повинні були звільняти, це буде очевидно, оскільки програма аварійно завершить роботу. Вам слід переглянути звіт про помилку, виданий вашим менеджером вікон (наприклад, Gnome), і подивитися на трасу стека. Починаючи з верхньої частини стека, спускайтеся, поки не знайдете першу функцію, код якої належить вам, а не бібліотеці GTK. Саме там знаходиться код, який спричинив проблему. + +Існують більш формальні методи виявлення витоків пам'яті, але попередній підхід є відносно простим у виконанні і не вимагає вивчення нового інструменту. Якщо ви хочете навчитися користуватися спеціалізованим інструментарієм, спробуйте [Valgrind](http://valgrind.org/) - безкоштовне програмне забезпечення з відкритим кодом для аналізу та керування пам'яттю. \ No newline at end of file diff --git a/post/keruvannia-pamiattiu-v-gtkmm-40.md b/post/keruvannia-pamiattiu-v-gtkmm-40.md new file mode 100644 index 0000000..06891fd --- /dev/null +++ b/post/keruvannia-pamiattiu-v-gtkmm-40.md @@ -0,0 +1,142 @@ +# Керування пам'яттю в gtkmm-4.0 + +[gtkmm](https://www.gtkmm.org) - це вільний об'єктно-орієнтований C++ інтерфейс для популярного GUI фреймворку [GTK](https://gtk.org), що поширюється за ліцензією LGPL. + +Основне призначення `gtkmm` полягає в отриманні усіх переваг ООП: +* наслідуванні класів GTK +* скороченні конструкцій коду програми (порівняно з ієрархічною реалізацією аналогічної функціональності засобами C) +* вбудованих інструментів перевірок та корекції типізації нащадків дерева віджетів та багато іншого. + +Оскільки це абстрагований прошарок над стандартною бібліотекою GTK, при його використанні, може виникнути питання як саме працювати з пам'яттю відповідно документації, адже API `gtkmm` має певні поліморфічні відмінності, без розуміння яких, є ризик витоку пам'яті або помилок при зверненнях. + +Даний матеріал являє собою доповнений переклад офіційної сторінки [Chapter 25. Memory management](https://gnome.pages.gitlab.gnome.org/gtkmm-documentation/chapter-memory.html), який допоможе краще зрозуміти базові принципи роботи перед початком роботи з фреймворком: не писати зайві велосипеди для керування пам'яттю і не отримати замість переваг - segmantation fault. + +Переклад базується на двох окремих статтях, які представлені розділами: + +* Робота з віджетами ([Widgets](https://gnome.pages.gitlab.gnome.org/gtkmm-documentation/chapter-memory.html#sec-memory-widgets)) +* Робота зі спільними ресурсами ([Shared resources](https://gnome.pages.gitlab.gnome.org/gtkmm-documentation/sec-memory-shared-resources.html)) + +## Робота з віджетами + +*Важливою відмінністю `gtkmm-4.0` у [порівнянні зі старішими версіями](https://gnome.pages.gitlab.gnome.org/gtkmm-documentation/changes-gtkmm4.html) є те, що знищення батьківського віджета GTK більше не призводить до знищення його дочірнього віджета, коли той знаходиться в його контейнері. Якщо ви покладалися на таку поведінку в старішій версії, то відтепер, в `gtkmm-4.0` потрібно спочатку видалити дочірній віджет, наприклад, через деструктор.* + +Робота з фреймворком GTK здебільшого передбачає створення та видалення контейнерів та їх дочірніх віджетів. Відповідно, розробник повинен самостійно контролювати виділення пам'яті, а також її очищення після того, як певний її віджет більше не використовується. + +Бібліотека `gtkmm` надає додаткові інструменти, які спрощують таку роботу. + +Нижче розглянемо, які способи керування пам'яттю взагалі доступні в `gtkmm` + +### Класичне керування пам'яттю C++ + +Загалом, `gtkmm` дозволяє програмісту контролювати тривалість життя будь-якого віджета так само, як і будь-якого іншого об'єкта C++. Ця гнучкість дозволяє використовувати: + +* оператори `new` і `delete` для створення та знищення динамічних об'єктів +* звичайні деструктори (які виконуються автоматично, коли клас знищується) +* локальні екземпляри об'єктів (які знищуються, коли екземпляр виходить з області видимості) + +Існують й інші способи, але розглянемо тільки деякі з них нижче. + +#### Віджети в області класу + +Якщо не потрібне динамічне виділення пам'яті, можна використовувати дочірні віджети в області батьківського класу. Однією з переваг таких віджетів є те, що керування пам'яттю зосереджено локально. Програма не ризикує отримати витоки пам'яті тому, що дані зберігаються на стеку. + +``` cpp +#include +#include +class Foo : public Gtk::Window +{ +private: + Gtk::Button theButton; // буде знищений, коли об'єкт Foo буде знищений +}; +``` + +Недоліком використання віджетів в області класу - є розкриття реалізації класу раніше, ніж його інтерфейсу в заголовковому файлі. + +#### Віджети в області функції + +Якщо програмісту не потрібен віджет в області всього класу, можна також використовувати віджет в локальній області функції. Перевагою такого підходу полягає у підвищеній прихованості даних та зменшенні залежностей: + +``` cpp +{ + Gtk::Button aButton; + aButton.set_visible(true); + ... + app->run(); +} +``` + +Однак ця техніка рідко буває корисною. Більшість віджетів не можуть бути безпечно створені до того, як програма буде зареєстрована або активована. Їх також не можна безпечно видалити після того, як програму було активовано типовими для GTK 4 засобами `Gtk::Application::run()` або `Gtk::Application::make_window_and_run()`. + +#### Динамічна алокація з `new` і `delete` + +Доволі часто, у програмах використовується динамічна пам'ять: + +``` cpp +auto pButton = new Gtk::Button("Test"); +// зробіть щось корисне з pButton +delete pButton; +``` + +* на цьому прикладі, програміст вручну видаляє `pButton`, після використання, щоб запобігти витоку пам'яті. + +Але у даному випадку, краще надати перевагу автоматичному способу знищення батьківським класом своїх нащадків, створюючи їх за допомогою інтегрованого методу `Gtk::make_managed()`. + +Це не є обов'язковим, оскільки також можна використовувати оператори `new` і `delete`, але сучасний стиль C++ відмовляється від них на користь більш безпечних моделей керування пам'яттю, тому краще створювати віджети за допомогою спеціального метода і потім видалити їх автоматично. + +Тому `gtkmm` й надає метод, який об'єднує створення та позначення в один крок, дозволяючи не писати `new` взагалі і чітко для інших розробників виразити намір створити саме віджет з автоматизованим керуванням пам'яті. + +Таким чином, коли за допомогою `Gtk::make_managed()` ми надаємо певному класу керування його дочірнім віджетом, такий батьківський клас називається *керуючим віджетом*. + +### Керуючий віджет + +Оскільки тепер ми використовуємо `Gtk::make_managed()`, то можемо дозволити контейнеру віджета контролювати, коли його дочірній віджет буде знищено. + +Розглянемо практичне використання на прикладі `append()` + +#### Динамічна алокація з `make_managed()` і `append()` + +Щоб делегувати керування тривалістю життя віджета його контейнеру, просто створімо його за допомогою `Gtk::make_managed()` і спакуймо результат до батьківського контейнера за допомогою `Gtk::Box::append()`: + +``` cpp +MyContainer::MyContainer() +{ + auto pButton = Gtk::make_managed("Test"); + append(*pButton); // додати *pButton до MyContainer +} +``` + +* *замість `Gtk::Box::append()` можна використовувати й інші методи* +* *приклад переходу реальної програми на модель керуючих віджетів, можна подивитись [тут](https://github.com/YGGverse/Yoda/commit/02b0b42b6b03f707242f461b0bea8bb4be16d16c)* + +Коли об'єкти типу `MyContainer` знищуються, кнопка також буде видалена. Більше не потрібно видаляти `pButton`, щоб звільнити пам'ять; контроль за її видаленням було делеговано об'єкту `MyContainer`. + +## Робота зі спільними ресурсами + +Деякі об'єкти, такі як `Gdk::Pixbufs` і `Pango::Fonts`, отримуються зі спільного сховища. Тому ви не можете створювати свої власні екземпляри. Ці класи зазвичай успадковуються від `Glib::Object`. + +`Gdk::Pixbuf` може бути створено лише за допомогою функції `create()`: + +``` cpp +auto pixbuf = Gdk::Pixbuf::create_from_file(filename); +``` + +Замість того, щоб вимагати від вас посилатися на ці об'єкти, `gtkmm` повертає розумний вказівник (smart-pointer) `Glib::RefPtr<>` + +* *наприклад, бібліотека [cairomm](https://www.cairographics.org/cairomm) - має свій власний розумний вказівник `Cairo::RefPtr<>`* + +У даному прикладі, `pixbuf` є розумним вказівником, тому ви можете працювати з ним так само як зі стандартним вказівником `std::shared_ptr`: + +``` cpp +auto width = 0; +if(pixbuf) +{ + width = pixbuf->get_width(); +} +``` + +Таким чином, коли `pixbuf` виходить з області видимості, у фоновому режимі відбудеться `unref()`. А оскільки немає оператора `new` - отже, немає і `delete`. + +В оригінальній статті наведено список літератури, щоб дізнатися більше про вказівники: + +* Bjarne Stroustrup, "The C++ Programming Language" Fourth Edition - section 34.3 +* Nicolai M. Josuttis, "The C++ Standard Library" - section 4.2 \ No newline at end of file diff --git a/post/kevacoin-detsentralizovana-baza-danykh-v-blokcheyn.md b/post/kevacoin-detsentralizovana-baza-danykh-v-blokcheyn.md new file mode 100644 index 0000000..41729c7 --- /dev/null +++ b/post/kevacoin-detsentralizovana-baza-danykh-v-blokcheyn.md @@ -0,0 +1,114 @@ +# KevaCoin - децентралізована база даних в блокчейн + +Існує багато криптовалютних проектів, серед яких часто лишаються непоміченими дійсно цікаві реалізації. Одним з таких є децентралізована перманентна база даних key/value типу - [KevaCoin](https://kevacoin.org). + +## Статус + +Забігаючи вперед, скажу що монета KevaCoin (KVA) у її поточному статусі і ймовірних перспективах - не буде цікавою трейдерам, оскільки була давно знята з торгівлі, проект покинули інвестори, а останній коміт датується 20 березня 2020 року. + +Щодо розробника є лише наступна [інформація](https://bitcointalk.org/index.php?topic=5213046.msg63660824#msg63660824): + +> According to the information disclosed by the community moderator, the developer/administrator of the project lost contact completely after the outbreak of COVID-19. + +Наразі середній хешрейт мережі складає приблизно 300 kH/s, кілька років тому - близько 700 kH/s, тому зберігається впевнена тенденція до колапсу (хоча деякі монети в такому стані можуть перебувати досить довго, поки не знайдуть новий імпульс). Частка потужності мережі при цьому іноді сягає 100% для одного пулу, що має застерегти від використання її у якості стабільного продукту. + +## Застосування + +Чому я вирішив написати про KevaCoin? + +Не зважаючи на свій статус і спірне майбутнє, це доволі цікавий проект, з основною метою зробити зберігання даних в блокчейн максимально простим і універсальним. + +Монета може бути цікавою розробникам децентралізованих продуктів у якості пісочниці, в певній мірі може використовуватись як NFT - оскільки реалізує функції трансферу, може зробити ваш проект "блокчейновим" у якості маркетингового ходу з реальним підгрунтям. Також, враховуючи практично нульову вартість і доступність майнингу на звичайному ПК, може стати альтернативним сховищем для зашифрованих бекапів, або як мікроблог для відкритих текстів, де наприклад `namespace` - може бути назвою, `key` - заголовком а `value` текстом публікації. + +Стосовно останнього варіанту, здебільшого вона так і використовується - у якості мікроблогів, тому недавно було прийняте рішення оновити закинутий [експлорер контенту](https://github.com/kvazar-network/webapp) (також є [версія](https://github.com/kvazar-network/geminiapp) для протоколу [Gemini](https://devzone.org.ua/post/protokol-gemini-iak-legka-alternativa-http)) і читати мало кому відомі дописи, які не зустрінеш у класичному веб-просторі. + +Недавно також "just-for-fun" було творено [чат в блокчейн](https://github.com/orgs/kevachat/repositories), утім з появою спамерів, повідомлення стали платними, що з цього вийде - не знаю, але буває і таке :) + +## Архітектура + +По суті, це форк LiteCoin, на базі PoW алгоритму RandomX, орієнтованого на майнинг з використанням CPU. + +Ядро включає в себе розширення стандартного способу зберігання повідомлень в транзаціях у форматі base58, схоже до того, як це реалізовано в BitCoin. + +Не зважаючи на те, що база даних декларується як CRUD, фактично до нових блоків додається лише мета-маркер `KEVA_PUT`, `KEVA_DELETE` тощо, а дані в попередніх блоках - клієнтами не видаляються. Таким чином, щось відправивши до блокчейну KevaCoin, це залишиться там перманентно. + +Довжина ключа складає 255 байт, значення - обмежене 3072 байтами. +В принципі, за допомогою спеціальних утиліт, можна "зклеювати" дані різних транзакцій в один файл, таким чином зберігати зображення і навіть відео. Утім, максимальна кількість транзакцій від одного публічного ключа на блок становить 20, тому це якоюсь мірою запобігає зловживанням, навіть при мінімальній вартості - шляхом обмеження відправника у часі. + +Окрім графічного інтерфейсу, з гаманцем можна взаємодіяти за допомогою [командного рядка](https://kevacoin.org/documentation.html). Повний список команд можна отримати командою `help`: +``` +... +keva_delete "namespace" "key" +keva_filter ("namespaceId" ("regexp" ("from" ("nb" ("stat"))))) +keva_get "namespace" "key" +keva_group_filter ("namespaceId" ("initiator" "regexp" ("from" ("nb" ("stat"))))) +keva_group_get "namespace" "key" "initiator" +keva_group_join "my_namespace" "other_namespace" +keva_group_leave "my_namespace" "other_namespace" +keva_group_show ("namespaceId" ("regexp" ("from" ("nb" ("stat"))))) +keva_list_namespaces +keva_namespace "display_name" +keva_pending ("namespace") +keva_put "namespace" "key" "value" "address" +... +``` + +Для створення веб додатків, є [бібліотека для PHP](https://github.com/kevachat/kevacoin-php). + +## Майнинг + +Монету легко отримати шляхом майнингу, наприклад з використанням [xmrig](https://xmrig.com). +Типова команда виглядає так: +``` +xmrig --url pool.hashvault.pro:80 --user WALLET --pass x --donate-level 0 --tls --tls-fingerprint ID +``` + +Щоб використовувати нульову комісію через `--donate-level 0`, потрібно зібрати `xmrig` самостійно, попередньо змінивши відповідний код: +``` +#src/donate.h +constexpr const int kDefaultDonateLevel = 0; +constexpr const int kMinimumDonateLevel = 0; +``` + +Якщо не планується використання пулу, цілком досяжно здобувати монету в режимі соло, скориставшись [офіційною інструкцією](https://kevacoin.org/tutorial_solo_mining.html). + +## Україномовні ресурси + +Нижче наведені неймспейси відомих ресурсів, скористайтесь експлорером щоб знайти більше або поділіться власним у коментарях! + +Бібліотека українських творів в електронному форматі (fb2, epub та інші) +``` +NPNAbcDG9ZEQojfvkzmRvQZurJFdYbg5vB +``` + +Збірник цитат українських авторів +``` +NRhwfDLHZuazir352YHFoTs4RbzmiR2Rfr +``` +``` +NQahrdTE2rAjhiwqa3vTbfLUBC1PzruaZf +``` + +Історико-культурні артефакти: фотокопії рукописів, малюнки та фотографії з різних музеїв +``` +NSkfeQfhWpuoyizRss5Qtt5GFpcAp3AA2K +``` + +## Висновки + +Шансів у цієї монети "захопити світ" немає, утім вона і досі виконує свою базову функцію - децентралізоване зберігання даних. +На відміну від Ethereum, де враховуючи вартість, транзакції обростають централізованими прошарками, в KevaCoin можна зберігати дані фактично безкоштовно. + +Після кількох років цікавості до проекту з точки зору децентралізації, а не трейдингу, маю свою критику, яка на жаль лишилась без відповідей. +Іноді повертаються думки зробити форк, знявши ліміти на значення, або збільшити їх до 1 Мб. Це б дозволило наприклад використовувати блокчейн для бекапу веб сторінок, які на жаль з плином часу зникають з мережі. + +Можливо, історія знайде продовження а можливо - часи цікавих децентралізованих проектів просто минули. Залишайте свої відгуки та діліться схожими проектами, якщо про такі знаєте! + +## Посилання + +* [Офіційний сайт](https://kevacoin.org) +* [Проект KevaCoin на GitHub](https://github.com/kevacoin-project/kevacoin) +* [Альтернативна збірка kevacoin-core](https://github.com/kvazar-network/kevacoin/releases) +* [Активні пули](https://miningpoolstats.stream/kevacoin) +* [Експлорер блоків](https://keva.one) +* [Експлорер контенту](https://kvazar.duckdns.org) \ No newline at end of file diff --git a/post/kopiiuvannia-veb-saytu-z-obkhodom-anty-bot-zakhystu-z-wget.md b/post/kopiiuvannia-veb-saytu-z-obkhodom-anty-bot-zakhystu-z-wget.md new file mode 100644 index 0000000..be5823d --- /dev/null +++ b/post/kopiiuvannia-veb-saytu-z-obkhodom-anty-bot-zakhystu-z-wget.md @@ -0,0 +1,70 @@ +# Копіювання Веб-сайту з обходом анти-бот захисту через wget + +В рамках проєкту [Реставрація українських Веб-архівів](https://devzone.org.ua/topic/restavratsiia-ukrayinskykh-veb-arkhiviv), я періодично роблю "сирі" дампи деяких Веб-сайтів, на випадок їх зникнення з онлайну. Утім один з ресурсів, який мене [зацікавив](https://prolinux.pp.ua/news/prolinuxua-stopped.html) - використовував динамічний анти-бот захист на основі JavaScript + Cookies, перший з яких утиліта `wget` обробляти не вміє, через що цей сайт також не може проіндексуватись проєктом [Webarchive](https://web.archive.org/web/20231130201641/https://prolinux.pp.ua/?i=1). + +Тобто у цьому випадку, його сторінка віддає кравлеру наступний код, точніше логічне завдання для клієнт-сайду, результат якого потрібно зберегти до Cookies та застосувати для подальших запитів: + +``` html + +``` + +Колись я вже мав подібний досвід (з логінами), тому швидко знайшов рішення для цього сайту і написав відповідний [гайд](https://codeberg.org/uarchive/prolinux.pp.ua#readme) для архівного репозиторію. За одно вирішив поділитись рішенням і тут, адже принцип роботи може бути корисним не тільки для архівації, але якщо ви хочете отримати копію сайту для читання в офлайн а той - має схожі заморочки. + +Це звісно не є універсальним рішенням, якщо сайт не віддає сторінки без JavaScript в принципі (що в плані [SEO](https://en.wikipedia.org/wiki/Search_engine_optimization) сьогодні зустрічається рідко) або використовуються якісь інші, більш складні алгоритми, наприклад на основі майнингу (типу [Anubis](https://github.com/TecharoHQ/anubis)). + +## wget + +Типова команда для отримання локальної копії звичайного сайту виглядає наступним чином: + +``` bash +wget -e robots=off\ + --recursive\ + --timestamping\ + --page-requisites\ + --convert-links\ + --adjust-extension\ + --reject="*/feed"\ + --user-agent="VALUE"\ + --load-cookies="path/to/cookies.txt"\ + --no-check-certificate\ + https://prolinux.pp.ua/ +``` +* `-e robots=off` - ігнорувати правила `robots` +* `--recursive` - режим кравлера +* `--timestamping` - завантажити файли лише в тому випадку, якщо вони новіші, аніж локальні +* `--page-requisites` - завантажити елементи, необхідні для коректного відображення веб-сторінки (CSS, JavaScript, медіа) +* `--convert-links` - конвертувати посилання у відносний формат для читання офлайн (з локальних файлів) +* `--adjust-extension` - додавати відповідні розширення, наприклад `.html` +* `--reject="*/feed"` - локальне правило виключно для цього сайту +* у випадку саме цього сайту я вказую `https` з `--no-check-certificate` бо сертифікат прострочений а посилання ведуть саме на SSL версію +* про значення аргументів `--user-agent` і `--load-cookies` - нижче + +Оскільки даний хостинг (або програмне забезпечення сайту) використовує захист JavaScript з використанням випадкового значення Cookies, потрібно спочатку згенерувати його, перейшовши на головну сторінку сайту програмою, яка це все "розуміє" наприклад, через Firefox. + +### --load-cookies + +Тут потрібно "витягнути" згенеровані Cookies у форматі `Netscape HTTP Cookie File` для подальшого використання з `--load-cookies`. +Для Firefox найпростіше це зробити [скриптом](https://gist.githubusercontent.com/hackerb9/d382e09683a52dcac492ebcdaf1b79af/raw/fcc8dcc0b8c3ca1d81e5ba2fb8d4f346dfe0a511/extract_cookies.sh): + +``` bash +chown +x extract_cookies.sh +extract_cookies.sh > cookies.txt +``` +* опціонально, з цього файлу можна видалити все, що не співпадає з хостом у прикладі - `prolinux.pp.ua` + +В принципі, можна й без скрипта вище руками зкопіювати значення вкладки, якщо не боїтесь помилитись з синтаксисом (`Ctrl+Shift+i` > *Storage* > *Cookies*) + +``` cookies.txt +# Netscape HTTP Cookie File +prolinux.pp.ua FALSE / FALSE 1758187902409 __test c2343463c82123417efd87dadb45678 +``` + +### --user-agent + +Для `wget` важливо вказати такий само `--user-agent` яким було отримано значення Cookies. Тут можна ввести в Google [запит](https://www.google.com/search?q=get+current+user+agent+online) - для Firefox, там виводиться рядок типу `Mozilla/5.0 (X11; Linux x86_64; rv:142.0) Gecko/20100101 Firefox/142.0`. + +## P.S. + +Як бачите, у конкретно цьому випадку - все відносно просто: якщо зрозуміти принцип взаємодії Cookies з `wget`, то можна легко застосувати дану логіку й для схожих завдань. Саму команду імпорту можна трохи адаптувати, але частину локальної адаптації я вже виконаю окремо. + +Оскільки я не впевнений чи варто розміщувати дані цього сайту на Codeberg, можливо хтось доєднається і створить в себе додаткову локальну копію за прикладом вище і коли автор блогу повернеться - ми зможемо таким чином підстрахувати його роботи від втрати даних. \ No newline at end of file diff --git a/post/manticore-iak-suchasna-alternatyva-sphinx.md b/post/manticore-iak-suchasna-alternatyva-sphinx.md new file mode 100644 index 0000000..b0227cc --- /dev/null +++ b/post/manticore-iak-suchasna-alternatyva-sphinx.md @@ -0,0 +1,120 @@ +# Manticore як сучасна альтернатива Sphinx + +Багато хто використовує для повнотекстового пошуку рушій [Sphinx](https://sphinxsearch.com) і пише для нього нові програми, але мало хто знає про його сучасну реалізацію - [Manticore](https://manticoresearch.com). + +Класично, сферою застосування може бути пошуковий сервер для сайту, будь якої бази даних, індекс кравлера інтернет / локальної мережі, тощо. + +У той час, як Sphinx версії 3 і досі має закритий код, Manticore - це форк відкритого Sphinx 2. +Наразі активно розробляється, має живу спільноту і містить багато доповнень, особливо в плані автоматизації real-time індексів. + +Окрім того, Manticore у більшості випадків може замінити собою сервер PostgreSQL або MySQL, де не потрібна гнучка вибірка і багаторівневі запити. Таким чином, можна значно заощадити на використанні жорсткого диску для великих індексів а також зменшити використання пам'яті у разі, якщо MySQL використовується просто як основна база даних (до якої раніше звертався Sphinx через document ID). + +## Встановлення + +На прикладі Debian / Linux, доволі просте: +``` +wget https://repo.manticoresearch.com/manticore-repo.noarch.deb +dpkg -i manticore-repo.noarch.deb +apt update +apt install manticore manticore-extra +``` + +Сервіс одразу буде доступний через systemd: + +``` +systemctl start manticore +``` + +Працює сервер на порті `9308` + +Після запуску, можна підключитись клієнтом MySQL і взаємодіяти через [SphinxQL](http://sphinxsearch.com/docs/current/sphinxql-reference.html): + +``` +mysql -P9306 -h0 +``` + +В рамках цього матеріалу не будуть описані детальні мануали. Оскільки проект має хорошу [документацію](https://manual.manticoresearch.com/Introduction), досвідчені користувачі Sphinx з легкістю можуть застосовувати свій досвід, наприклад в searchd, що має таку само архітектуру, а синтаксис [extended-search](https://sphinxsearch.com/docs/latest/extended-syntax.html) - повністю сумісний. + +## Інтеграція + +Проект має багато офіційних бібліотек для різних мов програмування, зокрема [manticore-php](https://github.com/manticoresoftware/manticoresearch-php). Розгортання програми з цією бібліотекою настільки просте, що зводиться до наступних рядків: + +`composer require manticoresoftware/manticoresearch-php` + +``` + 'localhost', + 'port' => 9308 + ] +); + +$index = $client->index( + 'test_index' +); + +$index->create( + [ + 'field1' => [ 'type' => 'text' ], + 'field2' => [ 'type' => 'text' ] + ] +); + +$index->addDocument( + [ + 'field1' => 'hello', + 'field1' => 'world' + ], +); + +$results = $index->search('hello')->get(); +``` + +Кому цікавий приклад програми на PHP, може відвідати сторінку дуже [простого кравлера з веб інтерфейсом](https://github.com/YGGverse/Yo), який був написаний в максимально короткі терміни для мережі [Yggdrasil](https://devzone.org.ua/post/yggdrasil-mereznii-protokol-z-decentralizovanim-routingom). Наразі містить в своєму індексі близько 10.000.000 сторінок, при цьому потужності серверу рівні мінімальному тарифу VPS за 5 євро. + +Для роботи з SphinxSQL в PHP зручно використовувати `php-pdo`; для фільтрації деяких текстових полів - можна додати окреме поле `crc32`, також такий підхід можна використовувати у якості Document ID (коли є потреба зменшити об'єм даних і не критична точність). + +## Бекапи + +Декілька слів про бекапи: їх традиційно два типи - логічний і фізичний. + +### Логічний бекап + +Дозволяє генерувати SQL дампи, які наприклад зручно читати, але вони потребують більше ресурсів, тому виконуються рідше. +Приклад створення дампу буде таким: +``` +mysqldump -h0 -P9306 manticore test_index > test_index.sql +``` + +### Фізичний бекап + +Такий спосіб передбачає копіювання "сирих" даних до відповідної теки, не вимагає багато ресурсів на обчислення і виконується частіше: + +``` +/usr/bin/manticore-backup --backup-dir=/home/backup/manticore +``` + +## Власний досвід + +Працювати з Manticore одне задоволення, що доводилось робити руками в Sphinx - вже працює з коробки. + +Маю не перший проект на цьому рушії, подекуди повністю відмовився від MySQL, хоч іноді це не дуже зручно, але більше не витрачаю свій час на +проектування в MySQL Workbench і віддаю перевагу прямому зберіганню даних до індексу в менш нормалізованому, але більш швидкому в розробці форматі. + +Єдине, що б радив зробити, це перемкнути [стандартний рівень binlog](https://manual.manticoresearch.com/Logging/Binary_logging#Binary-flushing-strategies) в режим "1", інакше можуть бути незручності з останніми транзакціями, якщо ваш сервер чутливий до вимикання світла, а транзакції відбуваються часто (у разі ймовірності отримати збій в посeкундному режимі зберігання). + +Зробити це можна у файлі: + +``` +#/etc/manticoresearch/manticore.conf +searchd { + ... + binlog_flush = 1 +} +``` + +## Посилання + +* [Офіційний сайт Manticore](https://manticoresearch.com) \ No newline at end of file diff --git a/post/misfin-poshtovyy-satelit-geminispace.md b/post/misfin-poshtovyy-satelit-geminispace.md new file mode 100644 index 0000000..f881af7 --- /dev/null +++ b/post/misfin-poshtovyy-satelit-geminispace.md @@ -0,0 +1,169 @@ +# Misfin - поштовий сателіт Geminispace + +Про Misfin я знаю давно, декілька разів намагався ним користуватись, але жодного - не доводилось перевірити його в реальних умовах. Зокрема, через брак аудиторії та відсутність такої необхідності в сучасному світі месенджерів, де й сама пошта є епохою динозаврів. Утім, хотілось би трішки освітити цю альтернативну технологію для широкого загалу, адже з протоколом [Gemini](https://devzone.org.ua/post/protokol-gemini-iak-alternatyva-http) - знайомий не заочно і вже встиг написати для його специфікації, у свій час, повнофункціональний GTK браузер [Yoda](https://github.com/YGGverse/Yoda) а також ряд [інших серверних рішень](https://github.com/orgs/YGGverse/repositories?q=gemini) різними мовами. + +Протокол доставки повідомлень Misfin - названо на честь космічного проєкту [MSFN](https://en.wikipedia.org/wiki/Manned_Space_Flight_Network) і в цілому, він є спробою реалізації мінімалістичної альтернативи класичній пошті e-mail, за образом і подобою концепції протоколу Gemini. + +З офіційного маніфесту: + +> Електронна пошта така ж погана, як і Інтернет. Вона стала складною, захищеною лише за допомогою інших протоколів, які до неї приєднані, і підтримує всі неприємні функції, які є в Інтернеті, такі як Cookies та трекінгові маячки. Ще гірше, що вона зазнає активної ворожості з боку великих гравців Інтернету. Більшість провайдерів блокують трафік на порту 25, і ви не можете доставити пошту до жодного з великих сервісів (як-от Gmail), не проходячи через численні перешкоди - і навіть тоді це лотерея. + +Від себе зауважу, що вбачаю в ньому не стільки боротьбу з корпораціями та монополією, але опцію отримувати чистий вміст листів: без розмітки та зайвих заголовків, що можуть бути частиною трекінгу, характерних для сучасного Веб 2.0 та вище. Якщо ж у вас є проблеми з 25 портом чи бажаєте розгорнути повноцінну локальну пошту за NAT, ознайомтесь з наступними матеріалами: +* [Організація поштової скриньки e-mail для локальних мереж без DNS](https://devzone.org.ua/post/orhanizatsiia-poshtovoyi-skrynky-e-mail-dlia-lokalnykh-merez-bez-dns) +* [Yggmail - месенджер з поштовим інтерфейсом](https://devzone.org.ua/post/yggmail-mesendzer-z-poshtovym-interfeysom) + +## Реалізація + +На мою думку, Misfin не є альтернативною класичній пошті, це швидше модифікація протоколу відправки даних Titan для Gemini або NPS для [Nex](https://devzone.org.ua/post/protokol-nex-lehka-alternatyva-gemini), але на відміну від таких, не є частиною інфраструктури жодного з них. + +Схоже до Titan, Misfin використовує сервер, який працює на сокеті з виділеним портом (стандартно `1958`) і отримує на нього дані через обов'язковий шар шифрування TLS. Окрім тексту повідомлень, дані містять мінімалістичні заголовки (які на відміну від Titan, орієнтовані не на файл, а текст) і включають: +* `USER_ID` - поштова скринька користувача (`admin`, `santa` і тд.) +* `COMMON_NAME` - псевдонім користувача +* `SUBJECT_ALT_NAME` - хост (`example.com`, `misfin.org`, тощо) + +Сирий рядок запиту виглядатиме як: +``` +misfin://mailbox@hostname.com \r\n +Текст повідомлення +``` + +Відповідь сервера-отримувача при цьому буде такою: +``` +20 \r\n +``` + +Хто вже працював з Gemini, може побачити, що ці протоколи - між собою схожі, як дві краплі води. Більш детально вдаватись в специфікацію тут не буду, вона описана на офіційному сайті (посилання внизу сторінки). Занотую тільки, що наразі існує три базові редакції протоколу: +* `A` - сама перша, рання офіційна редакція (наразі вважається архівною) +* `B` - друга офіційна редакція протоколу, що включає корекції `A` +* `C` - після релізу версії `B` його автор (Лем) на деякий час пропав з радарів, тому декілька користувачів не дочекавшись випустили версію `C`, яка збільшує довжину повідомлення та включає деякі оптимізації, що стосуються формату заголовків + +Наскільки мені відомо, автор редакцій `A` і `B` згодом вийшов на зв'язок та збирався включити редакцію `C` в офіційну специфікацію, але потім зник. Формально, протоколи `B` і `C` є сумісними, а новий софт пишеться з урахуванням специфікації останнього. Тому майте на увазі цю невеличку історію. Більше про неї вам розкаже один з учасників цього процесу - [@clseibold](gemini://bbs.geminispace.org/u/clseibold). + +## Програмне забезпечення + +Для доставки пошти, передбачається класична пара клієнт-сервер, при цьому для сервера відкривається вказаний в його налаштуваннях порт TCP: + +``` bash +ufw allow 1958/tcp +``` + +### Сервер + +Є різні реалізації, але мені відомо про щонайменше дві стабільні: +* [estampa](https://sr.ht/~nixgoat/estampa/) - Rust +* [misfin-server](https://gitlab.com/clseibold/misfin-server) - Go + +#### estampa + +Так як сам розробляю програмне забезпечення на Rust, для себе обрав `estampa`. Встановлюється цей сервер наступним чином: + +``` bash +git clone https://git.sr.ht/~nixgoat/estampa +cd estampa +cargo build --release +install target/release/estampa /usr/local/bin/estampa +``` +* останньою командою ми встановлюємо `estampa` до канонічного розташування бінарних програм Linux, для подальшої роботи сервера з `systemd`; якщо ви не плануєте встановлення, цей крок можна пропустити і пускатись через звернення до `target/release/estampa` +* для першої збірки програм на Rust, дивіться [Встановлення останньої версії Rust в Linux](https://devzone.org.ua/post/vstanovlennia-ostannyoyi-versiyi-rust-v-linux) + +У поточній теці проєкту, відкриваємо файл конфігурації `nano config.toml` та додаємо в кінці поштові скриньки користувачів: +``` config.toml +[mailbox.user1] +enabled = true +name = "user 1" + +[mailbox.user2] +enabled = true +name = "user 2" +``` + +Опціонально, можна вказати й інші налаштування (якщо плануєте запуск від `systemd`) але я лишив стандартно. Далі, сервер запускається командою: + +``` bash +estampa +``` + +При першому запуску, у поточній теці буде згенеровано сертифікат сервера TLS (такий само як для сервера Gemini) - `server.crt` і `server.key`, у цій же теці буде створено файлову структуру для збереження листів, за адресою в прикладі це буде: + +``` +store/ + certs/ + user1.pem + user2.pem + priv/ + user1.pem + user2.pem + mbox/ + user1/ + user2/ +``` +* таким чином, при отриманні листів, вони будуть зберігатись у теці `store/mbox/user/*.gmi` + +##### systemd + +Публічний сервер можна (і варто) запускати від окремого користувача `useradd -m estampa`: + +``` +# /etc/systemd/system/estampa.service +[Unit] +After=network-online.target +Wants=network-online.target + +[Service] +Type=simple + +User=estampa +Group=estampa + +# Якщо в конфігурації вказано відносні шляхи до сховища та сертифікатів, +# важливо вказати шлях до робочої теки: +# WorkingDirectory=/path/to/working/directory + +ExecStart=/usr/local/bin/estampa + +# Вкажіть null якщо не бажаєте журналювати роботу сервера +StandardOutput=file:/home/estampa/debug.log +StandardError=file:/home/estampa/error.log + +[Install] +WantedBy=multi-user.target +``` +* `systemctl daemon-reload` - оновити конфігурацію +* `systemctl enable estampa` - автостарт при запуску системи +* `systemctl start estampa` - запуск +* `systemctl status estampa` - перевірити статус + +### Клієнт + +Наразі, я не знайшов готового рішення для десктоп чи мобільних платформ, як це можна було б очікувати від класичного поштового клієнта. Натомість, листи що надходять - складаються в файлову систему, з розширенням `.gmi` (Gemtext) і читаються вручну. Можливо, колись таки зроблю повноцінний GUI клієнт зі сповіщеннями і .. блек джеком, а наразі - маємо наступні опції: + +#### Lagrange + +Найпростіший спосіб відправити листа - зробити це через GUI браузер [Lagrange](https://gmi.skyjake.fi/lagrange/) (адже з вірогідністю 99% ви будете користуватись саме ним). + +Для цього потрібно відкрити в основному меню `Identity` > `Import` та додати сертифікат (я просто зкопіював туди згенерований сертифікат одного з юзерів `store/certs`). Після цього, в боковому меню `Identities` (`Ctrl`+`4`) на імпортованому сертифікаті правою кнопкою миші відкриваємо контекстне меню та шукаємо пункт `Send mail as...`, пишемо адресу отримувача, повідомлення та тиснемо кнопку `Send`. + +#### CLI + +У якості клієнта, можна використовувати командний рядок, засобами утиліт, які розраховані на відправлення даних через сокет з шаром SSL: + +``` bash +echo -e "misfin://test@localhost < test@localhost2 Test \r\nMESSAGE" | openssl s_client -connect localhost:1958 -cert /path/to/cert.pem -key /path/to/key.pem -ign_eof +``` +* вказуючи `< test@localhost2` ми додаємо відправника і підписуємо його `/path/to/cert.pem` і `/path/to/key.pem` з набору згенерованих скриньок, що вказані в прикладі `estampa`, тобто лежать вони в `store/certs/*` (запуск окремого сервера `localhost2` не потрібен) +* згідно [специфікації](gemini://misfin.org/specification.gmi), тут важливо зберегти пробіл перед `\r\n` інакше буде помилка `59` +* нагадаю, що для сумісності з редакцією `B`, максимальна довжина повідомлення разом із заголовком, повинна складати не більше `2048` байт +* при успішному відправленні, як і у випадку з Gemini, має бути код `20` (успішно) + +## Висновки + +Як писав вище, я не вважаю даний експеримент заміною e-mail, адже той пройшов довгий шлях еволюції і став таким в умовах промислової реальності. Misfin, може стати хіба що базою для альтернативного обміну повідомленнями в локальних мережах або для спілкування з друзями-гіками, особливо, якщо ви - користувач CLI (хоча й тут, мабуть я обрав би комунікацію сокетами через `telnet` або IRC). + +Тут також немає відповіді на питання щодо боротьби з потенційним спамом, але як вказано на офіційному сайті, в Geminispace це явище майже відсутнє через малу популярність протоколів сімейства Gopher. + +## Посилання + +Надаю список локальних ресурсів, адже веб-дзеркала для них не знаю. Відкрити ці посилання можна у будь якому [браузері для протоколу Gemini](https://github.com/kr1sp1n/awesome-gemini#clients). Посилання також мають відкриватись, якщо відповідний браузер вже встановлено. + +* [Офіційний сайт](gemini://misfin.org/) +* [Група Misfin на BBS](gemini://bbs.geminispace.org/s/misfin) \ No newline at end of file diff --git a/post/miy-preset-alfis-dns-na-routeri-yggdrasilmycelium.md b/post/miy-preset-alfis-dns-na-routeri-yggdrasilmycelium.md new file mode 100644 index 0000000..51e1561 --- /dev/null +++ b/post/miy-preset-alfis-dns-na-routeri-yggdrasilmycelium.md @@ -0,0 +1,173 @@ +# Мій пресет Alfis DNS на роутері Yggdrasil / Mycelium + +Я тут знову примудрився грохнути операційну систему, тому довелося піднімати конфіги руками. Цього разу, вирішив створити нотатку, щоб не обмірковувати все спочатку кожного разу коли партачу з системою. + +Отже, [Alfis DNS](https://devzone.org.ua/post/alfis-dns-reyestratsiia-domenu-v-blokcheyn) в мене крутиться на окремому девайсі ARM, який слугує спільним резольвером для звичайних доменів Інтернет а також внутрішніх `.ygg`, `.srv` тощо. Для синхронізації блокчейну, тут використовуються оверлейні мережі [Yggdrasil](https://devzone.org.ua/post/yggdrasil-mereza-z-detsentralizovanym-routynhom) і [Mycelium](https://devzone.org.ua/post/vstanovlennia-routera-merezi-mycelium-v-linux). Таким чином, цей роутер виступає у якості проксуючого DNS на всі локальні девайси, що до нього підключаються - тобто я не ставлю Alfis на всі клієнти але користуюся резольвом доменних зон через спільний сервер, що поєднує собою усі альтернативні напрямки. Також цей роутер підтримує саму мережу Alfis, виступаючи для неї у якості публічного піра (для шейрингу блокчейн та обміну DHT) + +Оскільки мій резольвер не взаємодіє з пірами Інтернет (хоча така опція там є стандартною), мій пресет також передбачає особистий [форк](https://github.com/YGGverse/Alfis/tree/mycelium-network-mode) до [PR#386](https://github.com/Revertron/Alfis/pull/386) який додатково включає комбіновані правила фільтрації вихідних з'єднань на Yggdrasil і Mycelium (в оригіналі там тільки опція Yggdrasil) - будьте уважні, бо конфіг тут також відрізнятиметься! + +## Роутер + +Я встановлюю редакцію Alfis зі свого репозиторію з патчем який досі вісить в PR: + +* `git clone https://github.com/YGGverse/Alfis.git && cd Alfis` +* `git checkout mycelium-network-mode` - гілка з підтримкою Mycelium +* `cargo build --release --no-default-features` - збірка без GUI (демон) +* `install target/release/alfis /usr/local/bin/alfis` + +Далі створюю окремого юзера, так як не планую пускати від рута (на системних портах) + +``` bash +useradd -m alfis +``` + +### Конфігурація + +Створюю файл конфігурації `/etc/alfis.toml` наступного змісту: + +``` /etc/alfis.toml +# сід-блок, лишаємо як є, якщо він не відрізняється від оригіналу мережі +# https://github.com/Revertron/Alfis/blob/master/alfis.toml#L2 +origin = "0000001D2A77D63477172678502E51DE7F346061FF7EB188A2445ECA3FC0780E" + +# шляхи до ключів: на роутері я не адмініструю домени, тому просто коментую +key_files = [ +#"key1.toml", "key2.toml", "key3.toml", "key4.toml", "key5.toml" +] + +# скільки блоків перевіряти на старті - лишаю стандартно +check_blocks = 8 + +# налаштування мережі +[net] +# вузли ініціації: коментую стандартні бо не довіряю Інтернет-доменам, +# лишаю тільки пачку відомих мені вузлів (але з часом вони можуть втратити свою актуальність) +peers = [ + #"peer-v4.alfis.name:4244", "peer-v6.alfis.name:4244", "peer-ygg.alfis.name:4244" + "[200:f8e5:2383:c1f8:7c73:8e74:2a5e:197e]:4244", + "[200:f8e5:2383:c1f8:7c73:8e74:2a5e:197e]:4244", + "[200:31b6:1c3a:3a1c:d322:3ed7:e109:4b3]:4244", + "[200:1106::a702:9841:e607:9b3]:4244", + "[208:84:68:55:2f91:8484:8d60:2fca]:4244", + "[208:25:40:bd:6ea9:89fc:ac75:87be]:4244", + "[208:62:45:62:59b8:f1a2:62ca:f87c]:4244", + "[225:ca89:40a8:611e:78b8:ab81:999a:d4d7]:4244", + "[301:84f7:4bc0:2f3a::53]:4244" +] + +# слухаю з'єднання на всіх IPv6 бо користуюсь Yggdrasil / Mycelium а від провайдера Інтернет на цьому інтерфейсі немає +listen = "[::]:4244" + +# цією опцією долучаюсь до підтримки мережі (обмін DHT і блокчейн даними) +public = true + +# опція з мого патчу: дозволяю вихідні з'єднання тільки на мережу Mycelium +mycelium_mode = true + +# аналогічно для Yggdrasil +yggdrasil_mode = true + +[dns] +# резольвер слухатиме клієнтів на IPv4 інтерфейсі +listen = "0.0.0.0:5353" + +# скільки потоків виділяти для резольву DNS +threads = 10 + +# для Інтернет-резольву я користуюсь клаудфлейр бо він швидкий, ви можете обрати собі інший +forwarders = ["1.1.1.1:53"] + +# тут здається воно використовується якщо ви збирались з флагом --features="doh" +bootstraps = ["9.9.9.9:53", "94.140.14.14:53"] + +# не знаю, чому закоментив, лишаю оригінальний комент: +# Hosts file support (resolve local names or block ads) +# hosts = ["system", "adblock.txt"] + +[mining] +# кількість потоків CPU для майнингу (якщо використовується) +threads = 2 + +# понизити пріоритет для операцій майнингу (якщо використовується) +lower = true +``` + +### systemd + +Додаю конфігурацію `/etc/systemd/system/alfis.service` на основі оригінального [файлу](https://github.com/Revertron/Alfis/blob/master/contrib/systemd/alfis.service): + +``` /etc/systemd/system/alfis.service +[Unit] +Wants=network.target +After=network.target + +[Service] +User=alfis +Group=alfis + +# закоментив бо я переїхав в домашню теку юзера +# ProtectHome=true +ProtectSystem=true + +SecureBits=keep-caps +CapabilityBoundingSet=CAP_NET_BIND_SERVICE +AmbientCapabilities=CAP_NET_BIND_SERVICE + +SyslogIdentifier=alfis +WorkingDirectory=/home/alfis +ExecStart=/usr/local/bin/alfis -n -c /etc/alfis.toml + +# цю опцію можливо є сенс закоментити але лишив як в оригіналі +ExecReload=/bin/kill -HUP $MAINPID + +# це закоментив бо хай краще не працює аніж ребутається втіхаря +# Restart=always +# TimeoutStopSec=5 + +StandardOutput=file:///home/alfis/debug.log +StandardError=file:///home/alfis/error.log + +[Install] +WantedBy=multi-user.target +``` +* `systemdtl restart alfis` - запуск +* `systemdtl enable alfis` - автостарт +* `systemdtl status alfis` - перевірка статусу + +### Фаєрвол + +На роутері в мене Debian, тому додаю правила через `ufw`: + +``` bash +ufw allow from 0200::/7 to any port 4244 +ufw allow from 0400::/7 to any port 4244 +``` +* ці правила дозволяють підтримувати мережу Alfis DNS для мереж Yggdrasil (`0200::/7`) та Mycelium (`0400::/7`) відповідно + +``` bash +ufw allow from xx:xx:xx:xx to any port 5353 proto udp +``` +* хостів може бути декілька, я додаю таким чином дозвіл на резольв окремим локальним девайсам а іншим - забороняю (хоча чи не роздає часом мій атрибут `public` подібні відповіді через сокет `4244` - я не знаю, сподіваюсь що ні, бо так гіпотетично можлива DDOS атака на Інтернет DNS) + +## Клієнт + +На клієнті в мене Fedora, тому нижче інструкції для неї (для Ubuntu додам згодом, бо поки не під рукою для перевірки) + +``` /etc/systemd/resolved.conf +#/etc/systemd/resolved.conf +[Resolve] +DNS=xx.xx.xx.xx:5353 +#FallbackDNS=1.0.0.1 8.8.4.4 +#Domains=~. +LLMNR=no +MulticastDNS=no +#DNSSEC=yes +#Cache=yes + +[Network] +DNSStubListener=no +``` +* `xx.xx.xx.xx:5353` - адреса роутера, на якому крутиться резольвер Alfis DNS +* після змін перезапускаємо сервіс `systemctl restart systemd-resolved` + +Тут, теоретично можна (і було б зручно) змінити налаштування в GUI менеджері з'єднань GNOME, але в мене він не підтримує параметр порту - хоче `53` (пустий) а його я не хочу пускати на роутері. Тому таке рішення з `resolved.conf` \ No newline at end of file diff --git a/post/moyi-pershi-kroky-v-gtk.md b/post/moyi-pershi-kroky-v-gtk.md new file mode 100644 index 0000000..d4f6d5a --- /dev/null +++ b/post/moyi-pershi-kroky-v-gtk.md @@ -0,0 +1,99 @@ +# Мої перші кроки в GTK + +## Передмова + +Не так давно захотілось зробити простенький браузер для [протоколу Gemini](https://devzone.org.ua/tag/gemini). Зокрема - додати детекцію Geo-IP капсул, власний пошук на базі [Manticore](https://devzone.org.ua/tag/manticore) та інтегрувати екосистему [Yggdrasil](https://devzone.org.ua/tag/yggdrasil). +Для обраного протоколу задача виглядала тривіальною, залишалось обрати графічний фреймворк і накидати туди крутих функцій. + +Я постійний користувач Linux, вибір був між Qt та GTK. З Qt вже поверхнево знайомий, в рамках одного з проектів, що довелось апгрейдити і разом з тим оновлювати залежності API (досі приходить спам після обов'язкової реєстрації для завантаження останньої SDK) +Але оскільки завжди був користувачем середовища GNOME, все таки пішов стежкою GNU і вирішив глянути що там і як робиться на практиці. + +Забігаючи вперед, скажу що займався професійно виключно веб розробкою, добре знаю суміжні мови, патерни і фреймворки. Але це була фактично моя перша десктоп програма, яку захотілось написати з нуля. +Заради цікавості вбив у пошук знайому для себе комбінацію "PHP-GTK" і щось там побачив! Ну, а оскільки вже чимало часу провів з PHP, то написати браузер цією мовою було доволі простою задачею - в запасі вже було з десяток власних бібліотек, лишалось їх тільки натягнути на віконний API і все. + +Отже, знайшов пару прикладів, написав простенький код виклику вікна - працює, значить працюватиме й інше! + +## Інформація в мережі + +Перше з чим зіткнувся, на відміну від Qt, PHP/JS та інших поп-технологій, пошукові видачі по GTK - порівняно порожні або посилаються на якісь старі мануали по другій версії. + +Звісно є [офіційна документація](https://docs.gtk.org), але без розуміння загальної картини.. взагалі складно.. як, що, до чого. На ютубі теж тотальний кошмар, але частково є англомовний контент в основному по двійці, якості не краще. Краще не дивитись. Довелось пробиратись через тернові хащі розуміння самому. Вирішив нарешті задати дільне питання Chat GPT і Haiku, останній до речі доволі точно і лаконічно генерує приклади на Python, C та C++ + +## Документація + +В принципі, вона є але доволі скупа і потребує деякого рівня для входу. Але так якщо писати програму по інструкціям ШІ та паралельно читати документацію по кожному методу і його типам то по-трохи можна в'їхати що до чого. + +## Поняття Widget + +В якийсь момент, почав розуміти що є деякі класи, з яких подібно блокам конструктора, складається вікно і всі його елементи. Ці класи потрібно зібрати до купи в рамках іншого класу-віджету у потрібному порядку і вкладеності: копки, поля та інше - формуючи в результаті вікно і заголовок (який тут теж є віджетом). + +Згодом, працюючи безпосередньо з компонуванням, стало відомо про такий інструмент як Glade, який дозволяє робити те само не в коді а через UI (як Unity для ігор) а проект експортувати - в XML, який в свою чергу, потім можна імпортувати в об'єкти - вже засобами класу [Builder](https://docs.gtk.org/gtk4/class.Builder.html). + +В принципі, використовувати Glade чи ні - справа вподобань. Особисто мені не подобається UI та XML як явище в ООП, але офіційного аналогу JSON не знайшов, тому й далі описую віджети об'єктами в коді програми. Варто зазначити, що для GTK 4, Glade вже не використовується, а на його офіційному сайті висить [заглушка nginx](https://glade.gnome.org). Тут на зміну для четвірки приходить нова і мабуть єдина, неофіційна програма і новий формат проекту файлів - [Cambalache](https://gitlab.gnome.org/jpu/cambalache). + +## Ієрархія і наслідування + +Через певний час роботи, виявиться що деяких методів в прикладах, які описує ШІ немає в документації класу і це не помилка! Як виявляється, на сторінці кожного віджету можна побачити [дерево наслідування](https://docs.gtk.org/gtk4/class.Box.html#hierarchy), і воно там проілюстроване не спроста + +![gtk box hierarchy](https://i.imgur.com/fi9MnBx.png) + +Таким чином, в пошуках певної функції для тюнінгу програми, вже можна читати не тільки документацію робочого класу, але й обов'язково його батьківського елементу - де виявляється ціла купа нових методів про які на сторінці самого методу не згадується окрім клікабельної мапи наслідування. Це була суто моя, користувацька неуважність і як наслідок самостійна реалізація функціональності яка вже була давно доступна, просто з інструментами які знаходяться рівнем вище у батьківських класах та групах об'єктів. + +Якщо пишете програму на C, то ймовірно це буде простіше, оскільки компілятор підсвітить та авто-доповнить семантику. Але в моєму випадку - був шлях пробивного першопрохідця на PHP, який ще й писав порт на PHP-CPP мабуть довше ніж сам браузер. + +По цій темі можна ще додати наступне. Якщо ви пишете першу програму GTK на C++, то ймовірно вже віднайшли і користуєтесь враппером [gtkmm](https://gtkmm.org) (і його сателітами - glibmm і giomm) - така собі абстракція, яка спростить код але добряче заплутає в офіційній документації для C через свою абстрагованість. Частина API в gtkmm досі не реалізована. Також офіційні класи GTK 4, на відміну від GTK 3, вже оголошені як final, тобто не можуть наслідуватись і передбачають або агрегацію або композицію. Разом з тим, gtkmm все ще дозволяє наслідування (подібно Qt) і тут звісно теж не дуже зрозуміло як краще проектувати програму згідно стандартам і що на gtkmm чекатиме завтра. Але бібліотека безумовно зручна, дійсно зменшує об'єм коду в рази три. + +Все таки мені здається оптимальним писати першу програму GTK на С а не C++, чи наприклад Rust. C, не зважаючи на потенційно більшу кількість коду і відсутність ООП, на мою думку, буде простішим шляхом для початківця, зокрема у вивченні того як влаштована ієрархія фреймворку, події, використання пам'яті. І вже тільки потім варто переходити на абстрактний рівень вище. Для себе все ще не визначився бо все життя провів в ООП, і вже не уявляю без цього програму більшу за "hello world" + +## Типи даних і допоміжні функції + +Низькорівневі бібліотеки [Gio](https://docs.gtk.org/gio)/[Glib](https://docs.gtk.org/glib) містять чимало зручних функцій для роботи з кодуванням, розміткою, сокетами і взагалі все що потрібно для роботи з контентом. Багато з них наслідують STL. Коли я вперше відкрив для себе розділ функцій то чомусь мені згадалось різноманіття препроцесору розмітки PHP - де є все для роботи з веб, а у веб зараз є все. + +Таким чином, майже не використовую STL бо майже на кожну функцію і тип даних, в середовищі GTK для них є розширена і адаптована реалізація. + +## Робота з контентом + +Якщо говорити про вивід даних в контексті браузеру, який у моєму випадку має відображати простий gemtext з клікабельними посиланнями, починається деякий дисонанс після роботи з JS/HTML. + +По-перше в GTK є тільки два відомих мені віджети для тексту - [Label](https://docs.gtk.org/gtk4/class.Label.html) і [TextView](https://docs.gtk.org/gtk4/class.TextView.html) + +`Label` - це по суті блок для невеликого тексту, який може підтримувати розмітку або звичайний текст. Розмітка при цьому використовує формат [Pango](https://docs.gtk.org/Pango), тобто це такий собі мінімальний варіант HTML з декількома тегами і класами, що використовується для інших, специфічних для стаціонарних програм цілей. Поле `Label` не розраховане на великий об'єм даних, тому як і варто очікувати мігрантам з HTML - сенс його однозначний - невеличкий опис до якогось блоку і все. Текст на 100Кб відправить вашу програму в роздуми на 10 секунд, особливо якщо спробуєте відформатувати [Layout](https://docs.gtk.org/Pango/class.Layout.html) своїм способом, наприклад перерахувати word-wrap чи відмалювати інакше шрифти перед додаванням у віджет. + +Тим не менше, усі дороги і поради "бувалих" ведуть саме на `Label`, але якщо залізти у вихідний код інших, швидких GTK браузерів то побачимо, що часто використовується багаторядкове текстове поле вводу `TextView`. По суті, це аналог поля `textarea`, в якому достатньо сховати курсор засобами CSS (наприклад, через `caret-color: transparent`) та перевести віджет в режим [readonly](https://docs.gtk.org/gtk4/method.Editable.set_editable.html). +Цей віджет так само підтримує розмітку, події, користувацькі теги а також буфер, в який можна складати і відмальовувати невеликий текст по мірі прокрутки контенту, як багато хто і робить що і являє собою розгадку швидкодії рендерингу великих документів. + +Говорячи про прокрутку. Ще не знаю як в GTK 4, але в GTK 3 віджет, який не підтримує засоби скролу, потрібно додати у відповідний контейнер [Viewport](https://docs.gtk.org/gtk3/class.Viewport.html). Інакше ви довго не будете розуміти чому ваш текст сам по собі відскролюється на верх, наприклад при перемиканні табів `Label` віджету `Notebook`. Так само в іншому, очевидно вектор спрощення торкається не тільки візуальної але і внутрішньої частини GTK - розробник повинен сам реалізувати потрібні йому набори, навіть якщо вони здаються тривіальними і такими, що здавалось би, мають бути з коробки. + +Щодо CSS - тут від нього тільки три літери, і мабуть три властивості в залежності від двох тегів, які оберете :) + +Є віджет для картинки ([Picture](https://docs.gtk.org/gtk4/class.Picture.html)) і є для більш простої піктограми, наприклад для віджету кнопки ([Image](https://docs.gtk.org/gtk4/class.Image.html)) якщо треба більше - пишіть самі. Мінімалізм. + +## Робота з діями + +Фреймворк базується на системі подій, тому, хто добре знайомий з JavaScript / Node.js, буде просто розібратись. +Кожен віджет здатен оголошувати власні [Action](https://docs.gtk.org/gtk4/actions.html), [ActionGroup](https://docs.gtk.org/gio/iface.ActionGroup.html), а також звертатись до глобальних рівнів вікна (`win`) та застосунку (`app`) + +Взагалі тема велика і було б добре написати про неї окремо, але нагадаю, особливо бекендерам: на систему дій потрібно звернути увагу в першу чергу і не писати передачу викликів методами класів, ін'єкцією залежностей і т.д., як робив це я спочатку. + +## Спільнота + +В GNU спілньота живе десь в чистилищах своїх гітлабів та інших екзотичних місцях. На гітхабі звісно ваша присутність не допоможе, доведеться по всім баг-репортам реєструватись на окремих сайтах. А для підтримки користуватись [форумом](https://discourse.gnome.org/). В принципі 2-3 користувача час від часу відповідають (схоже на співробітників фундації) але особисто мені це не зовсім зручно, в порівнянні з системою Issues та PR на GitHub. + +Для себе, в навчанні, й досі користуюсь ШІ, потім читаю документацію, пишу код, і так по колу. Пошук Google тут видає аж нічого цікавого. В принципі форум читати не дуже інформативно бо старі теми там закриваються і коментувати туди вже не можна. + +## Висновки + +Мені чимось подобається графічне середовище GNOME: можливо своєю простотою та однорідністю застосунків, що схоже за філософією на MacOS, користувачем і фаном якої я був довгий час. Й досі, використовую GOME на машині iMac (оскільки для розробки користуюсь Linux) + +GTK має більш інтуїтивний, матеріальний інтерфейс, в той час як Qt та різноманітні JS/Electron чомусь асоціюються з пластиком, де кожна програма має різні властивості, таймінги відгуку, що не зручно коли фокусуєшся на роботі. + +Звісно з трійкою все пішло мобільним шляхом, робочий стіл став пустим місцем, а програми такі прості що виконують буквально одну функцію. Все що більш менш в побуті - на Qt. + +Власне кажучи, буду намагатись й надалі в'їхати в тему GTK, а коли все вляжеться і засвоїться то може буде сенс написати якийсь навчальний матеріал по одній конкретній темі. Ну а зараз, думаю краще поділитись тим що маю - першим враженням з точки зору розробника, і як бачу GTK під капотом в цілому після декількох місяців користування. + +Щодо браузера - наразі в нього утворилось дві гілки: + +* [PHP-GTK3](https://github.com/YGGverse/Yoda/tree/PHP-GTK3) - більш функціональна але ймовірно вже архівна версія, оскільки писати на GTK 3 не бачу сенсу а вкладатись в контрибуцію бібліотеки Zend / GTK 4 зараз не цікаво. По суті включає базові функції - відкриває файли, протоколи [nex](https://devzone.org.ua/post/protokol-nex-lehka-alternatyva-gemini) та gemini. Останньою фічею була авторизація сертифікатами TLS (коди групи 60), тому якщо комусь цікаво - може поколупатись в робочих прикладах багатопоточної реалізації PHP/GTK так і обробки протоколу Gemini зокрема. +* [CPP-GTK4](https://github.com/YGGverse/Yoda/tree/CPP-GTK4) - остання версія, стан чернетки, в якій я досі не визначився з патерном. Можливо взагалі переключусь на C або відкрию нову гілку на Rust, який мене давно цікавить своїм пакетним менеджером. + +Поки що все, діліться вашим досвідом знайомства з GTK, якщо такий є ;) \ No newline at end of file diff --git a/post/moyi-pershi-vrazennia-vid-c.md b/post/moyi-pershi-vrazennia-vid-c.md new file mode 100644 index 0000000..ec925d1 --- /dev/null +++ b/post/moyi-pershi-vrazennia-vid-c.md @@ -0,0 +1,254 @@ +# Мої перші враження від C++ + +Я займаюсь професійною Веб розробкою вже близько п'ятнадцяти років, переважно у сфері back-end. Починаючи свій шлях в програмуванні, мав іншу вищу освіту і в мене не було змоги попрацювати та оцінити C++ в рамках класичної навчальної програми. Тому цей огляд являє собою точку зору аматора, для якого хоббі стало професією; хто вивчав програмування самостійно і починав це вивчення з мови високого рівня а не навпаки (у моєму випадку - PHP) + +## Передмова + +Так сталось, що у сферу Веб потратив через особистий інтерес до її потенціалу на ті часи, а домінантною мовою в мережі - була PHP, тому і обрав саме її. Так і вивчав: спочатку різні CMS, потім CMF, почав створювати власні самописи, потім бібліотеки. + +Звісно, в процесі довелось працювати і з суміжними мовами та їх технологіями (HTML, CSS, JS), так і альтернативи back-end, такі як Python. Оскільки вже більше 10 років користуюсь Linux, час від часу вносив невеличкі контрибуції в проекти на C/C++, відповідно працював з інструментами компіляції, встановлюючи останні версії таких програм. Також в мене був деякий досвід програмування контролерів Arduino, наприклад обробка сигналів різноманітних датчиків, взаємодія з GSM та інше. + +Отже заочно з мовами групи C знайомий, оскільки постійно натикався в роботі. Але мені ніколи не доводилось писати на ній щось нове, наприклад з використанням графічного інтерфейсу GTK або для роботи з мережевими інструментами. Одного разу, я знайшов собі таку задачу на вільний час - браузер для протоколу Gemini. Спочатку, написав прототип на вже знайомій мові PHP, з використанням бібліотек [PHP-CPP](https://github.com/CopernicaMarketingSoftware/PHP-CPP) та [PHP-GTK3](https://github.com/scorninpc/php-gtk3/), але виявив що в останній - багато методів GTK не реалізовані, тому витрачав багато часу на доробку бібліотеки, а не створення самої програми. До того ж на момент роботи, актуальною версією була GTK 4 і тут потрібно було або писати бібліотеку з нуля, або створити для браузера окрему гілку і писати його тією мовою, якою написаний сам фреймворк. Власне так і почався мій фундаментальний квест у світ C++ + +## Перехід з PHP + +PHP має C-подібний синтаксис, а об'єктне програмування - вже давно стало мейнстрім стандартом а не опцією, тому особисто мій перехід на C++ був відносно легким. Можна швидко переписати скелет програми, при цьому трохи видозмінивши оголошення класів, додавши їх до заголовкових файлів. + +Утім, присутня й кардинальна відмінність у моделі роботи з пам'яттю, яка може змусити переписати з нуля майже всю програму. + +## Вказівники і посилання + +Деякі складнощі можуть виникнути з "новими" типами даних: зокрема вказівниками та посиланнями. Це швидко стає зрозумілим, як тільки отримаєте segmentation fault і почнете копати причину вже на реально написаній програмі а не теорії. + +Оскільки C++ не використовує рантайм, як PHP, Python чи Go - написати програму без прямого звернення до пам'яті ви просто не зможете, а отже - доведеться трохи розібратись з тим, як для кожного з типів даних програма буде виділяти пам'ять та звільняти її. + +Що добре - в мережі просто навалом як документації, так і авторських уроків, зокрема відео курсів українською мовою. Тому зупинятись не будемо, в цілому в мене тема зайняла пару днів поки засвоїлась практично. Трохи ускладнюють тему так звані "розумні вказівники", які потягнуть за собою шаблони функцій та ще купу всього. Плюс вони мають різні реалізації в різних бібліотеках, але я вивчав їх послідовно - коли вирішував окремі проблеми вже потім, зрозумівши основи. + +Від себе тільки додам, що вказівник та посилання - це по суті "ярлик" на певну частину пам'яті, а не копія його об'єкту. Оскільки ваша програма не створює копії об'єктів кожного разу при їх зверненні, це і робить C/C++ таким швидким. З іншого боку, доступ до спільних ресурсів знижує стабільність бо є ризик перезаписати одні і ті само дані в різних частинах програми. Цим і займаються "розумні вказівники" тож по суті - все просто! + +## Конструкції `if` та `switch` + +Стосовно конструкцій `if` - в C++ всі вони повинні оперувати з типами `true` / `false`. В принципі, те само й для інших мов, просто там корекція типу відбувається автоматично, а тут неправильний тип викличе помилку компіляції. Я завжди намагаюсь оголошувати типи даних в PHP, тому в C++ це нарешті для мене велика перевага, а ніж незручність. + +Також є певні відмінності при роботі зі `switch` - тут ви не можете просто закинути в умову будь що, а повинні створити або число або попередньо оголошений `enum` (також є числом). Не зважаючи на те, що С++ синтаксично схожа на PHP, все таки це "найбільш низькорівнева мова високорівневих" (с) + +## Робота з типами даних + +На від міну від C та їй подібних мов, C++ має розширений інструментарій для роботи з висхідними і низхідними операціями перетворення даних: `const_cast`, `static_cast`, `dynamic_cast`, `reinterpret_cast`. На перший погляд, додаткові конструкції заплутують на етапі вивчення та ускладнюють читабельність коду - потім, але на практиці, дозволяють регулювати співвідношення продуктивності та стабільності програми. Характерні для C перетворення типу `(MyClass*)` не бажані, оскільки є найменш стабільними, у чому особисто я переконався доволі швидко :) + +## Перезавантаження функцій + +Якось рефакторив код і випадково помітив, що у класі лишилось два однойменні методи, при чому а ні аналізатор, а ні компілятор - не видавали помилок: + +``` cpp +void MyClass::method1(); +void MyClass::method1(int x, int y); +``` + +Наткнувся випадково і швиденько видалив, щоб не соромитись. Але згодом зрозумів, що це є фіча, якої мені давно не вистачало в інших мовах! Таким чином, можна створити багато методів з різним API, а не оголошувати значення атрибутів за замовчуванням та не придумувати якісь ініціальні дані для обов'язкових атрибутів. Тут є своя специфіка, але в цілому - дуже зручна опція! + +## Рядкові типи даних + +Де я спочатку застряг - так це на рядках. По-перше їх можна оголосити (і зберігати у пам'яті) різними способами: + +``` cpp +char str[] = "C++"; +char str[4] = {'C','+','+','\0'}; +const char* str = "C++"; +std::string str = "C++"; +// ... +``` + +По-друге, якщо ви працюєте з сирим типом `char`, вам доведеться постійно працювати з ними як з масивом і відповідно - його довжиною, наприклад ви не зможете просто передати змінні до макросів через `sprintf`, для цього вам знадобиться також знати кількість символів рядка, спочатку створивши його буфер (а також збільшувати його за необхідності). + +На щастя, в C++ багато попередньо реалізованих класів, що полегшують таку роботу. Зокрема, в своїй програмі на GTK, я оголошую рядки як об'єкти класів `Glib::ustring` (для STL є схожий метод) та працюю з ними так само, як в PHP: + +``` cpp +using Glib::ustring; // namespace для виклику sprintf + +sprintf( + "%s and %d", + "substring", + 1 +); +``` + +Тим не менше, все одно доведеться розібратись з примітивними типами даних, оскільки без цього ви просто не зможете зрозуміти як працювати з пам'яттю, не викликаючи її помилок роботи. + +## Масиви + +В C++ можна так само працювати зі статичними масивами, як і в інших мовах, але в більшості випадків, масиви використовуються саме для операцій з динамічними даними. Якщо в PHP можна просто накидати нові елементи масиву "в процесі роботи" програми, то в C++ потрібно спочатку подбати про виділення пам'яті, щоб випадково не зчитати взагалі дані іншої програми, коли відбувся вихід за рамки оголошеного діапазону. + +Це непорозуміння може виникнути тому, що PHP являє собою препроцесор, по суті заголовковий файл, де вся робота з динамічними даними виконується на етапі інтерпретації до запуску програми середовищем (runtime) якого в C++ просто немає. Тому таку роботу повинен виконати програміст - або на рівні препроцесору або виділяти пам'ять безпосередньо під час роботи програми. + +Коли стомитесь від ручного керування, дуже скоро віднайдете для себе "вектори" стандартної бібліотеки, які по суті являють собою допоміжні класи для роботи з динамічними масивами, так само як і відповідні класи для рядків - автоматизують рутинні задачі і роблять це безпечно (хоч і за рахунок зменшення швидкодії): + +``` cpp +std::vector numbers; + +numbers.push_back(10); // додати 10 в кінець масиву +``` + +Вектори будуть створювати новий простір (capacity) для ваших даних автоматично, тому не потрібно цим займатись самостійно. У класу векторів є багато інших методів, якими також можна задати початковий розмір простору або алгоритм перезапису, щоб збільшити швидкодію програми за рахунок виділення більшого об'єму пам'яті заздалегідь. Тому всі ці на перший погляд "складнощі" стають перевагами тоді, коли ви розберетесь в принципах їх роботи. + +## Класи + +Якщо в PHP класи - це такі собі структурні одиниці, де описані всі їх компоненти то в C++ класи розділені на файли для компіляції (`.cpp`) та окремі заголовкові файли (`.h`, `.hpp`), що описують структуру та забезпечують попередню логіку препроцесора. По суті, функціональність програми ділиться на таку, що виконується препроцесором до компіляції і ту, яка виконується після такої. + +### Простори імен + +Починаючи зі стандарту C++17, працювати з класами у заголовкових файлах доволі інтуїтивно: можна використовувати як інлайн, так і вкладені конструкції, скорочуючи об'єм коду через `using`: + +``` cpp +namespace myNamespace +{ + class myClass; +} + +using myNamespace::myClass; +``` + +Щодо синтаксису файлів для компіляції - мені не дуже сподобався формат оголошення членів класу, на рівні написання їх у спільному просторі файлу замість розміщення у фігурних дужках цього класу: + +``` cpp +void myClass::methodOne() {} +void myClass::methodTwo() {} +// ... +``` + +Звісно, я можу користуватись `using` для скорочення, але так як це виглядає класично - являє собою суцільні дублі назв одного й того ж класу. Поточний формат мабуть успадкував цей стиль від процедурного підходу C, або ж так зроблено для зворотної сумісності компілятора - іншого логічного пояснення не бачу. + +Складається враження, що сучасний підхід ООП з його практиками (зокрема, 1 клас - 1 файл) відбувся вже після того, як сформувалась мова C++, тут відчувається ретро. + +### Ключове слово `this` + +Після роботи з іншими мовами ООП, може збивати з толку відсутність ключового слова `this`, хоча воно доступне, але на практиці майже не використовується. Може виникнути ситуація, коли ви захочете передати однойменний аргумент функції: + +``` cpp +class MyClass +{ + int argument; + + void Method(int argument); +}; + +void MyClass::Method(int argument) +{ + argument = argument; // :) +} +``` + +Звісно, ви можете зробити так: + +``` cpp +void MyClass::Method(int argument) +{ + this->argument = argument; +} +``` + +Але такий спосіб мені здається не надійним і його робота може залежати від настрою певного компілятора, тому потрібно вигадувати якісь нові назви для однойменних змінних, у той час як стандартом C++ не рекомендується використання нижніх підкреслень на початку назви (через зарезервовані імена), а підкреслення в кінці - як правило означає назву приватного члена класу. + +Так як `this` чомусь не використовується цією мовою іншими програмістами, я це питання вирішую вбиваючи для себе двох зайців: і створюю константу (класично - у верхньому реєстрі) і пришвидшую роботу з пам'яттю через роботу з посиланням: + +``` cpp +void MyClass::Method(const int & ARGUMENT) +{ + argument = ARGUMENT; +} +``` + +### Інкапсуляція + +Так як фішкою C++ є саме ООП, зі всіма перевагами використання класів - зокрема інкапсуляції, не так давно я стикнувся з неприємним нюансом при роботі з посиланнями, раз вже згадав про них у попередньому розділі. + +Наприклад в мене є клас, що має певну закриту (private) структуру. Щоб пришвидшити роботу програми, я хотів би передавати її вміст за посиланням (через публічний getter) замість створення окремої копії об'єкта. Але оскільки я надаю зовнішньому компоненту посилання на "приватний" блок пам'яті, а також тип даних, який там зберігається, цей компонент може змінити дані, що розташовані в закритій структурі мого класу. + +Тут також є дуже спірні моменти з рівнем доступа `friend`, що ламає звичну інкапсуляцію. Це звісно не проблема, якщо ви самі пишете код програми або маєте програмний регламент, але таким чином немає жодних гарантій, що якийсь інший блок програми не перепише приватні дані. + +Таким чином, посилання або вказівники на дані приватних структур, які я віддаю на зовні, відкривають їх, і мені варто одразу зробити такі структури публічними, що по суті робить їх не класом а `struct`. + +## Заголовкові файли + +Для того, хто ніколи не зустрічав таких файлів (`.h`, `.hpp`) у сучасних мовах вищого рівня, постане логічне питання: навіщо вони взагалі потрібні. Наскільки мені вдалось усвідомити для себе, оскільки в C++ все повинне мати оголошений тип (для відповідного виділення пам'яті) це свого роду мета-інформація для зовнішнього об'єкту, без підключення безпосередньо його логіки (`.cpp`). + +Наприклад, коли потрібно використати певний тип даних з іншого файлу, що підключається - компілятор нічого не знає про цей тип, адже його не було оголошено раніше у поточному файлі. Саме тому, через заголовковий файл, передається структура доданого об'єкту і те, скільки і яким чином потрібно виділити під цей тип даних пам'яті. + +З іншого боку, я все ще не дуже зрозумів, як саме працює варіант у прикладі нижче і чому взагалі є така можливість оголосити тип даних у поточному файлі без підключення повних заголовків відповідних класів: + +``` hpp +// оголошення типів даних без підключення Class1, Class2, Class3 через include +class Class1; +class Class2; +class Class3; + +void someMethod(Class1, Class2, Class3); // це не викличе помилки компіляції +``` + +В заголовкових файлах іноді прописують допоміжну логіку для автоматичної "підстановки" даних до компіляції, наприклад - макроси або певні функції, які не потрібні безпосередньо під час виконання програми. Використання макросів, ніби як не рекомендоване в мові C++ тому я поки що не використовую в заголовках динаміку, але трішки експериментую з "пресетами", щоб не писати наприклад тексти та стандартні значення чисел в коді. Також, в цих файлах можуть бути оголошені стандартні визначення для аргументів функцій: + +``` hpp +void someMethod(int arg1 = 1, bool arg2 = true); +``` + +Мені це здається не дуже зручним, оскільки стандартні значення не можна вказати у файлах `cpp` і я постійно про них забуваю. Доводиться постійно тримати в голові два різних файли, з різним синтаксисом, які по суті являються компонентами одного майбутнього об'єкта. + +В іншому - по цій темі нічого складного немає, оскільки такі файли використовуються препроцесором, швидко приходить розуміння що саме в них писати, таким чином значно розвантажуючи основні файли для компіляції. Це простіше зрозуміти, якщо сприймати заголовки у якості "спільної" інформації для всієї програми, свого роду "абстракції". Структури, оголошені в заголовках, ще називають "прототипами". + +Додам ще кілька слів стосовно мого вибору назви розширення для заголовків. Адже можна назвати файл `.h`, а можна `.hpp`. Особисто я користуюсь в проекті C++ саме `.hpp` тому що розробники, які читатимуть код потім, можуть сприйняти `.h` як сумісний формат також для компіляції C. Це моя суто теоретична логіка. + +## Багатофайлові проекти + +Типовий для PHP `include` має аналогічний спосіб підключення файлів в C++ через `#include` який по суті копіює код донора в цільовий файл. + +Якщо потрібно використати `include_once`, то в C для цього використовується директива `#pragma once`. Можна також користуватись перевіркою визначення констант через `#ifdef`. + +* *`include` C++ є аналогом `require` в PHP, оскільки виконується на етапі передкомпіляції* + +Оскільки в PHP я користуюсь пакетним менеджером та засобами autoload, мені здалось трішки не зручним роботи це знову вручну. Більше того, потрібно в ідеалі спроектувати програму так, щоб не користуватись `#pragma once` взагалі, оскільки це зайва робота для процесору на етапі збірки, яка виконується постійно. + +## Пакетний менеджер + +Поки що, мені не доводилось користуватись такими бібліотеками, яких немає в репозиторії мого дистрибутиву. Звісно, розумію що одного разу такий момент настане, тим паче, що я звик виносити окремі функції програми на рівень маленьких бібліотек і працювати з ними через пакетний менеджер, замість використання моделей. + +В принципі, такі пакетні менеджери для C++ є, наприклад [Conan](https://conan.io). Але мене трохи засмучує, що немає якось єдиного хабу по типу Composer/Packagist для PHP. В цьому плані, звісно дуже виграє Rust з його пакетним менеджером Cargo. Хотілось би подібний набір для C++ з коробки, але все таки це мова, яка сформована спільнотою і має трохи іншу, свого роду анархічну парадигму в своїй основі, яка не прив'язує вас до конкретного рішення і дає повну свободу дій. + +Коли дійдуть руки до організації сторонніх бібліотек, в першу чергу, хочу спробувати варіант з `git submodule`. Це не зовсім пакетний менеджер, але у даного рішення є таке поняття як "теги", з яких формуються безпосередньо версії, `git` також перед-встановлено на більшості систем тих, хто займається збіркою і не доведеться вимагати від користувача додаткових рухів. Тому наразі це рішення для мене виглядає найбільш привабливим, перш за все - з точки зору універсальності. + +## Автоматична збірка + +До цього поки що не дійшов, але історія обіцяє бути веселою. Не кажучи про мобільні платформи, наразі я розробляю та запускаю програму на Debian 12, але досі не можу зробити цього на Ubuntu 24.04 через конфлікти залежностей, жорстко прописані у `Makefile`. Окрім `make` існує чимало альтернативних рішень, але використовуючи їх для себе, мені також доведеться подбати і про класичний спосіб в `README`. + +Вже заздрю користувачам більш сучасних екосистем Rust та Electron :) + +## IDE + +Однією з помилок, якої припускався, коли працював з C++ до того як взявся за справу фундаментально, була відсутність правильно налаштованого середовища для розробки. Раніше я користувався продуктами JetBrains, але потім перейшов на VSCodium, де середовище під кожну мову потрібно збирати самому. + +Тема IDE потягне на окремий матеріал, але у будь якому випадку, перш ніж почати вивчення і практичну роботу, важливо одразу користуватись відповідними інструментами, що полегшують інтерпретацію та аналіз коду локально, до компіляції всієї програми. Інакше ви ризикуєте пропустити вивчення мови тільки по тій причині, що вона була погано інтегрована! + +### Аналізатор коду + +Тут, окрім вбудованої підсвітки синтаксису, важливо також довстановити аналізатор коду, який буде здійснювати попередню компіляцію і підсвітку типів даних, а також додасть підтримку вбудованої документації та авто-доповнень, що особливо зручно при роботі з наслідуванням класів. + +Особисто я користуюсь плагіном [clangd](https://github.com/clangd/vscode-clangd) і навіть не уявляю, як раніше редагував і збирав програми без його функцій! Важливо не тільки встановити плагін, але й правильно налаштувати залежності проекту (у даному плагіні це `clangd.fallbackFlags`), які використовуються в лінкері - без них, встановлений аналізатор просто не розкриє свій потенціал. + +### Дебагер + +Іншим рішенням, без якого не обійтись - це звичайно дебагер для відлагодження коду, у моєму випадку - [codelldb](https://github.com/vadimcn/codelldb) + +### Авто-доповнення + +Є думки поставити плагін ШІ, наприклад Copilot, але поки що надаю перевагу Chat GPT і Claude 3 Haiku в браузері. Якщо ви користуєтесь перевіреними плагінами ШІ для C++ то будь ласка, поділіться ними в коментарях - для мене це актуально! + +## Висновки + +Безумовно, C++ є дуже потужним інструментом, що відкриває можливості, які ви не отримаєте від подібних мов на рантаймі. З іншого боку, не завжди такі можливості можуть бути виправдані по часовим витратам, які потрібні на забезпечення їх стабільності. + +Це можна порівняти з ручною коробкою передач в автомобілі: у певних моментах вона виручає, але у більшості випадків ви будете робити зайві рухи, стоячи десь у заторі тільки витрачаючи зайве пальне (так само як і розробляти певні програми на потоці). + +В плані мови для загального розвитку, як хоббі, C++ нагадує мені гру з відкритим світом, де можна робити все, що завгодно і як завгодно. Ця гра має великий всесвіт, який наврят може бути колись вивчений, навіть якщо ви працюєте з плюсами все життя. Тут немає основного сюжету чи сценарію, це динамічне середовище, яке об'єднує тільки стандарт, що оновлюється кожні три роки. + +Подібно як з PHP - мало розуміти мову, потрібно знати певний фреймворк, патерни та інструменти, і вже тоді можна робити якісь практичні речі ефективно. Так само і тут: шлях, яким ви підете вірогідно залежатиме від бібліотеки, яку почнете вивчати, наприклад у моєму випадку - це GTK, тому я вже мабуть краще знаю API цієї групи бібліотек, ніж STL. + +C++ це основа, свого роду класика. Я не міг пройти повз. Особисто, вивчаю її у вільний час просто тому, що мені вже не цікаво розробляти програми на тих мовах, які я знаю добре. А розуміння того, як написаний їх інтерпретатор допомагає мені також краще розуміти принцип роботи таких програм, вносити контрибуції в їх рушій, та експериментувати з новими технологіями, які працюють на пристроях з обмеженою пам'яттю або вимагають максимальної швидкодії. \ No newline at end of file diff --git a/post/moyi-pershi-vrazennia-vid-flarum.md b/post/moyi-pershi-vrazennia-vid-flarum.md new file mode 100644 index 0000000..82d1212 --- /dev/null +++ b/post/moyi-pershi-vrazennia-vid-flarum.md @@ -0,0 +1,30 @@ +# Мої перші враження від Flarum + +Я знаю, що тема вибору рушія для форуму й досі актуальна для адмінів, тому лишу деякі спостереження після свого першого досвіду користування [Flarum](https://flarum.org) впродовж місяця. + +## Сподобалося + +* Мінімалістичний. Якщо порівнювати з популярним [Discourse](https://www.discourse.org) - я не втопився в налаштуваннях, а також не мучусь з усіляким задротним JS типу кастомних `Ctrl+F` (та їх вимиканням) +* Зручно писати довгі дописи, статті. Усі навороти організовані модулями, я просто вимкнув [BBCode](https://uk.wikipedia.org/wiki/BBCode) і лишив [Markdown](https://uk.wikipedia.org/wiki/Markdown), тому усі мої попередні дописи вдалося просто портувати. Також тут зручна форма редагування: вона влаштована так, що вікно можна просто масштабувати, при цьому текст вирівняно до ширини реального блоку стандартної теми; оновлення існуючого блоку відбувається прямо під час написання. +* Правильна архітектура на базі [Symfony](https://symfony.com) / [Laravel](https://laravel.com): це значить що тут використовуються промислові, перевірені часом стандарти, а не самопал. Це трохи ускладнює модифікації в плані часових витрат на розгортання середовища але воно того варте, плюсую. +* Є більш-менш робочий сценарій перегляду форуму без JavaScript. Це може бути зручно для користувачів Інтернет в плані [SEO](https://uk.wikipedia.org/wiki/Оптимізація_для_пошукових_систем) просування а також альт-мереж, в плані приватності - бо тут часто зустрічаються садомазохісти що орієнтуються без ~~світла~~ JS. +* Адаптивність: підтримка мобільних пристроїв і світла/темна теми з коробки (для другої версії рушія) - останнє це суб'єктивно важливий фактор на мої "професійні" очі. + +## Не сподобалося + +* Повільний. Реально настільки, що я спочатку не розумів які такі функції працюють на бекграунді, але вимкнувши дебаг режим, інших не знайшов. Тут можна грішити на SSD і той факт, що я не налаштував сесії в пам'яті, але в мене на цьому ж диску працює [βtracker](https://devzone.org.ua/post/vtracker-bittorrent-ahrehator-na-bazi-rust), який швиденько собі шукає по нинішнім 500 торентам, враховуючи час на bencode парсинг, scrape та повнотекстовий пошук файлів, яких там буває до десятка тисяч на торент. Не знаю коротше, поки загадка для мене. +* Немає RSS з коробки. Це сабж другої версії рушія, котру я поставив від балди як "профі" не боючись бета-тестів і можливих доопрацювань - я потім не хочу мігрувати, мені простіше почати з другої. Отже, я [поцікавився](https://discuss.flarum.org/d/38016-rss-feed-for-v2) сабжем, та зрозумів, що адміни ультра-модні, RSS для них вже не камільфо - враховуйте. +* Немає української локалізації і покищо хз як натягнути першу версію локалізацію рушія на другу, хоча б частково, я думаю там має бути спільний скелет. Майнейнеру на моє питання - [пофіг](https://discuss.flarum.org/d/31921-ukrainian-language-pack-for-flarum/54). +* Налаштування теми. Зокрема - теґів: тут якийсь фільтр палітри, дрочусь і по цей день: бо в темній темі одне а в світлій інше. Утім, це не є великою траблою якщо зайнятись питанням фундаментально. +* Хоч і наявне сховище файлів для аватарок, не можливо просто додати вкладення до повідомлення, тому наприклад, далекі від технічних приколів адміни сусіднього форуму `zabytki.in.ua` - публікують історичні фото на сторонніх сервісах, типу `twimg.com` (про що я їм своєчасно [нагадав](https://zabytki.in.ua/community/d/618-zovnisnye-sxovishhe-dlya-zobrazen)) +* З коробки немає функціональності відтермінованих дописів, чернеток та автозбереження, що б могло бути зручним в локальних умовах перебоїв з живленням. +* Немає підтримки альтернативних URL парсером Markdown, зокрема - сирих IPv6 і усіляких протоколів (схем) [Gemini](https://devzone.org.ua/post/protokol-gemini-iak-alternatyva-http), [Nex](https://devzone.org.ua/post/protokol-nex-lehka-alternatyva-gemini) тощо: й досі вісять не клікабельні лінки, знаю, але фікситиму потім - тут в нас під капотом патерни Symfony / Laravel. +* Немає підтримки кастомних E-mail та альтернативної системи авторизації зокрема. Є окремі модулі OAuth, але в рамках моєї [спільноти](https://devzone.org.ua/topic/ukrayinska-spilnota-administratoriv-alternatyvnykh-merez) не актуально. Думаю, це частково вирішується модулями але з побаченого я зустрів залежність від констант [filter_var](https://www.php.net/manual/en/function.filter-var.php) аж на рівні хвалених вище компонентів Symfony. Також є науково-популярні рішення від типових веб-розробників, які мені замість дебатів, таки довелось просто вирішити для себе [форком](https://github.com/YGGverse/flarum-framework/commit/bd421ee6604b358a9c81e59d4f100c93e2ce264d) фреймворку. + +## Висновки + +Якщо не брати до уваги моє ставлення до сучасних Веб технологій в цілому - то я задоволений. Археологію типу [phpBB](https://www.phpbb.com), [SMF](https://simplemachines.org) я в принципі сьогодні для створення форуму спільноти відкинув, а сучасний Discourse мені не зайшов на рівні користувача (зокрема - форумів [GTK](https://discourse.gnome.org) і [Rust](https://users.rust-lang.org)). Тому планую користуватись Flarum надалі. + +## Дивіться також + +* [Встановлення і налаштування Flarum v2 (beta)](https://devzone.org.ua/post/vstanovlennia-i-nalashtuvannia-flarum-v2-beta) \ No newline at end of file diff --git a/post/moyi-pershi-vrazennia-vid-rust.md b/post/moyi-pershi-vrazennia-vid-rust.md new file mode 100644 index 0000000..4ea4da3 --- /dev/null +++ b/post/moyi-pershi-vrazennia-vid-rust.md @@ -0,0 +1,215 @@ +# Мої перші враження від Rust + +У попередній [публікації](https://devzone.org.ua/post/moyi-pershi-vrazennia-vid-c), я описував свій перший досвід переходу з PHP на C++, в рамках створення [браузеру](https://github.com/YGGverse/Yoda) для протоколу [Gemini](https://devzone.org.ua/post/protokol-gemini-iak-alternatyva-http). Не зважаючи на те, що у цьому напрямку було виконано багато [роботи](https://github.com/YGGverse/Yoda/tree/CPP-GTK4), все таки, виникли певні труднощі з ручним контролем пам'яті, а саме - в рамках відсутності досвіду, просто не розумів де саме я міг допустити помилку, як довго живуть мої об'єкти і про які з них міг забути. Іншою проблемою стала збірка програми на різних пристроях, і врешті останнім фактором стало встановлення додаткових залежностей, адже в C++ немає єдиної екосистеми пакунків, накшталт `npm`, `Composer` і т.д. З іншого боку, можна поставити [Conan](https://conan.io/), але мені просто не захотілось в тому розбиратись, оскільки ця система вимагає використання Python, що якось трохи дисонує в плані подальших перспектив такого симбіозу. + +Орієнтовно через місяць роботи з плюсами, вирішив не витрачати час, та спробувати сучасну і давно розхайповану мову [Rust](https://www.rust-lang.org/), від якої, чесно сказати, довго морозився. Від самого початку, всі дороги вели сюди і врешті перевів реалізацію браузеру в окрему гілку, почавши розробку спочатку. + +## Синтаксис + +Синтаксис Rust, мені здається якимось мультяшним, мовою, яка пишеться з права на ліво, своєю екзотичністю чимось нагадує Ruby, якою бувши розробником PHP, в принципі цікавився але в так і не дійшли руки. З іншого боку, нагадує JS, який особисто мені чомусь ніколи не подобався (як і Python). Тобто я був таким собі консерватором і варився в C-подібній екосистемі, нічого не хотів змінювати, але тут сталось те, що описано вище - тепер мені не хотілось розбиратись в додаткових інструментах контролю пам'яті а не синтаксисі. + +### Стилістика + +Стиль коду горизонтально-орієнтований, у той час як мені подобається писати і читати короткі рядки, віддаючи пріоритет скролу. +Якщо використовувати стандартну конфігурацію [rustfmt](https://rust-lang.github.io/rustfmt/), то написаний в ручному форматуванні код перетворюється в таку собі склеєну горизонтально кашу, яку реально важко читати, але вирішив не відмовлятись від загально-прийнятого оформлення і адаптуватись, максимально розділяючи об'ємні структури коду на окремі моди, про що згодом. + +### Відсутність NULL + +Що мене постійно манило в Rust - це сучасні підходи програмування, які виключають застарілі конструкції через їх спірне застосування. `NULL` - це тип даних, який по суті може означати і нічого і все водночас, цим часто зловживають розробники, використовуючи її як `false` або як тип, що має бути визначений батьківським елементом. Особисто в мене проблем з цим не було, оскільки й не такого бачив в * кодах PHP. З іншого боку, в C++ постійно доводилось працювати з `nullptr` що в принципі знову натякає на давню потребу модернізації цього типу даних. + +Rust від самого початку вимагає певного стилю програмування з коробки, таким чином ведучи за руку "праведною стезею" сучасних підходів, виключаючи помилки та власну хвору фантазію. Більше інформації про спірність використання `NULL` можна знайти в мережі, окремо описувати подробиці тут не буду. + +### Option, Result, match + +Продовжуючи тему сучасних конструкцій, на заміну `NULL` в Rust присутні такі типи нумерованих списків як `Option` та `Result` а також новий оператор `match` для роботи з ними і не тільки. + +`Option`, на прикладі двох змінних, перша з яких повертає "рядок" а друга - "нічого": + +``` rust +let some_string: Option = Some(String::from("Hello, Rust!")); +let none_string: Option = None; + +match some_string { + Some(string) => println!("деяке значення: {string}"), + None => println!("значення відсутнє"), +} +``` + +* по аналогії можна додати необмежену кількість варіантів + +Якщо певній функції потрібно повернути статус успішної операції або помилки, у таких випадках доречніше використовувати тип `Result`: + +``` rust +// Функція повертає i32 суми або String з описом помилки +fn sum(a: i32, b: i32, max: i32) -> Result { + let sum = a + b; + if sum > max { + return Err(format!("sum > max")); + } + Ok(sum) +} + +// якщо 1 + 2 менше максимального значення - друкуємо результат, +// якщо більше - викликаємо паніку або делегуємо обробку помилки +match sum(1, 2, 10) { + Ok(value) => println!("sum: {value}"), + Err(reason) => panic!("{reason}"), +} +``` + +Це звісно дуже спрощений приклад, для наочної демонстрації обробки результатів без `NULL` - такий підхід забезпечує коректність виконання програми вже на етапі її написання. + +Оператор `match` дозволяє замінити собою класичні конструкції `if` та `switch`, і подібно до останнього, створювати вкладене розгортання умов. + +### Лаконічність + +Як і в Ruby, для Rust характерна лаконічність коду, про це, зокрема, натякають максимально скорочені ключові слова функцій (`fn`) та інших елементів. Кажучи безпосередньо про опис логіки, в нагоді початківцям стане такий інструмент як [Clippy](https://github.com/rust-lang/rust-clippy), який окрім пошуку помилкових конструкцій (типу зайвих референсів) запропонує замінити такий код: + +``` rust +match result.get(0) { + Some(value) => Some(value.row), + None => None, +} +``` + +на такий: + +``` rust +result.first().map(|value| value.row) +``` + +Скорочення зустрічаються тут всюди, наприклад замість `if`/`else` або його цукру +``` +a = b > c ? true : false +``` +можна зустріти мабуть найкоротший варіант умови "`?`", яка поверне стандартну відповідь дочірнього компонента батьківському при невідповідності результату: + +``` rust +// зупинка циклу з select return при помилці +for record in select(&tx)? { + // зупинка циклу з update return при помилці + update(&tx, record.id, false, record.time, record.name)?; +} +``` +* тут варто зазначити, що тип, який повертається повинен відповідати патерну `return` батька + +### Не мутабельність за замовченням + +Про синтаксис Rust можна багато чого розповісти. Наприклад, усі змінні (variables) є незмінними за замовченням. Якщо дані, що зберігаються в змінній повинні бути мутабельними, вона повинна бути від початку оголошена ключовим словом `mut`: + +``` rust +let var_1: bool = true; +let mut var_2: bool = true; + +var_1 = false; // помилка, оскільки змінна var_1 оголошена як не мутабельна +var_2 = false // ok +``` + +Це може здаватись дивним, але на практиці дані у пам'яті для оголошених змінних доволі рідко змінюються. Наприклад, в плюсах я майже всюди оголошував константи, щоб у великій програмі не забути переписати значення випадково там, де це не було заплановано. + +Не мутабельність насправді здебільшого потрібна для коректної роботи компілятора та контролера посилань (borrow-checker), які забезпечують безпечну роботу з пам'яттю за рахунок виконання однієї простої умови: може бути або одне мутабельне посилання або не обмежена кількість не мутабельних. Тут немає збірника сміття та рантайм-середовища, але є ось такі принципи. + +### Вказівники + +В Rust є і вказівники (pointers) і посилання (references). Перші в сирому вигляді тут зустрічаються доволі рідко, можливо просто саме я не зустрічав їх в екосистемі `gtk-rs`. Пояснити це можна безпекою роботи з пам'яттю. Для роботи з вказівниками, в Rust використовуються обгортки (wrappers) типу `Box`, `Rc`, `Arc`, та інші, - по суті вони є аналогами таких класів як `std::shared_ptr` в C++. Різняться сферою застосування: одні гарантують унікальність копії, інші - безпечну роботу в асинхронних потоках, або реалізують внутрішній лічильник посилань, або прапор блокування, тощо. + +Зупинятись не буду, це просто величезна тема, вказівники тут є, але в режимі safe (окрім типів, що копіюються) майже всі вони обгорнені класом, формуючи такі "страшні" для незнайомців конструкції як `Rc>>` - де `Rc` просто надає можливість клонування вказівника на ту саму область пам'яті, `Mutex` забезпечує мутабельність, а про `Option` ви вже знаєте ;) + +## Організація файлів + +В Rust файли проекту організовані у вигляді модів (mods) та крейтів (crates). + +Моди - це по суті окремі файли, або модулі, з яких складається крейт, тобто фінальний пакунок програми. В проекті також може бути декілька крейтів а також локальні їх форки, оголошені наприклад через наступну конструкцію в `Cargo.toml`: + +```rust +[dependencies.crate_1] +package = "crate_1" +version = "0.1.0" + +[patch.crates-io] +crate_1 = { path = "crate_1" } // локальний форк crate_1 +``` + +Кастомізація `Cargo.toml` - також окрема тема, тут не будемо зупинятись, скажу тільки що це головний файл маніфесту кожного для крейту, в той час як моди - просто файли, які підключаються до основного контролеру `main.rs`. + +Окрім цього, в екосистемі присутнє поняття бібліотек (`lib.rs`) - це по суті бібліотечні файли, які не можуть бути виконані без батьківського `main.rs`. Кожен крейт може включати в себе бібліотеку або тільки реалізовувати її, тоді такий крейт буде вважатись бібліотекою. + +На відміну від C/C++, в Rust немає заголовкових файлів (headers), файли підключаються через ключове слово `use` засобами неймспейсів. Не потрібно руками резольвити файлові залежності, що найменше такий підхід є опціональним (з використанням анотацій). При стандартному підході, є два способи організації файлів: + +1. Старий (2015) - це коли файл моду має зарезервовану назву `mod.rs` та розміщується в теці з назвою модуля +2. Новий (2018) - файл моду має довільну назву, але такий підхід вимагає розміщення дочірніх модів у теці з однойменною назвою. + +Новий підхід з'явився по причині того, що при відкритті багатьох файлів в IDE, всі таби будуть мати назву `mod.rs`, що погодьтесь, не дуже зручно в плані навігації, коли мізки зайняті кодом а не пошуком файлу в дереві. + +Детальніше про тему йменування файлів, можна почитати [тут](https://doc.rust-lang.org/edition-guide/rust-2018/path-changes.html). + +У цілому, мені знадобився певний час, щоб розібратись в темі. Тут немає такої файлової "анархії", яка є в інших мовах. Це на мою думку і добре, оскільки інші розробники зможуть розібратись у вашому коді (якщо ви не реорганізуєте проект анотаціями чи специфічними оголошеннями залежностей в `Cargo.toml`) + +## Cargo + +Що дійсно круто в Rust - це система пакунків Cargo та її репозиторій [crates.io](https://crates.io/). Основна система пакунків тут одна, вона офіційна і є стандартною. Більше немає солянки з різних компіляторів, пакункових менеджерів, аналізаторів та іншого. Все зібрано довкола Cargo. + +Легко ставити, легко майнтейнити. Єдине, якщо залежності програми вимагають бібліотек C, тим паче, як в моєму випадку - інтеграція з робочим столом GNOME, будьте готові змінювати дистрибутив на Fedora чи компілювати самому пів системи, ну або щось думати з контейнерами. Чудес не буває. А ось руками резольвити дикі бібліотеки під різні платформи не треба, Cargo все зробить сам - перевірить залежності, оновить код, перевірить помилки, скомпілює та опублікує ваш пакунок. + +До речі, опублікований пакунок на crates.io видалити не вийде, оскільки такий підхід гарантує доступність версії для залежних пакунків, що встигли її включити в свій склад. Є опція `Yank` але будьте готові що вона просто позначить версію як відхилену, а не видалить її з репозиторію. Оновити пакунок можна тільки додавши нову його версію. + +Варто окремо згадати про дані, які вивантажує команда `cargo publish`. Спочатку я забув ознайомитись з документацією, але пощастило, що Cargo вже про все подбав і заігнорив сенситивні дані типу `.vscode`, `.git` та інші теки в корені проекту. Перевірити, чи це дійсно так, можна командою в корені проекту до його публікації: + +```bash +cargo package --list +``` + +Оптимальним способом встановити Rust і Cargo останніх версій, є утиліта [rustup](https://rustup.rs/). + +## Інше + +### Наслідування + +Як відомо, Rust не реалізує класичну систему наслідування класів, оскільки така парадигма вважається застарілою. В той час, як мій браузер вже було написано з використанням обгортки [gtkmm](https://www.gtkmm.org), тут мені довелось знову проектувати скелет програми з нуля. Хоча я довго намагався натягувати логіку наслідування на ненатягуване. + +По нормальному, програму все таки краще писати тією мовою і парадигмою, на якій і засобами якої реалізовано обраний фреймворк. Тоді, мій вибір на плюси пав саме тому, що не хотів руками реалізовувати всі ті низькорівневі обгортки для C, які є в плюсовому `gtkmm` і так вийшло що з Rust я отримав подвійний трабл - це і новий підхід і новий синтаксис як бонус :) + +Загадавши процедурний підхід, в останній раз користувався ним років 15 тому, коли тільки починав програмувати. Потім купив книгу з патернами, перейшов на ООП і повністю забув що це таке, не знаю взагалі які реалізації зараз. Тобто для мене ієрархічні конструкції та функції - це дрімучий ліс, всі нейрони побудовані під об'єктно орієнтоване мислення, з наслідуванням та іншими плюхами, деякі з яких, до речі, сьогодні критикують через їх надмірні залежності від батьківських класів. + +Поки що вивчаю цю тему на практиці, думаю розібратись з патернами на основі `enum`, тому додати нічого. Якщо вирішили йти шляхом Rust - це не про ООП та розширення батьківських класів. Іноді, мені доводиться наслідувати певні класи `GObject` в `GTK` обхідними шляхами, але нативно, майже все обертається довкола трейтів (`trait`) та їх імплементацій (`impl`-ementation). + +### Лайфтайми + +Тут хочу згадати тільки про один, не зручний особисто для мене момент: якщо ви працюєте з C-ішними обгортками типу `gkt-rs`, можуть виникнути певні непорозуміння з часом життя об'єктів, оскільки в Rust, автоматично відбувається зменшення лічильника посилань (або знищення), при виході об'єкта з області видимості. + +Наприклад, в GTK є такий клас як [SocketConnection](https://docs.gtk.org/gio/class.SocketConnection.html), а також асинхронні методи як наприклад [SocketClient::connect_async](https://docs.gtk.org/gio/method.SocketClient.connect_async.html), який виконується на стороні фреймворку, але й живе в основному потоці програми Rust. Якщо ви НЕ передасте клон `SocketConnection` в цей метод, то компілятор автоматично зменшить для нього лічильник посилань, при виході за область видимості, а GTK, у свою чергу - видалить, коли його значення сягне нуля. Таким чином, отримаємо закрите з'єднання ще до того, як воно буде оброблене через асинхронний метод. Саме ця проблема, вирішується передачею клону поінтера `SocketConnection` в тред, який триматиме кількість посилань до свого завершення, або передаватиме його володіння дочірнім структурам. На практиці це не завжди зручно і часто стає причиною непорозумінь в пошуках багів, коли сторона C очікує від вас ручного видалення посилання, а Rust робить це автоматично. + +Тему керування пам'яттю в GTK засобами C та C++, описував окремо у попередніх публікаціях: + +* [Керування пам’яттю в програмах GTK](https://devzone.org.ua/post/keruvannia-pamiattiu-v-gtk) +* [Керування пам'яттю в gtkmm-4.0](https://devzone.org.ua/post/keruvannia-pamiattiu-v-gtkmm-40) + +### Асинхронність + +Асинхронність тут є, така опція була не від самого початку (як наприклад в Go) але все таки її згодом впровадили. Як писав вище, я особисто не користуюсь штатними засобами Rust, оскільки API мого фреймворку надає зручні методи роботи в основному потоці, за рахунок вбудованої системи подій. + +Багато хто сварить штатну реалізацію за використання `await`, тут не скажу. Особисто для себе, зрозумів що асинхронні методи працюють з асинхронними методами, тобто якщо програма вимагає асинхронних потоків, така функціональність повинна бути закладена в її архітектуру на етапі проектування. Звісно тут з'являється додатковий thread-safe інструментарій та відповідні розумні вказівники такі як `Arc`, блокувальники `Mutex`, `RwLock` та інше. + +Особисто на практиці, намагався тільки швидкоруч натягнути інтеграційні тести (оскільки вже встиг написати зовнішню бібліотеку, щоб розвантажити основний код програми), але поки відклав це заняття, оскільки схоже, що для моєї задачі потрібна інтеграція [Tokio](https://crates.io/crates/tokio). + +## Спільнота + +Спільнота Rust - супер жива та активна. Наразі є два форуми - безпосередньо [для розробників мови](https://internals.rust-lang.org/) та [для користувачів](https://users.rust-lang.org/), де вам не тільки оперативно допоможуть з будь яким питанням, а також безкоштовно проведуть аудит якості коду у відповідній гілці. + +Є також чати, але мені вони не цікаві, тому у будь якому разі ком'юніті є і воно дружнє до новачків. + +Щодо соціальних мереж, на YouTube мені подобається один канал [Bitωise](https://www.youtube.com/@bitwiseuwu). Якщо ви початківець в програмуванні, можу порекомендувати також хоч не про Rust, але якісний україномовний канал [Blogan](https://www.youtube.com/@BloganProgramming), зокрема курс теорії C++ який стане в нагоді для розуміння основ організації пам'яті для різних типів даних. + +Існує [проект](https://rust-lang-ua.github.io/) української локалізації [Rust Book](https://doc.rust-lang.org/book/) на [GitHub](https://github.com/rust-lang-ua) та колективним перекладом на [Crowdin](https://crowdin.com/project/rustukrainian/uk). + +## Висновки + +В цілому, я задоволений своїм новим вибором. Чи готовий повертатись на плюси чи скажімо на C - не знаю, на плюси точно ні, а до C в мене якісь перманентно теплі почуття, не знаю чому. Можливо ця мова є найпростішою, можливо тому що програмував на Arduino та рахував там доступну пам'ять. Тим не менше, я не можу довіряти собі настільки, щоб впевнено писати чистий код без витоків пам'яті у програмах більших за контролер датчика температури. Враховуючи кількість підказок компілятора та аналізатора Clippy, ця впевненість остаточно зникла а ставити додатковий софт чи витрачати час на вивчення такого, коли є все готове - не хочу. + +Звісно, чутки про те, що швидкість Rust дорівнює C не зовсім однозначні. Тут можуть використовуватись зовсім інші підходи для однакових конструкцій. Без розуміння що діється під капотом компілятора, написати такий само швидкий код навряд чи можливо, але на першому місці тут саме безпека роботи з пам'яттю, швидкість для мене і не є пріоритетною. Впевнений, що копнувши тему оптимізацій, дізнаюсь ще багато нового. + +Кажучи про сфери застосування і комерс, тут поки що далі свого аматорського проекту діло не зайшло. Просто не знаю, де шукати ті замовлення окрім декількох контор на доу, які щось ляпають там англійською по лекалу. До того ж саму мову знати мало, тут більше відіграє роль екосистеми під яку ви пишете. У моєму випадку це малопопулярні `GTK`, `Glib`, `Gio` та інші, цих бібліотек мені вистачає як для роботи з сокетами, сертифікатами TLS, мультимедійними даними, так і для реалізації графічного інтерфейсу, але всі вони написані у свою чергу на C, на Rust я поки що не знайшов такого фреймворку, який би мені сподобався ([egui](https://github.com/emilk/egui) і [tauri](https://github.com/tauri-apps/tauri) це трохи не те що мені підходить) + +Чи замінить ця мова C - не знаю, як писав на початку, мені вона здається якоюсь мультяшною та іграшковою, хоча цією іграшкою можна бити горіхи. У той час як C - це скло, класика, на якій написане мабуть все сучасне програмне забезпечення. Як мені відповіли на одному з форумів GTK, ніхто не буде просто так переписувати мільйони строк відтестованого коду, тому я думаю що нинішній тренд пройде і залишить Rust на тому ж рівні та в ніші, у яких сьогодні знаходиться Ruby. + +Ось мабуть і все, чим хотів поділитись, можливо згадаю ще щось - доповню. \ No newline at end of file diff --git a/post/nalashtuvannia-fediverse-serveru-snac-dlia-roboty-v-merezi-yggdrasil.md b/post/nalashtuvannia-fediverse-serveru-snac-dlia-roboty-v-merezi-yggdrasil.md new file mode 100644 index 0000000..94bf29c --- /dev/null +++ b/post/nalashtuvannia-fediverse-serveru-snac-dlia-roboty-v-merezi-yggdrasil.md @@ -0,0 +1,298 @@ +# Налаштування 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` відповідно до типу з'єднання клієнтського резольвера. Так чи інакше, це - вже зовсім інша історія! \ No newline at end of file diff --git a/post/nalashtuvannia-vykhidnoho-proksi-na-bazi-squid.md b/post/nalashtuvannia-vykhidnoho-proksi-na-bazi-squid.md new file mode 100644 index 0000000..0ec3fcf --- /dev/null +++ b/post/nalashtuvannia-vykhidnoho-proksi-na-bazi-squid.md @@ -0,0 +1,83 @@ +# Налаштування вихідного проксі на базі squid + +Якось, був організував собі [віртуальну систему, ізольовану від Інтернет засобами VSOCK](https://devzone.org.ua/post/izoliatsiia-linux-vid-priamykh-internet-zyednan-na-bazi-qemu-virtual-machine-manager-i-vsock), з доступом виключно до мережі [Yggdrasil](https://devzone.org.ua/post/yggdrasil-mereza-z-detsentralizovanym-routynhom). Оскільки мені потрібно звідти якось брати оновлення і робити це безпечно, вирішив на іншій машині, де є Інтернет - підняти вихідний проксі (out-proxy) + +## Сервер + +В Debian, сервер squid ставиться з репозиторію: + +``` bash +apt install squid +``` + +### Налаштування + +Тут в мене передбачається взаємодія між проксі-сервером "a" та клієнтом "b" на IPv6 інтерфейсі: + +``` /etc/squid/debian.conf +#/etc/squid/debian.conf + +# інтерфейс, на якому слухати вхідні підключення +http_port [aaa:aaaa:aaaa:aaaa:aaaa]:3128 + +# налаштування журналів +logfile_rotate 0 + +# оголошуємо групу "remote", куди входить IP "b" +acl remote src bbb:bbbb:bbbb:bbbb:bbbb + +# оголошуємо набір правил "updates" типу "dstdomain" +# мені проксі потрібен тільки для системних оновлень, +# окрім iptables, на всяк випадок, я вирішив додати правила на піддомени (у вас може бути інший набір - дивіться /etc/apt/sources.list.d) +acl updates dstdomain .debian.org .debian.net .armbian.com .github.com .gitlab.com .crates.io .fastlydns.net .librewolf.net .nodesource.com + +# вмикаємо обмеження у послідовності +http_access allow remote updates +http_access deny all +``` + +Запускаємо і дивимось чи все в порядку: + +* `systemctl restart squid` - застосувати зміни конфігурації (перезапуск) +* `systemctl enable squid` - автозапуск проксі сервера при старті системи +* `systemctl status squid` - статус проксі сервера +* `netstat -tulpn | grep 3128` - перевірити, де слухається порт + +### Фаєрвол + +Yggdrasil - це локальна мережа, відповідно до нашого проксі можуть підключитись усі її користувачі (а не тільки наша віртуальна машина) +Якщо ви не хочете щоб усі ходили з вашого Інтернет IP на вказані сервери групи "updates", потрібно дозволити доступ тільки конкретній адресі, у нашому випадку це "b". Для керування iptables, я використовую фронт-енд ufw, тому приклад такий: + +``` +ufw allow from bbb:bbbb:bbbb:bbbb:bbbb to aaa:aaaa:aaaa:aaaa:aaaa port 3128 proto tcp +``` +* переконайтесь що фаєрвол активний і правило додане: `ufw status` + +Також, в мене [вимкнені вихідні (пірингові) підключення в Інтернет](https://devzone.org.ua/post/obmezennia-vykhidnykh-zyednan-na-internet-z-ufw) з машини "a" де працює проксі-сервер, тому я окремо дозволив вихід на 80/443 TCP: + +``` bash +ufw allow out 80,443/tcp +``` + +На Веб-порти, я згодом планую дозволяти тільки конкретні IP по списку. Для цього знадобиться скрипт, що резольвитиме DNS і оновлюватиме правила iptables. Є готові "комбайни" типу Dansguardian, але мені тут не потрібен батьківський контроль з його семантичними фільтрами. Вирішуйте це питання для себе окремо. + +## Клієнт + +На клієнтській машині, в залежності від програмного забезпечення, шукаємо його налаштування і вказуємо там проксі. На прикладі apt в Debian, це буде файл: + +``` /etc/apt/apt.conf.d/proxy.conf +#/etc/apt/apt.conf.d/proxy.conf +Acquire::http::Proxy "http://[aaa:aaaa:aaaa:aaaa:aaaa]:3128"; +Acquire::https::Proxy "http://[aaa:aaaa:aaaa:aaaa:aaaa]:3128"; +``` + +Для підключення репозиторіїв утилітою extrepo, використовується команда з префіксом: + +``` +https_proxy=http://[aaa:aaaa:aaaa:aaaa:aaaa]:3128 extrepo enable librewolf +``` +* або / і `http_proxy` + +І так далі: тут гуглимо для себе, що кому треба. + +Якщо при підключенні бачите помилку групи 400, ймовірно потрібно додати домен до білого списку: йдемо на сервер і змінюємо нашу групу ACL ("updates"), після чого перезапускаємо squid і пробуємо підключитись знову. \ No newline at end of file diff --git a/post/nazad-v-chasi.md b/post/nazad-v-chasi.md new file mode 100644 index 0000000..274c852 --- /dev/null +++ b/post/nazad-v-chasi.md @@ -0,0 +1,23 @@ +# Назад в часі + +Днями знайшов старенький архів PasswordBoss і спробував відкрити перебором через Wine - тоді паролі були ручної роботи! + +Оригінальний .exe успішно запустив зв'язку ключів від забутих профілів з веб-адресами, які вже й не пригадав би. + +Багато сайтів офлайн - не пройшли тест меркантильності Вебу. + +Особливий архівний інтерес для мене становив гугл диск, який на той час був передовою фішкою в особистому арсеналі, знайти б там контакти або старі фото... +Але, гугл - такий гугл. Обліковий запис виявився "трішки" старшим, ніж нова політика активності користувачів - вже закопав можна сказати заживо - може й на краще. + +Утім, виявились і такі ресурси, що за майже двадцять років відкриваються, де навіть адміни дбайливо перенесли старі бази на новий форумний софт типу Flarum +(я то думав, що Vanilla був останнім рушієм своєї два-нульної епохи) + +Логінитись не став, в режимі гостя з цікавістю почитав свої повідомлення, конструкцію думки, граматику... розібрав собі ніч замість спати. +Сказати що був дурним - ні, чи став розумнішим - теж ні, але стільки всього відбулось за той час... + +З посмішкою перечитав булу спробу популяризації біткоїн, як валюти 2.0 - тоді про неї ще ніхто не знав; почитав про те, що хвилювало і що цікавило. + +Нікнейми в переписці ностальгічно нагадали минулі події, офлайн зустрічі... +Де хто зараз... останні дати відвідувань пиляться, а в пошуку - вже давно інші автори. + +В такі моменти розумієш, що для тебе двері відкриті, навіть якщо забув пароль та втратив скриньку. Але щось спиняє просто зайти і привітатись, не сьогодні. \ No newline at end of file diff --git a/post/nezruchni-momenty-v-roboti-z-bazoiu-danykh-manticore.md b/post/nezruchni-momenty-v-roboti-z-bazoiu-danykh-manticore.md new file mode 100644 index 0000000..4cfa463 --- /dev/null +++ b/post/nezruchni-momenty-v-roboti-z-bazoiu-danykh-manticore.md @@ -0,0 +1,83 @@ +# Незручні моменти в роботі Manticore як основної БД + +В коментарях до матеріалу [Manticore як сучасна альтернатива Sphinx](https://devzone.org.ua/post/manticore-iak-suchasna-alternatyva-sphinx) мене просили зробити більш детальні інструкції роботи з цим рушієм. Утім, вважаю що інформації достатньо в документації. Натомість, хотілось би описати деякі моменти, які можуть відверто вибісити особливо тих, хто планує повний перехід з класичних баз даних типу MySQL та використання Manticore, як основної бази. + +## Відсутність нормалізації даних + +Перше, з чим може зіткнутись користувач реляційних баз даних, який вирішив зберігати усі дані в Manticore - неможливість розбити дані по таблицям таким чином, щоб збудувати їх подальші нормалізовані зв'язки по зовнішнім ключам. + +Здебільшого вся база конкретного проекту буде зберігатись в одній таблиці і варто бути готовим сприймати цю базу як по-рядкове сховище key/value. +А всі дані в ній - простий масив індексів. Тому на етапі проектування, можна забути про UML, та MySQL Workbench - логіка бази даних в результаті буде повністю відрізнятись від класичної моделі зберігання SQL. + +## Вибірка тексту + +В Manticore / SphinxQL неможливо просто зробити чітку вибірку тексту, таку як: +``` +SELECT * FROM table WHERE field = 'value' +``` + +Якщо потрібно фільтрувати рядки по тексту, потрібно спочатку створити числовий атрибут, наприклад хеш `CRC32` і фільтрувати по ньому. Інакше запит завершиться помилкою. + +## Вибірка підрядка + +Можливо, є сторонні рушії, але стандартно ви не зможете наприклад додавати записи по умові PHP `strpos` та потім зробити таку само вибірку підрядка назад: +``` +SELECT * FROM table WHERE MATCH ('@field "*alu*"') +``` + +До того ж, результат такої вибірки залежатиме від налаштувань `min_word_len` і `min_prefix_len`. + +Обходити цю проблему доводиться в циклі пошукових результатів, з додатковою перевіркою `strpos`. + +## Спеціальні символи SphinxQL + +Наприклад, на стадії індексації, ви заігнорили URL з символами `#` (якорі), `?` (динамічні параметри) через умову PHP. При спробі видалити такі старі записи в індексах запитом: +``` +DELETE FROM table WHERE MATCH ('@url "#"') +``` +сервер видалить будь який URL, оскільки у цьому випадку `#` буде вважатись порожньою строкою `''` :) + +## Операції UPDATE + +Якось з'явилась ідея оптимізувати об'єм бази, перенесенням одного з полів `CRC32` в індексну колонку `ID`. +Зробити це типовою командою SQL не вдалось: +``` +UPDATE table SET id = crc32field; +``` + +Наступною спробою, була попередня вибірка і обробка скриптом в циклі результатів, але звісно, рушій не дав того зробити, тому довелось створювати індексну таблицю повторно. + +## Лімітування результатів + +Стандартно рушій лімітує вибірку до 1000 рядків, це не завжди зручно і потрібно додавати атрибут `cutoff` + +## Містика пагінації + +Приклад вибірки на одному з проектів з використанням [manticoresearch-php](https://github.com/manticoresoftware/manticoresearch-php) +``` +echo $index->search('')->maxMatches(10000)->offset(1000)->limit(1000)->get()->getTotal(); +> 5000 +``` + +## Журналювання SELECT + +Наприклад у вашому скрипті пошуку інтернет сторінок, використовується перевірка наявності поля по його URL, таким чином, відбуваються мільйони перевірок і всі вони потрапляють до `query.log` як пошукові запити користувача. Це неймовірний треш, рішення якого так і не знайшов. + +## Стандартна стратегія Binlog + +Користувачі серверів з нестабільним підключенням до мережі живлення, можуть зіткнутись з проблемою крашу `binlog`, після чого сервіс Manticore просто не стартує при завантаженні системи. + +Вирішується спочатку це видаленням `/var/lib/manticore/binlog/*` і ручним рестартом сервісу. + +Коли проблема набридає, виявляється, що [стандартно](https://manual.manticoresearch.com/Logging/Binary_logging#Binary-flushing-strategies) Manticore зберігає транзакції по-секундно, замість збереження після кожної транзакції. Можливо, щоб зробити маркетинговий ефект швидкості в роботі, але по факту доставить вам мороки в пошуках причини. + +До того ж ця проблема є причиною "загадкових зникнень" деяких записів з бази (бо власне ви отримали server fault та скинули журнал відновлення `binlog`) + +## Інше + +Відсутність більшості вбудованих функцій, типових для серверів SQL, можливість комбінованих запитів JOIN та інше - все це знайома ситуація, але на замітку. Будьте готові до емуляції SQL а не повної заміни. + +Створення проекту на базі Manticore у якості основної бази - виправдано тільки для пошукових систем з великим об'ємом тимчасових даних, які перш за все не потребують чіткої вибірки, де MySQL є тягарем. + +Можливо будуть додані рішення, якщо такі будуть знайдені. +Також діліться вашим досвідом в коментарях! \ No newline at end of file diff --git a/post/obmezennia-vykhidnykh-zyednan-na-internet-z-ufw.md b/post/obmezennia-vykhidnykh-zyednan-na-internet-z-ufw.md new file mode 100644 index 0000000..3df4ac6 --- /dev/null +++ b/post/obmezennia-vykhidnykh-zyednan-na-internet-z-ufw.md @@ -0,0 +1,90 @@ +# Обмеження вихідних з'єднань на Інтернет з ufw + +*`ufw` - це фронтенд утиліта командного рядка для спрощеного керування правилами `iptables`; зручна своєю простотою а також тим, що зберігає правила перманентно між системними сесіями.* + +Як відомо, стандартна конфігурація правил `ufw` блокує виключно вхідний трафік: + +``` bash +# ufw status verbose +Status: active +... +Default: deny (incoming), allow (outgoing), disabled (routed) +... +``` + +Тут, статус `allow (outgoing)` передбачає вихідні з'єднання, що може бути чутливим питанням у випадках, коли сервер має взаємодіяти виключно з мережами [Yggdrasil](https://devzone.org.ua/post/yggdrasil-mereza-z-detsentralizovanym-routynhom) / [Mycelium](https://devzone.org.ua/post/vstanovlennia-routera-merezi-mycelium-v-linux), але його софт реалізує інтерактивний API (наприклад ActivitiPub, BitTorrent / PEX, SMTP, деякі реалізації FTP, тощо) і таким чином, можливі зовнішні звернення в Інтернет через вхідні запити з локальних мереж. + +Найпростішим рішенням є блокування усіх вихідних з'єднань поряд з `deny (incoming)` та використання явних правил по білому списку, зокрема для DNS, NTP, apt, git, тощо: + +``` bash +ufw default deny outgoing +``` + +Після цього, дозволимо вихід на усі адреси Yggdrasil / Mycelium по відповідному діапазону: + +``` bash +ufw allow out to 0200::/7 +ufw allow out to 0400::/7 +``` +* можна явно задати порт, наприклад `... port 80 proto tcp` + +Для підключення оверлейних мереж вище до публічних вузлів: + +``` bash +ufw allow out to xx.xx.xx.xx port xxxx proto tcp +... +``` + +## DNS + +Далі, дивимось адреси DNS для системного резольвера (наприклад в `/etc/systemd/resolved.conf`), та додаємо їх до списку; для Cloudflare - це буде `1.1.1.1:53`: + +``` bash +ufw allow out to 1.1.1.1 port 53 proto udp +``` + +або просто дозволяємо всі можливі: + +``` bash +ufw allow out to any port 53 proto udp +``` + +Якщо використовується [локальний DNS для Yggdrasil](https://yggdrasil-network.github.io/services.html#dns) або Mycelium, цей крок можна пропустити, адже вище ми вже дозволили усі вихідні з'єднання на ці мережі. + +## NTP + +Важливо дозволити синхронізацію годинника: + +``` bash +ufw allow out 123/udp +``` + +## apt + +Для системних оновлень, найпростіше підключити локальні дзеркала або проксі, для Yggdrasil це: + +``` /etc/apt/apt.conf.d/proxy.conf +#/etc/apt/apt.conf.d/proxy.conf +Acquire::http::Proxy "http://[xxx:xxxx:xxxx:xxxx::xxxx]:xxxx"; +Acquire::https::Proxy "http://[xxx:xxxx:xxxx:xxxx::xxxx]:xxxx"; +``` + +Для команди `extrepo` на прикладі додавання репозиторію LibreWolf: + +``` bash +https_proxy=http://[xxx:xxxx:xxxx:xxxx::xxxx]:xxxx/ extrepo enable librewolf +``` +* або/і `http_proxy` + +## Git + +``` bash +git config --global http.proxy http://[xxx:xxxx:xxxx:xxxx::xxxx]:xxxx +``` +* або/і `https.proxy` + +Додаючи програмне забезпечення через сторонні проксі, важливо розуміти, що ви отримуєте через них й ключі. Відповідно, маєте усвідомлювати, що такий спосіб отримання системних оновлень може бути потенційно небезпечним і краще користуватись окремими джерелами або власним сервером (у даному випадку - локальним) + +## Дивіться також + +* [Ізоляція Linux від прямих Інтернет з'єднань на базі QEMU / Virtual Machine Manager з VSOCK](https://devzone.org.ua/post/izoliatsiia-linux-vid-priamykh-internet-zyednan-na-bazi-qemu-virtual-machine-manager-i-vsock) \ No newline at end of file diff --git a/post/orhanizatsiia-hostyovoho-servera-ftp.md b/post/orhanizatsiia-hostyovoho-servera-ftp.md new file mode 100644 index 0000000..634964c --- /dev/null +++ b/post/orhanizatsiia-hostyovoho-servera-ftp.md @@ -0,0 +1,96 @@ +# Організація гостьового сервера FTP + +Постало [завдання](https://devzone.org.ua/topic/restavratsiia-ukrayinskykh-veb-arkhiviv) десь зберігати дампи україномовних сайтів у сирому HTML. Розгортати повноцінний Git хостинг поки не хочу, зокрема й тому що при кожному апдейті там будуть оновлюватись хеші файлів, це не дуже доречно. Утім, хочеться якось "розподілити" дані між користувачами. + +Для цієї мети, вирішив підняти старий добрий публічний сервер FTP (для локальної мережі) з анонімним доступом до сервера в активному та пасивному режимах. Доступ до файлів тут в режимі _read-only_, я додаю їх від користувача по окремому потоку SSH. + +Нижче описані кроки з налаштувань, щоб наступного не лазити по закладкам, може комусь стане в нагоді. + +``` bash +apt install vsftpd +``` + +## Налаштування + +* В операційній системі Fedora, файл конфігурації знаходиться за адресою `/etc/vsftpd/vsftpd.conf` +* В Debian - це `/etc/vsftpd.conf` + +``` vsftpd.conf +# дозволити підключення в анонімному режимі +anonymous_enable=YES + +# заборонити логін локальних користувачів +local_enable=NO + +# заборонити запис (додаю файли через SSH) +write_enable=NO + +# маска, я її лишив здається стандартною +local_umask=022 + +# дозволити отримання списку директорій (для зручності навігації) +dirmessage_enable=YES + +# вимкнув, щоб отримувати журнали в файл (див. vsftpd_log_file) +xferlog_std_format=NO + +# вимкнув IPv4, оскільки я користуюсь тільки IPv6 (Yggdrasil/Mycelium) +listen=NO +listen_ipv6=YES + +# в мене не стандартна локація на примонтованому пристрої +anon_root=/path/to/public + +# дозволив відображення "прихованих" файлів що починаються з крапки +force_dot_files=YES + +# увімкнув пасивний режим явно та вказав діапазон портів для iptables +# про активний і пасивний режими, конструктивно тут: https://vps.ua/wiki/ukr/ftp-mode/ +pasv_enable=YES +pasv_min_port=10000 +pasv_max_port=10100 + +# можна також обмежити конективність для певних конфігурацій +# pasv_address= +# pasv_addr_resolve=NO +# connect_from_port_20=YES + +# файл журналів +vsftpd_log_file=/var/log/vsftpd.log +``` +* інші опції я лишив як є, але це актуально лише для моєї версії vsftdp - будьте уважні! + +## Фаєрвол + +Тепер важливо відкрити потрібні порти. Так як я не використовую режим SSL, мені потрібно дозволити тільки порти `20`, `21` і `10000-10100` (`pasv_min_port` і `pasv_max_port` відповідно). + +На Fedora, я досі не навчився користуватись новомодним _firewalld_ через командний рядок, тому зробив це через GUI [Firewall](https://firewalld.org) (_firewall-config_). Тут важливо додати ці правила до групи Permanent щоб вони не злетіли після ребуту. + +В Debian, для перманентної зміни `iptables`, я користуюсь `ufw`: + +``` bash +ufw allow 20/tcp +ufw allow 21/tcp +ufw allow 10000:10100/tcp +``` + +### Альтернативні мережі + +Якщо потрібно дозволити підключення тільки для користувачів Yggdrasil: + +``` bash +ufw allow from 0200::/7 port 20 proto tcp +ufw allow from 0200::/7 port 21 proto tcp +ufw allow from 0200::/7 port 10000:10100 proto tcp +``` +* для Mycelium вказуємо `0400::/7` + +#### Мульти-мережний режим + +Я особисто використовую правила без `from` (дозволяю підключення звідусіль) оскільки в мене крутиться декілька IPv6 мереж і якщо правильно не забіндити сервер на одній з них - можливі помилки підключення в пасивному режимі. Якщо не вдається підключитись через таймаут - дивіться `/var/log/ufw.log` і спробуйте тимчасово викнути фаєрвол взагалі, щоб перевірити чи справа дійсно в ньому (`ufw disable`). + +## Запуск + +* `systemctl restart vsftpd` - застосовуємо конфігурацію перезапуском сервера +* `systemctl enable vsftpd` - автозапуск сервера при старті системи +* `systemctl status vsftpd` - перевіряємо статус, після чого можна підключатися \ No newline at end of file diff --git a/post/orhanizatsiia-poshtovoyi-skrynky-e-mail-dlia-lokalnykh-merez-bez-dns.md b/post/orhanizatsiia-poshtovoyi-skrynky-e-mail-dlia-lokalnykh-merez-bez-dns.md new file mode 100644 index 0000000..3e6a435 --- /dev/null +++ b/post/orhanizatsiia-poshtovoyi-skrynky-e-mail-dlia-lokalnykh-merez-bez-dns.md @@ -0,0 +1,118 @@ +# Організація поштової скриньки e-mail для локальних мереж без DNS + +Даний матеріал є адаптацією інструкції для користувачів локальної [спільноти адміністраторів альтернативних мереж](https://devzone.org.ua/topic/ukrayinska-spilnota-administratoriv-alternatyvnykh-merez). Може стати в нагоді, якщо ви бажаєте створити власний поштовий сервер для локальної групи користувачів на підприємстві або для дому, без використання сторонніх серверів Google (та інших провайдерів) для переписки засобами [DeltaChat](https://delta.chat/uk/) або іншого клієнта, що підтримує протоколи SMTP/IMAP. + +Подібним займаюсь вперше, матеріал може доповнюватись, але в цілому, мій персональний сервер на базі локальної мережі [Yggdrasil](https://devzone.org.ua/post/yggdrasil-mereza-z-detsentralizovanym-routynhom) - вже працює. Користуючись мережами Yggdrasil та/або [Mycelium](https://github.com/threefoldtech/mycelium), ви також можете отримувати і надсилати пошту з локального простору через їх вбудований оверлейний режим: тобто пересилати листи через Інтернет без виділеного IP за NAT. + +Якщо плануєте користуватись Yggdrasil але для вас ця інструкція виглядає складною, зверніть увагу на більш простий, коробковий варіант для локальних мереж - [Yggmail](https://devzone.org.ua/post/yggmail-mesendzer-z-poshtovym-interfeysom), але він є швидше месенджером з API для поштових клієнтів з [лімітом на вкладення в 1 Мб](https://github.com/neilalexander/yggmail#notes), а не повноцінним поштовим сервером; до того ж реалізує не стандартні адреси, що може стати проблемою сумісності з альтернативними клієнтами, які валідують хост без підтримки локальних псевдонімів. + +## Створення користувача пошти + +Є багато способів адміністрування скриньок, але я обрав самий простий: системний, за паролем: + +``` bash +useradd -m USER +passwd USER +echo "root: USER" >> /etc/aliases +``` +* `USER` - замініть на вашого користувача +* для логіну буде використовуватись саме `юзернейм` а не `юзернейм@хост`, оскільки технічно я хочу потім зробити прийом пошти з Yggdrasil та Mycelium на один і той же системний обліковий запис +* вказаний пароль буде також використовуватись для авторизації в клієнті + +## Dovecot + +``` bash +apt install dovecot-core dovecot-imapd +``` + +### /etc/dovecot/dovecot.conf + +Вимикаю IPv4, але "слухаю" всі мережі IPv6 (включно з Yggdrasil та у моєму випадку - Mycelium) + +``` /etc/dovecot/dovecot.conf +listen = :: +``` + +### /etc/dovecot/conf.d/10-auth.conf + +``` /etc/dovecot/conf.d/10-auth.conf +disable_plaintext_auth = no +``` + +### /etc/dovecot/conf.d/10-master.conf + +``` /etc/dovecot/conf.d/10-master.conf +service auth { + unix_listener /var/spool/postfix/private/auth { + mode = 0666 + user = postfix + group = postfix + } +} +``` + +## Postfix + +``` bash +apt install postfix +``` + +### /etc/postfix/main.cf + +``` /etc/postfix/main.cf +myhostname = localhost + +smtpd_banner = $myhostname ESMTP $mail_name (Debian/GNU) +biff = no +append_dot_mydomain = no +readme_directory = no +smtpd_use_tls=no +compatibility_level = 3.6 + +smtpd_relay_restrictions = permit_mynetworks permit_sasl_authenticated defer_unauth_destination + +alias_maps = hash:/etc/aliases +alias_database = hash:/etc/aliases + +mydestination = +relayhost = +mynetworks = 127.0.0.0/8 [::ffff:127.0.0.0]/104 [::1]/128 +mailbox_size_limit = 0 +recipient_delimiter = + +inet_interfaces = all +inet_protocols = ipv6 + +# деякі поради від спаму з мережі, але як вони працюють ще не знаю :) +smtpd_etrn_restrictions=reject +disable_vrfy_command = yes +smtpd_helo_required = yes + +# інтеграція з Dovecot +smtpd_sasl_auth_enable = yes +smtpd_sasl_type = dovecot +smtpd_sasl_path = private/auth +mailbox_command = +``` +* опцію `smtpd_use_tls` вимкнено, тому що весь внутрішній трафік Yggdrasil (у моєму випадку) вже захищено на рівні транспорту; для класичних мереж - можна скористатись само-підписаним сертифікатом, додавши на нього виключення. + +## Фаєрвол + +Моя особиста конфігурація не передбачає прийом пошти від користувачів окрім локальних: + +``` bash +ufw allow from CLIENT_IP to any port 25 proto tcp +ufw allow from CLIENT_IP to any port 143 proto tcp +``` +* замінити `CLIENT_IP` на той, з якого підключатиметься клієнт DeltaChat + +У випадку, якщо ви налаштовуєте скриньку для прийому відправлень від інших користувачів локальної мережі, можете додати виключення на його хост тієї мережі, якою користуєтесь, або замість фаєрволу - налаштувати доступ засобами [SpamAssassin](https://spamassassin.apache.org/). + +## DeltaChat + +Налаштовуючи даний клієнт, вказуємо: +* E-mail - у форматі [RFC 5321](https://www.rfc-editor.org/rfc/rfc5321#section-4.1.3) (тобто з літералом IPv6 `user@[IPv6:xxx:xxxx:xxxx:xxxx::]`) +* User SMTP/IMAP - такий як до `useradd` (без хосту) +* Host SMTP/IMAP - ваш IPv6 в мережі Yggdrasil або Mycelium +* Password - такий як до `passwd` +* Обов'язково вказуємо стандартні порти, інакше DeltaChat використовує відмінні від 25/143 +* Усі види шифрування TLS вимикаємо \ No newline at end of file diff --git a/post/ozkladka-ukrayinskoyi-latynky-dlia-linux.md b/post/ozkladka-ukrayinskoyi-latynky-dlia-linux.md new file mode 100644 index 0000000..68a26af --- /dev/null +++ b/post/ozkladka-ukrayinskoyi-latynky-dlia-linux.md @@ -0,0 +1,88 @@ +# Розкладка української латинки для Linux + +Для тих, кому цікава тема української латинки, хочу поділитися способом швидкого налаштування клавіатури для Linux. + +Варіант передбачає роботу з абеткою версії Максима Прудеуса з наступним набором символів: + +``` +A B Cц Čч D E F G Ĝґ H I Jй K L M N O P R S Šш T U V Yи Z Žж ' +``` + +Як програміст, користуюсь версією клавіатури, що базується на мапі `us`, оскільки вона дозволяє не змінювати розкладку під час написання коду і відвідування англомовних ресурсів. + +В прикладі використовується код `ul` - обрати вільний слот можна в таблиці [ISO_639](https://en.wikipedia.org/wiki/List_of_ISO_639_language_codes#Table_of_all_possible_two_letter_codes). + +``` +#/usr/share/X11/xkb/symbols/ul +partial alphanumeric_keys + +xkb_symbols "ul" { + + include "us(basic)" + + name[Group1]= "Adaptive (UL)"; + + key { [z, Z, zcaron, Zcaron] }; + key { [c, C, ccaron, Ccaron] }; + key { [s, S, scaron, Scaron] }; + key { [g, G, gcircumflex, Gcircumflex] }; + + include "level3(ralt_switch)" +}; +``` + +У версії Прудеуса, допускається використання літери `ï` замість `ji`. Стандартно ця літера не є частиною вказаної абетки, але на практиці, без неї не зручно писати і читати такі слова як наприклад `jiji`. + +Додати літеру до розкладки можна так: +``` +key { [i, I, idiaeresis, Idiaeresis ] }; +``` + +Після редагування файлів `/usr/share/X11/xkb/symbols/*` потрібно застосувати зміни командою без атрибутів: +``` +setxkbmap +``` + +В середовищі i3, мною використовується конфігурація на правий `alt`: + +``` +#~/.xinitrc +setxkbmap -layout ul,ua +setxkbmap -option 'grp:alt_shift_toggle' +``` + +На Fedora 42 / GNOME, я виконую наступні кроки: + +1. Додаю до файлу `/usr/share/X11/xkb/rules/evdev.xml` (після ``): + +``` /usr/share/X11/xkb/rules/evdev.xml + + + ul + ul + Ukrainian (Latin) + + UA + + + ukr + + + + +``` + +2. Щоб не вказувати вручну в графічних налаштуваннях клавіатури: +``` bash +gsettings set org.gnome.desktop.input-sources sources "[('xkb', 'ua'), ('xkb', 'ul')]" +``` + +3. Перезапускаю GDM: +``` bash +sudo systemctl restart gdm.service +``` + +Детальніше про абетку читайте на сайті проекту: +https://www.ukr-latynka.org + +Pobačymos' o/ \ No newline at end of file diff --git a/post/pro-server-fediverse-v-alternatyvnykh-merezakh.md b/post/pro-server-fediverse-v-alternatyvnykh-merezakh.md new file mode 100644 index 0000000..40e793f --- /dev/null +++ b/post/pro-server-fediverse-v-alternatyvnykh-merezakh.md @@ -0,0 +1,74 @@ +# Про сервер Fediverse в альтернативних мережах + +Я давно користуюсь [Fediverse](https://uk.wikipedia.org/wiki/Федиверс), але свій інстанс - підняв відносно недавно: приблизно пів року тому. При чому, його було створено у якості експерименту з працездатності екосистеми [ActivityPub](https://uk.wikipedia.org/wiki/ActivityPub) в рамках оверлейної мережі [Yggdrasil](https://devzone.org.ua/post/yggdrasil-mereza-z-detsentralizovanym-routynhom) як спроба не просто зробити веб-проксі, а організації локального сузір'я. + +Так як це був перший досвід, я ще збирався організувати доступ до нього з Інтернет (повноцінну між-серверну комунікацію) але згодом усвідомив, що цей протокол на таке не розрахований і навіть якщо це технічно можливо, адміністратори інших серверів потребуватимуть додаткового шару специфікації для такої взаємодії. + +## Сервери + +Їх вистачає: популярний [Mastodon](https://uk.wikipedia.org/wiki/Mastodon_(програмне_забезпечення)) я не ставив бо не хотів морочитись з PostgreSQL та й в цілому, його розгортання виглядає для мене ледачого складно. + +### Snac + +Мені своєчасно порадили сервер [Snac](https://codeberg.org/grunfink/snac2), який не тільки розрахований на маленький персональний сервер без зовнішніх реєстрацій, а ще й не потребує бази даних, при тому клієнт функціонує без JS і до цього сервера є доволі якісні теми оформлення, наприклад [Pika](https://codeberg.org/voron/snac-style/raw/branch/master/pika.css). Написаний Snac мовою C. + +Функціонування (слідування, коментарі, бусти, приватні повідомлення тощо) в локальній мережі було успішно перевірено на живих вузлах Yggdrasil в комбінаціях: + +* Snac (IPv6) / Snac (IPv6) +* Snac (IPv6) / Snac (DNS) +* Snac (IPv6) / Mitra (IPv6) +* Snac (IPv6) / Mitra (DNS) + +[Налаштування Fediverse-сервера Snac для мережі Yggdrasil](https://devzone.org.ua/post/nalashtuvannia-fediverse-serveru-snac-dlia-roboty-v-merezi-yggdrasil) + +Мій інстанс для тестування: +[302:68d0:f0d5:b88d::fed](http://[302:68d0:f0d5:b88d::fed]) +* взаємний фоловінг по запиту + +### Mitra + +Вже після встановлення Snac, я відкрив для себе [Mitra](https://codeberg.org/silverpill/mitra). Цей сервер вимагає PostgreSQL, JS для стандартного клієнта і включає в себе навороти для автоматичної реєстрації та (мабуть) менеджменту користувачів. Я планую його спробувати потім, мені подобається його простий інтерфейс, користуюсь для свого Інтернет-профілю, який спочатку створив для тестів а потім там й лишився "жити". + +Окремо згадую про це програмне рішення, тому що його вдалось протестувати в між-серверній комунікації з одним з реальних користувачів мережі. Також в нього була якимось чином налаштована взаємодія з Інтернет-федерацією. Подробиці цього збочення я не знаю, але довкола нашого експерименту, розробниками рушія Mitra було проявлено цікавість і спеціально натягнуто деякі адаптації по частині IPv6, тому в контексті цей сервер вартий уваги, а кому цікаві подробиці - зконтактуйте з `@madamada@mitra.void.my` (Yggdrasil) + +### Tootik + +Окремо з цікавих платформ, я б ще відмітив сервер для протоколу [Gemini](https://geminiprotocol.net) - [Tootik](https://github.com/dimkr/tootik). Його бекенд, як і передбачає ActivityPub, взаємодіє по HTTP/JSON, але на рівні користування там буде інтерфейс Gemini на 1965 порті з TLS. + +Згадую про дане рішення в контексті того, що альтернативними мережами часто користуються з метою приватності, тому протокол Gemini тут може мати деяку нішу користувачів. + +## Ідентифікатори хостів + +Власне, саме цікаве і чому взагалі я задумав написати цей матеріал. + +Щоб створити свій інстанс, вам потрібно ідентифікувати свій вузол як `user@host`. Коли ви це зробите, то вже не зможете безпроблемно його змінити потім, бо інші сервери федерації втратять зв'язок з вами. Тому він повинен бути обраний один раз і обраний правильно. + +### DNS + +ActivityPub розроблявся як протокол високого рівня для комунікації засобами класичної мережі Інтернет з DNS, у той час як мережах типу Yggdrasil прийнято користуватись IPv6 а домен - вже надавати опцією. + +Локальна мережа з ідентифікацією DNS, здавалось би дозволяє легко змінювати адреси IP або навіть перескочити з мережі Yggdrasil на Mycelium, а з того - на ще якусь нову екосистему. Але за цими перескоками повинні слідувати не тільки ваші читачі, але й сервери федерації. Тому сумнівно, якщо це не якийсь спеціалізований на одну мережу домен `.ygg` в Alfis DNS, що тільки відсіче потенційну частину сегменту, яка ним не користується. + +### IP + +Свій ID я вирішив робити на IPv6 адресі мережі Yggdrasil бо такий формат, на відміну від домена, не може бути протермінований з часом (так, я можу зникнути на рік два і неочікувано для себе повернутись до мережі через десять). + +З іншого боку, перманентні IP на основі криптографії - теж не панацея: ключі можуть бути загублені, випадково опубліковані тощо, а тому можуть потенційно потребувати генерації нових. + +#### Адреси підмереж + +В мене ще лишається відкритим питання з адресами підмереж, бо ті є менш стійкими до колізій і такими, що потенційно можуть бути випиляні з майбутніх релізів роутера. Так, вони зручні, але сьогодні я б радив користуватись основними адресами для ідентифікації сервера, коли це можливо. + +## Гібридна маршрутизація + +На початку матеріалу зауважив, що особисто для себе - відмовився від ідеї мульти-мережної комунікації для свого інстансу, спробувавши просто ініціювати локальне сузір'я, галактику, фізично ізольованої на рівні ActivityPub від інших мереж. Але зараз, спостерігаю занепад обраної мною мережі Yggdrasil, вже експериментую з Mycelium, і вже завтра - буду експериментувати з наступною. Тому трохи розкрию свої думки стосовно гібридного формату вузла. + +Вже після запуску сервера в альтернативній мережі (або мережах з налаштованим проксі для між-серверного API), постане питання поширення посилань. Адже ви захочете ділитись публікаціями з друзями в Інтернет, Yggdrasil, I2P та інших екосистемах. Бажано, це має бути якесь одне канонічне посилання, яке приведе користувача до цільової сторінки в незалежності від налаштувань його маршрутизації і функціонування самої мережі на момент відвідування. + +Були думки щодо організації центрального сервісу "коротких посилань" по типу колишнього bit.ly який підтримуватиме усі можливі альтернативи і для кожної з них надаватиме веб-проксі за форматом хосту... + +З іншого боку, можна створити ще один "шейред роутер" як окремий протокол та програму для нього, саме з метою обслуговування гібридних сервісів без реалізації їх маршрутизації. У цьому питанні явно не вистачає сучасних рішень, адже зберігається тенденція до створення нових мереж та деградації старих, як це сталось з CJDNS і сьогодні відбувається з Yggdrasil. Технічно, діапазон IPv6 надає широке поле для експериментальних роутерів, їх кількість в умовах сучасних перешкод мережі буде тільки зростати, тому це питання є відкритим. + +## Висновки + +Особисто для себе, вирішив не міксувати екосистеми і вести ізольовані тематичні ресурси по кожній з них, намагаючись ділитись посиланнями в рамках тої екосистеми, для якої створено інстанс. Звичайно, на практиці виходить інакше, станом на сьогодні, назвати локальний інстанс - соціальним складно, утім якщо ви цієї соціальної взаємодії не шукаєте на глобальному рівні, то чому б не спробувати. \ No newline at end of file diff --git a/post/proksuvannia-m3u8-zasobamy-icecast.md b/post/proksuvannia-m3u8-zasobamy-icecast.md new file mode 100644 index 0000000..6a0eb3a --- /dev/null +++ b/post/proksuvannia-m3u8-zasobamy-icecast.md @@ -0,0 +1,125 @@ +# Проксування потоку m3u8 засобами ffmpeg в Icecast + +Я не маю значного досвіду з адміністрування стрімінгових сервісів, раніше на базі Icecast робив тільки локальну [ротацію музичної колекції](https://devzone.org.ua/post/veb-radio-v-linux-vstanovlennia-servera-icecast-ta-bazove-nalashtuvannia-rotatsiyi-z-ezstream), але зацікавив проєкт довкола-айтішних стрімів [eQtv](https://tv.equalitie.org/uk/live/). Цей матеріал - невеличка нотатка про налаштування проксі на прикладі сервісу [eQtv українською мовою для локальних мереж](https://devzone.org.ua/topic/retransliatsiia-eqtv-ukrayinskoiu-movoiu-dlia-lokalnykh-merez-audio-32-kbs). + +Витягнути потік виявилось задачею не тривіальною, я її постійно відкладав але згодом таки знайшов в дебагах (`ctrl+shift+i`) наступні доріжки: + +* `https://eqtv.live:8083/eqtvua/eqtvua480/chunks_dvr.m3u8` - схоже, що відповідає за відео +* `https://eqtv.live:8083/eqtvua/eqtvua_hd_ukr/chunks_dvr.m3u8` - відповідає за аудіо-ряд (їх 3) + +## ffmpeg + +Вже звичний мені ezstream не вміє проксувати потоки з URL, тому віднайшов спосіб з ffmpeg: + +``` bash +ffmpeg -i "https://eqtv.live:8083/eqtvua/eqtvua_hd_ukr/chunks_dvr.m3u8?nimblesessionid=xxx" -c:a copy icecast://user:password@127.0.0.1:8000/eQtv.aac +``` + +У прикладі вище - потік передається в Icecast "як є" у форматі AAC, але такий формат мені не зовсім підходить, бо я хочу окрім оверлейних мереж Yggdrasil і Mycelium ще й стрімити в I2P з його "вузьким" каналом. Хоч конвертація вимагає додаткових ресурсів CPU, все ж вирішив її застосувати, звівши до поширеного формату MP3 з бітрейтом 32 kb/s: + +``` bash +ffmpeg -i "https://eqtv.live:8083/eqtvua/eqtvua_hd_ukr/chunks_dvr.m3u8?nimblesessionid=xxx" -b:a 32k icecast://user:password@127.0.0.1:8000/eQtv.mp3 +``` + +Як видно на прикладах вище, я додав до URL джерела аргумент `?nimblesessionid=xxx` - він потрібен для того, щоб ffmpeg не плодив нові сесії в процесі читання. Тут я ще не знаю, як довго протримається поточна сесія, але якщо будуть проблеми - напишу скрипт, що витягає актуальний її номер та доповню цей матеріал. + +UPD. очікувано, сесія прожила менше доби, тому створив такий скрипт, будемо пускати його в `ExecStart` сервісу systemd, замість сирої команди `ffmpeg`: + +``` /home/eqtv/stream.sh +#!/bin/bash + +# завантажуємо актуальний файл m3u8, що містить активний номер сесії +# та витягаємо перше знайдене значення в змінну SESSION_ID +CHUNKS_DVR="/home/eqtv/chunks_dvr.m3u8" +wget -O $CHUNKS_DVR https://eqtv.live:8083/eqtvua/eqtvua_hd_ukr/chunks_dvr.m3u8 +SESSION_ID=$(grep -oP '(?<=sessionid=)\d+' $CHUNKS_DVR -m 1) +if [ -z "$SESSION_ID" ]; then + echo "SESSION_ID is empty. Exiting script." + exit 1 +fi + +# запускаємо стрім з актуальним значенням SESSION_ID +ffmpeg -i "https://eqtv.live:8083/eqtvua/eqtvua_hd_ukr/chunks_dvr.m3u8?nimblesessionid=$SESSION_ID" -b:a 32k icecast://user:password@127.0.0.1:8000/eQtv.mp3 +``` +* цей скрипт передбачає роботу з systemd: під час помилки процесу, буде виконано `Restart=on-failure`, таким чином ключ сесії буде актуалізовано + +## Icecast + +Декілька слів про налаштування сервера Icecast. Раніше, для локальних колекцій, мета-інформація про стрім в мене обслуговувалась сервером ezstream. Тут його немає, тому я додав такий набір до конфігурації точки монтування Icecast: + +``` /etc/icecast2/icecast.xml + + /eQtv.mp3 + user + password + eQtv українською мовою (аудіо, 32 kb/s) + eQtv — це проект eQualitie, неприбуткової організації, що розробляє технології для підвищення цифрової стійкості, особливо для спільнот, яким загрожують цензура, стеження, зміна клімату та мережева ізоляція. + https://tv.equalitie.org/uk/live + +``` +* відповідно, `user:password` мають відповідати тим, що вказані в команді `ffmpeg` + +## Systemd + +Команду `ffmpeg` я виконую від системного сервісу, створивши відповідного користувача: + +``` /etc/systemd/system/eqtv-mp3.service +#/etc/systemd/system/eqtv-mp3.service +[Unit] +# якщо Icecast локальний, можна додати icecast2.target поряд з network-online +# After=network-online.target icecast2.target +After=network-online.target + +[Service] +Type=simple + +User=eqtv +Group=eqtv + +# Затримка потрібна у моєму випадку через залежність від Icecast +# ExecStartPre=/bin/sleep 5s + +# ExecStart=/usr/bin/ffmpeg -i ... +# ExecStart=/bin/bash /path/to/script.sh +ExecStart=/path/to/script.sh + +# Журнали я вимкнув, але можна продебажити наступним чином +StandardOutput=null +# file:///home/eqtv/eqtv-mp3-debug.log +StandardError=null +# file:///home/eqtv/eqtv-mp3-error.log + +# Бажано увімкнути, якщо замість команди ffmpeg в ExecStart +# використовується скрипт оновлення номеру сесії +Restart=on-failure + +[Install] +WantedBy=multi-user.target +``` +* в `ExecStart` треба вказати відповідний набір атрибутів `ffmpeg` або шлях до скрипта (вище) + +## Nginx + +Як видно з прикладу, мій сервер Icecast крутиться на інтерфейсі `127.0.0.1` для локальних потреб (щоб не ганяти трафік через оверлей). Окрім локалхосту, в мене відбувається ретрансляція на різні мережі IPv6, тому для зручності я проксую клієнтський трафік через Nginx: + +``` /etc/nginx/sites-available/default +#/etc/nginx/sites-available/default +server { + listen [202:68d0:f0d5:b88d:1d1a:555e:2f6b:3148]:8000; + listen [505:6847:c778:61a1:5c6d:e802:d291:8191]:8000; + listen xx.xx.xx.xx:8000; + + access_log /var/log/nginx/icecast.access.log; + + location / { + proxy_pass http://127.0.0.1:8000; + 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_redirect off; + } +} +``` +* на прикладі `listen` - проксі на Yggdrasil та Mycelium і одна на локальну мережу IPv4 +* `proxy_pass` - може бути відмінний локальний порт від вказаного у прикладах \ No newline at end of file diff --git a/post/protokol-gemini-iak-alternatyva-http.md b/post/protokol-gemini-iak-alternatyva-http.md new file mode 100644 index 0000000..99a9e2e --- /dev/null +++ b/post/protokol-gemini-iak-alternatyva-http.md @@ -0,0 +1,191 @@ +# Протокол Gemini як альтернатива HTTP + +[Gemini](https://geminiprotocol.net/) - мережний протокол прикладного рівня, в своїй основі є спадкоємцем протоколу Gopher. + +Орієнтований на мінімалізм та конфіденційність, оскільки виключає використання таких веб-технологій як Cookies та JavaScript, а формат даних, що передається - обмежений текстом або бінарними даними без підтримки стиснення та фонового завантаження зі сторонніх ресурсів. Обов'язковим є захист трафіку сертифікатом TLS. + +Не зважаючи на простоту, протокол цілком багатофункціональний і дозволяє працювати з різними типами ресурсів: от як хостинг статичних файлів для персональних сторінок так і системи з авторизацією та обробкою запитів користувача - форуми, пошукові системи, організація радіо та відео стрімів, обмін зображеннями та іншими мультимедійними даними. + +Враховуючи архітектуру, Gemini здебільшого буде цікавий тим, кого не влаштовує "роздутість" сучасного HTTP; стане в нагоді користувачам командного рядка, E-ink планшетів. + +## Термінологія + +Як і в багатьох екосистемах, для Gemini характерна своя термінологія, знання якої допоможе краще розуміти контекст і формувати пошукові запити: + +* **Gemini space** - екосистема Gemini, подібно терміну "Fediverse" - для федеративної тематики +* **Capsule** - капсула, веб-сайт - назва протоколу походить від тематики космічної програми, тому багато проектів наслідують концепцію у своїх назвах +* **Gemlog** - блог, персональна сторінка, фід профілю + +## Розмітка + +### Gemtext + +Текстові ресурси gemtext - це звичайний текст (MIME text/gemini), схожий до Markdown. +Опціонально містить наступні мета-теги на початку кожного рядка (без зворотнього слешу): + +``` +# h1 +## h2 +### h3 +=> url [заголовок] +> цитата +* елемент списку +\``` опціональний заголовок блоку або розширення файлу для аналізатора синтаксису +код +\``` +``` +* `\` - екранування, цей символ в розмітці не використовується! + +У розмітці Gemtext не використовуються декоративні технології накшталт CSS, при цьому завдання відображення ресурсу повністю делеговане клієнтові. + +Різні браузери по-різному декорують вміст, деякі додають відступи. +Тому головний принцип створення крос-браузерних сторінок простий: розмітка має зручно читатися у вигляді початкового коду. + +### Gemfeed + +Оскільки текстовий регламент протоколу не передбачає використання мета-тегів, такі стандарти сповіщень як Atom та RSS, без зовнішніх засобів інтеграції, в Gemini - не застосовні. + +Незважаючи на це, підписки можливі, зокрема - засобами стандарту Gemfeed для інтерпретації браузером змін документу Gemtext. +Наприклад, в браузері [Lagrange](https://github.com/skyjake/lagrange), відстежувати оновлення сторінки можна за допомогою меню Bookmarks - Subscribe to page… і обрати відповідний сторінці спосіб - підписка при зміні заголовків на сторінці або за зміною дат у посиланнях. + +#### Відстеження за датою посилань + +У цьому підході здійснюється відстеження за датою, що вказана наступною за посиланням у форматі ISO 8601 (Y-m-d), наприклад: + +``` +# Заголовок сторінки виконує роль заголовка стрічки + +Вміст параграфа ігнорується у відстеженні + +=> /index.gmi будь-яке посилання, ігнорується + +## Підрозділ сторінки, ігнорується + +Довільний вміст підрозділу, що ігнорується + +## Публікації + +=> /pub1.gmi 2024-01-28 Посилання на першу публікацію - відстежується +=> /pub2.gmi 2024-01-29 Посилання на другу публікацію - відстежується +=> /pub3.gmi Посилання публікацію - не відстежується (оскільки не містить дати) +=> /pub4.gmi Посилання публікацію - не відстежується (оскільки дата є частиною заголовка і розташована зправа) 2024-01-30 +``` + +Таким чином, документ не втрачає зручності для візуального сприйняття людиною і при цьому містить мета-інформацію, зрозумілу для програмного інтерпретатора в браузері, що відслідковує сторінку. + +Єдиний недолік такого підходу полягає в тому, що оновлення не можна отримувати частіше ніж раз на добу. Як заявлено в документації, це пов'язано з часовими зонами. + +#### Відстеження заголовків + +Альтернативний підхід, який відстежує зміни у заголовках документу: + +``` +# Мій блог, заголовок стрічки + +Параграф опису + +## Перша публікація - відстежується + +Опис першої публікації + +=> /pub1.gmi Читати + +## Друга публікація - відстежується + +Опис другої публікації + +=> /pub2.gmi Читати + +... +``` + +### Коди статусів + +Протокол Gemini має власний реєстр кодів заголовків, відмінний від типових для HTTP 200, 301, 404, 500, тощо + +* `10-19` - input expected - очікується введення - використовується для відправлення та отримання даних форм +* `20-29` - success - код 20 аналогічний коду 200 в HTTP +* `30-39` - redirection - діапазон переадресації, у пакеті, що відправляється, код зазвичай супроводжується мета-посиланням: code + link + \n\r + text +* `40-49` - temporary failure +* `50-59` - permanent failure +* `60-69` - client certificates - у Gemini сертифікати використовуються для ідентифікації та авторизації користувачів + +Клієнт зобов'язаний відхиляти будь-який код менше 10 або більше 69, при цьому повідомити користувача. +У невизначених випадках пріоритет буде відданий коду з початковим значенням діапазону, наприклад, 10 для 11 або 20 для 27. + +### Обробка запитів + +Протокол передбачає обмін пакетами із заголовками довжиною максимум 1024 байт. У цю довжину необхідно вмістити мета інформацію про пакет - наприклад рядок URI та/або дані введення користувача. + +Текстові дані мають бути закодовані у стандарт RFC 3986 (відомий такими функціями, як urlencode). Якоюсь мірою це скорочує корисний обсяг заголовка при використанні наприклад кирилиці. + +Тіло пакету складається із "сирих" текстових або бінарних даних без стиснення; сервер закриває з'єднання після надсилання останнього байту. + +За допомогою статусів групи "10", що відправляються клієнту, сервер здатний запитувати введення користувача (замість звичних форм, у браузерах Gemini - це спливаюче текстове вікно) +Після отримання та обробки даних, сервер зазвичай повертає статус "20", статус "51" (не знайдено) або переадресацію на цільову сторінку з кодом "30". + +Приклад запиту головної сторінки `gemini.circumlunar.space` для командного рядка: +``` bash +echo -e "gemini://gemini.circumlunar.space:1965/\r\n" | openssl s_client -connect gemini.circumlunar.space:1965 -ign_eof +``` + +Приклад умовної відповіді сервера з текстом "Hello World": +``` +20 text/gemini; charset=utf-8; lang=en\r\nHello world +``` + +### Клієнт + +Щоб відкрити ресурс з адресою `gemini://` потрібен спеціальний браузер, що стандартно працює з портом `1965` + +Користувачі GUI можуть почати з популярних Lagrange, Eva, Kristall, Castor і т.д. + +**Що варто знати:** + +* Оскільки більшість клієнтів повертають одно-текстове рядкове поле запиту, додати новий рядок можна комбінацією клавіш Shift+Enter. Розширити спосіб введення можна за допомогою суміжного протоколу Titan, але розляд цього способу вартує окремого матеріалу. +* У протоколі Gemini немає webstorage та cookies, тому деякі інтерактивні сайти для функцій авторизації використовують сертифікати, які також можуть використовуватись для швидкої зміни облікового запису в браузері. +* Замість фідів RSS є вбудовані в браузер інструменти відстеження контенту (див. Gemfeed) + +### Сервер + +Поняття "сервер" у середовищі Gemini може бути не звичним для користувачів веб, які звикли до поширених веб проксі Nginx або Apache "на всі випадки". Термін часто передбачає повноцінний сервіс для конкретного завдання, що резервує за собою окремий системний хост і порт. + +У каталозі [awesome-gemini](https://github.com/kr1sp1n/awesome-gemini) представлено велику кількість таких рішень. + +Наприклад, для запуску простої статики підійде сервер Agate (Rust). При цьому назва звичного `index.html` залежатиме від обраного рішення, для Agate - це `index.gmi` але для іншого серверу постфікс може відрізнятись. + +Запуск динамічних ресурсів часто передбачає розробку власного сокет-серверу для реалізації специфіки окремо взятої програми. + +Веб-розробникам простіше зрозуміти принцип роботи "server-side" на прикладі декількох файлів [gemini-php](https://github.com/eapl-gemugami/gemini-php). Утім, в нових проектах краще використовувати більш актуальні рішення, наприклад, з простих - форк бібліотеки [titan-II](https://github.com/YGGverse/titan-II), приклади реалізації на базі якої можна подивитись у вихідному коді [β-Doku](https://github.com/YGGverse/bdoku) - проекті Gemini-проксі для DokuWiki. + +#### Віртуальні хости + +Щоб запускати різні сервіси на одному IP (не змінюючи стандартний порт), зручно встановити загальний проксі-сервер, який здійснюватиме маршрутизацію запитів на відповідну адресу/інтерфейс або на зовнішній сервер. + +Якщо в HTTP - це Apache / Nginx, то для протоколу Gemini підійдуть [Gmid](https://gmid.omarpolo.com/) (C) або [Twins](https://code.rocket9labs.com/tslocum/twins) (Go). З другим помічені деякі проблеми з передачею довгих текстів, тому для початківців, бажано розпочати роботу з Gmid, який наразі активно розвивається та має дружній фідбек від розробника. + +Приклад налаштування віртуальних хостів для Gmid описаний [тут](https://devzone.org.ua/post/gmid-bagatofunkcionalnii-proksi-server-dlia-gemini). + +### З чого почати + +* gemini://geminiprotocol.net - домашня сторінка проекту +* gemini://geminispace.info - пошукова система +* gemini://station.martinrue.com - відома соціальна мережа +* gemini://bbs.geminispace.org/s/Ukrainians - ще одна соціальна мережа (та україномовна група) +* gemini://gemlog.blue - найпростіший спосіб опублікувати свої сторінки в мережі Gemini +* gemini://cities.yesterweb.org - хостинг з піддоменом, підтримкою Titan і WebDAV +* gemini://flounder.online - ще один хостинг з агрегатором останніх записів, є SFTP +* gemini://astrobotany.mozz.us - ASCII гра з догляду за рослинами, що затягує :) +* gemini://piratezeppel.in - перша україномовна збірка поезії в просторі Gemini +* gemini://meadow.hmmm.zt.ua/local - україномовний інстанс [Tootik](https://github.com/dimkr/tootik) (Fediverse) +* gemini://ps.cities.yesterweb.org - мій блог + +### Схожі протоколи + +* Gopher +* Guppy +* Spartan +* Nex / Nps (другий - для відправлення даних, по типу Titan для Gemini) +* Scroll +* Text +* Finger \ No newline at end of file diff --git a/post/protokol-nex-lehka-alternatyva-gemini.md b/post/protokol-nex-lehka-alternatyva-gemini.md new file mode 100644 index 0000000..75ba731 --- /dev/null +++ b/post/protokol-nex-lehka-alternatyva-gemini.md @@ -0,0 +1,125 @@ +# Протокол NEX - легка альтернатива Gemini + +Останнім часом стало поширеним явище інтеграції TLS навіть там, де використання цієї технології може бути зайвим. + +Сьогодні майже кожна програма включає в себе параноїдальні пресети безпеки з коробки, через які результуючий трафік часто шифрується в декілька шарів, тим само створюючи часові затримки на handshake і вичерпуючи заряд мобільних акумуляторів при передачі великих об'ємів даних. + +Звісно, протокол Gemini - не про великий трафік. Тим не менше, хотілось би поділитись його спрощеною альтернативою - NEX, яка може згодитись деяким гікам, зокрема - в альтернативних мережах Onion та [Yggdrasil](https://devzone.org.ua/search?query=yggdrasil), де канальний трафік між двома підключеннями вже зашифрований а використання класичного DNS вважається поганим тоном. + +## Архітектура + +NEX - це мережний протокол прикладного рівня, сімейства Gopher / Gemini. + +Окрім відсутності характерних для Gemini вимог шифрування, протокол також зручний для прямих підключень по IP без використання DNS та SNI. Не потрібно генерувати сертифікати, слідкувати за їх актуалізацією та вигадувати костилі для Certbot. + +Подібно до Gemini, взаємодія між клієнтом і сервером відбувається шляхом надсилання пакету через сокет. Для ідентифікації типу підключення клієнтом використовується схема `nex://`, а замість порту `1965` стандартним є `1900`. + +NEX не містить жодних мета заголовків: переадресацій, кодів статусів, MIME типів та іншого. Тим не менше, він підтримує і дозволяє працювати з різними типами даних, включно з Gemtext і медіа форматами. + +Ось кілька простих правил з документації: + +* формат файлу визначається клієнтом (браузером) за його розширенням у назві +* якщо розширення не вказано, клієнт буде інтерпретувати документ як `text/plain` +* якщо адреса закінчується слешем, таку адресу прийнято вважати директорією + +Специфікація протоколу не декларує власного типу даних і розмітки за виключенням синтаксису посилань, формат яких подібний до [Gemtext](https://geminiprotocol.net/docs/gemtext.gmi) - тобто посилання можна робити клікабельними, використовуючи префікс `=>` в `text/plain`. + +## Клієнти + +Протокол NEX є поширеним і давно відомим в середовищі Geminispace - такий популярний браузер як [Lagrange](https://gmi.skyjake.fi/lagrange/) підтримує його з коробки, тому не потрібно шукати додаткових рішень для GUI чи змінювати улюблений клієнт. + +Говорячи про CLI, на мою думку, NEX є зручнішим і більше орієнтованим для користувачів саме командного рядка: для взаємодії можна використовувати такі базові утиліти як `telnet`, `nc`, `ncat` тощо: + +``` bash +telnet nightfall.city 1900 +nex +``` + +## NPS + +Подібно [Titan](https://communitywiki.org/wiki/Titan) для Gemini, NPS є сателітом для відправки даних на сервер - для NEX. + +### Специфікація + +Стандартним для NPS є порт `1915`. + +В залежності від реалізації та специфіки конкретного сервера, клієнт може відправляти команди і типи даних в порядку що відрізняється і так, як задумано автором конкретного сервісу. + +Зокрема, сервіс чату в блокчейн KevaChat NPS ([приклад реалізації на PHP](https://github.com/kevachat/npsapp) з використанням асинхронної бібліотеки [Ratchet](https://github.com/ratchetphp/Ratchet)) - вимагає на першому кроці введення ASCII каптчі, вибору кімнати і вже потім - безпосередньо відбувається надсилання текстового повідомлення. + +Кожен сервер має власний гайд в режимі CLI, та як правило посилання на документацію. + +Характерною рисою NPS є одна крапка в останньому рядку повідомлення, яка також сигналізує кінець з'єднання. + +### Приклади + +Розберемо приклад надсилання оголошення на сервіс `classifieds` серверу `nightfall.city` + +``` bash +nc nightfall.city 1915 +classifieds +YOUR MESSAGE GOES HERE +. +``` + +Якщо коротко розібрати команди: + +* першим кроком відбувається підключення до серверу по DNS чи IP на `1915` порт +* далі слідує вибір URI ресурсу, у даному випадку `classifieds` +* після цього надсилається тіло повідомлення по одному рядку +* якщо рядок містить тільки крапку - пакет вважається надісланим + +Згідно такому принципу, можна надсилати і багато-рядкові дані з певного файлу: + +створимо `file.txt` з наступним вмістом: + +``` file.txt +classifieds + ___ + /\__\ ___ + /:/ / /\ \ + /:/__/ \:\ \ + /::\ \ ___ /::\__\ + /:/\:\ /\__\ __/:/\/__/ + \/__\:\/:/ / /\/:/ / + \::/ / \::/__/ + /:/ / \:\__\ + /:/ / \/__/ + \/__/ +. +``` + +тепер надішлемо його на сервер `nightfall.city`, до розділу `classifieds` (що вказаний у першому рядку) за допомогою утиліти `cat` + +``` bash +cat file.txt | nc nightfall.city 1915 +``` + +* сервер у прикладі не є тестовим, експериментуючи дотримуйтесь мережної етики і змістовності повідомлень +* для коректної роботи `nc` в середовищі IPv6, можливо доведеться встановити пакет `netcat-openbsd` +* варто зауважити, що деякі емулятори терміналу (наприклад, [Tilda](https://github.com/lanoxx/tilda)) можуть некоректно обробляти кириличні символи командою backspace, через відсутність підтримки мультибайтового UTF-8. Виправити це можна зміною емулятора або спробувавши вказати `stty iutf8` в `~/.bashrc` + +## Проксі + +Потокові дані легко проксуються через сервер Nginx, простий приклад з `nginx.conf`: + +``` /etc/nginx/nginx.conf +stream { + server { + listen 1915; + proxy_pass [IP]:1915; + } +} +``` + +## Висновки + +Протокол NEX - це окремий різновид взаємодії та спілкування у мережі. Його переваги здебільшого стають зрозумілими після досвіду роботи з Gemini, який не дивлячись на лаконічність, все ж має ряд особливостей, які у певних випадках бувають зайві. + +Якщо ви знаєте цікаві ресурси, доступні у форматах NEX/NPS, поділіться в коментарях! + +## Посилання + +* [Специфікація NEX](https://nightfall.city/nex/info/specification.txt) +* [Специфікація NPS](https://nightfall.city/nps/info/specification.txt) +* [Офіційний сайт](https://nightfall.city) \ No newline at end of file diff --git a/post/publikatsiia-kapsuly-gemini-v-i2p-na-prykladi-servera-agate.md b/post/publikatsiia-kapsuly-gemini-v-i2p-na-prykladi-servera-agate.md new file mode 100644 index 0000000..5331f65 --- /dev/null +++ b/post/publikatsiia-kapsuly-gemini-v-i2p-na-prykladi-servera-agate.md @@ -0,0 +1,115 @@ +# Публікація капсули Gemini в I2P на прикладі сервера Agate + +Стало цікаво перевірити роботу [протоколу Gemini](https://devzone.org.ua/post/protokol-gemini-iak-alternatyva-http) в контексті I2P, адже вочевидь, ці технології - ніби створені одна для одної: + +1. мінімальний розмір контенту, та одно-поточні запити до сервера: що добре для швидкості відгуку та легкі для мережі I2P в цілому, адже не тягатимуть купу асинхронних скриптів, трекерів, стилів, медіа та іншого мотлоху +2. приватно-орієнтованість: немає заголовків з рефералами, сторонніх підвантажень контенту, а отже - не потрібні костилі з [ізольованими контейнерами](https://devzone.org.ua/post/izoliatsiia-linux-vid-priamykh-internet-zyednan-na-bazi-qemu-virtual-machine-manager-i-vsock), [окремі браузери з PAC](https://devzone.org.ua/post/bezpechnyy-perehliad-saytiv-yggdrasil-z-yggstack) чи [правила фаєрволу](https://devzone.org.ua/post/obmezennia-vykhidnykh-zyednan-na-internet-z-ufw), що фільтруватимуть потенційні витоки з'єднань на Інтернет +3. відсутність потреби у виділеному IP: роутер I2P може поставити собі кожен, для цього не потрібно навіть відкривати порт +4. ідеально для соціальних децентралізованих застосунків: [авторизація на базі TLS](https://geminiprotocol.net/docs/protocol-specification.gmi#client-certificates) не передбачає передачу на сервер класичних для Веб e-mail, паролів та іншої потенційно чутливої інформації + +Сьогодні таки зібрався і вирішив спробувати опублікувати [свою капсулу](gemini://ps.cities.yesterweb.org) (у просторі Gemini - так називається сайт) При цьому, вирішив не проксувати трафік I2P на віддалений хостинг [Yesterweb](https://yesterweb.org/community/gemini/), а саме задзеркалити копію ресурсу (mirror) на локальному сервері. + +## I2P + +Тут передбачається, що вже встановлено роутер I2P. Якщо ні - перегляньте [попередній матеріал](https://devzone.org.ua/post/vstanovlennia-routera-i2p-z-pidkliuchenniam-cherez-yggdrasil) або скористайтесь [офіційною документацією](https://i2pd.readthedocs.io/en/latest/) (i2pd) + +Для публікації капсули, достатньо додати наступну конфігурацію до файлу `/var/lib/i2pd/tunnels.conf`: + +``` tunnels.conf +[gemini] +type = server +host = 127.0.0.1 +port = 1965 +keys = gemini.dat +``` +* `gemini` - умовна назва вашого профілю +* шлях до `tunnels.conf` може відрізнятись, в залежності від налаштувань + +Тепер потрібно перезапустити i2pd: + +``` bash +systemctl i2pd restart +``` + +Після цього, роутером буде згенеровано ключ тунелю - по суті, він є вашим доменом в мережі I2P (знаходиться відповідно в файлі `/var/lib/i2pd/gemini.dat`) тому бажано забекапити його копію у надійному місці для можливості відновлення. + +## Agate + +[Agate](https://github.com/mbrubeck/agate) - це [один з відомих](https://github.com/kr1sp1n/awesome-gemini) серверів Gemini для публікації статики Gemtext, написаний мовою Rust. + +*Взагалі, я хотів би опублікувати дзеркало на усі мережі: I2P, [Yggdrasil](https://devzone.org.ua/post/yggdrasil-mereza-z-detsentralizovanym-routynhom) і Mycelium, але сервер Agate має свою специфіку роботи з SNI: чомусь, більшість розробників в очі не бачили IPv6 і таке поняття як мульти-стек. Тому забігаючи на перед - ця інструкція підходить лише для публікації одного домену в I2P; якщо ви хочете забіндитись на різні мережі, то подивіться сервер [gmid](https://devzone.org.ua/post/gmid-bahatofunktsionalnyy-proksi-server-dlia-gemini) (clang)* + +### Встановлення + +Інформація доступна в репозиторії проєкту, але я зібрав для себе по вже натоптаному шляху: + +``` bash +git clone https://github.com/mbrubeck/agate.git +cd agate +cargo build --release +sudo install target/release/agate /usr/local/bin/agate +``` +* [встановлення останньої версії Rust в Linux](https://devzone.org.ua/post/vstanovlennia-ostannyoyi-versiyi-rust-v-linux) + +Створімо окремого користувача з домашньою текою, від якого запускатимемо сервер і де зберігатимемо статичні файли: + +``` bash +useradd -m agate +``` + +### Systemd + +``` /etc/systemd/system/agate.service +#/etc/systemd/system/agate.service + +[Unit] +After=network-online.target +Wants=network-online.target + +[Service] +Type=simple + +User=agate +Group=agate + +WorkingDirectory=/home/agate +ExecStart=/usr/local/bin/agate --content /home/agate/public --addr 127.0.0.1:1965 --hostname xxxx.b32.i2p + +StandardOutput=file:///home/agate/debug.log +StandardError=file:///home/agate/error.log + +[Install] +WantedBy=multi-user.target +``` +* `xxxx.b32.i2p` - ваш домен в I2P +* `/home/agate/public` - тека зі статичними файлами для публікації + +## Запуск + +Порт 1965 відкривати не потрібно, адже вхідний трафік йтиме з локального роутера i2pd. + +* `systemctl start agate` - запускаємо сервер +* `systemctl enable agate` - додаємо в автозапуск при старті системи +* `systemctl status agate` - перевіряємо роботу + +## Клієнт + +Для доступу до ресурсів I2P, потрібен клієнт (браузер) Gemini з функціями проксування SOCKS або HTTP. Наскільки мені відомо, популярний браузер [Lagrange](https://github.com/skyjake/lagrange/) (на момент публікації матеріалу) того не вміє. Останнім часом, активно розвивається клієнт [Alhena](https://github.com/mochaman/alhena), але там альтернативно реалізована клієнтська частина, що виходить за рамки специфікації і підвантажує inline-медіа на фоні. + +Особисто я користуюсь спеціально створеним для моїх потреб GTK браузером [Yoda](https://github.com/YGGverse/Yoda), де реалізовано підтримку різних типів проксі а також маршрутизацію по regex і пріоритетам: + +``` +\.i2p$ http://127.0.0.1:4444 +``` +* regex + локальний проксі відповідно +* стандартно, сервер HTTP в i2pd працює на порту 4444 +* правило регулярного виразу актуальне для версії 0.12.2+ + +В інтерфейсі налаштувань це виглядає так: + +![Налаштування проксі I2P в Yoda](https://devzone.org.ua/storage/posts/2025/10/09/wy8JwY93lcSfZKBJ0VjFtYZvqlfOJnlVsrG93sL4.png) +* на прикладі видно окремий рядок налаштувань з іншого проксі ([Yggstack](https://github.com/yggdrasil-network/yggstack)) для адрес Yggdrasil, не звертайте на нього уваги - його вимкнено, але якщо у вас є інші активні правила, для них можна вказати пріоритет (замість `0`) + +Після цього, можна відкрити адресу в браузері, наприклад: + +[gemini://shxxkkrws2m6qowjse5jpgmu64vzupnnhxrhdzrn6fr6m7ynddbq.b32.i2p](gemini://shxxkkrws2m6qowjse5jpgmu64vzupnnhxrhdzrn6fr6m7ynddbq.b32.i2p) \ No newline at end of file diff --git a/post/pyseeder-pidtrymka-ekosystemy-i2p-v-merezi-yggdrasil.md b/post/pyseeder-pidtrymka-ekosystemy-i2p-v-merezi-yggdrasil.md new file mode 100644 index 0000000..0c15d63 --- /dev/null +++ b/post/pyseeder-pidtrymka-ekosystemy-i2p-v-merezi-yggdrasil.md @@ -0,0 +1,165 @@ +# Pyseeder: підтримка екосистеми I2P в мережі Yggdrasil + +Так як при першій [ініціалізації вузла i2pd засобами Yggdrasil](https://devzone.org.ua/post/vstanovlennia-routera-i2p-z-pidkliuchenniam-cherez-yggdrasil) (для форуму [спільноти](https://devzone.org.ua/topic/ukrayinska-spilnota-administratoriv-alternatyvnykh-merez)) виникли деякі затримки, вирішив слідом за конектом свого роутера - підняти й додатковий ресід для інших локальних юзерів. + +## Pyseeder + +[pyseeder](https://github.com/PurpleI2P/pyseeder) - утиліта командного рядка, написана мовою Python. Вона використовується для різних задач, зокрема для генерації файлу `i2pseeds.su3`, необхідного для пошуку вузлів при першому запуску роутера I2P (в i2pd, він звичайно вказується в `i2pd.conf`). Це працює приблизно так, як DNS сід гаманців BitCoin. + +Інструкції з користування pyseeder описані в [README.md](https://github.com/PurpleI2P/pyseeder/blob/master/README.md) проєкту. Нижче опишу лише свою конфігурацію. + +### Встановлення + +Для Debian / Linux знадобились такі пакунки: + +``` bash +apt install python3 python3-virtualenv build-essential libssl-dev libffi-dev +``` + +Від користувача, яким планується подальший запуск: + +``` bash +git clone https://github.com/PurpleI2P/pyseeder.git +cd pyseeder +virtualenv --python=python3 venv +. venv/bin/activate +pip3 install . +``` + +### Nginx + +В pyseeder, мене спочатку заплутала наявність сервера, адже для DNS сіда BitCoin - потрібен саме він. Тут же все простіше: потрібно просто згенерувати дамп "пірів" у статичний файл і розмістити його у теці яку слухає будь який Веб-сервер, зокрема Nginx. + +Створімо публічну теку: + +``` bash +mkdir /var/www/pyseeder +chown USER:GROUP /var/www/pyseeder +``` +* `USER:GROUP` - ваше значення + +Копіюємо туди файл `seed.html` з теки [yggdrasil](https://github.com/PurpleI2P/pyseeder/tree/master/yggdrasil): + +``` bash +cp yggdrasil/seed.html /var/www/pyseeder +``` + +Приклад налаштувань хосту Nginx можна знайти у файлі [reseed_nginx.conf](https://github.com/PurpleI2P/pyseeder/blob/master/yggdrasil/reseed_nginx.conf). Я організував свій хост наступним чином: + +``` reseed_nginx.conf +limit_req_zone $binary_remote_addr zone=reseed:512k rate=10r/m; +server { + root /var/www/pyseeder; + listen [302:68d0:f0d5:b88d::9216]:80; + # на цьому сервері також прослуховується 0400::/7 (Mycelium) + # listen [505:6847:c778:61a1::9216]:80; + index seed.html; + location /i2pseeds.su3 { + limit_req zone=reseed burst=5; + if ($http_user_agent !~* "Wget/1.11.4" ) { return 403; } + } + location /user_at_mail.i2p.crt { + limit_req zone=reseed; + } +} +``` +* не забудьте додати відповідні маски підмережі або використовуйте основну адресу на іншому порту, якщо 80 зайнятий +* також після змін треба оновити конфігурацію `systemctl reload nginx` + +### Генерація ключа і сертифіката + +``` bash +pyseeder keygen --cert /var/www/pyseeder/user_at_mail.i2p.crt --private-key /path/to/priv_key.pem --signer-id noreply@localhost +``` +* `--cert` потрібен якщо на цільовому сервері увімкнена опція `reseed.verify = true`; якщо ви плануєте стати "офіційним" ресідом, його також потрібно додати до репозиторію i2pd через PR (інакше користувачу потрібно буде вказати цей сертифікат при імпорті пірів для верифікації постачальника `i2pseeds.su3`) +* `--private-key` - власне **приватний** ключ, яким підписуються дані для експорту +* `--signer-id` - поки не зрозумів, чому цей аргумент обов'язковий, доповню згодом, поки вказав `noreply@localhost` +* пароль на запит можна вказати пустим + +### Генерація i2pseeds.su3 + +``` bash +YOUR_PASSWORD="Pa55w0rd" +echo $YOUR_PASSWORD | pyseeder reseed --netdb /path/to/netDb --private-key /path/to/priv_key.pem --outfile /var/www/pyseeder/i2pseeds.su3 --signer-id noreply@localhost +``` +* `YOUR_PASSWORD` - пароль, який було вказано при `pyseeder keygen` (або кропаємо все до `|` та додаємо аргумент `--no-encryption` якщо пароль відсутній) +* `--netdb` - шлях до бази мережі I2P, це може бути відфільтрована скриптом або "жива" база вузла +* `--outfile` - публічна тека на Веб-сервері, звідки інші піри зможуть її забрати +* `--signer-id` - той само, що вказувався при генерації ключів + +Для оновлення по розкладу crontab, створімо скрипт `crontab.sh`: + +``` crontab.sh + #!/bin/bash +cd /path/to/pyseeder +. venv/bin/activate +#YOUR_PASSWORD="Pa55w0rd" +#echo $YOUR_PASSWORD | pyseeder reseed --netdb /path/to/netDb --private-key /path/to/priv_key.pem --outfile /var/www/pyseeder/i2pseeds.su3 --signer-id noreply@localhost +pyseeder reseed --no-encryption --netdb /path/to/netDb --private-key /path/to/priv_key.pem --outfile /var/www/pyseeder/i2pseeds.su3 --signer-id noreply@localhost +deactivate +``` +* в прикладі вказані сценарії з використанням паролю і без (розкоментуйте потрібне) +* замініть значення `YOUR_PASSWORD`, `/path/to` і `noreply@localhost` на ваші +* додаємо права на виконання: `chmod +x crontab.sh` + +І додамо завдання: + +``` bash +crontab -e +``` +* виконується від потрібного користувача + +``` bash +@hourly /path/to/crontab.sh +``` + +## Фільтрація вузлів Yggdrasil + +В принципі, можна користуватись утилітою з командного рядка (після активації `. venv/bin/activate`) але у випадку піра Yggdrasil, бажано не просто здампити накопичену базу NetDB, але й відфільтрувати вузли Yggdrasil для локальних користувачів, зокрема - віддавати їх в пріоритеті. Цю задачу виконує скрипт [y2r.sh](https://github.com/PurpleI2P/pyseeder/blob/master/yggdrasil/y2r.sh). + +Перед його запуском, важливо актуалізувати в ньому наступні шляхи: + +* `netdb` - донор: ймовірно, база активного вузла `/var/lib/i2pd/netDb` +* `outdb` - не публічна копія з відфільтрованими результатами роботи `y2r.sh` +* `sed -i "s/>[0-9]\{1,1000\}$yggaddr /sys/class/backlight/radeon_bl0/brightness +``` +або ж подивіться що у вас в теці (і підставте в команду вище вашу карту): +``` +ls /sys/class/backlight +``` + +Зауважу, що наведена вище інструкція, у моєму випадку, вимагає зміни `acpi_backlight` в значення `native` +``` +#/etc/default/grub +GRUB_CMDLINE_LINUX_DEFAULT="quiet acpi_backlight=native" +``` + +Після чого потрібно оновити конфігурацію командою `update-grub` і перезавантажити систему. +Остання дія також додасть підтримку сенсору автоматичної яскравості, зокрема в Ubuntu. + +Сподіваюсь, дана замітка стане в нагоді. + +Якщо маєте новішу модель, поділіться вашим досвідом в коментарях! \ No newline at end of file diff --git a/post/reticulum-vstanovlennia-na-prykladi-meshchat-z-pidkliuchenniam-cherez-yggdrasil.md b/post/reticulum-vstanovlennia-na-prykladi-meshchat-z-pidkliuchenniam-cherez-yggdrasil.md new file mode 100644 index 0000000..c95cc3f --- /dev/null +++ b/post/reticulum-vstanovlennia-na-prykladi-meshchat-z-pidkliuchenniam-cherez-yggdrasil.md @@ -0,0 +1,189 @@ +# Reticulum / MeshChat з підключенням через Yggdrasil + +[Reticulum](https://github.com/markqvist/Reticulum) - відносно новий протокол зв'язку, створений в першу чергу для радіо-мереж з метою захищеної, децентралізованої передачі даних в умовах високої затримки сигналу, але може працювати в оверлейному режимі (поверх інших мереж). На відміну від наявних реалізацій локальних мереж CJDNS, [Yggdrasil](https://devzone.org.ua/post/yggdrasil-mereza-z-detsentralizovanym-routynhom), [Mycelium](https://devzone.org.ua/post/vstanovlennia-routera-merezi-mycelium-v-linux) та інших, в основі не використовує стек IP як такий, натомість реалізує передачу даних в етері через пірингові ретранслятори, вже знайомі користувачам I2P як "хопи". + +Сфери застосування технології Reticulum можуть бути різними: в цивільному сегменті - розробляються такі протоколи обміну даними як [LXMF](https://github.com/markqvist/lxmf) та вже функціонуюча його засобами соціальна платформа [NomadNet](https://github.com/markqvist/NomadNet), що віддалено може нагадувати гібрид ZeroNet і [Gemini Protocol](https://devzone.org.ua/post/protokol-gemini-iak-alternatyva-http) поверх I2P, з функціональністю месенджера. + +В технічну складову роботи мережі вдаватись не буду, оскільки сам в ній тільки розбираюсь. Натомість, пропоную ознайомитись з Reticulum на рівні користувача, встановивши клієнтське програмне забезпечення соціальної платформи та налаштувавши вузол ретрансляції для локальних користувачів мереж Yggdrasil та Mycelium. + +## Клієнт + +Оригінальний клієнт NomadNet я пробував встановлювати багато разів, але інтерфейс TUI видавався не зручним; через вибраний мною не стабільний пір я також не міг підключитися до внутрішніх сайтів і зрозуміти як це працює. Невдовзі, перечитуючи документацію, вирішив спробувати [MeshChat](https://github.com/liamcottle/reticulum-meshchat), який використовує ті само технології, але надає їх в сучасному та інтуїтивно зрозумілому форматі Web UI (засобами Vue / Node.js) + +Окрім перегляду ресурсів NomadNet, текстового месенджера та голосових викликів, MeshChat надає деякі інструменти адміністрування і візуалізації мережі Reticulum. + +Для встановлення MeshChat в Debian / Linux, є проста і робоча [інструкція](https://github.com/liamcottle/reticulum-meshchat/blob/master/docs/meshchat_on_raspberry_pi.md): + +### Системні залежності + +Реалізації самої мережі Reticulum та застосунку MeshChat зокрема - написані мовою Python: + +``` bash +sudo apt install git python3-pip +``` + +Для збірки Web UI сервера MeshChat, додатково знадобляться інструменти Node.js + +``` bash +curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | sudo gpg --dearmor -o /usr/share/keyrings/nodesource.gpg +NODE_MAJOR=22 +echo "deb [signed-by=/usr/share/keyrings/nodesource.gpg] https://deb.nodesource.com/node_$NODE_MAJOR.x nodistro main" | sudo tee /etc/apt/sources.list.d/nodesource.list +sudo apt update +sudo apt install nodejs +``` + +Також додамо системного користувача з текою для даних профілю: + +``` bash +sudo useradd -m reticulum +``` + +### Встановлення + +Командами нижче буде встановлено маршрутизатор Reticulum, сервер MeshChat та зкомпільовано його Веб-інтерфейс: + +``` bash +su reticulum +cd ~ +git clone https://github.com/liamcottle/reticulum-meshchat +cd reticulum-meshchat +pip install -r requirements.txt --break-system-packages +npm install --omit=dev +npm run build-frontend +``` + +Забігаючи наперед, при запуску в мене була [помилка](https://github.com/liamcottle/reticulum-meshchat/issues/110), тут я руками створив теку: + +``` bash +mkdir -p ~/reticulum-meshchat/public +``` + +### Системний сервіс + +Сервер зручно пускати через systemd. Для цього створімо сервіс (в мене він трохи відрізняється від оригіналу) + +``` /etc/systemd/system/reticulum-meshchat.service +#/etc/systemd/system/reticulum-meshchat.service + +[Unit] +Description=reticulum-meshchat + +After=network.target +Wants=network-online.target +StartLimitIntervalSec=0 + +[Service] +Type=simple + +User=reticulum +Group=reticulum + +WorkingDirectory=/home/reticulum/reticulum-meshchat + +# я пускаю слідом за Yggdrasil та Mycelium, в мене там затримка в 5 секунд для інших залежностей +# ExecStartPost=/bin/sleep 10s + +# можна (і варто) вказати конкретний хост і порт, з якого відбуватиметься підключення до веб-адмінки +ExecStart=/usr/bin/env /usr/bin/python3 /home/reticulum/reticulum-meshchat/meshchat.py --headless --host 127.0.0.1 --port 1234 + +# куди писати журнали +StandardOutput=file:///home/reticulum/debug.log +StandardError=file:///home/reticulum/error.log + +[Install] +WantedBy=multi-user.target +``` +* `sudo systemctl enable reticulum-meshchat` - авто-запуск при старті системи +* `sudo systemctl start reticulum-meshchat` - запуск +* `sudo systemctl status reticulum-meshchat` - перевірка статусу + +### Фаєрвол + +Якщо сервер пускається локально, відкривати порт для адмінки не потрібно, просто заходимо на http://localhost:1234 + +На віддаленій машині, потрібно відкрити відповідний порт TCP (на прикладі вище - `1234`) + +``` bash +sudo ufw allow from xx.xx.xx.xx to xx.xx.xx.xx port 1234 proto tcp +``` +* `xx.xx.xx.xx` - IP сервера та клієнта відповідно + +## Підключення до Reticulum через Yggdrasil / Mycelium + +Підключитись до Reticulum в оверлейному режимі (mesh) можна різними засобами. Особисто я користуюсь мережами Yggdrasil і Mycelium, тому нижче приклад розгортання конекту для такої екосистеми. + +Наразі, мені відома одна [точка доступу для Yggdrasil](https://yggdrasil-network.github.io/services.html#reticulum-nodes) / TCP, вона додається в налаштування роутера Reticulum, після чого потрібно перезавантажити сервер MeshChat, що через нього підключається: + +``` ~/.reticulum/config +#~/.reticulum/config +[interfaces] + [[rothbard_RNS_transport_ZA_ygg]] + type = TCPClientInterface + enabled = true + target_host = 200:73eb:2e4:14be:aac7:90b3:784b:71a3 + target_port = 4242 +``` +* ця нода не є стабільною, якщо у вас відвалиться конект, то майте на увазі (спробуйте віднайти та додати інші) + +## Налаштування вузла ретрансляції + +Коли ви успішно підключились до мережі Reticulum, буде корисним поділитись доступом до мережі з іншими. До налаштувань роутера Reticulum потрібно додати / змінити наступні налаштування: + +``` ~/.reticulum/config +#~/.reticulum/config + +# дозволити транзитний трафік (стандартно False) +enable_transport = true + +[interfaces] + + # вказуємо серверний інтерфейс, на якому будемо "слухати" вхідні підключення + # якщо у вас використовується Інтернет IPv6 і ви не хочете його використовувати, + # краще вказати дві окремі опції для локальних інтерфейсів Mycelium і Yggdrasil. + [[TCP Server Interface]] + type = TCPServerInterface + interface_enabled = true + listen_ip = :: + listen_port = 4242 +``` + +Таким чином, з іншого сервера, я можу підключитись на обидві мережі Mycelium і Yggdrasil: + +``` ~/.reticulum/config +#~/.reticulum/config +[interfaces] + + # клієнтські інтерфейси (ведуть на :: сервера) + [[ygg]] + type = TCPClientInterface + enabled = true + target_host = 202:68d0:f0d5:b88d:1d1a:555e:2f6b:3148 + target_port = 4242 + [[myc]] + type = TCPClientInterface + enabled = true + target_host = 505:6847:c778:61a1:5c6d:e802:d291:8191 + target_port = 4242 +``` +* ви можете додати будь які інші [піри](https://github.com/markqvist/Reticulum/wiki/Community-Node-List) та [інтерфейси](https://reticulum.network/manual/interfaces.html) з поміж доступних, в тому числі UDP або навіть I2P (зробити це можна також через Веб-адмінку MeshChat) + +Для доступу до сервера, в залежності від його типу, потрібно також відкрити відповідні порти (у прикладі це `4242`) + +``` bash +sudo ufw allow from 0200::/7 to 202:68d0:f0d5:b88d:1d1a:555e:2f6b:3148 port 4242 proto tcp +sudo ufw allow from 0400::/7 to 505:6847:c778:61a1:5c6d:e802:d291:8191 port 4242 proto tcp +``` +* актуалізуйте ваші адреси IPv6 (`ifconfig`) + +Після цього перезавантажуємо сервіс: + +``` bash +sudo systemctl restart reticulum-meshchat +``` + +Перевіряємо + +``` bash +sudo systemctl status reticulum-meshchat +netstat -tulpn | grep 4242 +``` \ No newline at end of file diff --git a/post/reyestratsiia-domenu-v-merezi-i2p.md b/post/reyestratsiia-domenu-v-merezi-i2p.md new file mode 100644 index 0000000..c3a66c0 --- /dev/null +++ b/post/reyestratsiia-domenu-v-merezi-i2p.md @@ -0,0 +1,80 @@ +# Реєстрація домену в мережі I2P + +Останнім часом, захопився дослідженням мережі I2P, зокрема: + +* [встановлення роутера i2pd з підключенням до мережі I2P через Yggdrasil](https://devzone.org.ua/post/vstanovlennia-routera-i2p-z-pidkliuchenniam-cherez-yggdrasil) +* [обміном файлами через технологію BitTorrent](https://devzone.org.ua/post/anonimne-korystuvannia-bittorrent-z-i2psnark-ta-i2pd) +* [опублікував в ній сайт спільноти](https://devzone.org.ua/topic/ukrayinska-spilnota-administratoriv-alternatyvnykh-merez) +* [налаштував](https://devzone.org.ua/post/proksuvannia-m3u8-zasobamy-icecast) ретрансляцію [радіо eQtv](https://devzone.org.ua/topic/retransliatsiia-eqtv-ukrayinskoiu-movoiu-dlia-lokalnykh-merez-audio-32-kbs) +* а також успішно [інтегрував свій блог](https://devzone.org.ua/post/publikatsiia-kapsuly-gemini-v-i2p-na-prykladi-servera-agate), що реалізує протокол [Gemini](https://devzone.org.ua/post/protokol-gemini-iak-alternatyva-http) + +На цьому, думав завершити свій експеримент з налаштуванням інфраструктури, але довгий час залишалось питання стосовно доменів: усі відомі мені ресурси мали короткі адреси, які я знайшов у списку [notbob.i2p](http://notbob.i2p), але цей сайт не відображає перманентні B32. Від того, про мої сервіси ніхто окрім мене не знає, від того крутити їх онлайн - сенсу мало. Трохи полиставши документацію, віднайшов деяку інформацію і організував перший домен для одного зі своїх проєктів у списку, що виявилось тривіально просто. Нижче опишу свій скромний досвід, можливо, він стане в нагоді початківцям. + +## Набір утиліт i2pd-tools + +Для адміністрування доменів I2P, нам знадобиться утиліта [i2pd-tools](https://github.com/PurpleI2P/i2pd-tools). В її репозиторії є детальна [інструкція](https://github.com/PurpleI2P/i2pd-tools?tab=readme-ov-file#i2pd-tools). Збірка з початкового коду, на момент написання матеріалу, виглядає наступним чином: + +``` bash +git clone https://github.com/PurpleI2P/i2pd-tools.git && cd i2pd-tools +git submodule init && git submodule update +git submodule update --init +git pull --recurse-submodules +make +``` + +Розглянемо декілька варіантів реєстрації доменів та відповідні утиліти з набору `i2pd-tools`. + +## Майнинг семантичного префіксу для домена B32 + +Однією з опцій створення домену - може бути майнинг семантичного префіксу для хешу B32 (актуально для EDDSA-SHA512-ED25519). Цей спосіб вартий уваги до розгляду наступних опцій реєстрації, адже згенерувавши візуально зрозумілий префікс, такий сервіс буде простіше знайти в історії браузеру. До того ж це буде канонічний, перманентний ідентифікатор I2P, що згідно оригінальної парадигми, належатиме тільки вам. + +Для цієї мети, в `i2pd-tools`, використовується утиліта `vain`: + +``` bash +./vain domain -o mydomain.dat +``` +* `domain` - бажаний префікс для адреси типу `domain1ad...2u7adq.b32.i2p`: чим більша його довжина - тим [складнішою](https://github.com/PurpleI2P/i2pd-tools?tab=readme-ov-file#time-to-generate-on-a-270ghz-processor) буде процедура видобутку! +* `mydomain.dat` - шлях до файлу, куди буде записано знайдений ключ (тунелю) +* додатково можна вказати: + * `-t` (`--threads`) - кількість паралельних потоків для майнингу + * `-r` (`--reg`) - якщо потрібно генерувати по регулярному виразу замість статичного рядка + +## Централізовані короткі домени + +В цілому, класична технологія DNS в мережі I2P відсутня в принципі. Можливо є засоби інтеграції, але вони так чи інакше пов'язані з централізацією. Короткі домени в I2P - є також частково централізованими, але таких "центрів" як мінімум два: [stats.i2p](http://stats.i2p) (від [команди Java роутера](https://github.com/i2p)) і [reg.i2p](http://reg.i2p) (від [команди роутера C++](https://github.com/PurpleI2P)). Працюють вони за іншим принципом, аніж класичні DNS, але при тому так само виконують свою функцію надання семантичних адрес безкоштовно, хоч і з певними умовами стосовно цензури і терміну дії (умов пролонгації). + +Умови реєстраторів переписувати не буду, актуальну версію можна почитати на їх сайтах. Якщо коротко, то для реєстрації такого формату, адреса повинна бути не зайнята а ваш сервіс - доступним онлайн. В іншому випадку адресу буде звільнено для інших користувачів. + +Працює така реєстрація за принципом автоматизованого створення реєстрів [hosts.txt](http://reg.i2p/hosts.txt) що по суті являють собою індекс псевдонімів для хешу вашого тунелю (домену B32). Коли додається новий домен, він підписується приватним ключем власника тунелю, перевіряється системою реєстратора і за виконання умов - поширюється між іншими роутерами I2P, що підтримують цього реєстратора. + +### Короткий домен другого рівня (2LD) з i2pd-tools/regaddr + +Передбачається, що у вас є умовний сайт з ключем тунелю `domain.dat`, щоб згенерувати для нього підписаний запит на реєстрацію, виконуємо `regaddr`: + +``` bash +./regaddr domain.dat domain.i2p > domain.txt +``` +* `domain.i2p` - бажана адреса для `domain.dat` + +### Короткий домен третього рівня (3LD) з i2pd-tools/regaddr_3ld + +Тут майже те само що й для 2LD, але нам потрібно підписати піддомен третього рівня ключем власника домену другого рівня. Виконується така операція вже в три кроки, утилітою `regaddr_3ld`: + +``` bash +./regaddr_3ld step1 sub_domain.dat sub.domain.i2p > step1.txt +./regaddr_3ld step2 step1.txt domain.dat domain.i2p > step2.txt +./regaddr_3ld step3 step2.txt sub_domain.dat > step3.txt +``` +* `sub.domain.i2p` - бажана адреса для `sub_domain.dat` + +### Реєстрація + +Згенерувавши підписи, переходимо на сайти обраних реєстраторів: + +* [reg.i2p](http://reg.i2p/add) +* [stats.i2p](http://stats.i2p/i2p/addkey.html) + * якщо ви користуєтесь роутером i2pd і бачите помилку "403 Denied - Inproxy access denied. You must run I2P to access this site." - переключіть ваш роутер в режим проксі HTTP замість SOCKS ([джерело](https://github.com/PurpleI2P/i2pd/issues/507)) + +Заповнюємо форму, вказавши наш вміст файлів `domain.txt` і `step3.txt` відповідно до типу домену і слідуємо інструкціям. Звичайно, потрібно почекати декілька днів, поки заявку буде оброблено, а інші роутери мережі оновлять локальні реєстри. Щоб не чекати самому, можна скористатись посиланнями API, що надаються при реєстрації, для миттєвого оновлення вашого локального реєстру. + +На прикладі реєстратора `stats.i2p`, статус домену можна перевірити на [сторінці нових хостів](http://stats.i2p/cgi-bin/newhosts.cgi). \ No newline at end of file diff --git a/post/rozrobka-kartkovoyi-hry-z-vidkrytym-kodom-openlegends.md b/post/rozrobka-kartkovoyi-hry-z-vidkrytym-kodom-openlegends.md new file mode 100644 index 0000000..ede0901 --- /dev/null +++ b/post/rozrobka-kartkovoyi-hry-z-vidkrytym-kodom-openlegends.md @@ -0,0 +1,140 @@ +# Розробка карткової гри з відкритим кодом OpenLegends + +[OpenLegends](https://github.com/openlegends) - це проект реалізації вільного рушія та інтерфейсу багатокористувацької карткової гри [The Elder Scrolls Legends](https://bethesda.net/en/game/legends), в першу чергу орієнтованого на браузерний формат з використанням технологій HTML5. + +Початковий код реалізовано мовою PHP 8, з використанням пакетного менеджеру Composer. Причина обрання цієї мови - наявний досвід та переконання що її можливостей буде цілком достатньо для опису ігрової логіки. Також це зручно для інтеграції веб-застосунків. + +В процесі, можливо буде виконано адаптацію для десктопів на Rust з використанням QT, хоча більш ймовірною буде імплементація на Electron. Окремо хотілось би мати альтернативу для CLI та протоколу [Gemini](https://devzone.org.ua/post/protokol-gemini-iak-legka-alternativa-http), з підтримкою графіки ASCII. + +Враховуючи функціональний потенціал оригінальної гри - завдання для однієї людини не тривіальне, утім мені все ж цікаво спробувати здійснити цю давню мрію і можливо, за цей час хтось також долучитися до розробки! + +## Структура + +Проект ділиться на три окремі гілки розробки: + +* `core-php` - базовий інтерфейс рушія та набір абстрактних класів ядра +* `asset-php` - ігрові набори: карти, ігрові режими та інші компоненти, що реалізують `core-php` +* *сервер* - багатокористувацький інтерфейс, ймовірно буде реалізований на базі фреймворку Symfony + +## Ядро (core-php) + +Мета [core-php](https://github.com/openlegends/core-php) - відділити будь яку ігрову логіку від базової структури рушія, що дозволить робити менш часті оновлення ядра (зберігаючи сумісність як найдовше) та активніше розвивати зовнішні інтерфейси - наприклад, ігрові набори, що реалізують певну його версію. + +Наразі перебуває на стадії прототипу, для встановлення використовується остання версія з репозиторія: +``` +composer require openlegends/core:dev-main +``` + +З появою першої стабільної версії, буде виконано перехід на [Семантичні версії](https://semver.org/lang/uk/) релізів. + +По суті, ядро складається з класів двох типів - Інтерфейси, що власне описують можливості класів карт, гравців, ігрових сесій тощо, та Абстрактні класи - що частково реалізують ці інтерфейси та використовуються ігровими наборами. + +Здебільшого, Абстрактні класи містять у собі такі універсальні методи як Сетери і Гетери, але можуть містити й інші - для запобігання їх дублікації в наборах (де наприклад тільки карткових класів може бути близько тисячі). + +Також, Абстрактні класи містять деякі абстрактні методи, які вимагають специфічної реалізації окремим класом: наприклад, дія карти, або гравця, яка в TES Legends мало чи не описує всю ігрову логіку в одному акті. + +## Ігрові набори (asset-php) + +Ігрові набори представлені в репозиторії [asset-php](https://github.com/openlegends/asset-php) і описують ігрову логіку. + +Сюди входить оголошення та опис кожної карти, гравця, режиму гри та їх можливостей. Ігровий набір у своєму релізі має орієнтуватись на певну версію ядра `core-php`, що спростить розробку та супровід окремо взятого ігрового серверу, який реалізує певний набір. + +В розробці використовується остання версія репозиторія, яка наразі не містить стабільних релізів: +``` +composer require openlegends/asset:dev-main +``` + +Дана версія включає тільки умовний тестовий набір класів, що складається з декількох умовних гравців (Player1, Player2), карт (Mouse, Goblin, Rat) та режиму гри з двох гравців (Arena), достатніх для тестування `core-php` та майбутнього ігрового серверу. + +Після появи першого стабільного релізу ядра, можна буде створити реальний набір, оскільки ядро перебуває в розробці а інтерфейс може змінюватись, у тестовому наборі не доречно обслуговувати більше трьох карт. + +Говорячи про реліз, ймовірно, це буде копія набору TES Legends, оскільки розрахунок нового ігрового балансу - це окреме завдання з області математики і потребує немало часу, включно з тестуванням реальними гравцями. + +Ігрові дані для набору можна подивитись на сайті [TES Legends Pro](https://teslegends.pro) а також запозичити звідти графіку. По зрозумілим причинам, розмістити такі дані в рамках проекту OpenLegends не є доречним, але це не заважає розгорнути альтернативний форк `asset-php`. + +## Приклади роботи + +Завдання будь якого рушія - спрощення розробки гри. Отже, для кращого розуміння що собою являє рушій OpenLegends, розглянемо декілька найпростіших прикладів на основі тестового набору. + +У даному матеріалі не будуть описані інші можливості, враховуючи що вони можуть різнитися в новіших версіях. + +### Взаємодія карт + +``` +card( + $rat +); + +var_dump( + $rat->getHealth() +); +``` + +### Картковий пул + +Використовується здебільшого в ігрових сесіях для організації карткових наборів, наприклад колоди, стопки скиду, карт в руці, тощо: + +``` +addCard( + new \OpenLegends\Asset\Test\Card\Goblin() +); + +$deck->addCard( + new \OpenLegends\Asset\Test\Card\Mouse() +); + +$deck->addCard( + new \OpenLegends\Asset\Test\Card\Rat() +); + +var_dump( + $deck->getRandomCard()->getName() +); +``` + +### Ігрова сесія + +На реальному сервері, гра та її учасники створюються інтерактивно через запити графічного інтерфейсу. + +У тестовому наборі, сесія кожного гравця ініціюється [в конструкторі його класу](https://github.com/openlegends/asset-php/blob/main/src/Test/Game/Player/Player1.php) автоматично, реалізуючи абстрактний клас ядра `\OpenLegends\Core\Abstract\Game\Player`, що включає готові набори карт, об'єкт користувача, статус здоров'я тощо. + +Після того, як об'єкт або клас гравця з картковими пулами було створено, ці дані передаються до об'єкту нової гри та ініціюється сценарій старту: + +``` +addPlayer( + new OpenLegends\Asset\Test\Game\Player\Player1() +); + +$game->addPlayer( + new OpenLegends\Asset\Test\Game\Player\Player2() +); + +$game->start(); +``` + +Інші приклади (зокрема специфіка роботи ігрового серверу через веб-сокети, взаємодія з базами даних, створення інтерактивних інтерфейсів) вартують розгляду в рамках окремої гілки серії. + +Сподіваюсь, на даному етапі, мені вдалось описати загальний статус та принципи розробки проекту. + +Кому цікава реалізація TES Legends з відкритим кодом - ласкаво просимо до OpenLegends! + +## Посилання + +* [Сторінка на GitHub](https://github.com/openlegends) \ No newline at end of file diff --git a/post/rssto-konvertatsiia-fidiv-u-rizni-formaty.md b/post/rssto-konvertatsiia-fidiv-u-rizni-formaty.md new file mode 100644 index 0000000..ee2ec9c --- /dev/null +++ b/post/rssto-konvertatsiia-fidiv-u-rizni-formaty.md @@ -0,0 +1,119 @@ +# rssto: конвертація фідів у різні формати + +Днями, мене знову [зацікавила](https://devzone.org.ua/qna/dodatok-rss-dlia-firefox) тема RSS додатку для браузера, але нічого путнього для себе не знайшов. Звісно, є різні розширення і програми, але в мене є ряд вимог для них: + +* мінімалізм - мені не потрібен комбайн з вбудованим браузером, а лише список заголовків з певного сайту +* приватність - сучасні рішення мало приділяють уваги цьому питанню і часто пропускають сторонні запити навіть при використанні проксі +* агрегація - можливість читати новини в зручних для мене форматах + +Будучи розробником, мені простіше написати власну реалізацію аніж шукати існуючу і виявляти в ній недоліки. Забігаючи на перед скажу, що подібних прототипів у мене багато (пошукати можна по відповідному тегу в репозиторіях [YGGverse](https://github.com/orgs/YGGverse/repositories)), але `rssto` - це останній з них, тим паче що сьогодні я випустив для нього оновлення `0.2`. + +[rssto](https://github.com/YGGverse/rssto) - CLI утиліта конвертації RSS фідів з оцією кравлера, що дозволяє слідкувати за різними RSS фідами і зберігати їх у заданому форматі, зокрема HTML або [Gemtext](https://geminiprotocol.net/docs/gemtext.gmi). + +Іншими словами, дана програма читає фід по URL та зберігає його сутності у статичний файл згідно патерну, звідки його потім можна відкрити зручною для себе програмою. Особисто я користуюсь протоколом [Gemini](https://devzone.org.ua/post/protokol-gemini-iak-alternatyva-http), точніше [Nex](https://devzone.org.ua/post/protokol-nex-lehka-alternatyva-gemini) при чому експортовані дані знаходяться на локальному сервері [Yggdrasil](https://devzone.org.ua/post/yggdrasil-mereza-z-detsentralizovanym-routynhom). На зображенні до цього матеріалу - зображена саме така конфігурація (на базі браузеру [Yoda](https://github.com/YGGverse/Yoda)), тому ви бачите саме такі UI та URL, сама ж утиліта `rssto` - просто форматує заданий XML фід у статичний файл, із заданими налаштуваннями експорту. + +## Встановлення + +Програма написана мовою Rust, тому тут все стандартно: + +``` bash +git clone https://github.com/YGGverse/rssto.git +cd rssto +cargo build --release +sudo install target/release/rssto /usr/local/bin/rssto +``` +* [Встановлення останньої версії Rust в Linux](https://devzone.org.ua/post/vstanovlennia-ostannyoyi-versiyi-rust-v-linux) + +## Налаштування + +Я спеціально навів приклад встановлення з репозиторію, тому що версія `0.2` ще не опублікована в стабільних релізах на [crates.io](https://crates.io/crates/rssto). По цій же причині, не буду детально описувати усі доступні опції (можна подивитись з `rssto --help`), але в цілому розповім про базові принципи конфігурації. + +### Файл конфігурації + +Перше, що потрібно зробити - це створити файл конфігурації, який потрібен для запуску `rssto`. Актуальний приклад конфігурації [доступний в репозиторії](https://github.com/YGGverse/rssto/blob/main/config/example.toml): + +``` example.toml + +# якщо дана опція вказана, rssto працюватиме як фоновий демон а не разовий конвертор, +# тобто оновлюватиме фіди із заданим інтервалом в секундах +update = 60 + +# налаштування каналу для імпорту даних +[[feed]] +# URL шлях до фіду, як на прикладі +url = "https://assets.censor.net/rss/censor.net/rss_uk_news.xml" + +# куди зберігати експортовані дані +storage = "./public/censor.net/rss_uk_news" + +# у яких форматах експортувати дані +# ці теки є в репозиторії, ви можете вказати на базі них власні патерни, використовуючи макроси +templates = ["./template/html","./template/gmi"] + +# обмежити кількість сутностей в каналі (стандартно стільки, скільки є у фіді) +list_items_limit = 20 + +# формати часу для відповідних макросів, залежатимуть від вашої локалі +pub_date_format = "%Y/%m/%d %H:%M:%S %z" +last_build_date_format = "%Y/%m/%d %H:%M:%S %z" +time_generated_format = "%Y/%m/%d %H:%M:%S %z" + +# додайте інші канали по аналогії +#[[feed]] +``` + +Опція `templates` дозволяє використовувати свої шаблони для експорту але тут важливо зберігати поточну структуру їх файлової системи, утім можна довільно змінювати сам вміст шаблонів. Нище наводжу перелік макросів що підтримуються даною версією: + +#### index + +* `{title}` - [channel:title](https://www.w3schools.com/xml/rss_tag_title_link_description_channel.asp) +* `{description}` - [channel:description](https://www.w3schools.com/xml/rss_tag_title_link_description_channel.asp) +* `{link}` - [channel:link](https://www.w3schools.com/xml/rss_tag_link.asp) +* `{language}` - [channel:language](https://www.w3schools.com/xml/rss_tag_language.asp) +* `{pub_date}` - [channel:pub_date](https://www.w3schools.com/xml/rss_tag_pubdate.asp) +* `{last_build_date}` - [channel:last_build_date](https://www.w3schools.com/xml/rss_tag_lastbuilddate.asp) +* `{time_generated}` - час генерації статичної версії фіду (програмою) +* `{items}` - список сутностей каналу, кожну з яких відформатовано за шаблоном `index/item` + +#### index/item + +* `{title}` - [channel:item:title](https://www.w3schools.com/xml/rss_tag_title_link_description_item.asp) +* `{description}` - [channel:item:description](https://www.w3schools.com/xml/rss_tag_title_link_description_item.asp) +* `{link}` - [channel:item:link](https://www.w3schools.com/xml/rss_tag_title_link_description_item.asp) +* `{pub_date}` - [channel:item:pub_date](https://www.w3schools.com/xml/rss_tag_pubdate_item.asp) + +### Systemd + +Програмою `rssto` можна користуватись як конвертором, але особисто в мене вона працює на фоні, постійно оновлюючи фіди з інтервалом в одну годину (`update = 3600`). + +Щоб запустити її на фоні, створімо новий сервіс `/etc/systemd/system/rssto.service`: + +``` +[Unit] +After=network-online.target +Wants=network-online.target + +[Service] +Type=simple + +User=rssto +Group=rssto + +# розкоментуйте та вкажіть актуальний рівень дебагу +# опція "NO_COLOR" вимикає ANSI сутності для запису журналу у файл +# Environment="RUST_LOG=DEBUG" +# Environment="NO_COLOR=1" + +ExecStart=/usr/local/bin/rssto -c /path/to/config.toml + +# якщо не використовується, можна вказати null +StandardOutput=file:///home/rssto/debug.log +StandardError=file:///home/rssto/error.log + +[Install] +WantedBy=multi-user.target +``` +* значення `User` і `Group` та шляхів у мене вказані як користувач створеного мною сервера [Nexy](https://github.com/YGGverse/nexy), але якщо він у вас окремим обліковим записом, то створіть його командою `useradd -m rssto` +* `sudo systemctl enable rssto` - додати до авто-запуску при старті системи +* `sudo systemctl start rssto` - запуск +* `sudo systemctl status rssto` - перевірка статусу \ No newline at end of file diff --git a/post/stvorennia-hrafiti-half-life-v-gimp.md b/post/stvorennia-hrafiti-half-life-v-gimp.md new file mode 100644 index 0000000..6704899 --- /dev/null +++ b/post/stvorennia-hrafiti-half-life-v-gimp.md @@ -0,0 +1,29 @@ +# Створення графіті Half-Life в GIMP + +В мультиплеєрі Half-Life можна малювати графіті (або логотипи) + +Для цього, в стандартній конфігурації, використовується клавіша `T`. +Обрати таке зображення можна в меню `Multiplayer → Customize`, але якщо жодне з них не відповідає бажаному, є можливість зробити його самому, в редакторі з відкритим кодом - GIMP! + +Файл графіті/логотипу для Half-Life має бути у відтінках сірого, розміром `64х64` пікселя та у форматі `BMP`. +При тому, чорний колір у поточній схемі буде відповідати 100% прозорості. Палітру можна потім обрати безпосередньо в інтерфейсі ігрових налаштувань. + +Щоб створити логотип на базі існуючого кольорового зображення, наприклад: + +![half-life-ipv6-source](https://github.com/YGGverse/hl-customs/blob/main/icons/ipv6/128x128.png?raw=true) + +Відкриваємо його в редакторі GIMP і виконуємо наступні кроки: + +1. `Image → Scale Image → 64×64 / Quanlity: Linear` +2. `Layer → Transparency → Remove Alpha Channel` +3. `Colors → Components → Decompose` +4. `Colors → Invert` +5. `File → Export As.. → зберігаємо до valve/logos/filename.bmp` + +Якщо все зроблено правильно, результат буде наступним: + +![half-life-ipv6-target](https://raw.githubusercontent.com/YGGverse/hl-customs/main/icons/ipv6/valve/logos/ipv6.bmp) + +## Дивіться також + +* [Half-Life в Linux на базі рушія Xash3D/FWGS](https://devzone.org.ua/post/half-life-v-linux-na-bazi-rusiia-xash3dfwgs) \ No newline at end of file diff --git a/post/stvorennia-pakunku-flatpak.md b/post/stvorennia-pakunku-flatpak.md new file mode 100644 index 0000000..a7ad1c3 --- /dev/null +++ b/post/stvorennia-pakunku-flatpak.md @@ -0,0 +1,117 @@ +# Створення пакунку Flatpak + +Маю декілька улюблених програм, зокрема - пірингова платформа мікроблогів [twister p2p](https://devzone.org.ua/post/twister-detsentralizovana-platforma-mikroblohiv) та key/value база даних у блокчейн - [KevaCoin](https://devzone.org.ua/post/kevacoin-detsentralizovana-baza-danykh-v-blokcheyn), які доволі важко збираються на сучасних системах, тим не менше потребують нових юзерів для існування їх пірингових мереж. Раніше, робив для них локальні збірки у бінарному форматі та пакунках `deb`, але з часом вони втрачали свою актуальність через залежність від батьківського середовища. Тому врешті зібрався часом та створив два пакунки [Flatpak](https://flatpak.org/), які обіцяють не старіти з часом, щонайменше, в осяжній перспективі. + +Тема Flatpak - не нова, у цьому матеріалі я не стану вдаватись в детальні гайди і архітектуру, але поділюсь власним, першим досвідом роботи з цією системою пакунків, наведу декілька прикладів роботи а також спробою публікації на хостингу [Flathub](https://flathub.org/). Особисто мені не вистачало такого коротенького гайду, а попередні спроби перечитати документацію від і до, закінчувались нудьгою і відкладаннями на потім у моментах, які мені були одразу не очевидними без практики. + +## Платформи + +Екосистема Flatpak влаштована довкола так званих "[рантаймів](https://docs.flatpak.org/en/latest/available-runtimes.html)" або платформ, тобто програмних комплексів або середовищ, які характерні для певної операційної системи, на якій і засобами якої планується збірка програми. + +Основною платформою є [Freedesktop](https://gitlab.com/freedesktop-sdk/freedesktop-sdk/), можна сказати це такий собі голий Linux, який підійде для програм, що не потребують характерних залежностей GTK чи Qt. Для останніх, існують додаткові платформи, які по суті розширюють Freedesktop - зокрема [GNOME](https://gitlab.gnome.org/GNOME/gnome-build-meta), [KDE](https://invent.kde.org/packaging/flatpak-kde-runtime) та інші. Для програм, що використовуватимуть дані фреймворки, специфічна платформа не обов'язкова, ви можете зібрати всі необхідні залежності з початкового коду для Freedesktop, якщо у вас звісно є таке бажання. + +Кожна з платформ має версію релізу, і оновлюється як мені здається раз на рік, тому важливо актуалізовувати вашу програму, якщо ви не бажаєте вимагати від юзера ставити додаткову "операційну систему" лише задля запуску однієї вашої програми. + +## Інкапсуляція + +У контексті Flatpak, мабуть, правильніше використовувати поняття "пісочниці" або [Sandbox](https://docs.flatpak.org/en/latest/sandbox-permissions.html). Якщо описувати влаштування ізоляції програм і їх дозволів Flatpak, то воно має більше спільного з контейнеризацією, хоча автори не задумували проект як альтернативу [Docker](https://www.docker.com/), орієнтуючись саме на користувачів "робочого столу" (Desktop). Чому це так, мені не відомо, адже логіка і принцип роботи Flatpak і Docker - доволі схожі, обидва проекти мають спільні цілі і у відомих мені випадках можуть бути взаємозамінними. + +Якщо коротко, то ізоляція програми Flatpak для кінцевого користувача зумовлена наступними базовими критеріями: + +### Ідентифікатори програм + +Програми в екосистемі Flatpak мають певний формат назви, що запобігає конфліктам з назвами (і відповідно файловими шляхами) інших програм, зокрема у випадку, якщо вони мають схожу назву, але походять від іншого постачальника або є однойменним форком існуючої програми. Повний перелік вимог до йменування є в документації [Application ID](https://docs.flathub.org/docs/for-app-authors/requirements#application-id). Тому при створенні програми і тим більше, якщо ви плануєте її подальшу публікацію на Flathub, важливо дотримуватись специфікації та мати можливість підтвердження володіння простором імен / доменом, в якому оголошено назву. + +### Файлова система + +Файлова система в контексті Flatpak характеризується специфікою її розташування відносно батьківської системи. А саме - в ізольованому просторі імен, що відноситься до умовної "програми" Flatpak. Більше того, щоб зберігати файли у батьківській системі (яка по суті для пісочниці є "зовнішньою") програмі Flatpak потрібно надати відповідний доступ при оголошенні маніфесту. Наприклад, якщо програма в Linux стандартно зберігає дані профілю користувача в теці `~/.app`, то у маніфесті Flatpak цей шлях має бути оголошений атрибутом `--persist=.app`, таким чином, після оголошення шляху у даному атрибуті, реальний шлях до профілю буде `~/.var/app/org.domain.app-id/.app`). Те само стосується й інших каталогів (`share`, `usr` і тд), що планується використовувати із-зовні. + +Також, аргумент `persist` вказує на те, що дані потрібно зберегти після завершення роботи програми Flatpak, оскільки всі дані (окрім виконавчих файлів програми) в процесі роботи зберігаються у тимчасовому сховищі і будуть очищені. По цій причині, ви також не зможете просто дістатись до користувацьких ресурсів пісочниці, оскільки вони зберігатимуться десь у тимчасових файлах з рандомним хешем замість назви файлу, якщо не залінкувати їх на-зовні у маніфесті. + +Окрім згаданого вище `persist`, існують і додаткові дозволи на використання певних каталогів, таких як наприклад `~/Download`. Усі ці дозволи в процесі будуть підсвічені на Flathub (чи в центрі застосунків) як необхідні для роботи програми, і надмірне їх використання без зайвої необхідності може відлякувати юзерів, що не хочуть надавати невідомому застосунку забагато прав доступу. Тому варто їх вказувати тільки тоді, коли програма того дійсно потребує. + +### Інтерфейси + +Як і у випадку з файловою системою, потрібно також явно оголошувати які саме інтерфейси та сокети потрібні для роботи вашої програми. У більшості випадків, програма потребуватиме доступу до Інтернет, тому для неї вказується запит на `--share=network` або `--share=ipc`, якщо потрібен доступ до спільної пам'яті. Програми на базі серверу X11, потребуватимуть `--socket=x11` і так далі. Все це є [в документації](https://docs.flatpak.org/en/latest/sandbox-permissions.html#standard-permissions), тому в рамках даного матеріалу розглядати окремо не будемо. + +## Маніфест + +Маніфест в контексті Flatpak - це доволі простий, але й водночас - головний текстовий файл у форматі `.yml` або `.json`, у якому вказані: + +* платформа та її версія +* SDK +* дозволи програми +* параметри зовнішніх інтерфейсів +* аргументи запуску +* перелік модулів та інструкції для їх послідовної збірки +* інструкції очищення після збірки пакунку + +По суті, цього невеличкого файлу достатньо для запуску вашої програми з будь якого пристрою, підключеного до мережі Інтернет, оскільки його зміст вказує тільки на те, звідки завантажити компоненти, яка в них очікувана контрольна сума, та як саме їх зкомпілювати на вказаному у маніфесті середовищі. + +Щоб не писати багато, пропоную приклад двох своїх маніфестів для [twister p2p](https://github.com/twisterarmy/twister/blob/main/io.github.twisterarmy.twister.json) та [KevaCoin](https://github.com/kvazar-network/kevacoin/blob/kvazar/io.github.kvazar_network.kevacoin-qt.json), хоча ви можете обрати [будь який інший маніфест](https://github.com/orgs/flathub/repositories), що краще відповідає вимогам саме вашої програми - їх там наразі близько чотирьох тисяч! + +Вважаю, що розглядати опції в рамках одного матеріалу немає сенсу, оскільки все має бути інтуїтивно зрозуміло для тих, хто цікавиться даною темою. Особисто я не користуюсь жодними CLI утилітами і просто створюю цей файл в ручну для проекту, для якого планую створити збірку Flatpak. Якщо ви проектуєте нову програму з нуля, а не портуєте спадковий код вже існуючої програми, то рекомендую базовий патерн [gtk-rust-template](https://gitlab.gnome.org/World/Rust/gtk-rust-template), за допомогою якого також можете згенерувати новий десктоп проект з CLI утилітою на Python, що постачається в репозиторії. + +Для компіляції та встановлення програми з валідного маніфесту, достатньо однієї команди: + +``` bash +flatpak-builder --force-clean build\ + --install-deps-from=flathub\ + --install\ + --repo=repo\ + --user\ + org.domain.app-id.json +``` +* де `org.domain.app-id.json` - ваш файл маніфесту + +## Збірка пакунку + +Збірка, або бандл (bundle) - це зкомпільований та оптимізований в реліз файл з розширенням `.flatpak`, який по суті є аналогом пакунку встановлення `.exe` у системах Windows. На відміну від "сирого" маніфесту, де вам потрібно для запуску програми завантажувати первинний код та компілювати його на слабкому залізі, ви можете зробити те само на більш потужному пристрої і передати вже готовий файл збірки на флешці. + +Наприклад, щоб зібрати пакунок, з теки, де знаходиться репозиторій маніфесту, виконується команда: + +``` bash +flatpak build-bundle repo app-id.flatpak org.domain.app-id.json +``` +* `repo` - тека з локальним репозиторієм Flatpak +* `app-id.flatpak` - назва пакунку для генерації +* `org.domain.app-id.json` - файл маніфесту + +Цільовий пристрій, на якому запускається файл інсталяції, тільки дозавантажить необхідну платформу, якщо така не була встановлена раніше (при встановленні інших Flatpak застосунків). Для встановлення в користувацький простір (є ще варіант глобального встановлення від `root`) + +``` bash +flatpak install --user app-id.flatpak +``` +* де `app-id.flatpak` - назва файлу з попереднього кроку + +Щоб не передавати пакунки `.flatpak` на флешках (і не тільки) й було створено хостинг Flathub, про який декілька слів нижче! + +## Flathub + +[Flathub](https://flathub.org/) - це безкоштовний хостинг для пакунків Flatpak. Утім, не все з ним так просто як може здаватись, оскільки публікація пакунку на даній платформі потребує певного контролю якості та відповідності програми вимогам сервісу. + +### Публікація + +Суть публікації пакунку на Flathub полягає в створенні форку [ініціативного репозиторію](https://github.com/flathub/flathub), в якому створюється окрема гілка для подальшого [запиту на додавання](https://github.com/flathub/flathub/pulls) (Pull Request). + +До PR, окрім файлу маніфесту, додається ще один, схожий до маніфесту файл [metainfo](https://docs.flathub.org/docs/for-app-authors/metainfo-guidelines) у форматі XML. Наприклад для twister p2p - він [виглядає так](https://github.com/twisterarmy/flathub/blob/twister-bundle/io.github.twisterarmy.twister.metainfo.xml). А саме: містить назву, опис проекту, версію збірки а також скріншоти та деяку іншу інформацію, необхідну для коректного відображення на сайті каталогу. + +Після модерації, ваш проект буде (або не буде) розміщено в [офіційному списку постачальників](https://github.com/orgs/flathub/repositories) Flathub, а вам (у разі схвалення) - надано статус майнтейнера до відповідного репозиторію для його подальших оновлень. + +Опціонально, постачальники можуть отримати верифікований статус та інші фічі для подальшого просування власного проекту. + +### Вимоги до публікації + +Основною вимогою до усіх застосунків Flathub є наявність графічного інтерфейсу (GUI). Веб-інтерфейс (як у випадку з twister p2p) і тим паче CLI тут не проходять і модератори вимагатимуть від вас або веб-фрейм або лаунчер, по типу Steam. Веб-фрейми мені не подобаються, оскільки надаю перевагу браузеру. Саме тому, проект twister p2p я там досі не опублікував і пішов робити [twister-control-center](https://github.com/twisterarmy/twister-control-center) на базі GTK 4 та додаткової ліби [rust-twistercore-rpc](https://github.com/twisterarmy/rust-twistercore-rpc), порт якої навіть не знаю коли завершу і чи завершу взагалі. Натомість реліз `.flatpak`, який просто запускає демон і відкриває вікно в браузері через `xdg-open`, було [розміщено на GitHub](https://github.com/twisterarmy/twister/releases/download/0.1.1/twister.flatpak), в розділі стабільних релізів. + +По-друге, модератори можуть приколупатись до заміни інструкцій `simple` на `autotools`. Це не зважаючи на те, що майже всі вже додані до їх репозиторію проекти мають `make` в своїй імплементації. Звісно можна переписати, але я користуюсь офіційними інструкціями для модулів і мені ані цікаво, ані виглядає краще з їх побажанками у маніфесті. + +По-третє, якщо з вимогами GUI чи оптимізацією команд маніфесту, модераторів можна зрозуміти, то таке явище як зміна сортування деяких (не array) елементів в маніфесті - для мене загадка. Наприклад, перенести `finish-args` на початок файлу (коли до цього конструкція була внизу) або побажання доопрацювати інструкції очищення `cleanup` при цьому не надаючи конкретних файлів, які не влаштовують. + +Коротше таке, я спробував, але не в моїх інтересах туди пробиватись, для мене головне, щоб юзер міг скачати. Наразі він може зробити це на GitHub, в розділі релізів. Можливо, повернусь до питань модерації через рік-другий :) З іншого боку, може це й на краще, оскільки проект Flathub орієнтований на десктоп користувачів, і це мотивує програмістів створювати більш якісний контент, аніж публікація різноманітного сміття, як це часто відбувається на [crates](https://crates.io/), [packagist](https://packagist.org/), [npm](https://www.npmjs.com/) - де взагалі ручна модерація відсутня. + +## Висновки + +В принципі, мій перший досвід виявився позитивним, після збірки вже другої програми, подумую над іншими проектами, оскільки перевстановлюю системи часто і кожного разу доводиться звідкись збирати чи то Berkeley DB чи то Boost, або стару версію Qt. Тут же я можу просто зібрати пакунок на робочій станції і потім легко поставити його на будь якому пристрої та операційній системі, наприклад на слабкому ноутбуці не від'їдаючи батарею. + +Сподіваюсь, матеріал буде комусь корисним, можливо щось згадаю то доповню! \ No newline at end of file diff --git a/post/twister-detsentralizovana-platforma-mikroblohiv.md b/post/twister-detsentralizovana-platforma-mikroblohiv.md new file mode 100644 index 0000000..4f1c7ce --- /dev/null +++ b/post/twister-detsentralizovana-platforma-mikroblohiv.md @@ -0,0 +1,87 @@ +# twister - децентралізована платформа мікроблогів + +[twister](http://twister.net.co/) (*твістер*, пишеться саме в нижньому регістрі) - це альтернативна пірингова платформа мікроблогів, створена на базі технологій BitTorrent і Bitcoin у 2013 році [@miguelfreitas](https://github.com/miguelfreitas/twister-core) як альтернатива колишньому централізованому сервісу twitter.com + +У 2020 році, розробник платформи [припинив](http://twister.net.co/archives/617) її супровід, але оскільки мережа є децентралізованою, вона все ще продовжує функціонувати у своєму первинному стані, хоча кількість користувачів значно знизилась і наразі мережа близька до колапсу. + +Дана стаття носить історично-інформаційний характер, і створена більше для зацікавлених у децентралізованих мережах користувачів з базовим досвідом програмування мовою C++ та адміністрування Linux, а ніж рядових споживачів сучасного контенту. Також нижче я не описуватиму весь досвід, оскільки наразі працюю над [книгою](https://twisterarmy.github.io/book/) у форматі документації [mdBook](https://devzone.org.ua/topic/introduction-mdbook-documentation) (англійською мовою), яку планую також в релізі перекласти українською. + +## Принцип роботи + +Як згадувалось вище, ядро twister (`twister-core`) по своїй суті є форком `bitcoin-core` з видаленням фактору цифрової валюти як такої, а блокчейн приведено до моделі схожої з [Namecoin](https://uk.wikipedia.org/wiki/Namecoin) - де користувачі зберігають свої дані, замість платіжних транзакцій. Оскільки мережа twister не має власної валюти, мотивація майнингу передбачала рекламні повідомлення у якості "нагороди". Окрім рекламних повідомлень майнерів, блокчейн зберігає мета дані DHT контенту користувачів у часовому порядку та прийняті майнерами запити на їх реєстрацію з рою (swarm). + +Таким чином, і реєстрація і користування для кінцевого користувача задумувалось зробити безкоштовними і такими, що не потребуватимуть жодних обчислювальних потужностей, окрім хіба що, обчислення хешів торентів самих повідомлень (як це працює у звичайному біт-торент клієнті). + +## Випробовування часом + +Звісно, десять років - це не той термін, по якому можна робити висновки, хоча у просторі ІТ й цього часу буває достатньо: у зв'язку зі своєю не прибутковістю та не спекулятивністю, майнинг від самого початку відбувався силами ентузіастів а рекламні повідомлення - містили пусті за змістом дублікати тексту, лише задля підтримки цієї мережі небайдужими користувачами. + +Більшість критики була об'єктивною ще від релізу цієї платформи: +* відсутність анонімізації, що ставило під сумнів можливість використання цієї платформи у країнах з цензурою без додаткових обгорток; +* низький рівень стійкості до спам атак на мережу - у зв'язку з цим був різкий пік сквотингу юзернеймів, з подальшими намаганнями виправити вразливість на швидкоруч; оскільки атака була здійснена невідомим користувачем, лише задля тестів, ймовірно вона була зупинена лише ним самим а не вирішенням фундаментальних недоліків архітектури; +* повідомлення користувачів зберігаються у DHT, щоб зробити блокчейн мінімально "легким" з іншого боку, багато контенту просто втрачено з часом, оскільки блокчейн містить лише мета дані (info-hash) і щоб повідомлення зберігалось на іншому пірі, цей пір (користувач) повинен бути підписаним фоловером. + +В ті роки, мав місце певний "бум" децентралізованих рішень, зокрема з'являлись і такі проекти як OpenBazaar та ZeroNet. Останній, окрім мікроблогінгу, дозволяв хостити свої веб-сторінки у децентралізованому форматі (по типу того, як це було згодом реалізовано в IPFS). Деякі користувачі перебрались на ZN, інші віддавали перевагу більш захищеним платформам накшталт RetroShare. + +У свій час, я теж переключився на інші заняття та коли повернувся - помітив що з усього рою, лишилась буквально пара користувачів. Ще через декілька років, вже не вдавалось під'єднатись, оскільки майже [всі сервісні DNS](https://twisterarmy.github.io/network) були офлайн. + +## Як спробувати + +Наразі, мабуть з великим запізненням, спільнотою розробляється [пакунок Flatpak](https://github.com/twisterarmy/twister/releases/download/0.1.0/twister.flatpak), мабуть це самий простий спосіб запустити twister в сандбокс-режимі, щоб просто подивитись що це таке. Варто зауважити, що при користуванні Flatpak, клієнт падатиме при спробі запустити функцію майнингу, все інше наче працює добре. + +Для тих, хто хоче нативно зібрати повністю робоче ядро під свою систему, напишу коротеньку інструкцію для Linux (на базі [матеріалу з книги](https://twisterarmy.github.io/book/twister-core/twisterarmy/build-on-linux)), оскільки мережа все ще жива і можливо буде комусь цікавою у якості постійної іграшки. + +Наразі є дві опції встановлення: [офіційний twister-core](https://github.com/miguelfreitas/twister-core) та [twister-core від спільноти](https://github.com/twisterarmy/twister-core). Різниця полягає лише в тому, що останній має додаткові DNS вузли для ініціалізації та підтримку IPv6, тобто є більш конективним. Замініть адресу репозиторію у прикладі нижче - вашим вибором! + +### Системні залежності + +**Debian / Ubuntu** + +``` bash +sudo apt install git autoconf automake build-essential libtool\ + libboost-all-dev libssl-dev libdb++-dev libminiupnpc-dev +``` + +**Fedora** + +``` bash +sudo dnf install git autoconf automake libtool make\ + boost-devel openssl-devel libdb-cxx-devel miniupnpc-devel +``` + +### Збірка + +Даний приклад підходить для встановлення на `localhost`, при використанні архітектури `amd64`, тому кроки спрощено. + +1. Ядро ([twister-core](https://github.com/twisterarmy/twister-core)) + - `git clone https://github.com/twisterarmy/twister-core.git` + - `cd twister-core` + - `./autotool.sh` + - `./configure` + - `make` +2. Клієнт: застосунок не має GUI, тому ставимо Web-UI ([twister-html](https://github.com/twisterarmy/twister-html)) + - `mkdir ~/.twister` + - `echo -e "rpcuser=user\nrpcpassword=pwd\nrpcallowip=127.0.0.1" > ~/.twister/twister.conf` - однією командою створюємо стандартний файл конфігурації для Web-UI; для локального запуску вузла, складні логін/пароль не потрібні + - `chmod 600 ~/.twister/twister.conf` + - `git clone https://github.com/twisterarmy/twister-html.git ~/.twister/html` +3. Запуск + - `./twisterd` + - відкриваємо у браузері [http://127.0.0.1:28332](http://127.0.0.1:28332) + * вказуємо стандартні `user` і `pwd` (спадкова реалізація оригінального Bitcoin RPC API) + * далі з'являється діалог вітання із запрошенням перейти на сторінку мережі, де відбуватиметься завантаження блокчейну. + +### Користування + +Коли завантажиться блокчейн, можна спробувати зареєструвати вільний юзернейм (який буде записано в кінець цього ж блокчейну, тобто коли будь який майнер отримає ваш запит і включить його до блоку). Після створення юзера, зберігаємо згенерований приватний ключ, він знадобиться тільки для доступу до акаунту з іншого пристрою або для подальшого бекапу. + +Реєстрація займатиме деякий час. Також, з урахуванням поточного онлайну, деякий час (пара хвилин або навіть годин чи днів) може зайняти ініціалізація DHT процесів, тримайте це діло увімкненим допоки [на сторінці мережі](http://127.0.0.1:28332/network.html) не засвітиться зелений індикатор. + +У списку останніх користувачів, спробуйте знайти активного та щось йому написати. Або напишіть у свою стрічку вітання. + +## Перспективи + +Не стану давати оцінку суб'єктивним поглядом на подальші перспективи цього проекту, оскільки мережа twister є повністю децентралізованою і жодним чином не залежить від моїх висновків. Особисто, мені - вона подобається, на відміну від централізованих сервісів, у тому числі Fediverse, де по суті інстанси є такими само сервер-орієнтованими платформами, але з більш сучасним форматом агрегації (ActivityPub замість RSS). + +Дана мережа обіцяла колапсувати ще п'ять років тому: деякі юзери раз на рік надсилають свій меседж про існування і зникають ще на стільки само. Тим не менше, вона існує у власному вимірі, і коли всі монітори пишуть офлайн, через деякий час виявляється що до неї ще можна під'єднатись. + +Якщо вам подобаються різні екзотичні проекти по типу twister, та ви бажаєте підтримати дану пірингову мережу - доєднуйтесь до користування, щоб продовжити її функціонування - мабуть, в тому і суть роботи по справжньому не залежних соціальних платформ! \ No newline at end of file diff --git a/post/veb-radio-v-linux-vstanovlennia-servera-icecast-ta-bazove-nalashtuvannia-rotatsiyi-z-ezstream.md b/post/veb-radio-v-linux-vstanovlennia-servera-icecast-ta-bazove-nalashtuvannia-rotatsiyi-z-ezstream.md new file mode 100644 index 0000000..13f3f1c --- /dev/null +++ b/post/veb-radio-v-linux-vstanovlennia-servera-icecast-ta-bazove-nalashtuvannia-rotatsiyi-z-ezstream.md @@ -0,0 +1,330 @@ +# Веб-радіо в Linux: встановлення сервера Icecast та базове налаштування ротації з Ezstream + +Давно планував і нарешті вирішив таки створити власну "лампову" радіо-станцію для локальної мережі [Yggdrasil](https://devzone.org.ua/post/yggdrasil-mereza-z-detsentralizovanym-routynhom). Інформації про налаштування [Icecast](https://icecast.org) в мережі повно, але для мене знайдена інфа виглядала заплутаною: я не розумів, чому окремо розглядається Icecast, [Ices](https://icecast.org/ices/), [Ezstream](https://icecast.org/ezstream/) та додатковий скриптинг, у той час як мені для початку було потрібно просто зробити тривіальну річ: мотати по колу плейлист музичних файлів. + +В темі стрімінгу та аудіо-обробки - я нуб. З цієї причини вирішив написати невеличкий гайд для Debian/Linux, зрозумілий для себе "в минулому" а також практичну нотатку, на випадок якщо завалю сервер та доведеться пригадувати що робив. Згодом, планую також навчитись додавати ретрансляцію етерів новин за розкладом, можливо - пускати в ефір живі стріми, тоді й розширю цей матеріал до серії. + +## Icecast + +Icecast - це класичний сервер, на базі якого створюється публічний сервіс для слухачів (клієнтів). Його можна порівняти з проксі сервером Nginx для Веб-сайтів, от тільки створений він спеціально для операцій з потоковими даними. Цей сервер ніяк не взаємодіє з медіа-колекціями напряму, він просто отримує на себе сирий потік аудіо (або інших) даних з бекенду (по API) та розподіляє його на активні підключення, виступаючи в ролі хабу, своєрідної "радіо-вежі". + +**Встановлення** + +``` bash +apt install icecast2 +``` + +**Налаштування** + +Після встановлення пакету з репозиторію, до системи буде автоматично додано сервіс systemd (`/etc/init.d/icecast2`) та стандартний файл конфігурації (`/etc/icecast2/icecast.xml`). Зайдемо до останнього та адаптуємо його під свої потреби: + +``` bash +nano /etc/icecast2/icecast.xml +``` + +``` /etc/icecast2/icecast.xml + + + Earth + icemaster@localhost + + 100 + 2 + 524288 + 30 + 15 + 10 + 1 + 65535 + + + + + PASSWORD + + + PASSWORD + + + admin + PASSWORD + + + + ua + + + + UTF-8 + + + + + 8000 + :: + + + + +
+ + + + 1 + + + + /usr/share/icecast2 + + /var/log/icecast2 + /usr/share/icecast2/web + /usr/share/icecast2/admin + + + + + + + + access.log + error.log + + + 3 + + + 10000 + + + + + + + + 0 + + + +``` +* не забудьте вказати актуальне значення `PASSWORD` +* після внесення змін, необхідно перезапустити сервіс `systemctl restart icecast2` + +### Фаєрвол + +У прикладі вище, використовується IPv6 маска хостів `::` (у вас це може бути IPv4 типу `0.0.0.0`) для доступу без Веб-проксі. Тому відкриваємо й відповідний порт: + +``` bash +ufw allow 8000/tcp +``` +* приклад відкриття порту `8000` для `IPv4` + `IPv6` (TCP) + +Якщо ви плануєте проксуватись через `80`/`443` порти Nginx (у вас там має бути вже відкритим `80/tcp` / `433/tcp` відповідно), або у вас локальний хост на кшталт `::1` чи `127.0.0.1` (стандартно) тоді відкривати не потрібно. + +### Веб-проксі через Nginx + +Хоча сам не користуюсь такою комбінацією, але додам нотатку з налаштування проксі для віртуального хосту Nginx, на прикладі конфігурації бекенд-сервера `127.0.0.1:8000`: + +``` default +server { + # розкоментуйте, якщо використовуєте IPv4 + # listen IPv4:80 + + listen [IPv6]:80; + + server_name IPv4 IPv6 DOMAIN_1 DOMAIN_2; + + location / { + proxy_pass http://127.0.0.1:8000; + + # розкоментуйте значення якщо делегуєте журналювання серверу icecast + # proxy_set_header X-Real-IP $remote_addr; + # proxy_set_header X-Forwarded-Proto $scheme; + } +} +``` +* `IPv4` і `IPv6` - публічні адреси вашого Веб-сервера (Nginx) +* `DOMAIN_1` і `DOMAIN_2` - домени/субдомени/аліаси через пробіл, якщо використовуються +* не забуваємо відкрити порт `80` (і/або `443` якщо використовується) + +### SSL/TLS + +В принципі, можна використовувати модні "захищені" з'єднання, але для стрімінгу я бачу його використання не доречним з точки зору енергозаощадження і здорового глузду в плані шифрування потоків з публічними даними. Дивіться самі, особисто я по причині вище завжди прослуховую радіо-станції по HTTP, якщо така опція надається сервером. + +## Ezstream + +Коли "станцію" Icecast налаштовано, нам потрібно надіслати їй дані для трансляції. Такі дані можуть бути голосовими / потоковими або зберігатись у бінарних файлах. Оскільки мені потрібен останній варіант, з ним є одна не очевидна на перший погляд проблема: усі вони мають різні формати (`ogg`, `flac`, `mp3`, тощо) а також різні бітрейти та інші характеристики (в яких не торопаю). Оскільки вихідний стрім для слухача завжди має один формат, нам потрібно якимось чином відформатувати нашу колекцію до уніфікованого потоку: для цієї задачі й потрібен такий софт як Ezstream. + +На відміну від популярної комбінації Icecast2 + [Ices2](https://icecast.org/ices/), Ezstream відрізняється тим, що підтримує більше медіа-форматів з коробки та є простішим в налаштуванні (для поточних завдань) + +**Встановлення** + +``` bash +apt install ezstream +``` + +**Налаштування** + +Створімо окремого користувача з домашньою текою, де зберігатимуться налаштування та аудіо-файли: + +``` bash +useradd -m ezstream +``` + +### Створення списку ротації + +Насамперед, варто зауважити, що якщо це ротація великої колекції по колу, то медіа файли бажано звести до єдиного формату. Якщо цього не зробити то Ezstream буде конвертувати файли на льоту, що потребуватиме додаткових ресурсів CPU. На цьому етапі я не робив того, використовуючи колекцію що складається з `mp3` і стрім мій буде в цьому ж форматі. Єдине що, я сумніваюся щодо різного бітрейту та інших аудіо-характеристик, це буде окремою темою про конвертацію наприклад з `ffmpeg`, але майте на увазі цей нюанс. + +Для початку, завантажимо потрібні аудіофайли до умовної теки `/home/ezstream/my-stream/collection`. Якщо планується декілька тематичних стрімів, буде зручно заздалегідь створити для них простір імен у вигляді підтек, наприклад: + +* `mkdir -p /home/ezstream/my-stream-1/collection` +* `mkdir -p /home/ezstream/my-stream-2/collection` +* ... + +Після того, як до колекції додано медіа-файли, згенеруємо для них мета-індекс для ротації в Ezstream. На виході це буде звичайний текстовий список шляхів до файлів по одному на рядок: + +``` bash +find /home/ezstream/my-stream/collection -name *.mp3 > /home/ezstream/my-stream/playlist.txt +``` +* у даному прикладі, всі мої файли у форматі `mp3`, тому пошук відбувається за відповідним суфіксом, інші розширення у цьому прикладі будуть проігноровані + +Оскільки операції вище я проводив від рута (без попереднього логіну з `su ezstream`) варто виправити права Unix, адже наш сервіс systemd пускатиметься саме від користувача `ezstream`: + +``` bash +chown ezstream:ezstream -R ~/ezstream +``` + +### Конфігурація Ezstream + +Коли дані ротації готові, можна перейти безпосередньо до налаштувань Ezstream. + +Щоб не переварювати зайві опції, копіюємо мінімальний конфіг в домашню теку користувача `ezstream`: + +``` bash +cp /usr/share/doc/ezstream/examples/ezstream-minimal.xml /home/ezstream/my-stream/config.xml +``` +* замініть `my-stream` на логічну назву проєкту та занотуйте шлях - він також буде потрібен для налаштування systemd + +Якщо вам потрібні додаткові опції, дивимось доступні пресети з коментарями: + +``` bash +ls /usr/share/doc/ezstream/examples/ +``` + +Мій файл для конфігурації Icecast вище, містить деякі додаткові опції та зараз виглядає так: + +``` /home/ezstream/my-stream/config.xml + + + + + + ::1 + + + PASSWORD + + + + + + NAME + DESCRIPTION + GENRE + + + /my-stream.mp3 + + + mp3 + + + + + + /home/ezstream/my-stream/playlist.txt + + + No + + + No + + + +``` + +Зауважу, що на кожен потік (стрім) потрібен окремий файл конфігурації / systemd процес, не додавайте масив `` в `` - це так не працює і якщо ви додасте декілька, потік просто видаватиме один з потоків. Тобто для кожного стріму - має бути окремий файл конфігурації і окремий конфіг systemd для нього, тому вище ми і створили неймспейс `/home/ezstream/my-stream` + +### systemd + +Останнє, що потрібно зробити - це створити сервіс systemd для налаштованого потоку Ezstream. Запускатиметься він від створеного вище користувача `ezstream` та вище створеної конфігурації `/home/ezstream/my-stream/config.xml`: + +``` bash +nano /etc/systemd/system/ezstream-my-stream.service +``` +* назвати файл можна як завгодно, головне щоб ви його потім впізнали та відрізнили від інших сервісів / потоків Ezstream + +``` /etc/systemd/system/ezstream-my-stream.service +[Unit] +After=network.target + +[Service] +Type=simple + +# актуальні користувач/група +User=ezstream +Group=ezstream + +# я додав затримку, оскільки потік не зможе стартувати, +# якщо процес icecast того зробити не встиг (наприклад, при старті системи) +ExecStartPre=/bin/sleep 5s + +# вказуємо коректний шлях до конфігурації +ExecStart=/usr/bin/ezstream -c /home/ezstream/my-stream/config.xml + +# вказуємо актуальні шляхи до майбутніх журналів +StandardOutput=file:/home/ezstream/my-stream/mainstream-debug.log +StandardError=file:/home/ezstream/my-stream/mainstream-error.log + +# можна розкоментувати, але тоді ви можете не помітити збій окрім як читаючи логи +# Restart=on-failure + +[Install] +WantedBy=multi-user.target +``` + +Зберігаємось (`Ctrl+X` + `Y`) і виконуємо один раз в послідовності: + +1. `systemctl enable ezstream-my-stream` - додаємо автозапуск стріма при старті системи +2. `systemctl start ezstream-my-stream` - стартуємо стрім +3. `systemctl status ezstream-my-stream` - перевіряємо статус + * `systemctl stop ezstream-my-stream` - зупинити стрім (опціонально) + +Ось власне і все, перезавантажувати процес Icecast не потрібно, конкретний потік Ezstream буде змонтовано та відмонтовано автоматично під час запуску / зупинки його процесу. + +## Тестування + +Тепер можна в браузері перейти за адресою вашого сервера Icecast: на прикладі конфігурації вище, це `http://[IP]:8000`. Якщо у вас активні процеси Ezstream, вони мають відображатися в списку на головній сторінці. До кожного потоку, з права є кнопки експорту в форматах `M3U` і `XSPF` - якщо на них клікнути вони або скачаються або відкриються в плеєрі, що підтримує стрімінг. В себе на Fedora, я відкриваю `.m3u` як текстовий файл та виймаю з нього URL, після чого копіюю в розділ "Radio" програвача "Rhythmbox" :) \ No newline at end of file diff --git a/post/vstanovlennia-bittorrent-trekera-aquatic-v-linux.md b/post/vstanovlennia-bittorrent-trekera-aquatic-v-linux.md new file mode 100644 index 0000000..cebc3d2 --- /dev/null +++ b/post/vstanovlennia-bittorrent-trekera-aquatic-v-linux.md @@ -0,0 +1,149 @@ +# Встановлення BitTorrent трекера Aquatic в Linux + +Давно збирався підняти [BitTorrent трекер](https://uk.wikipedia.org/wiki/BitTorrent-трекер) відкритого типу (обмін файлами без реєстрації) для мережі [Yggdrasil](https://devzone.org.ua/post/yggdrasil-mereza-z-detsentralizovanym-routynhom). Для цієї мети існує відомий сервер з 15-річною історією розробки [OpenTracker](https://erdgeist.org/arts/software/opentracker/), реалізований мовою C, але останнім часом, я надаю перевагу більш сучасній мові Rust, тим паче що потім планую реалізувати цією мовою додатковий веб-агрегатор на базі кешованих трекером інфо-хешів у зв'язці з бібліотекою [rqbit](https://github.com/ikatson/rqbit). + +Трохи полиставши результати пошуку на GitHub, віднайшов сервер [Aquatic](https://github.com/greatest-ape/aquatic), який також підтримує IPv4/IPv6, протоколи UDP, HTTP, WS і так само зберігає дані в оперативній пам'яті, не зношуючи своєю роботою носій SSD. Нижче опишу покрокову інструкцію збірки, встановлення та налаштування для мережі Yggdrasil, можливо даний матеріал буде цікавий початківцям, зокрема - для використання в класичній мережі Інтернет. + +## Підготовка системи + +### Створення системного користувача + +Як і для інших програм Linux, що збираються з початкового коду і запускаються через `systemd`, я створюю окремого системного користувача з домашньою текою, для ізоляції прав доступу: + +``` bash +useradd -m aquatic +``` + +### Встановлення системних залежностей + +Програмне середовище Rust у мене розгорнуте в профілі `root`, куди я клоную репозиторії і засобами профілю якого проводжу збірку всіх бінарних файлів. +Якщо в системі не встановлена інфраструктура Rust (`rustc`, `cargo` та інше) тоді вам [сюди](https://rustup.rs/). + +Стандартна інсталяція залежностей Rust вимагає близько 2 Гб дискового простору. Якщо ви, як і я, користуєтесь VPS - встановіть лише необхідні для роботи компілятора пакунки. Для цього при першому запуску команди `curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh`, оберіть пункт `2) Customize installation` і замість стандартного `Profile (which tools and data to install)? (minimal/default/complete) [default]` вкажіть `minimal`. Це пропустить встановлення зайвих на сервері пакунків, що зменшить розмір до ~600 Мб. У випадку використання `minimal`, для збірки Aquatic важливо до-встановити деякі глобальні залежності: + +``` bash +apt install clang libclang-dev +``` +* при інсталяції в `rustup` в режимі `default` цей крок можна пропустити + +Якщо `rustup` вже встановлено, також переконаймось, що використовуються актуальні версії: + +* `rustup update` +* `cargo update` + +### Встановлення Aquatic + +Виконуємо наступні кроки у послідовності: + +1. `su root && cd ~` - логінимось (якщо досі не `root`) і переходимо в домашню теку +2. `git clone https://github.com/greatest-ape/aquatic.git && cd aquatic` - клонуємо вихідний код і переходимо до теки проєкту +3. `cargo build --release -p aquatic_udp` - збираємо оптимізовані бінарні пакети [aquatic_udp](https://github.com/greatest-ape/aquatic/tree/master/crates/udp) + +Таким чином, бінарники будуть доступні у теці `/root/aquatic/target/release/*`. Оскільки ми будемо використовувати сервіс `systemd`, де відсутні змінні середовища, важливо скопіювати (або залінкувати) необхідні пакети в системне розташування, де вони матимуть відповідні права на виконання: + +``` bash +install target/release/aquatic_udp /usr/local/bin/aquatic_udp +``` +* мені потрібен тільки сервер UDP, якщо ви хочете підняти HTTP та WS, виконайте аналогічні кроки для крейтів `aquatic_*` відповідно +* тут ми використовуємо команду `install` замість `cp`, оскільки вона встановлює правильні права доступу замість `chmod`/`chown` +* якщо не плануєте оновлення, на даному етапі можна видалити не потрібні вихідні коди командою `rm -rf /root/aquatic` + +### Файл конфігурації + +Сервер Aquatic можна запускати зі стандартним набором опцій, без аргументів (отримати поточні налаштування для конфігурації сервера можна командою `aquatic_udp -p`) + +Оскільки мій сервер буде працювати в режимі Yggdrasil-only, як скопіюю цей вивід у спільний системний файл конфігурації командою: +``` bash +aquatic_udp -p > /etc/aquatic.toml +``` +* приклад `systemd` нижче буде використовувати цей модифікований файл + +Стандартна конфігурація передбачає запуск на всіх інтерфейсах і порті `3000`, я ж змінюю на окрему адресу підмережі Yggdrasil і більш типовий для відкритих UDP трекерів порт `6969` (детальніше про те, як створити адресу підмережі Yggdrasil - читайте [тут](http://[222:a8e4:50cd:55c:788e:b0a5:4e2f:a92c]/yggdrasil:subnet_setting)): + +* `use_ipv4 = false` - вимикаю IPv4, так як цей інтерфейс в Yggdrasil не обслуговується +* `address_ipv6 = "[xxx:xxxx:xxxx:xxxx::fdb]:6969"` - де `xxx:xxxx:xxxx:xxxx::fdb` - актуальна адреса IPv6, `fdb` - це імпровізований постфікс у діапазоні `A-f0-9` типу "file database" + +Якщо потрібен вивід публічної веб-статистики, вказуємо також: +* `write_html_to_file = true` +* `html_file_path = "/var/www/aquatic/index.html"` - шлях може бути іншим + * у цьому випадку створіть каталог командою `mkdir /var/www/aquatic` + * надайте відповідні права `chown aquatic:aquatic /var/www/aquatic` +* в конфігурації хосту `nginx` додаємо наступне (не забуваємо також відкрити `80` порт): +``` /etc/nginx/default.conf +# /etc/nginx/default +server { + listen [xxx:xxxx:xxxx:xxxx::fdb]:80; + server_name xxx:xxxx:xxxx:xxxx::fdb; + + root /var/www/aquatic; + index index.html; + + location / { + try_files $uri $uri/ =404; + } +} +``` +* де `xxx:xxxx:xxxx:xxxx::fdb` - актуальна адреса IPv6 (або домен для `server_name`) +* не забуваємо застосувати зміни `nginx -t` / `systemctl reload nginx` + +Інші налаштування лишаю стандартними. + +### Налаштування Firewall + +В системах Debian, в режимі UDP / Yggdrasil-only (трекер прийматиме та віддаватиме виключно внутрішньомережеві адреси пірів) я використовую наступне перманентне правило `ufw` для таблиці `iptables`: +``` bash +ufw allow from 0200::/7 to xxx:xxxx:xxxx:xxxx::fdb port 6969 proto udp +``` +* якщо в конфігурації вказано інший порт, використовуйте його замість `6969` + +Якщо у вас звичайний Інтернет трекер, можна просто додати дозвіл на всі вхідні типи підключень: +``` bash +ufw allow 6969 +``` + +Не забуваємо по аналогії відкрити порт на веб-статистику, якщо така використовується у конфігурації вище! + +### Сервіс systemd + +Від `root` створюємо новий файл конфігурації `/etc/systemd/system/aquatic.service`. В ньому, на моєму прикладі, вказано тільки запуск сервера UDP, якщо буде потрібно, додам й інші протоколи в рамках спільного сервісу (послідовністю команд групи `exec` або додатковим скриптом `sh`). Ви можете створити для себе окремі юніти типу `aquatic_ws.service`, але на мою думку це не зручно і краще адмініструвати спільною короткою командою. + +``` /etc/systemd/system/aquatic.service +# /etc/systemd/system/aquatic.service +[Unit] +After=network-online.target +Wants=network-online.target + +[Service] +Type=simple +User=aquatic +Group=aquatic +ExecStart=/usr/local/bin/aquatic_udp -c /etc/aquatic.toml +StandardOutput=file:/home/aquatic/debug.log +StandardError=file:/home/aquatic/error.log + +[Install] +WantedBy=multi-user.target +``` +* `systemctl daemon-reload` - оновлюємо конфігурацію systemd +* `systemctl enable aquatic` - автостарт при запуску системи +* `systemctl start aquatic` - запуск +* `systemctl status aquatic` - перевіряємо статус + +## Тестування + +Після запуску сервісу, перевіряємо наявність активного процесу командою: +``` bash +netstat -tulpn | grep aquatic_udp +``` +* на прикладі моєї конфігурації Yggdrasil, має бути щось типу такого: `udp6 0 0 xxx:xxxx:xxxx:xxxx:6969 :::* 123456/aquatic_udp` + +Також дивимось журнали: +1. `/home/aquatic/debug.log` - робочі звіти +2. `/home/aquatic/error.log` - помилки + +На стороні клієнта, створюємо новий торент (в [qBittorrent](https://www.qbittorrent.org/) це `Tools` -> `Torrent Creator`), вказуємо адресу нашого нового трекеру та перевіряємо оновлення веб-статистики. Так само, можна додати трекер до існуючої роздачі. + +В принципі, це все, якщо комусь цікаво - мій сервер розташований тут: + +* `udp://[302:68d0:f0d5:b88d::fdb]:6969` - анонси +* http://[302:68d0:f0d5:b88d::fdb] - статистика \ No newline at end of file diff --git a/post/vstanovlennia-i-nalashtuvannia-flarum-v2-beta.md b/post/vstanovlennia-i-nalashtuvannia-flarum-v2-beta.md new file mode 100644 index 0000000..91a14d6 --- /dev/null +++ b/post/vstanovlennia-i-nalashtuvannia-flarum-v2-beta.md @@ -0,0 +1,208 @@ +# Встановлення і налаштування Flarum v2 (beta) + +Днями було [засновано](https://devzone.org.ua/topic/ukrayinska-spilnota-administratoriv-alternatyvnykh-merez) українську спільноту адміністраторів альтернативних мереж. У якості платформи для спілкування - обрано рушій [Flarum](https://flarum.org). + +Цей матеріал - адаптована версія інструкції для Debian/Linux, якщо комусь потрібно буде швидко розгорнути дану платформу, адже в [документації](https://docs.flarum.org/2.x/install) не вказані деякі, на мою думку, важливі моменти. + +## Системні залежності + +``` bash +apt install composer\ + php-fpm php-curl php-xml php-common php-sqlite3\ + php-gd php-json php-mbstring php-tokenizer php-zip +``` + +## Рушій + +> В рамках спільноти, також було створено спеціалізований [форк](https://github.com/YGGverse/flarum-framework/tree/yggverse), для роботи з локальними поштовими скриньками без DNS. Нижче описана збірка для звичайної (офіційної) версії. + +1. `cd /var/www` +2. `composer create-project flarum/flarum:^2.0.0 --stability=beta` - встановлюємо через Composer +3. `cd flarum` - переходимо до теки проєкту +4. `composer update` - оновлюємо реєстр залежностей PHP +5. `php flarum install` - встановлюємо базу даних (тут створюється файл `config.php`) + * при виконанні команди `php flarum install` і використання драйвера SQLite, важливо вказати `name.sqlite` (з розширенням) інакше інсталяція не відбудеться + * назву хосту лишаємо як є (`localhost`) + +Так як форум спільноти працює на обидві мережі [Yggdrasil](https://devzone.org.ua/post/yggdrasil-mereza-z-detsentralizovanym-routynhom)/Mycelium, у файлі `config.php` я вказав: +``` config.php +'url' => null, +``` +* є ще [такий рецепт](https://discuss.flarum.org/d/2244-tor) (`'url' => '//' . $_SERVER['HTTP_HOST']`), але у такому разі компіляція статики не можлива, щонайменше на версії 2 буде помилка JS, ну і якщо робити то якось так, щоб не було warning при скиданні кешу: `'url' => isset($_SERVER['HTTP_HOST']) ? '//' . $_SERVER['HTTP_HOST'] : null` +* якщо встановити `null` то можливі проблеми з редіректами, наприклад, при виході з акаунту +* якщо ставите форум для класичного Інтернет - просто вкажіть свій основний хост + +Далі вийшов з цієї теки та надав права веб-серверу, згідно інструкції: + +``` bash +chown -R www-data:www-data /var/www/flarum +``` +* тут треба придумати щось з групами, бо від `root` менеджер `composer` пускати не рекомендовано (наприклад, для оновлень рушія чи встановлення нових пакунків) + +## Nginx + +``` /etc/nginx/default +server { + # для спільноти 443 порт не використовується, + # натомість є два "дзеркала" IPv6 + listen [xxx:xxxx:xxxx:xxxx::x1]:80; + listen [xxx:xxxx:xxxx:xxxx::x2]:80; + + server_name xxx:xxxx:xxxx:xxxx::x1 xxx:xxxx:xxxx:xxxx::x2; + + access_log /var/log/nginx/flarum.access.log; + root /var/www/flarum/public; + index index.php index.html; + + include /var/www/flarum/.nginx.conf; + + location ~ \.php$ { + fastcgi_pass unix:/run/php/php8.2-fpm.sock; + include fastcgi_params; + fastcgi_index index.php; + fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; + } +} +``` +* версію сокета PHP замініть актуальною (`php -v`) +* шляхи `/var/www/flarum` я пишу з пам'яті, бо довго тягав локацію туди-сюди і зараз вона в мене розташована на окремому, примонтованому диску, тому перевірте відповідність у себе актуальну щоб не було конфузів +* тут я ще думаю залочити локацію `/admin` по IP + +## Локалізація + +Пакети локалізації постачаються українською спільнотою [Joomla-UA](https://github.com/flarum-lang/ukrainian), але схоже що тільки для першої версії, бо встановлення ніяк не позначилось на поточному інтерфейсі. Тому потрібно якось зайнятись цим питанням. + +## Бекапи + +Я зберігаю дані в SQLite, тому тут все просто: +``` bash +mkdir -p /path/to/flarum/daily +crontab -e +@daily /usr/bin/rsync -av --delete /path/to/flarum.sqlite /path/to/flarum/daily-dir +``` +* аватарки та інші медіа ще не вияснив де зберігаються, доповню + +## Виявлені проблеми + +### Швидкість + +Рушій здивував своєю тормознутістю. Особливо в режимі дебаг. Можливо, це залежить від SSD та кількості PHP файлів Symfony, які потребують кешування препроцесору. Або ж in-memory драйвера сесій замість БД. Це питання я ще для себе вирішую. Якщо знаєте відповідь - поділіться в коментарях! + +### Зовнішні запити + +Згодом, в консолі були помічені зовнішні запити до кешуючих серверів: +``` +Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at https://cdn.jsdelivr.net/gh/s9e/hljs-loader@1.0.36/loader.min.js. (Reason: CORS request did not succeed). Status code: (null). +``` +Для Веб - це "нормально", але для оверлейних мереж і спільноти, виправлено так: +вимикаємо або вказуємо локальне дзеркало "CDN mirror address" для додатка `flarum-emoji` + +### Тривалість сесій авторизації + +Форум викидає користувача через відносно короткий час (120 хвилин). Рішення проблеми: + +``` php +// File /extend.php + +use Flarum\Extend; +use Flarum\Foundation\AbstractServiceProvider; +use Illuminate\Contracts\Config\Repository as ConfigRepository; + +class MyServiceProvider extends AbstractServiceProvider { + public function register () { + $this->container->make(ConfigRepository::class)->set('session.lifetime', 43829); // In minutes. Default is 120 + } +} + +return [ + // Register extenders here to customize your forum! + (new Extend\ServiceProvider) + ->register(MyServiceProvider::class), +]; +``` +* за [посиланням](https://discuss.flarum.org/d/21562-login-session-timeout/15) є ще один приклад для старішої версії +* UPD: згодом змінив `session.lifetime` на `session.gc_maxlifetime` +* щоб не робити окремий файл-костиль, можна просто вказати ці опції в `/etc/php/{VERSION}/fpm/php.in` та оновити налаштування командою `systemctl restart php{VERSION}-fpm` + +### Пошта + +Класична електронна пошта (e-mail) - ядро системи облікових записів для цього рушія. В принципі, можна вимкнути (точніше перемкнути пересилання листів до журналу) і вручну створювати користувачів, але вони не зможуть навіть скинути свій пароль без пересилання їм коду підтвердження. + +#### Sendmail + +Підійде у разі, якщо ви плануєте роботу з поштою але у вас з якихось причин немає повноцінного SMTP провайдера, наприклад Google. + +##### Postfix / SMTP + +Нижче показано самий простий приклад, який передбачає опцію налаштування "sendmail" що вказується в адмінці. У цьому випадку, 25 порт є закритим на фаєрволі. + +``` bash +apt install postfix +``` +Мій файл конфігурації `/etc/postfix/main.cf` саме для цього ресурсу виглядає так: + +``` /etc/postfix/main.cf +myhostname = localhost + +smtpd_banner = $myhostname ESMTP $mail_name (Debian/GNU) +biff = no +append_dot_mydomain = no +readme_directory = no +smtpd_use_tls=no +compatibility_level = 3.6 + +smtpd_relay_restrictions = permit_mynetworks permit_sasl_authenticated defer_unauth_destination + +alias_maps = hash:/etc/aliases +alias_database = hash:/etc/aliases + +mydestination = +relayhost = +mynetworks = 127.0.0.0/8 [::ffff:127.0.0.0]/104 [::1]/128 +mailbox_size_limit = 0 +recipient_delimiter = + +inet_interfaces = all +inet_protocols = ipv6 + +smtpd_etrn_restrictions=reject +disable_vrfy_command = yes +smtpd_helo_required = yes +``` +* тут `myhostname` - ваш актуальний хост +* `inet_protocols` можна вказати як "all" якщо планується взаємодія з Інтернет + +##### Тестування + +Якщо бачите повідомлення: +> Flarum currently does not send emails. This can be due to the selected driver, or errors in its configuration. + +то це не критично, в мене відправлення працює й з ним, головне, щоб не було помилок в поп-апах. + +#### Варіант без пошти + +В адмінці є варіант "log" тобто пошта буде надсилатись у файл-заглушку, з якого можна пересилати різні лінки верифікації та інші запити - руками або скриптом. Знаходиться цей журнал в `flarum/storage/logs/*.log` + +Вимкнути самі поля форм без патчу не вийде, в адмінці доступна лише опція вимкнення реєстрацій, таким чином, користувач бачитиме помилку при надсиланні відповідного запиту (так як це зараз). + +## Розробка + +Так як форум спільноти має деякі не характерні фічі, що не мають сенсу для загального PR, з'явився певний досвід по розгортанню власної версії а також може стати в нагоді, якщо вам потрібно використовувати цей рушій для локальної розробки. Як і у випадку з основною документацією, тут є свої нюанси, про які в [офіційній версії](https://docs.flarum.org/contributing/#setting-up-a-local-codebase) не сказано. + +Якщо коротко то вам потрібен "скелет" форуму [flarum](https://github.com/flarum/flarum) та [framework](https://github.com/flarum/framework), якийсь в документації також називається `monorepo` і включає в себе різні модулі (для яких також є окремі read-only репозиторії). При чому `framework` у даному прикладі - є вашим об'єктом модифікації, ви також можете забрати його копію з офіційного апстріму. + +Збірка `dev` версії рушія виглядає наступним чином: + +1. `cd /var/www` +2. `git clone https://github.com/flarum/framework.git` +3. `cd framework` +4. `git checkout yggverse` +5. `cd ..` +6. `git clone https://github.com/flarum/flarum.git` +7. `cd flarum` +8. `composer config repositories.0 path "/var/www/framework/*/*"` +9. `composer config minimum-stability dev` +10. `composer install` або `php flarum migrate` +11. `php flarum cache:clear` +12. `php flarum assets:publish` - згенерувати статику шрифтів ([1](https://discuss.flarum.org/d/37999-missing-font-assets-after-the-monorepo-compile), [2](https://discuss.flarum.org/d/27673-missing-fonts)) + +При оновленні компонентів фронт-енд, важливо (після `yarn install`) зкомпілювати оптимізовані файли дистрибутиву командою `yarn build` для подальшого встановлення в рамках компонента `flarum`. Але для цього вам треба додати такий [фікс](https://github.com/YGGverse/flarum-framework/commit/b02e3b3600d5c0ff1a613590e72d91e7b9d88111). \ No newline at end of file diff --git a/post/vstanovlennia-ostannyoyi-versiyi-go-v-debian.md b/post/vstanovlennia-ostannyoyi-versiyi-go-v-debian.md new file mode 100644 index 0000000..5799ee0 --- /dev/null +++ b/post/vstanovlennia-ostannyoyi-versiyi-go-v-debian.md @@ -0,0 +1,60 @@ +# Встановлення останньої версії Go в Debian + +Це коротенька інструкція для початківців, яка дозволить швидко розгорнути останню версію інфраструктури Go в Debian для збірки програм. + +## Видалення попередньої версії + +Спочатку перевірте, чи не встановлена певна версія Go за допомогою пакетного менеджеру: +``` +go version +``` + +Якщо так, попередньо переконайтесь, що її не використовують інші програми, після чого її можна видалити із системи: +``` +apt remove golang +``` + +Додатково можна перевірити наявність старих файлів та зачистити їх: +``` +rm -rf /usr/lib/go-ВЕРСІЯ +``` + +## Встановлення останньої версії + +Наступні кроки виконуються в домашній теці користувача, від якого планується запуск програми: +``` +cd ~ +``` + +Переходимо на [сторінку завантажень Go](https://go.dev/dl/) і обираємо версію для потрібної архітектури: +``` +wget https://go.dev/dl/go1.22.1.linux-amd64.tar.gz +``` + +Розпаковуємо архів, після чого його можна видалити: +``` +tar -xzf go1.22.1.linux-amd64.tar.gz +rm go1.22.1.linux-amd64.tar.gz +``` + +Переміщуємо файли до системної теки від `root`: +``` +sudo mv go /usr/local/ +``` + +Створимо робочу теку Go для поточного користувача: +``` +mkdir ~/go +``` + +Налаштуємо шляхи та перезавантажимо профіль: +``` +export PATH=$PATH:/usr/local/go/bin +export GOPATH=~/go +source .bashrc +``` + +Перевіряємо результат: +``` +go version +``` \ No newline at end of file diff --git a/post/vstanovlennia-ostannyoyi-versiyi-rust-v-linux.md b/post/vstanovlennia-ostannyoyi-versiyi-rust-v-linux.md new file mode 100644 index 0000000..c275008 --- /dev/null +++ b/post/vstanovlennia-ostannyoyi-versiyi-rust-v-linux.md @@ -0,0 +1,45 @@ +# Встановлення останньої версії Rust в Linux + +Користувачі дистрибутивів з довгостроковою підтримкою, ймовірно зіткнуться з проблемою збірки програм з версією Rust, що міститься в репозиторіях. + +Вирішується це досить просто, за допомогою утиліти [Rustup](https://www.rust-lang.org/tools/install). + +Спочатку потрібно видалити встановлені раніше версії, разом з їх залежностями. + +Наприклад, в Debian: +``` +apt remove cargo rustc +apt autoremove +``` + +Наступні кроки виконуються від того користувача, від якого планується подальша робота з програмою. +Таким чином, пакети будуть стандартно встановлюватись до теки `~/.cargo` + +Після введення наступної команди слідуємо підказкам: +``` +curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh +``` + +Оновити пакети до останньої версії можна командою: +``` +rustup update +``` + +Якщо потрібно зібрати будь яку програму на більш ранній версії, наприклад `1.63` - достатньо встановити її поряд з іншою: +``` +rustup install 1.63 +``` + +Для збірки програми, вказуємо відповідну версію компілятора: +``` +cargo +1.63 build --release +``` + +Щоб видалити встановлені таким чином `cargo`, `rustc` включно з `rustup`: +``` +rustup self uninstall +``` + +## Нотатки +* якщо плануєте збірку програм на примонтованому девайсі (наприклад USB) - додайте параметр `exec` в `options` що в `/etc/fstab` +* інфраструктура та збірка тягне чимало дискового простору (від 1-2 Гб і більше), ви можете зменшити обсяг вимог до диску, встановивши мінімальні залежності з атрибутом `--profile=minimal`, що пропустить встановлення документації, `clippy` та інших інструментів; а звільнити простір - використанням додаткового крейту [cargo-cache](https://crates.io/crates/cargo-cache) і команди `cargo cache -a` \ No newline at end of file diff --git a/post/vstanovlennia-routera-i2p-z-pidkliuchenniam-cherez-yggdrasil.md b/post/vstanovlennia-routera-i2p-z-pidkliuchenniam-cherez-yggdrasil.md new file mode 100644 index 0000000..7705154 --- /dev/null +++ b/post/vstanovlennia-routera-i2p-z-pidkliuchenniam-cherez-yggdrasil.md @@ -0,0 +1,216 @@ +# Встановлення роутера i2pd з підключенням до мережі I2P через Yggdrasil + +I2P - це альтернативна мережа, характерною рисою якої є анонімізація користувача. На відміну від Tor, має принципово іншу модель маршрутизації, де стек IP не використовується як такий, а дані відправника вільно циркулюють роєм через випадкові вузли ретрансляції, допоки не досягнуть точки призначення. Таким чином, відправник даних є не визначеним мережею. + +Утім, для підключення до I2P глобально, часто використовується Інтернет. Особливістю цього гайду є спосіб підключення до мережі I2P без прямих звернень Інтернет, через додатковий шар [Yggdrasil](https://devzone.org.ua/post/yggdrasil-mereza-z-detsentralizovanym-routynhom). Може бути корисним у випадках, якщо ваш провайдер блокує піринговий трафік. + +* _В інших випадках, такий спосіб може бути менш "анонімним" через можливість генерації необмеженої кількості адрес Yggdrasil з підміною ними "незалежних" вузлів ([Атака Сивілли](https://uk.wikipedia.org/wiki/Атака_Сивілли)) - таким чином, для себе важливо правильно обирати пріоритети налаштувань і розуміти мету, з якою використовується I2P. Адже якщо метою є саме анонімізація а не наприклад ретрансляція певного сервісу для користувачів з обмеженим доступом - то для таких цілей, на мою думку, краще використовувати звичайний Інтернет піринг або приватний ботнет._ + +Метою цього матеріалу не є детальний опис принципу роботи мережі I2P і анонімізація зокрема. Матеріал створено лише у якості нотатки, з досвіду публікації Веб-сайту для [української спільноти адміністраторів альтернативних мереж](https://devzone.org.ua/topic/ukrayinska-spilnota-administratoriv-alternatyvnykh-merez) на прохання деяких користувачів, для чого й був потрібен локальний роутер. Якщо вам не потрібно публікувати сайт чи анонімно користуватися мережею I2P, можна просто скористатися наявними вихідними SOCKS/HTTP проксі, в т.ч. для Yggdrasil. + +## Роутер + +Існує декілька відомих мені реалізацій роутера I2P: + +* [i2p.i2p](https://github.com/i2p/i2p.i2p) (Java) - оригінальна реалізація +* [i2p-rs](https://github.com/i2p/i2p-rs) (Rust) - спроба реалізації роутера сучасною мовою, утім стан готовності цього проєкту мені не відомий +* [i2pd](https://github.com/PurpleI2P/i2pd) (C++) - на мою думку, найбільш повноцінна реалізація, що підтримує з коробки режим роботи Yggdrasil-only + +### i2pd + +Для i2pd є готові збірки та репозиторії для різних дистрибутивів, але сам я завжди надаю перевагу збірці останніх версій програм з початкового коду. Нижче описаний саме такий спосіб, можливо з часом він може втратити свою актуальність через оновлення кодової реалізації. Детальна інструкція доступна в [документації](https://i2pd.readthedocs.io/en/latest/devs/building/unix/), а нижче - приклад встановлення для Debian / Linux. + +#### Встановлення + +``` bash +git clone https://github.com/PurpleI2P/i2pd.git +cd i2pd/build +cmake . +make +``` + +Власне, для Debian, можна зібрати пакунок `.deb` (що може бути більш правильним з точки зору адміністрування) але я встановлював вручну, адже буде зкомпільовано лише один бінарний файл i2pd, який потрібно перемістити до системної теки: + +``` bash +sudo make install +``` + +#### Створення системного користувача + +Роутер `i2pd` я запускатиму від окремого системного користувача зі стандартними привілеями: + +``` +useradd -m i2pd +``` + +#### Конфігурація i2pd + +Тепер потрібно створити файл конфігурації, його я розмістив за адресою `/etc/i2pd.conf`, хоча ви можете додати його до теки користувача, при цьому змінивши шляхи у прикладах нижче: + +``` bash +nano /etc/i2pd.conf +``` + +Оригінальний файл конфігурації [доступний в репозиторії](https://github.com/PurpleI2P/i2pd/blob/openssl/contrib/i2pd.conf), нижче я наведу тільки змінені його опції, але буде правильним звірити їх актуальність на момент встановлення: + +``` /etc/i2pd.conf +# рівень деталізації журналів, я змінив щоб не засмічувати пам'ять +loglevel = error + +# вимикаємо стандартні інтерфейси, +# так як в цьому прикладі підключення відбувається через Yggdrasil +ipv4 = false +ipv6 = false + +# вимикаємо не задіяну в конфігурації Yggdrasil функціональність +[ntcp2] +enabled = false +[ssu2] +enabled = false + +# доступ до веб-адмінки роутера +# цей пункт можна лишити стандартним, +# але в мене в address вказано статичну адресу (віддаленого сервера) +# IP не повинен бути 0.0.0.0, бо доступ буде відхилено через фільтр +[http] +address = 127.0.0.1 +auth = true +user = user +pass = pass + +# дані опції потрібні, якщо ви користуєтесь I2P, наприклад в браузері +# в мене роутер працює на сервері (для веб-сайту) тому ці сокети вимкнено +[socksproxy] +enabled = false +[httpproxy] +enabled = false + +# в мережі I2P використовується окремий протокол комунікації, +# якщо ви не плануєте підключатись до роутера через клієнтський софт, вимикаємо +[sam] +enabled = false + +# вмикаємо конфігурацію роутера для роботи в режимі Yggdrasil +[meshnets] +yggdrasil = true + +# роутер в режимі yggdrasil слухатиме всі мережі IPv6 (::) +# оголосити сервіс можна й на конкретній адресі нижче +# перевірити результат можна з `netstat -tulpn | grep i2pd` +# yggaddress = xxx::xxx... +``` + +Так як я не змінював стандартні шляхи, через помилку запуску згодом, додатково руками створив теку для збереження даних профілю (у вас це може бути тека користувача або інша) + +``` bash +mkdir /var/lib/i2pd +chown i2pd:i2pd /var/lib/i2pd +``` + +Також, вручну скопіював [сертифікати](https://github.com/PurpleI2P/i2pd/tree/openssl/contrib/certificates) ініціальних вузлів до створеної теки `/var/lib/i2pd`, інакше в конфігі доведеться вимкнути опцію `reseed.verify` + +#### Systemd + +Тепер створімо сервіс systemd. В мене він відрізняється від [стандартного](https://github.com/PurpleI2P/i2pd/blob/openssl/contrib/i2pd.service): + +``` /etc/systemd/system/i2pd.service +#/etc/systemd/system/i2pd.service + +[Unit] +Wants=network.target + +# сервіс повинен запускатись після сервісу Yggdrasil +# у вас тут може бути інша конфігурація, залежна від конкретної мережі +# та способу її ініціації +After=yggdrasil.service + +[Service] + +# запуск від окремого користувача +User=i2pd +Group=i2pd + +# роутер використовує дочірні процеси +Type=forking + +ExecStart=/usr/local/bin/i2pd --conf=/etc/i2pd.conf --daemon --service + +# лишив як в оригіналі, можливо це потрібно для реалізації i2pd +LimitNOFILE=8192 + +# фактично, шлях журналів буде таким, як вказано в /etc/i2pd.conf +# StandardOutput=file:///home/i2pd/debug.log +# StandardError=file:///home/i2pd/error.log + +[Install] +WantedBy=multi-user.target +``` +* `systemctl enable i2pd` - автостарт +* `systemctl start i2pd` - запуск (на цьому етапі, роутер Yggdrasil має бути активним) +* `systemctl status i2pd` - статус + +#### Ініціальний запуск роутера + +При першому запуску, ініціалізація мережі в мене відбувалась не одразу: системний сервіс вилітав з помилками підключення до вузлів. Наскільки я розумію, це пов'язано з відсутністю або малою кількістю ресідів Yggdrasil в мережі I2P. Якщо у вас вже є аналогічний вузол, що працює на такому (локальному) конфігі, можна спробувати імпортувати профіль мережі з нього або підняти звідти ресід: це робиться утилітою [pyseeder](https://github.com/PurpleI2P/pyseeder). Якщо ви знаєте адресу Yggdrasil вузла онлайн, то можете просто спробувати додати його в конфігурацію `reseed.yggurls` і перезапустити i2pd. + +Якщо конекту досі немає, і ваш роутер по якимось причинам вже пускався в режимі Інтернет, варто (після зупинки сервіса i2pd) спробувати видалити теку: + +``` bash +rm -rf /var/lib/i2pd/netDb +``` + +або якщо тека нова і там немає налаштувань тунелів, то очистити її повністю: + +``` bash +rm -rf /var/lib/i2pd/* +``` + +Після змін вище, не забуваємо перезапустити роутер: + +``` bash +systemctl restart i2pd +``` + +Через деякий час, ініціалізація мережі таки відбулась, але що було причиною точно не знаю. + +Перевірити статус роботи роутера можна перейшовши у браузері за адресою його адмінки. Цю адресу ми вказували в опції `http.address` + +#### Публікація Веб-сайту в I2P + +Власне, сайт (та будь який інший сервіс) публікується доволі просто: для цього використовуються так звані "[тунелі](https://i2pd.readthedocs.io/en/latest/user-guide/tunnels/)" які проксуватимуть внутрішній трафік I2P на вказаний локальний сервіс, наприклад сервер Nginx. + +Створімо новий файл `/var/lib/i2pd/tunnels.conf`, або відредагуємо існуючий, якщо його немає: + +``` /var/lib/i2pd/tunnels.conf +[nginx] +# протокол +type = http +# хост, який прослуховує веб-сервер або Nginx +host = 127.0.0.1 +# порт, у моєму випадку SSL/443 не використовується +port = 80 +# внутрішній порт вхідного трафіку з I2P +inport = 80 +# ідентифікатор тунелю, цей файл буде створено автоматично +keys = nginx.dat +``` + +Адреси `.i2p` не є частиною ваших приватних ключів, як це реалізовано в Yggdrasil, натомість адреса I2P є ідентифікатором маршруту. На прикладі нашої конфігурації вище, вам потрібно зберегти резервну копію `nginx.dat` що відповідає за публічну адресу Веб-сайту. + +Подивитись яка у вас адреса - можна у Веб-адмінці роутера, у вкладці _I2P tunnels_ > _Server Tunnels_. Опціонально, можна зареєструвати короткий домен, але така система реєстрації не є централізованою і має ряд вимог (зокрема щодо термінів обслуговування), від того це тема для окремого матеріалу. + +##### Nginx + +Налаштування Nginx я не змінював, бо сайт спільноти слухає різні інтерфейси, в той час як опція `host` тунелю `i2pd` пересилає з'єднання на локальний IPv6 Yggdrasil. Утім, якщо планується публікація сайту виключно в мережі I2P з `server_name = xxx.i2p` тоді потрібно додатково змінити файл `/etc/nginx.conf`: + +``` /etc/nginx.conf +server_names_hash_bucket_size 128; +``` + +## Додаткові заходи + +Як сказано на початку матеріалу, його метою не є анонімізація. Роботу роутера я не перевіряв на предмет витоків з'єднань в Інтернет, публікація анонімного сайту (a.k.a. eepsite) - окрема тема. Якщо для вас це чутливе питання, зверніть увагу на попередні публікації, що можуть частково допомогти з ізоляцією потенційних витоків: + +* [Ізоляція Linux від прямих Інтернет з'єднань на базі QEMU / Virtual Machine Manager з VSOCK](https://devzone.org.ua/post/izoliatsiia-linux-vid-priamykh-internet-zyednan-na-bazi-qemu-virtual-machine-manager-i-vsock) +* [Обмеження вихідних з'єднань на Інтернет з ufw](https://devzone.org.ua/post/obmezennia-vykhidnykh-zyednan-na-internet-z-ufw) +* [Безпечний перегляд сайтів Yggdrasil з Yggstack](https://devzone.org.ua/post/bezpechnyy-perehliad-saytiv-yggdrasil-z-yggstack) \ No newline at end of file diff --git a/post/vstanovlennia-routera-merezi-mycelium-v-linux.md b/post/vstanovlennia-routera-merezi-mycelium-v-linux.md new file mode 100644 index 0000000..4db9e03 --- /dev/null +++ b/post/vstanovlennia-routera-merezi-mycelium-v-linux.md @@ -0,0 +1,97 @@ +# Встановлення роутера мережі Mycelium в Linux + +Коротка інструкція по встановленню оверлейної мережі [Mycelium](https://github.com/threefoldtech/mycelium) в Linux, оскільки навіть з моїми скілами, вона видалась дещо заплутаною, через безлад в репозиторії та відсутність доків. + +По суті, Mycelium нічим особливим від [Yggdrasil](https://devzone.org.ua/post/yggdrasil-mereza-z-detsentralizovanym-routynhom) не відрізняється, окрім іншої моделі маршрутизації та реалізації роутера мовою Rust. Також, відомою мені характерною рисою цієї мережі - є наявність `traceroute` + +Варто зауважити, що проєкт в стадії розробки, користуйтесь на власний ризик! + +## Встановлення + +Якщо в системі ще не встановлено інфраструктуру Rust/Cargo, то вам [сюди](https://rustup.rs/) або + +``` bash +curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh +``` + +Далі клонуємо та збираємо пакунок з репозиторію: + +``` bash +https://github.com/threefoldtech/mycelium.git +cd mycelium/myceliumd +cargo build --release +sudo install target/release/mycelium /usr/local/bin/mycelium +``` +* можливо потрібно буде довстановити `apt install build-essential pkg-config libssl-dev` + +### systemd + +``` /etc/systemd/system/mycelium.service +#/etc/systemd/system/mycelium.service + +[Unit] +Wants=network.target +After=network.target + +[Service] +ProtectHome=true +ProtectSystem=true +SyslogIdentifier=mycelium +CapabilityBoundingSet=CAP_NET_ADMIN + +ExecStartPre=+-/sbin/modprobe tun +ExecStart=/usr/local/bin/mycelium -k /etc/mycelium.bin --peers tcp://xxx.xx.xxx.x:xxxx + +# Адреси підмереж +#ExecStartPost=/bin/sleep 5s +#ExecStartPost=/bin/ip address add xxx:xxxx:xxxx:xxxx::x1/64 dev mycelium +#ExecStartPost=/bin/ip address add xxx:xxxx:xxxx:xxxx::x2/64 dev mycelium + +#Restart=always +#RestartSec=5 + +TimeoutStopSec=5 + +# mkdir /var/log/mycelium +StandardOutput=file:/var/log/mycelium/debug.log +StandardError=file:/var/log/mycelium/error.log + +[Install] +WantedBy=multi-user.target +``` +* `/etc/mycelium.bin` - це приватний ключ, він створюється автоматично при першому запуску +* в `--peers` вказуємо піри для підключення в оверлейному режимі, знайти їх можна [тут](https://github.com/threefoldtech/mycelium?tab=readme-ov-file#hosted-public-nodes-v06x) і [тут](https://github.com/YGGverse/mycelium-catalog?tab=readme-ov-file#public-peers) +* якщо журнали не потрібні - додайте до `exec` опцію ` --silent` + +## Налаштування + +Фаєрволи обслуговуються так само, як і у випадку Yggdrasil, тільки діапазон тут - `0400::/7`; + +Для [Alfis DNS](https://devzone.org.ua/post/alfis-dns-reyestratsiia-domenu-v-blokcheyn) існує окремий [патч](https://github.com/YGGverse/Alfis/tree/mycelium-network-mode) ([PR#386](https://github.com/Revertron/Alfis/pull/386)), який теоретично вміє сікти все окрім Yggdrasil або Mycelium, але в мене там є деякі питання як і до іншого пірингового софту, які поки що не перевіряв. + +### PAC + +Мій файл конфігурації для окремого браузеру виглядає якось так: + +``` config.pac +function FindProxyForURL(url, host) +{ + if (/^0{0,1}[2-3][a-f0-9]{0,2}:/.test(host) || /\.ygg$/.test(host)) + { + return 'DIRECT'; + } + if (/^0{0,1}[4-5][a-f0-9]{0,2}:/.test(host)) + { + return 'DIRECT'; + } + return 'PROXY 127.0.0.1:123'; +} +``` +* тобто обслуговуються конекти Yggdrasil та Mycelium, а все інше йде на фіктивний проксі +* детальніше про конфіг PAC я писав у матеріалі [Безпечний перегляд сайтів Yggdrasil з Yggstack](https://devzone.org.ua/post/bezpechnyy-perehliad-saytiv-yggdrasil-z-yggstack) + +## Тестування + +* Доступний [репозиторій](https://github.com/YGGverse/mycelium-catalog) зі списком сервісів для цієї мережі +* [Українська спільнота адміністраторів альтернативних мереж](https://devzone.org.ua/topic/ukrayinska-spilnota-administratoriv-alternatyvnykh-merez) +* BitTorrent трекер `udp://[505:6847:c778:61a1::fdb]:6969` і [Веб-статистика](http://[505:6847:c778:61a1::fdb]) \ No newline at end of file diff --git a/post/vtracker-bittorrent-ahrehator-na-bazi-rust.md b/post/vtracker-bittorrent-ahrehator-na-bazi-rust.md new file mode 100644 index 0000000..95d6f5a --- /dev/null +++ b/post/vtracker-bittorrent-ahrehator-na-bazi-rust.md @@ -0,0 +1,43 @@ +# βtracker - BitTorrent агрегатор на базі Rust + +> Даний матеріал писався як гайд для локальної [спільноти адміністраторів альтернативних мереж](https://devzone.org.ua/topic/ukrayinska-spilnota-administratoriv-alternatyvnykh-merez), утім, я знайшов його потенційно цікавим для широкого загалу: зокрема для тих, хто бажає підняти свій торент трекер і автоматизований Веб-каталог з функцією пошуку та статистикою роздач на базі інфраструктури Rust. + +[βtracker](https://github.com/yggverse/btracker), _beta-tracker_ - BitTorrent каталог агрегаційного типу з Веб-інтерфейсом на базі фреймворку [Rocket](https://rocket.rs), що є продовженням попереднього проєкту [YGGtracker](https://github.com/YGGverse/YGGtracker) але на відміну від останнього - повністю автоматизований. + +Індекс тут складається на базі [форку](https://github.com/YGGverse/aquatic/tree/yggverse) UDP трекера [aquatic](https://github.com/greatest-ape/aquatic/tree/master/crates/udp), що генерує дамп інфо-хешів у бінарному форматі API та спеціально написаного для нього кравлера - [aquatic-crawler](https://github.com/YGGverse/aquatic-crawler). + +## Принцип роботи + +Схематично, робота усієї системи зображена в README: +``` +torrent client > aquatic_udp > infohash.bin < aquatic-crawler > * /preload/info-hash.torrent > β +torrent client <-----------------------| * /preload/info-hash/data | + <-------------------------------------| * /preload/.info-hash/tmp | + <-------------------------- scrape -------------------------------| +``` +На схемі βtracker є останнім оператором даних справа. + +Таким чином, торент-клієнти передають дані на UDP трекер, звідки (його форк) генерує бінарний API дамп `infohash.bin` з опціональною періодичністю, який в свою чергу читає кравлер і резольвить вказані дані (мета дані торент файлів, опціонально підвантажує зображення, txt файли, логи та інші) у спільну файлову систему, звідки їх вже читає та парсить агрегатор βtracker, тим само формуючи пошуковий індекс і Web UI. + +Наразі вся система є раннім прототипом, і при вдалому її тестуванні, планується створити спільну бібліотеку файлової БД, зі стандартизованим API для всіх компонентів системи, що працюють з файлами асинхронно. + +## Мережі + +βtracker розроблявся для мережі [Yggdrasil](https://devzone.org.ua/post/yggdrasil-mereza-z-detsentralizovanym-routynhom), але на відміну від YGGtracker, може без проблемно працювати з Інтернет / IPv4 включно, а також споріднених оверлейних мережах типу [Mycelium](https://github.com/threefoldtech/mycelium). + +## Проблеми + +В рамках того, що коментарі та інша мета-інформація не є частиною стандартного [словника BitTorrent](https://wiki.theory.org/BitTorrentSpecification#Info_Dictionary), ці дані втрачаються при резольві байтів торент файла з інфо-хешу. Оскільки сам інфо-хеш формує унікальність роздачі, отримати такі дані з першого зустрічного піра не варіант, навіть якщо це технічно можливо при наприклад запиті такої інформації через сокет за рамками протоколу BitTorrent. Утім, й наявних даних цілком вистачає для побудови навігації та пошуку торентів (включно з індексованим вмістом) у невеличких мережах. + +## Додаткова функціональність + +Так як URI сторінок торентів є унікальними для усієї мережі BitTorrent інфо-хешами, технічно, можливо реалізувати для них локальні соціальні функції: коментарі, лічильник завантажень, вподобання тощо. Але наразі така функціональність не запланована, щонайменше до першої стабільної версії усіх компонентів системи. + +## Інстанси + +Поки для тестування усіх компонентів системи, функціонує один інстанс Yggdrasil: +* [http://[302:68d0:f0d5:b88d::fdb]](http://[302:68d0:f0d5:b88d::fdb]) | http://tracker.ygg ([Alfis DNS](https://devzone.org.ua/post/alfis-dns-reyestratsiia-domenu-v-blokcheyn)) + +UPD. днями з'явилась [реалізація](https://github.com/YGGverse/btracker-gemini) серверної частини для протоколу [Gemini](https://devzone.org.ua/post/protokol-gemini-iak-alternatyva-http) та відповідний інстанс: + +* [gemini://[302:68d0:f0d5:b88d::fdb]](http://[302:68d0:f0d5:b88d::fdb]) | [gemini://tracker.ygg](gemini://tracker.ygg) \ No newline at end of file diff --git a/post/vypravlennia-pomylky-zapusku-fail2ban-v-debian-12.md b/post/vypravlennia-pomylky-zapusku-fail2ban-v-debian-12.md new file mode 100644 index 0000000..b5a6d10 --- /dev/null +++ b/post/vypravlennia-pomylky-zapusku-fail2ban-v-debian-12.md @@ -0,0 +1,21 @@ +# Виправлення помилки запуску fail2ban в Debian 12 + +[fail2ban](https://github.com/fail2ban/fail2ban) - це утиліта на Python для захисту серверів від атак підбору паролів. Вона сканує системні журнали на предмет шкідливої активності, та блокує підозрілі хости на заданий в налаштуваннях час. + +Програма має базові налаштування і працює одразу після встановлення, стандартно блокуючи на десять хвилин доступ до `ssh` після п'ятої невдалої спроби входу. Подивитись стандартні налаштування можна у файлі `/etc/fail2ban/jail.conf` + +Утім, в системах Debian 12 існує [відома проблема](https://github.com/fail2ban/fail2ban/issues/3292) її запуску зі штатних репозиторіїв, що звершується з помилкою сервісу `systemd`. Виправити це досить просто виконавши наступні кроки + +1. Переконайтесь, що `python3-systemd` встановлено: +``` +apt install python3-systemd +``` +2. Створіть файл `/etc/fail2ban/jail.local` та додайте наступні налаштування: +``` +[DEFAULT] +backend = systemd +``` +3. Перезавантажте сервіс: +``` +systemctl restart fail2ban +``` \ No newline at end of file diff --git a/post/yggdrasil-mereza-z-detsentralizovanym-routynhom.md b/post/yggdrasil-mereza-z-detsentralizovanym-routynhom.md new file mode 100644 index 0000000..7f9ccb8 --- /dev/null +++ b/post/yggdrasil-mereza-z-detsentralizovanym-routynhom.md @@ -0,0 +1,81 @@ +# Yggdrasil - мережа з децентралізованим роутингом + +[Yggdrasil](https://yggdrasil-network.github.io) - експериментальний протокол для побудови само-організованої локальної мережі з шифруванням трафіку та підтримкою оверлейного підключення через Інтернет. + +В своїй основі використовує приватний ключ для генерації постійних псевдо-адрес IPv6 в діапазоні `0200::/7` + +Має різні сфери застосування, зокрема: автоматизована побудова локальних мереж, маскування IP (як транспорт I2P), обхід блокувань та обмежень віртуальної адресації NAT (наприклад, для організації відео-спостереження без наявності виділеної адреси, розгортання домашнього веб-серверу, віддаленого керування інфраструктурою підприємства тощо). Завдяки простоті встановлення та здатності до саморозгортання підключень, здобув широку популярність зокрема серед користувачів CJDNS. + +## Встановлення + +Yggdrasil написаний на Go, початковий код доступний в репозиторії на [GitHub](https://github.com/yggdrasil-network/yggdrasil-go). + +Бінарні збірки можуть бути встановлені зі штатного репозиторію командою: +``` +apt install yggdrasil +``` + +або використовуючи останню збірку з релізу: +``` +wget https://github.com/yggdrasil-network/yggdrasil-go/releases/download/v0.5.5/yggdrasil-0.5.5-amd64.deb +dpkg -i yggdrasil-0.5.5-amd64.deb +``` + +## Налаштування + +У разі підключення до мережі Yggdrasil через Інтернет, необхідно додати до файлу конфігурації щонайменше один географічно близький [публічний вузол](https://publicpeers.neilalexander.dev/): + +``` +# /etc/yggdrasil/yggdrasil.conf + Peers: [ + tls://xx.xx.xx.xx:xxxx + ] +``` + +Також, зручно вказати назву для інтерфейсу Yggdrasil - щоб потім не плутати його наприклад з VPN: +``` +IfName: ygg0 +``` + +Після цього можна перезапустити сервіс, щоб застосувати зміни: +``` +systemctl restart yggdrasil +``` + +Якщо все зроблено правильно, можна спробувати відкрити будь яку з адрес в [каталозі сервісів](https://yggdrasil-network.github.io/services.html). + +## Додатково + +Перевірити наявність нового інтерфейсу можна командою: +``` +ifconfig +``` + +Отримати власну адресу: +``` +yggdrasilctl getself +``` + +Можна легко створювати маски підмереж для окремих сервісів - стріми, вебсайти, пошта, та інше: +``` +ifconfig lo inet6 add 3xx:xxxx:xxxx:xxxx::xx +``` +* де `3xx:xxxx:xxxx:xxxx` - адреса з `yggdrasilctl getself` + +Якщо підключення відбувається через власний публічний вузол, на ньому відкривається довільний порт. +У разі необхідності обмеження доступу для невідомих динамічних адрес, починаючи з версії 0.5.0 можна вказувати пароль: + +* `tls://[::]:12345?password=123456abcdef` - сервер +* `tls://a.b.c.d:12345?password=123456abcdef` - клієнт + +Початківцям важливо встановити мережний екран, наприклад `ufw` для `iptables`, інакше такі служби як принтери або локальні веб-сервери для розробки (що стандартно прослуховують `0.0.0.0`) можуть стати доступними для інших учасників локальної мережі. З цієї точки зору, буде зручним організація окремого роутеру наприклад на базі OpenWRT. + +Важливо розуміти, що Yggdrasil не має на меті захист від ідентифікації користувача в мережі, порівнюючи з анонімайзерами Tor чи I2P. Іноді, може використовуватись як додатковий канал, але походження його пакетів може бути встановлено, якщо не використовуються такі засоби маскування трафіку як Shadowsocks та інші. + +Yggdrasil - перш за все альтернативний маршрутизатор, який дозволяє автоматично створювати перманентні IPv6 адреси в середовищі IPv4 та є корисним інструментом для швидкого розгортання інфраструктури локальних дротових, лазерних та радіо мереж. + +### Посилання + +* https://yggdrasil-network.github.io - офіційна сторінка +* https://github.com/yggdrasil-network - проект на GitHub +* https://yggdrasil-map.cwinfo.net - орієнтовна мапа вузлів \ No newline at end of file diff --git a/post/yggmail-mesendzer-z-poshtovym-interfeysom.md b/post/yggmail-mesendzer-z-poshtovym-interfeysom.md new file mode 100644 index 0000000..7b586f5 --- /dev/null +++ b/post/yggmail-mesendzer-z-poshtovym-interfeysom.md @@ -0,0 +1,118 @@ +# Yggmail - месенджер з поштовим інтерфейсом + +[Yggmail](https://github.com/neilalexander/yggmail) - це дочірній проєкт від розробників [Yggdrasil](https://devzone.org.ua/post/yggdrasil-mereznii-protokol-z-decentralizovanim-routingom), написаний на Go та має відкритий код. Дозволяє в декілька простих кроків розгорнути сервер для підключення будь якого поштового клієнта, сумісного з протоколами IMAP / SMTP. Працює без залежності від центрального серверу та виділеної адреси IP. + +Для транспорту даних використовує вбудований вузол Yggdrasil, не потребує його окремого встановлення а також може ізольовано працювати поряд з ним. Трафік, що передається - захищений і не потребує додаткового шифрування TLS. + +З коробки, являє собою сервер, підключитись до якого можна через поштовий клієнт типу Thunderbird або [DeltaChat](https://delta.chat/uk/). Утім, від класичних поштових серверів відрізняється протоколом транспорту, тому не є сумісним зі звичайними скриньками і працює у власній мережі. Yggmail варто сприймати саме як месенджер, оскільки він має тільки спільний поштовий інтерфейс IMAP / SMTP але іншу модель транспорту. + +## Встановлення + +Yggmail знаходиться на стадії розробки, тому для встановлення будемо збирати з початкового коду на останній версії Go. +Про те, як встановити останню версію Go в Debian читайте [тут](https://devzone.org.ua/post/vstanovlennia-ostannyoyi-versiyi-go-v-debian). + +Збірка Yggmail виконується наступною командою в теці і користувачем, від якого планується робота з поштою: +``` +cd ~ +go install github.com/neilalexander/yggmail/cmd/yggmail@latest +``` + +Так як я користуюсь `systemd`, встановлюю бінарний файл до системної локації: +``` bash +install go/bin/yggmail /usr/local/bin/yggmail +``` +* після цього, теоретично, теку `~/go` можна видалити + +Наступним кроком, ініціюємо новий профіль: +``` +yggmail -password +``` + +Записуємо згенеровану адресу в полі `Mail address`, на неї будуть отримуватись листи і вона вказується при підключенні поштового клієнта. + +Профіль користувача буде збережено до файлу `~/yggmail.db` - його можна використовувати для резервного копіювання пошти. +Щоб вказати альтернативний шлях, вказується атрибут `-database=/path/to/yggmail.db`. + +Довідка доступна командою: +``` +yggmail -help +``` + +## Запуск + +Запуск відбувається однойменною командою, утім декілька слів про режими підключень: + +* Мультикаст +* Підключення через певний [публічний пір](https://publicpeers.neilalexander.dev) +* Обидва варіанти + +Відповідно: + +``` +yggmail -multicast +yggmail -peer=tls://... +yggmail -multicast -peer=tls://... +``` + +## Автозапуск + +Yggmail отримує пошту тільки тоді, коли він працює та підключений до мережі. Якщо вузол отримувача не досяжний, лист переміщується в до черги очікування і буде надіслано як тільки зв'язок між вузлами відновиться. + +Тому зручно створити системний сервіс з автозапуском: + +``` +#/etc/systemd/system/yggmail.service +[Unit] +After=network.target +Wants=network.target + +[Service] +User=yggmail +Group=yggmail +Type=simple +WorkingDirectory=/home/yggmail +ExecStart=/usr/local/bin/yggmail -peer=tls://... +StandardOutput=file:///home/yggmail/debug.log +StandardError=file:///home/yggmail/error.log + +[Install] +WantedBy=multi-user.target +``` + +Активуємо сервіс: +``` +systemctl daemon-reload +systemctl enable yggmail +systemctl start yggmail +``` + +Перевіряємо: +``` +netstat -tulpn | grep LISTEN +``` + +## Підключення поштового клієнта + +Yggmail прослуховує порти: + +* `1025` - SMTP / TCP +* `1143` - IMAP / TCP + +В залежності від поштового клієнта, при створенні нового підключення скриньки, вказуються наступні дані: + +* Логін - адреса, наприклад `123abc@yggmail` +* Пароль - пароль, вказаний при виконанні команди `yggmail -password` +* SMTP сервер - `localhost` +* SMTP порт - `1025` +* IMAP сервер - `localhost` +* IMAP порт - `1143` + +Якщо сервер працює на віддаленому VPS, на ньому потрібно відкрити відповідні порти, а `localhost` замінити на його IP. + +Єдиною рекомендацією можна додати, що не варто відправляти пошту самому собі для тестування, наразі це не працює. +Щоб перевірити роботу Yggmail - відправляйте пошту з іншого вузла або залиште вашу адресу в коментарях - можливо хтось вам відповість! + +## Посилання + +* [Проєкт на GitHub](https://github.com/neilalexander/yggmail) +* [Канал в Matrix](https://matrix.to/#/#yggmail:matrix.org) \ No newline at end of file diff --git a/post/zavantazennia-video-z-saytiv-rezka-hd-cherez-plahin-yt-dlp.md b/post/zavantazennia-video-z-saytiv-rezka-hd-cherez-plahin-yt-dlp.md new file mode 100644 index 0000000..1bfafce --- /dev/null +++ b/post/zavantazennia-video-z-saytiv-rezka-hd-cherez-plahin-yt-dlp.md @@ -0,0 +1,26 @@ +# Завантаження відео з сайтів Rezka HD через плагін yt-dlp + +Інструкція зі встановлення плагіна [rezka_yt_dlp_plugin](https://github.com/gnfalex/rezka_yt_dlp_plugin) в Linux, для завантаження останніх релізів озвучки з дзеркал Rezka. Може бути корисною для створення роздач BitTorrent, або для локального перегляду відео. + +Спочатку потрібно встановити утиліту [yt-dlp](https://github.com/yt-dlp/yt-dlp) зручним способом. Якщо у вас вже розгорнуто інфраструктуру Python, найпростіше це зробити з `pip`: + +``` bash +pip install yt-dlp +``` + +Наступним кроком, встановлюємо сам плагін. Є різні [способи](https://github.com/yt-dlp/yt-dlp#installing-plugins), але я робив так: + +1. `mkdir -p ~/.config/yt-dlp/plugins` +2. `cd ~/.config/yt-dlp/plugins` +3. `git clone https://github.com/gnfalex/rezka_yt_dlp_plugin.git` +4. `yt-dlp --verbose` - перевіряємо інсталяцію плагіна +5. `nano ~/.config/yt-dlp/plugins/rezka_yt_dlp_plugin/yt-dlp.conf` - вказати актуальні опції (звукову доріжку та URL) або пропустити, використовуючи у такому випадку аргументи CLI + +Відео завантажується наступним чином: + +``` bash +cd ~/Downloads +yt-dlp --config-location ~/.config/yt-dlp/plugins/rezka_yt_dlp_plugin/yt-dlp.conf URL +``` +* або ж просто `yt-dlp URL` і обрати звукову доріжку вручну +* для вибору сезону та епізоду: `--match-filter "season=1&episode=2"` \ No newline at end of file diff --git a/post/zmina-peerid-user-agent-v-transmission.md b/post/zmina-peerid-user-agent-v-transmission.md new file mode 100644 index 0000000..9f0227c --- /dev/null +++ b/post/zmina-peerid-user-agent-v-transmission.md @@ -0,0 +1,31 @@ +# Зміна PeerID / User-agent в Transmission + +Подібна задача може виникнути у разі, якщо ви адмініструєте агрегатор та хочете вирізняти його агент з поміж інших з'єднань або якщо з якихось причин ваш трекер блокує альфа-збірки клієнтів. + +## settings.json + +`User-agent` можна змінити через опцію `user-agent` у файлі налаштувань. В системах Linux, він звичайно знаходиться в `~/.config/transmission/settings.json`: + +``` ~/.config/transmission/settings.json +"user-agent" = "VALUE" +``` +* де `VALUE` - ваше значення +* після зміни файлу, потрібно перезапустити клієнт / сервер + +## CMakeLists.txt + +На відміну від першого способу, зміна версії в [CMakeLists.txt](https://github.com/transmission/transmission/blob/main/CMakeLists.txt#L94) також змінює і `PeerID` на базі вказаних версій, а також може впливати на конфігурацію збірки (через директиви компілятора) тому майте на увазі, якщо йдете цим шляхом: + +``` CMakeLists.txt +# these should be the only five lines you need to change +set(TR_VERSION_MAJOR "4") +set(TR_VERSION_MINOR "1") +set(TR_VERSION_PATCH "0") +set(TR_VERSION_BETA_NUMBER "2") # empty string for not beta +set(TR_VERSION_DEV FALSE) +``` +* після цього перезбираємо клієнт / сервер + +## version.h + +Як і попередній спосіб, є ще один варіант - через генерацію файлу `version.h` через скрипт [update-version-h.sh](https://github.com/transmission/transmission/blob/main/update-version-h.sh). Після виконання цього скрипта, файл `version.h` з'явиться у теці [libtransmission](https://github.com/transmission/transmission/tree/main/libtransmission). Так само, після внесення змін, потрібно перезібрати клієнт / сервер. \ No newline at end of file