IPFS без боли (но это не точно)

в 9:38, , рубрики: ipfs, p2p, децентрализованные сети, Программирование, хостинг

IPFS без боли (но это не точно) - 1

Не смотря на то, что на хабре была уже не одна статья про IPFS

Сразу уточню, что я не являюсь экспертом в этой области, но не раз проявлял интерес к этой технологии, но попытки поиграться с ней часто вызывало некоторую боль. Сегодня я опять взялся за эксперименты и получил кое-какие результаты, которыми хотел бы поделиться. Если коротко, то будет описан процесс установки IPFS и некоторые фишки (все выполнялось на ubuntu, на других платформах не пробовал).

Если вы пропустили что же такое IPFS, довольно подробно написано здесь: habr.com/ru/post/314768

Установка

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

Устанавливаем go

Официальная документация: golang.org/doc/install
Актуальную версию смотрите на golang.org/dl

Замечание: лучше устанавливать IPFS от имени пользователя, которым и предполагается наиболее частое использование. Дело в том, что ниже мы рассмотрим вариант с монтированием через FUSE и там есть тонкости.

cd ~
curl -O https://dl.google.com/go/go1.12.9.linux-amd64.tar.gz
tar xvf go1.12.9.linux-amd64.tar.gz
sudo chown -R root:root ./go
sudo mv go /usr/local
rm go1.12.9.linux-amd64.tar.gz

Затем надо обновить окружение (подробнее здесь: golang.org/doc/code.html#GOPATH).

echo 'export GOPATH=$HOME/work' >> ~/.bashrc
echo 'export PATH=$PATH:/usr/local/go/bin:$GOPATH/bin' >> ~/.bashrc
source ~/.bashrc

Проверяем, что go установился
go version

Устанавливаем IPFS

Мне больше всего понравился способ установки через ipfs-update.
Устанавливаем его командой go get -v -u github.com/ipfs/ipfs-update

После этого можно выполнять такие команды:
ipfs-update versions — чтобы увидеть все доступные версии для скачивания.
ipfs-update version — чтобы увидеть текущую установленную версию (пока у нас не установлена IPFS, будет none).
ipfs-update install latest — установить последнюю версию IPFS. Вместо latest соответственно можно указать любую желаемую версию из списка доступных.

Устанавливаем ipfs
ipfs-update install latest

Проверяем
ipfs --version

Непосредственно с установкой в общих чертах все.

Запуск IPFS

Инициализация

Для начала надо выполнить инициализацию.
ipfs init

В ответ вы получите что-то типа такого:
ipfs init
initializing IPFS node at /home/USERNAME/.ipfs
generating 2048-bit RSA keypair...done
peer identity: QmeCWX1DD7HnXXXXXXXXXXXXXXXXXXXXXXXXxxx
to get started, enter:
ipfs cat /ipfs/QmS4ustL54uo8FzR9455qaxZwuMiUhyvMcX9Ba8nUH4uVv/readme

Можно выполнить предложенную команду
ipfs cat /ipfs/QmS4ustL54uo8FzR9455qaxZwuMiUhyvMcX9Ba8nUH4uVv/readme

Результат

Hello and Welcome to IPFS!

██╗██████╗ ███████╗███████╗
██║██╔══██╗██╔════╝██╔════╝
██║██████╔╝█████╗ ███████╗
██║██╔═══╝ ██╔══╝ ╚════██║
██║██║ ██║ ███████║
╚═╝╚═╝ ╚═╝ ╚══════╝

If you're seeing this, you have successfully installed
IPFS and are now interfacing with the ipfs merkledag!

-------------------------------------------------------
| Warning: |
| This is alpha software. Use at your own discretion! |
| Much is missing or lacking polish. There are bugs. |
| Not yet secure. Read the security notes for more. |
-------------------------------------------------------

Check out some of the other files in this directory:

./about
./help
./quick-start <-- usage examples
./readme <-- this file
./security-notes

Вот тут, на мой взгляд, уже начинается интересное. Ребята еще на этапе установки уже начинают использовать свои же технологии. Предложенный хеш QmS4ustL54uo8FzR9455qaxZwuMiUhyvMcX9Ba8nUH4uVv — не сгенерированный специально для вас, а вшитый в релиз. То есть до релиза они подготовили приветственный текст, вылили его в IPFS и адрес добавили в инсталятор. По-моему, это очень круто. И этот файл (точнее, всю папку) теперь можно просмотреть не только локально, но и на официальном шлюзе ipfs.io/ipfs/QmS4ustL54uo8FzR9455qaxZwuMiUhyvMcX9Ba8nUH4uVv. При этом можно быть уверенным, что содержимое папки никак не менялось, ибо если бы поменялось, то хэш бы тоже поменялся.

К слову, в данном случае IPFS имеет некоторое сходство с сервером контроля версий. Если в исходные файлы папки внести изменения и опять вылить папку в IPFS, то она получит новый адрес. При этом старая папка никуда не денется просто так и будет доступна по своему прежнему адресу.

Непосредственно запуск

ipfs daemon
Должны в ответ получить типа такого:
ipfs daemon
Initializing daemon...
go-ipfs version: 0.4.22-
Repo version: 7
System version: amd64/linux
Golang version: go1.12.7
Swarm listening on /ip4/x.x.x.x/tcp/4001
Swarm listening on /ip4/127.0.0.1/tcp/4001
Swarm listening on /ip6/::1/tcp/4001
Swarm listening on /p2p-circuit
Swarm announcing /ip4/127.0.0.1/tcp/4001
Swarm announcing /ip6/::1/tcp/4001
API server listening on /ip4/127.0.0.1/tcp/5001
WebUI: http://127.0.0.1:5001/webui
Gateway (readonly) server listening on /ip4/127.0.0.1/tcp/8080
Daemon is ready

Открываем двери для Интернета

Обратите внимание на эти две строчки:
WebUI: http://127.0.0.1:5001/webui
Gateway (readonly) server listening on /ip4/127.0.0.1/tcp/8080

Вот если вы установили IPFS у себя локально, то вы будете к IPFS-интерфейсам обращаться по локальным адресам и вам будет все доступно (К примеру, localhost:5001/webui/). Но при установке на внешнем сервере по-умолчанию шлюзы закрыты для интернета. Шлюзов два:
1. Админка webui (github) на порту 5001.
2. Внешнее API на порту 8080 (readonly).

Пока для экспериментов можно открыть оба порта (5001 и 8080), но на боевом сервере конечно же порт 5001 надо бы закрыть фаерволом. Еще есть 4001 порт, он нужен, чтобы другие пиры могли вас найти. Его следует оставить открытым для запросов извне.

Открываем для редактирования ~/.ipfs/config и находим в нем эти строки:
"Addresses": {
"Swarm": [
"/ip4/0.0.0.0/tcp/4001",
"/ip6/::/tcp/4001"
],
"Announce": [],
"NoAnnounce": [],
"API": "/ip4/127.0.0.1/tcp/5001",
"Gateway": "/ip4/127.0.0.1/tcp/8080"
}

Меняем 127.0.0.1 на ip вашего сервера и сохраняем файл, после чего перезапускаем ipfs (запущенную команду останавливаем Ctrl+C и опять запускаем).
Должны получить
...
WebUI: http://ip_вашего_сервера:5001/webui
Gateway (readonly) server listening on /ip4/ip_вашего_сервера/tcp/8080

Вот теперь внешние интерфейсы должны быть доступны.
Проверьте
http://домен_или_ip_сервера:8080/ipfs/QmS4ustL54uo8FzR9455qaxZwuMiUhyvMcX9Ba8nUH4uVv/readme
Должен открыться приведенный выше ридми-файл.

http://домен_или_ip_сервера:5001/webui/
Должен открыться веб-интерфейс.

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

Настраиваем веб-интерфейс для работы со своим сервером

Вот тут первый подводный камень, на который было потрачено часа три.
Если вы IPFS установили на внешнем сервере, но не устанавливали или не запускали IPFS локально, то при заходе на /webui в веб-интерфейсе вы должны видеть ошибку подключения:
IPFS без боли (но это не точно) - 2

Дело в том, что webui, по моему мнению, работает весьма неоднозначно. Сначала он пытается подключиться к API того сервера, где открыт интерфейс (конечно же основываясь на адресе в браузере). и если не получается там, то пытается подключиться на локальный шлюз. И если у вас локально запущен IPFS, то у вас webui будет работать нормально, только вот работать вы будете с локальной IPFS, а не внешней, хоть и открыли webui на внешнем сервере. Потом заливаете файлы, но почему-то не видите их просто так на внешнем сервере…
А если и локально не запущен, то получаем ошибку соединения. В нашем случае ошибка скорее всего из-за CORS, о чем и говорит в том числе webui, предлагая добавить конфиг.
ipfs config --json API.HTTPHeaders.Access-Control-Allow-Origin '["http://ip_вашего сервера:5001", "http://127.0.0.1:5001", "https://webui.ipfs.io"]'
ipfs config --json API.HTTPHeaders.Access-Control-Allow-Methods '["PUT", "GET", "POST"]'

Я же у себя просто wildcard прописал
ipfs config --json API.HTTPHeaders.Access-Control-Allow-Origin '["*"]'
Добавленные заголовки можно найти все в том же ~/.ipfs/config. В моем случае это "API": {
"HTTPHeaders": {
"Access-Control-Allow-Origin": [
"*"
]
}
},

Перезапускаем ipfs и видим, что webui успешно соединился (во всяком случае должен, если вы открыли шлюзы для запросов извне, как и описано выше).

Теперь можно прям через веб-интерфейс заливать папки и файлы, а так же создавать свои папки.

Монтирование файловой системы FUSE

Вот это довольно интересная фишка.

Файлы (как и папки), мы можем добавлять не только через веб-интерфейс, но и непосредственно в терминале, например
ipfs add test -r
added QmfYuz2gegRZNkDUDVLNa5DXzKmxxxxxxxxxx test/test.txt
added QmbnzgRVAP4fL814h5mQttyqk1aURxxxxxxxxxxxx test

Последний хеш — это хеш корневой папки.
По этому хэшу мы можем открыть папку на любой ipfs-ноде (которая сможет найти нашу ноду и получить содержимое), можем в веб-интерфейсе на порту 5001 или 8080, а можем локально через ipfs.
ipfs ls QmbnzgRVAP4fL814h5mQttyqk1aUxxxxxxxxxxxxx
QmfYuz2gegRZNkDUDVLNa5DXzKmKVxxxxxxxxxxxxxx 10 test.txt

Но еще можно и как обычную папку открывать.

Создадим в корне две папки и права на них предоставим нашему пользователю.
sudo mkdir /ipfs /ipns
sudo chown USERNAME /ipfs /ipns

и перезапустим ipfs с флагом --mount
ipfs daemon --mount

Можно создать папки и в других местах и указать путь к ним через параметры ipfs daemon --mount --mount-ipfs /ipfs_path --mount-ipns /ipns_path

Теперь чтение из этой папки несколько необычное.
ls -la /ipfs
ls: reading directory '/ipfs': Operation not permitted
total 0

То есть прямого доступа к корню этой папки нет. Но зато можно получить содержимое, зная хеш.
ls -la /ipfs/QmbnzgRVAP4fL814h5mQttyqxxxxxxxxxxxxxxxxx
total 0
-r--r--r-- 1 root root 10 Aug 31 07:03 test.txt

cat /ipfs/QmbnzgRVAP4fL814h5mQttyqxxxxxxxxxxxxxxxxx/test.txt
test
test

При этом внутри папки даже автодополнение работает при указании пути.

Как я и говорил выше, при таком монтировании есть тонкости: по-умолчанию смонтированые FUSE-папки доступны только текущему пользователю (даже root не сможет читать из такой папки, не говоря уже про других пользователей в системе). Если вы хотите сделать эти папки доступными другим пользователям, то в конфиге надо изменить «FuseAllowOther»: false на «FuseAllowOther»: true. Но и это еще не все. Если вы IPFS запускаете от имени root, то все ОК. А если от имени обычного пользователя (пусть и sudo), то получите ошибку
mount helper error: fusermount: option allow_other only allowed if 'user_allow_other' is set in /etc/fuse.conf
В таком случае надо подправить /etc/fuse.conf, раскомментировав строчку #user_allow_other.

После этого перезапускаем ipfs.

Известные проблемы с FUSE

Не раз была замечена проблема, что после рестарта ipfs с монтированием (а может и в других случаях), точки монтирования /ipfs и /ipns становятся недоступны. Доступа к ним нет никакого, а ls -la /ipfs показывает ???? в списке прав.

Нашел такое решение:
fusermount -z -u /ipfs
fusermount -z -u /ipns

После чего рестартим ipfs.

Добавляем службу

Конечно же запуск в терминале годится только для первичных тестов. В боевом режиме демон должен запускаться автоматически при запуске системы.

От имени судо создаем файл /etc/systemd/system/ipfs.service и записываем в него:
[Unit]
Description=IPFS Daemon
After=syslog.target network.target remote-fs.target nss-lookup.target

[Service]
Type=simple
ExecStart=/home/USERNAME/work/bin/ipfs daemon --mount
User=USERNAME
Restart=always

[Install]
WantedBy=multi-user.target

USERNAME конечно же надо заменить на своего пользователя (и возможно полный путь к программе ipfs будет у вас другой (указывать надо именно полный путь)).

Активируем службу.
sudo systemctl enable ipfs.service

Запускаем службу.
sudo service ipfs start

Проверяем статус службы.
sudo service ipfs status

Для чистоты эксперимента можно будет в дальнейшем ребутнуть сервер, чтобы проверить, что ipfs успешно стартует автоматически.

Добавляем известные нам пиры

Рассмотрим ситуацию, когда у нас IPFS ноды установлены и на внешнем сервере и локально. На внешнем сервере мы добавляем какой-то файл и пытаемся его получить через IPFS локально по CID. Что будет происходить? Конечно же локальный сервер скорее всего ничего не знает о внешнем нашем сервере и будет просто пытаться найти файл по CID «спрашивая» у всех доступных ему IPFS-пиров (с которыми ему уже удалось «познакомиться»). Те в свою очередь будут у других спрашивать. И так, пока файл не будет найден. Собственно, то же самое происходит и когда мы пытаемся файл получить через официальный шлюз ipfs.io. Если повезет, то файл будет найден за несколько секунд. А если нет, то не будет найден и за несколько минут, что очень сильно бьет по комфорту работы. Но мы-то знаем где этот файл в первую очередь появится. Так почему нам сразу не сказать нашему локальному серверу «Ищи в первую очередь там»? Судя по всему, это можно сделать.

1. Заходим на удаленный сервер и в конфиге ~/.ipfs/config ищем
"Identity": {
"PeerID": "QmeCWX1DD7HnPSuMHZSh6tFuxxxxxxxxxxxxxxxx",

2. Выполняем sudo service ipfs status и ищем в нем записи Swarm, например:
Swarm announcing /ip4/ip_вашего_сервера/tcp/4001
3. Складываем из этого общий адрес вида "/ip4/ip_вашего_сервера/tcp/4001/ipfs/$PeerID".
4. Для надежности через наш локальный webui попробуем добавить этот адрес в пиры.
IPFS без боли (но это не точно) - 3
5. Если все ОК, открываем локальный конфиг ~/.ipfs/config, находим в нем «Bootstrap»: […
и дописываем первым в массив полученный адрес.
Рестартим IPFS.

Теперь добавим файл на внешний сервер и попробуем его запросить на локальном. Должен залететь быстро.

Но этот функционал пока нестабилен. Насколько я понимаю, даже если мы указываем адрес пира в Bootstrap, в процессе работы ipfs меняет список активных соединений с пирами. Во всяком случае обсуждение этого и пожелания на счет возможности указывать постоянные пиры ведется здесь и вроде как предполагается добавить какой-то функционал в ipfs@5.0+

Список текущих пиров можно посмотреть как в webui, так и в терминале.
ipfs swarm peers

И там и там можно добавить вручную свой пир.
ipfs swarm connect "/ip4/ip_вашего_сервера/tcp/4001/ipfs/$PeerID"

Пока не улучшили этот функционал, можно написать тулзу, чтобы проверяла наличие соединения с нужным пиром и если нет, чтобы добавляла соединение.

Рассуждения

Среди тех, кто уже знаком с IPFS, есть как аргументы за IPFS, так и против. В принципе, позавчерашнее обсуждение и побудило меня еще раз покопать IPFS. И вот касаемо упомянутой дискуссии: я не могу сказать, что сильно против какого-то приведенного довода высказавшихся (не согласен только с тем, что полтора программиста используют IPFS). В целом по-своему правы и те, и другие (особенно комментарий про чеки доставляет задуматься). Но если откинуть нравственную и юридическую оценку, кто какую даст техническую оценку данной технологии? Лично у меня какое-то внутреннее ощущение есть что «это надо однозначно, это имеет определенные перспективы». Но почему именно, нет четкой формулировки. Типа если посмотреть уже имеющиеся централизованные средства, то по многим параметрам они сильно впереди (стабильность работы, скорость работы, управляемости и т.п.). Тем не менее у меня есть одна мысль, которая вроде как имеет смысл и которая вряд ли может быть реализована без вот таких децентрализованных систем. Конечно, сильно уж я замахиваюсь, но я бы сформулировал ее так: принцип распространения информации в Интернете надо поменять.

Поясню. Если так подумать, то сейчас у нас информация распространяется по принципу «Я надеюсь, что тот, кому я ее передал, ее защитит и она не будет утеряна или получена тем, кому она не предназначалась». Для примера легко рассмотреть различные почтовые сервисы, облачные хранилища и т.п. И что мы имеем в итоге? На хабре хаб Информационная безопасность стоит на первой строчке и практически каждый день мы получаем новости про очередную глобальную утечку. В принципе, все самое интересное перечислено в <ирония>замечательной</ирония> статье Лето почти закончилось. Не утекших данных почти не осталось. То есть основные интернет-гиганты становятся все крупнее, аккумулируют они у себя все больше информации, и подобные утечки — это своего рода информационные атомные взрывы. Никогда такого не было, и вот опять. При этом, хотя многие понимают, что риски есть, продолжат доверять свои данные сторонним компаниям. Во-первых, альтернативы особо нет, а во-вторых, те обещают, что залатали все дыры и больше такого никогда не повторится.

Какой же мне видится вариант? Как мне кажется, данные изначально должны распространяться открыто. Но открытость в данном случае — это не значит, что все должно легко читаться. Я говорю про открытость хранения и распространения, но не тотальную открытость в чтении. Я предполагаю, что информация должна распространяться с открытыми ключами. Ведь принцип открытых/закрытых ключей уже стар, практически как Интернет. Если информация не конфиденциальная и рассчитана на широкий круг, то она выкладывается сразу с открытым ключом (но все равно в зашифрованном виде, просто ее любой может расшифровать имеющимся ключом). А если нет, то выкладывается без открытого ключа, а сам ключ передается тому, что должен иметь доступ к этой информации. При этом тот, кто должен ее прочитать, должен иметь только ключ, а где взять эту информацию, его не должно особо парить — он просто ее тянет из сети (это и есть новый принцип распространения по содержимому, а не по адресу).

Таким образом, злоумышленникам для массовой атаки надо будет получить огромное количество закрытых ключей, и вряд ли это получится сделать в одном месте. Эта задача, как мне видится, сложнее, чем взломать какой-то определенный сервис.

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

И вот что здесь интересно: IPFS уже несет в себе средства шифрования (ведь он построен на технологии блокчейн). В конфиге сразу указан приватный ключ.
"Identity": {
"PeerID": "QmeCWX1DD7HnPSuMHZSh6tFuMxxxxxxxxxxxxxx",
"PrivKey": "CAASqAkwggSkAgEAAoIBAQClZedVmj8JkPvT92sGrNIQmofVF3ne8xSWZIGqkm+t9IHNN+/NDI51jA0MRzpBviM3o/c/Nuz30wo95vWToNyWzJlyAISXnUHxnVhvpeJAbaeggQRcFxO9ujO9DH61aqgN1m+JoEplHjtc4KS5
pUEDqamve+xAJO8BWt/LgeRKA70JN4hlsRSghRqNFFwjeuBkT1kB6tZsG3YmvAXJ0o2uye+y+7LMS7jKpwJNJBiFAa/Kuyu3W6PrdOe7SqrXfjOLHQ0uX1oYfcqFIKQsBNj/Fb+GJMiciJUZaAjgHoaZrrf2b/Eii3z0i+QIVG7OypXT3Z9JUS60
KKLfjtJ0nVLjAgMBAAECggEAZqSR5sbdffNSxN2TtsXDa3hq+WwjPp/908M10QQleH/3mcKv98FmGz65zjfZyHjV5C7GPp24e6elgHr3RhGbM55vT5dQscJu7SGng0of2bnzQCEw8nGD18dZWmYJsE4rUsMT3wXxhUU4s8/Zijgq27oLyxKNr9T7
2gxqPCI06VTfMiCL1wBBUP1wHdFmD/YLJwOjV/sVzbsl9HxqzgzlDtfMn/bJodcURFI1sf1e6WO+MyTc3.................

Я не специалист по безопасности и не могу точно знать как это правильно использовать, но мне кажется, что на уровне обмена между IPFS-нодами эти ключи используются. А еще js-ipfs и такие проекты-примеры как orbit-db, на которой работает orbit.chat. То есть теоритически каждое устройство (мобильное и не только) может быть легко оснащено своими шифровально-дешифровальными машинами. В таком случае останется только каждому позаботиться о сохранении своих приватных ключей и каждый сам будет ответственнен за свою безопасность, а не быть заложником очередного человеческого фактора на каком-нибудь супер-популярном интернет-гиганте.

Автор: Ланец Николай

Источник


* - обязательные к заполнению поля