Введение

Этак три-четыре года тому назад я интересовался одноплатниками, микрокомпьютерами, мини ПК и т.п. вещами. Было удобно такие компьютеры транспортировать при путешествиях, перемещать при переездах, да и если брать миники (мини ПК), то их можно вполне корректно и нормально использовать в качестве основного ПК, если нет целей заниматься дизайном, программировать под Android, играть в современные игры или вовсе их писать. Но с того давнего и хорошего времени у меня сохранились ещё orange pi'йки, которыми я особо даже не пользовался, а купил их просто ради чего-то будущего и абстрактно интересного.

Но вот недавно, в ходе написания тестов для своей анонимной сети, я вдруг вспомнил, что где-то в одной из полок у меня завалялся один дешёвый одноплатник. Немного поискав, я его всё же нашёл. Это был Orange Pi Lite с 512MiB памяти и модулем Wi-Fi. Тогда он стоил примерно 1,5k рублей. На тот момент, если мне не изменяет память, он являлся вообще самым дешёвым экземпляром линейки Orange Pi наравне с аналогичным Lite, только вместо Wi-Fi модуля там стоял Ethernet порт.

Orange Pi Lite
Orange Pi Lite

Как только мной был наконец получен Orange Pi Lite, у меня возникла сразу идея протестировать свою анонимную сеть на этом маленьком зверьке. Суть такой затеи лежала в нескольких плоскостях: 1) анонимная сеть, а точнее узел в анонимной сети, не потреблял более чем 200MiB оперативной памяти (в среднем где-то 60MiB), что в память одноплатника вполне хорошо помещалось, 2) анонимная сеть уже была протестирована на самых дешёвых VPS со слабыми процессорами ~2-2.2Ггц, одним ядром и малой памятью в 512MiB. 3) golang, как язык на котором была написана анонимная сеть, является кросскомпилируемым и кроссплатформенным, вследствие этого мне не надо было адаптировать уже готовые программы написанные на x86-64 под ARM, а компилировать мог под любой платформой. Иными словами, в ходе всего вышеперечисленного, у меня были все шансы на успех.

Плюс ко всему этому у итогового решения также существовало прикладное использование, а именно - чтобы Orange Pi постоянно генерировал трафик в анонимной сети, и чтобы через него я мог посылать и принимать сообщения. Иными словами, Orange Pi в концепции локальной сети должен был являться для меня постоянно работающим сервером к которому я бы мог обращаться постоянно. И давайте посмотрим, что из этого вышло.

Настраиваем

Вся настройка сводилась буквально к трём частям: 1) к установке аппаратуры, 2) к установке линухи и 3) к установке анонимной сети. С первым пунктом всё просто - подключаем клаву, монитор, подготавливаем SD-карту с установленной линухой. На счёт второго я просто скачал ubuntu дистрибутив рассчитанный специально под H3 ARM процессор (инструкция установки со всеми ссылками) и при помощи balenaEtcher установил на SD-карту данный дистрибутив. В начале по глупости качал просто рандомные дистрибутивы, но получал то ошибки ненахождения драйверов Wi-Fi, то загрузка самого дистрибутива завершалась ошибками, то и вовсе дистрибутив не загружался. После успешной установки линухи настала пора устанавливать и ноду анонимной сети Hidden Lake. О самой анонимной сети более подробно вы можете узнать тут, а о полной настройке здесь.

Подключенный и готовый к работе Orange Pi
Подключенный и готовый к работе Orange Pi

Для того, чтобы установить узел анонимной сети достаточным условием является запуск программы HLS (Hidden Lake Service). Для этого дела у меня специально был подготовлен скрипт быстрой установки HLS из последней релизной версии. Но потом я вспомнил, что скрипт то рассчитан под архитектуру amd64, но явно не arm. Но готовить релизные версии под архитектуру armv6l в целях тестирования было явно проблемнее, чем просто установить уже на самой орэндж пайке golang и от туда скомпилировать из исходников HLS. Этим я и занялся.

git clone https://github.com/number571/go-peer # качаем библиотеку go-peer в cmd/ которой присутствует hidden_lake
wget https://go.dev/dl/go1.16.linux-armv6l.tar.gz
tar -C /opt -xzf go1.16.linux-armv6l.tar.gz
echo PATH="${PATH}:/opt/go/bin" >> ~/.bashrc
source ~/.bashrc
cd go-peer/cmd/hidden_lake/service && go build ./cmd/hls
mv go-peer/cmd/hidden_lake/service/hls .

Далее просто устанавливаем и запускаем HLS как сервис.

echo "
[Unit]
Description=HiddenLakeService

[Service]
ExecStart=/root/hls -path=/root -key=/root/priv.key
Restart=always
RestartSec=10

[Install]
WantedBy=multi-user.target
" > /etc/systemd/system/hidden_lake_service.service
systemctl daemon-reload
systemctl enable hidden_lake_service.service
systemctl restart hidden_lake_service.service

Когда сервис будет запущен, то он создаст два файла: hls.cfg, priv.key и одну директорию: hls.db. Директория - это просто хранилище хешей принимаемых и отправляемых сообщений. Такое хранилище исполняет одну единственную роль - проверка и игнорирование уже ранее полученных или сгенерированных хешей. Вследствие этого исключается циркуляция одной и той же информации в сети, а также исключается использование одного сообщения более чем одного раза даже спустя большой интервал времени. Файл priv.key - это сгенерированный приватный ключ из-под которого HLS начинает идентифицироваться сетью как действующий узел. Файл hls.cfg - это непосредственно конфигурация, и он нас как раз интересует. Пока что HLS генерирует мусорный трафик лишь для себя, но никому его не отправляет и фактически ничего не получает извне. Нам же нужно соединить HLS с глобальной сетью. Для этого просто редактируем hls.cfg (добавляем connections, удаляем address.tcp, удаляем backup_connections), например при помощи редактора nano. Действующие узлы (а точнее ретрансляторы) можно найти в таблице README.

{
	"settings": {
		"message_size_bytes": 8192,
		"work_size_bits": 20,
		"key_size_bits": 4096,
		"queue_period_ms": 5000,
		"limit_void_size_bytes": 4096,
		"messages_capacity": 2048
	},
	"logging": [
		"info",
		"warn",
		"erro"
	],
	"address": {
		"http": "127.0.0.1:9572"
	},
    "connections": [
        "195.133.1.126:9581",
        "94.103.91.81:9581"
    ],
	"services": {
		"go-peer/hidden-lake-messenger": "127.0.0.1:9592"
	}
}

После редактирования конфига просто перезапускаем сервис: systemctl restart hidden_lake_service.service. Немного ждём и проверяем работу через systemctl status hidden_lake_service.service.

Логи работающего HLS и "коммунициирующего" с другими узлами в сети. Здесь видно, что существуют логи с WARN, что на самом деле не есть хорошо. Далее я расскажу с чем такое поведение может быть связано.
Логи работающего HLS и "коммунициирующего" с другими узлами в сети. Здесь видно, что существуют логи с WARN, что на самом деле не есть хорошо. Далее я расскажу с чем такое поведение может быть связано.

Работает, но этого нам недостаточно. Наша основная цель - это не просто запустить узел анонимной сети Hidden Lake, но и также пользоваться этой анонимной сетью. В HL существует пока что лишь одно прикладное приложение - это мессенджер HLM. Данный мессенджер представляет GUI через HTTP сервер. Вследствие такого свойства данным мессенджером можно пользоваться с других устройств просто стучась по локальному IP-шнику и нужному порту. Следовательно, нам нужно также запустить HLM как отдельный сервис.

cd go-peer/cmd/hidden_lake/messenger && go build ./cmd/hlm
mv go-peer/cmd/hidden_lake/service/hlm .
echo "
[Unit]
Description=HiddenLakeMessenger

[Service]
ExecStart=/root/hlm -path=/root
Restart=always
RestartSec=10

[Install]
WantedBy=multi-user.target
" > /etc/systemd/system/hidden_lake_messenger.service
systemctl daemon-reload
systemctl enable hidden_lake_messenger.service
systemctl restart hidden_lake_messenger.service

При запуске создастся один файл: hlm.cfg и одна директория hlm.db. Директория - это БД всех отправляемых и принимаемых сообщений. Здесь нас интересует более сам файл hlm.cfg, в котором нужно поменять лишь одну строчку - изменить IP с 127.0.0.1 на 0.0.0.0 в поле interface. Получим следующее.

{
	"settings": {
		"messages_capacity": 2048
	},
	"logging": [
		"info",
		"warn",
		"erro"
	],
	"language": "ENG",
	"address": {
		"interface": "0.0.0.0:9591",
		"incoming": "127.0.0.1:9592"
	},
	"connection": "127.0.0.1:9572"
}

Также после редактирования конфига просто перезапускаем сервис: systemctl restart hidden_lake_messenger.service. Немного ждём и проверяем работу через systemctl status hidden_lake_messenger.service.

Чтобы проверить работу мессенджера, нам достаточно узнать IP Orange Pi устройства. Маршрутизатор мне выдал IP=192.168.0.121. Следовательно, заходим на данный IP по порту - 9591. Получаем следующее.

Если мы походим по разделам Friends, Settings, то будем получать примерно следующие логи с работающего сервиса HLM.

Логи работающего HLM
Логи работающего HLM

Теперь, чтобы проверить работоспособность HLM, нам необходимо с кем-либо соединиться в сети Hidden Lake. Для данной процедуры необходимо обменяться публичными ключами. Как этот обмен будет происходить - дело пользователей. Либо это оффлайн обмен, либо с использованием уже безопасного ранее установленного соединения, либо с помощью определённых протоколов, например этого. Hidden Lake - это F2F сеть, следовательно, публичные ключи абонентов должны быть у обоих.

В моём случае я просто внёс публичный ключ с Orange Pi на свой компьютер, и с компьютера перенёс другой публичный ключ на Orange Pi. Для того чтобы установить связь F2F, необходимо просто в разделе Friends добавить друга и его публичный ключ.

И тут начинаются проблемы

На самом деле проблемы во всей этой идее можно было заметить ещё на моменте установки и запуска HLS, по логам которого можно увидеть иногда WARN сообщения. Это не случайно, но давайте для начала посмотрим на эту проблему с более прикладного уровня. Как только я обменялся публичными ключами - настала пора обмениваться сообщениями в мессенджере. Первые сообщения я начал писать с HLM основного компьютера и параллельно проверять были ли сообщения доставлены. И лишь фактически третье сообщение было доставлено до HLM со стороны Orange Pi.

HLM со стороны основного компьютера
HLM со стороны основного компьютера
HLM со стороны Orange Pi
HLM со стороны Orange Pi

Здесь следует задаться вполне логичным вопросом - как так получилось? Но ответ на самом деле вполне логичный. Начнём пожалуй с архитектуры самого HLM. HLM обращается к HLS для отправления и получения сообщений. При этом HLM устанавливает дополнительную опцию со своей стороны, указывающую HLS не генерировать ответ на полученное сообщение (иными словами не ACK'ать факт получения сообщений). В более простой аналогии можно это рассматривать как отправление пакетов по протоколу UDP, а не TCP. Отправитель не дожидается ответа об успешном принятии сообщений от получателя. Он их просто отправляет в сеть и надеется, что сообщение дойдёт до отправителя. Эта архитектура на самом деле не такая странная, как может показаться на первый взгляд. Связано это со следующими условностями:

  1. Принятие ответа замедляло бы само отправление как минимум в два раза и N раз при неполучении, где N - константа ожидания ACK'a,

  2. Генерация ответа может частично приводить к появлению новых видов деанонимизирующих атак со стороны доверенных узлов, когда таковые становятся способны перенагружать очередь ответов, приводя тем самым к возможности слежения за временем и находить на основе этого факт коммуникации с кем-либо в сети,

  3. Связь в HLS использует слепую маршрутизацию и вероятность того, что получатель будучи в сети не получит ответ, будет стремиться к нулю при увеличении узлов / ретрансляторов в сети.

Окей, если взять в расчёт всё это, и особенно третий пункт, то как так случилось, что сообщение не дошло на получателя? И здесь теперь необходимо посмотреть на ещё одну сторону вопроса - соединения с самими узлами в сети. Сеть устроена так, что если узел начал отправлять ответ, но так до конца его не отправил, то узлы принудительно начинают от него отключаться. Такая же ситуация связана с тем, когда узлы отправляют информацию другому узлу, а тот в свою очередь очень медленно принимает байты. Вследствие этого узлы также автоматически прерывают связь с узлом. После прерывания один из узлов пытается вновь установить соединение и так может происходить до бесконечности.

Это можно как раз наблюдать на логе HLS, когда присутствовали WARN. Также, помимо WARN'а, следует взглянуть на время принятия/отправления сообщений. В идеале, каждый BRDCS должен генерироваться в период равный 5 секунд. Также и принятие зашифрованных сообщений от разных узлов должно укладываться в 5 секунд. Иными словами, в логах при корректной работе просто не должно существовать интервала "ничего не делания" более чем 5 секунд. Но там интервалы были в 15 и 27 секунд, что не есть хорошо.

Эту тенденцию легко проверить также и через сам HLM. Если мы просто будем постоянно перезагружать страницу с Settings, то соединения будут иногда гореть бирюзовым цветом, иногда будут становиться серыми. Бирюзовый цвет отвечает за успешное существование соединения, в то время как серый говорит об отсутствии реального соединения. Иными словами узел постоянно отключается (либо его отключают) и он постоянно вновь возобновляет соединения.

Соединения существуют
Соединения существуют
Соединения отсутствуют
Соединения отсутствуют

Поэтому когда первые сообщения отправлялись, сам узел HLS со стороны Orange Pi был отключен от сети. Третье же сообщение успешно пришло, потому как HLS со стороны Orange Pi смог вновь успешно подключиться к сети на определённый период времени.

Окей с этим мы разобрались, но всё же не ясна первопричина такого поведения, а именно - почему узел решает отключаться от остальных узлов в сети или почему остальные узлы в сети начинают отключаться от узла с Orange Pi? И тут начинается как раз проблема малой производительности самого Orange Pi, он просто не вывозит нагрузку в сети.

Нагрузка в сети состоит из нескольких ресурсозатратных операций, а также из-за самой архитектуры сети, где увеличение количества участников сети линейно влияет на увеличение нагрузки на всю систему. Ресурсозатратные операции состоят из:

  1. Доказательства работы. Сеть HLS для каждого отправляемого сообщения устанавливает сложность в 20 бит. Отправление (или накопление ложных) сообщений со стороны узла должно успевать происходить каждые 5 секунд,

  2. Шифрования и подписания. Каждое отправляемое сообщение должно шифровать сеансовый ключ публичным ключом получателя размером в 4096 бит, а также подписывать хеш сообщения приватным ключом отправителя также размером в 4096 бит.

  3. Расшифрования. Каждое принимаемое сообщение проходит через процедуру попытки расшифрования сообщения приватным ключом (расшифровывается сеансовый ключ) в 4096 бит. Узел должен успевать обрабатывать N пакетов в 5 секунд, где N - количество узлов в сети.

И тут я вспомнил, что ранее я проверял слабый VPS всего с тремя узлами, где одним из них был и сам сервер. При такой парадигме VPS успевал сам генерировать трафик и его потреблять. Но в ходе тестирования, когда я создавал ещё один узел, сервер начинал ровно также то ложиться, то вставать. Это говорило о том, что он дошёл до пика своих возможностей нагрузки. В такой парадигме он уже не мог эффективно параллелить и успевать производить операции доказательства, шифрования, подписания и расшифрования в интервале равном пяти секундам.

В определённые периоды процессор нагружен менее чем на половину
В определённые периоды процессор нагружен менее чем на половину
В другие периоды некоторые ядра процессора нагружались в сотку
В другие периоды некоторые ядра процессора нагружались в сотку

В ходе работы нельзя однозначно сказать, что Orange Pi нагружал процессор в сотку, но иногда такое действительно происходило (возможно когда он успешно коммуницировал со всеми узлами). По ощущениям на процессоре можно было жарить микро яичницу, но показатели температуры показывали вполне допустимые значения.

65 не так уж и плохо, но на правду со стороны внешнего нагрева было мало похоже
65 не так уж и плохо, но на правду со стороны внешнего нагрева было мало похоже

Заключение

В ходе такого экспериментального явления, как накатывания анонимной сети на Orange Pi были получены и положительные и отрицательные результаты. Фактически узел заработал, он подключился к сети, мог отправлять и принимать сообщения. Но всё это работало нестабильно, как минимум с четырьмя узлами в сети. Если бы узлов в сети было меньше, то и сам Orange Pi вёл бы себя более детерминировано. В любом случае, конечно не желательно использовать подобные устройства со столь слабым процессором в качестве узла в сети Hidden Lake. С другой стороны, Orange Pi вполне легко может быть использован в качестве ретранслятора в сети Hidden Lake, потому как ретрансляторы не генерируют трафик и не пытаются его расшифровывать. Вследствие этого, мощностей Orange Pi будет хватать для такого типа задач.

P.S.

Вместо использования слабых одноплатников, подобия Orange Pi Lite, в роли узла Hidden Lake можно вполне корректно использовать как раз мини ПК, либо эти же одноплатники, но с более хорошими характеристиками (в плане процессора). Этого должно быть достаточно для поддержания нормальной работы при небольших группах участников (10-30 узлов в зависимости от выбранной аппаратуры).

Так например, в одно свободное время я тестировал узлы Hidden Lake на разных VPS с размещением трёх узлов на одном сервере с характеристиками одноядерного процессора ~3.4Ггц. В сумме, в сети существовало 4 узла (3 узла на VPS, 1 узел на моём компьютере). Мощность процессора на таком сервере держалась в диапазоне 40-50%, отключений и переключений узлов от сети не наблюдалось. Таким образом, качество процессора является одним из самых важных критериев со стороны поднятия узлов в сети Hidden Lake.

Комментарии (4)


  1. M_AJ
    29.10.2023 05:52

    а также из-за самой архитектуры сети, где увеличение количества участников сети линейно влияет на увеличение нагрузки на всю систему.

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


    1. Number571 Автор
      29.10.2023 05:52

      Проблема отказа в обслуживании действительно существует, но сеть всё же может являться открытой. Одним из возможных решений может стать создание разделённых друг от друга сетей, чтобы они не могли сливаться воедино. В Hidden Lake для этого присутствует как раз сущность - Network Key. В таком случае, если одна сеть будет сильно перегружена, то можно подключиться к другой сети. Т.к. ретрансляторы крайне примитивны и не требуют больших вычислений, то для поддержания одной сети будет достаточно даже одного выделенного ретранслятора.

      Безусловно это не решает проблему полноценно, т.к. она находится очень глубоко, буквально зарыта в ядре всех ныне известных теоретически доказуемых анонимных сетей. Все попытки с моей стороны исследовать возможность искоринения линейной нагрузки на сеть приводили к дальнейшему ухудшению самой анонимности. И это наблюдалось не только в QB-сетях (к коим принадлежит Hidden Lake), но и также в EI-сетях и DC-сетях.


  1. select26
    29.10.2023 05:52

    слабых одноплатников, подобия Orange Pi Lite,

    Не нужно забывать, что PI Lite - это уже история. Современный PI5 может намного больше (все же 8 ядер). Зачастую может заменить и сервер приложений.
    Или те же Intel N100 based - для 6W TDP просто чумовая производительность.


  1. Teimir
    29.10.2023 05:52

    Интересная статья, хотя и жаль, что Апельсина не вывезла. Может действительно стоит попробовать сделать из неё ретранслятор.