- PVSM.RU - https://www.pvsm.ru -
Мало смысла в IPFS, если использовать его только как бесплатный
Пользователю это даст быстрый доступ к его локальной копии нашего сайта.
Также мы научимся переключать на локальный шлюз IPFS сайты, которые этого ещё не делают.
Напомню: InterPlanetary File System — это новая децентрализованная сеть обмена файлами (HTTP-сервер, Content Delivery Network [2]). О ней я рассказывал в статье "Межпланетная файловая система IPFS" [3].
У нашего сайта на данный момент уже есть как минимум 3 DNS записи:
A [Наш домен] [IP адрес хостинга]
TXT [Наш домен] dnslink=/ipfs/[CID контента]
TXT _dnslink.[Наш домен] dnslink=/ipfs/[CID контента]
Добавим к ним ещё 3:
A l.[Наш домен] 127.0.0.1
TXT l.[Наш домен] dnslink=/ipfs/[CID контента]
TXT _dnslink.l.[Наш домен] dnslink=/ipfs/[CID контента]
[CID контента] — Это идентификатор контента (CID) [11] раньше назывался мультихеш. Его мы получаем публикуя сайт [12] командой ipfs add
в сети IPFS.
В HTML тегах script и link появились поля integrity и crossorigin [13]. Они отвечают за проверку хеша до запуска скрипта или применения стилей. Их мы и используем для определения рабочего шлюза у посетителя сайта.
<script src="[адрес скрипта для проверки]"
integrity="sha384-[sha384 в base64]"
crossorigin="anonymous" defer async>
</script>
<link rel="stylesheet" href="[адрес стиля для проверки]"
integrity="sha384-[sha384 в base64]"
crossorigin="anonymous"/>
Расположить их лучше в конце страницы, чтобы они не задерживали загрузку и отображение.
Варианты адресов, которые нам надо проверить:
http://l.[наш домен]:8080
8080 это стандартный порт на котором по умолчанию запускается IPFS.
Если всё настроено правильно, то с http-версии сайта браузер загрузит скрипт или стиль.
https://l.[наш домен]:8443
8443 это порт на который пользователь может настроить stunnel.
Данный вариант нам понадобится, если запрос идёт с HTTPS сайта и наш домен добавлен в локальный сертификат.
http://127.0.0.1:8080/ipns/[наш домен]
Этот вариант на случай, если мы не задали l
домен для нашего сайта и запрос идёт с http.
https://127.0.0.1:8443/ipns/[наш домен]
l
домен или не добавлен в локальный сертификат.Аналогичным образом мы можем проверить порты 80 для http и 443 для https.
Добавив этот скрипт к странице вашего сайта вы автоматически переключите посетителя на его локальный IPFS шлюз.
<script src="redirect_to_ipfs_gateway.js" defer async ></script>
redirect_to_ipfs_gateway.js
var redirect_to_local;
/*
Эта функция добавляет к текущему домену третьим уровнем домен ```l```
*/
function l_hostname()
{
var l_hostname = window.location.hostname.split(".");
l_hostname.splice(-2,0,"l");
return l_hostname.join(".");
}
/*
Эта функция создаёт новый элемент script и с адресом скрипта, который должен загрузиться через локальный шлюз пользователя.
Также она создаёт функцию, которая будет вызвана этим скриптом в случае удачной загрузки.
В случае неудачи выполнится функция из переменной onerror, которая присваивается соответствующему полю элемента script.
*/
function add_redirect_script(prtocol, port, use_ip, onerror){
var script = document.createElement("script");
script.onerror = onerror;
script.setAttribute("integrity", "sha384-dActyNwOxNY9fpUWleNW9Nuy3Suv8Dx3F2Tbj1QTZWUslB1h23+xUPillTDxprx7");
script.setAttribute("crossorigin", "anonymous");
script.setAttribute("defer", "");
script.setAttribute("async", "");
if ( use_ip )
script.setAttribute("src", prtocol+"//127.0.0.1:"+port+"/ipns/"+window.location.hostname+"/redirect_call.js");
else
script.setAttribute("src", prtocol+"//"+l_hostname()+":"+port+"/redirect_call.js");
redirect_to_local = function()
{
var a = document.createElement("a");
a.href = window.location;
a.protocol = prtocol;
a.port = port;
if ( use_ip ){
a.pathname = "/ipns/" + a.hostname + a.pathname;
a.hostname = "127.0.0.1";
}else{
var hostname = a.hostname.split(".");
hostname.splice(-2,0,"l");
a.hostname = hostname.join(".");
}
window.location = a.href;
};
document.head.appendChild(script);
}
/*
Это главная функция, которая запускается сразу. Она проверяет, не находимся ли мы уже по адресу шлюза. Если нет, то начинает проверять его доступность, перебирая варианты адресов и протоколов.
*/
!function(location){
if ( location.protocol.indexOf("http") == 0 &&
location.hostname.length > 0 &&
location.hostname.indexOf("l.") != 0 &&
location.hostname.indexOf(".l.") < 0 &&
location.hostname != "127.0.0.1" )
{
add_redirect_script( "http:", 8080, false,
function(){
add_redirect_script( "https:", 8443, false,
function(){
add_redirect_script( "http:", 8080, true,
function(){
add_redirect_script( "https:", 8443, true );
} );
} );
} );
}
}(window.location)
В пару ему идет скрипт:
redirect_call.js (sha384-dActyNwOxNY9fpUWleNW9Nuy3Suv8Dx3F2Tbj1QTZWUslB1h23+xUPillTDxprx7)
redirect_to_local();
Integrity этого скрипта можно посчитать командой:
openssl dgst -sha384 -binary < "redirect_call.js" | openssl enc -base64 -A
У меня соответственно результат этой команды:
dActyNwOxNY9fpUWleNW9Nuy3Suv8Dx3F2Tbj1QTZWUslB1h23+xUPillTDxprx7
Если у вас результат другой, замените это значение в скрипте выше на своё.
Теперь пользователь будет автоматически перенаправлен на подходящий локальный адрес шлюза с сохранением остальных параметров адреса.
Создадим CSS файл который будет маяком работы шлюза.
httpl.css (sha384-9LLp4PYTHwNvd5whc7IOL6JLDJ4aoPufAFts3rMLZOg5b//BLQZTfe7krAzWAm+a)
.httpl{display: block;}
Скрываем элементы страницы, которые будут показаны только при доступности локального шлюза.
<style>
.gatewaylink{display: none}
</style>
Добавляем CSS маяк в конце страницы
<link rel="stylesheet" href="http://l.[наш домен]:8080/httpl.css"
integrity="sha384-9LLp4PYTHwNvd5whc7IOL6JLDJ4aoPufAFts3rMLZOg5b//BLQZTfe7krAzWAm+a"
crossorigin="anonymous" />
<div class="gatewaylink httpl">
Use your gateway: <a href="http://l.[наш домен]:8080/">http://l.[наш домен]:8080/</a>
</div>
Теперь даже если у пользователя отключены скрипты, он сможет перейти на шлюз самостоятельно по ссылке. Аналогично можно проверить и остальные варианты адреса шлюза.
Возьмём, к примеру, глобальный IPFS шлюз gateway.ipfs.io и перенаправим этот адрес на наш локальный IPFS шлюз.
Условие: У нас уже установлен и работает на стандартном порту 8080 IPFS шлюз.
В файл hosts [14] добавляем домен который хотим загружать с IPFS шлюза.
127.0.0.1 gateway.ipfs.io
Устанавливаем и настраиваем Stunnel [15].
stunnel.conf:
; Открываем дополнительный защищённый порт шлюза для того, чтобы сайты могли сами на него переключиться
[https gateway]
accept = 127.0.0.1:8443
connect = 127.0.0.1:8080
cert = stunnel.pem
TIMEOUTclose = 0
; Открываем стандартный порт 443 для HTTPS
[https]
accept = 127.0.0.1:443
connect = 127.0.0.1:8080
cert = stunnel.pem
TIMEOUTclose = 0
; Открываем стандартный порт 80 для HTTP
[http]
client = yes
accept = 127.0.0.1:80
connect = 127.0.0.1:443
Таким образом мы открываем 3 дополнительных порта (433, 8443, 80), которые подключают клиента к шлюзу IPFS.
Создаём сертификаты и ключи.
3.1. В директорию c конфигом копируем makecert.cmd
echo off
%~d0
cd %~p0
set STUNNELBIN = ..bin
set PATH=%STUNNELBIN%;%PATH%;
rem // Первый вызов openssl создаст ключ и корневой сертификат в формате PEM
rem // openssl попросит пользователя задать пароль, которым будет защищён ключ и при каждой новой подписи сертификата шлюза этот пароль потребуется
rem // Второй вызов openssl конвертирует сертификат из PEM в DER формат понятный Windows
rem // Корневой сертификат в PEM формате понадобится для Firefox
if not exist "rootkey.pem" (
echo [ req ] >openssl.root.cnf
echo distinguished_name = req_distinguished_name >>openssl.root.cnf
echo [v3_ca] >>openssl.root.cnf
echo subjectKeyIdentifier = hash >>openssl.root.cnf
echo authorityKeyIdentifier = keyid:always,issuer:always >>openssl.root.cnf
echo basicConstraints = critical, CA:TRUE >>openssl.root.cnf
echo keyUsage = keyCertSign, cRLSign >>openssl.root.cnf
echo [ req_distinguished_name ] >>openssl.root.cnf
openssl.exe req -newkey rsa:4096 -x509 -sha256 -days 5480 -config openssl.root.cnf -extensions v3_ca -utf8 -subj "/CN=127.0.0.1" -out rootcert.pem -keyout rootkey.pem
openssl.exe x509 -outform der -in rootcert.pem -out rootcert.crt
del openssl.root.cnf
)
rem // Теперь создаём ключ который будет использоваться шлюзом
if not exist "gatewaykey.pem" (
openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -out gatewaykey.pem
)
rem // Делаем запрос сертификата шлюза
if not exist "gateway.csr" (
echo [ req ] >openssl.req.cnf
echo req_extensions = v3_req >>openssl.req.cnf
echo distinguished_name = req_distinguished_name >>openssl.req.cnf
echo [ req_distinguished_name ] >>openssl.req.cnf
echo [ v3_req ] >>openssl.req.cnf
echo basicConstraints = CA:FALSE >>openssl.req.cnf
echo keyUsage = nonRepudiation, digitalSignature, keyEncipherment >>openssl.req.cnf
openssl req -new -key gatewaykey.pem -days 1096 -batch -utf8 -subj "/CN=127.0.0.1" -config openssl.req.cnf -out gateway.csr
del openssl.req.cnf
)
rem // Если это не первое выполнение данного скрипта, то в index.txt может храниться индекс следующей DNS записи.
if exist "index.txt" (
set /p index=<index.txt
)
if not exist "index.txt" (
set index=2
)
rem // Мы создаём openssl.cnf один раз и в дальнейшем дополняем его новыми доменами.
if not exist "openssl.cnf" (
echo basicConstraints = CA:FALSE >openssl.cnf
echo extendedKeyUsage = serverAuth >>openssl.cnf
echo subjectAltName=@alt_names >>openssl.cnf
echo [alt_names] >>openssl.cnf
echo IP.1 = 127.0.0.1 >>openssl.cnf
echo DNS.1 = localhost >>openssl.cnf
set index=2
del "index.txt"
)
rem // В цикле добавляем в openssl.cnf домены, которые заданы в командной строке либо будут введены пользователем.
:NEXT
set /a aindex=%index% + 1
set /a bindex=%index% + 2
set domain=%1
if !%domain% == ! (
set /p domain=enter domain name or space:
)
if not !%domain% == ! (
echo DNS.%index% = %domain% >>openssl.cnf
echo DNS.%aindex% = *.%domain% >>openssl.cnf
echo %bindex% >index.txt
set index=%bindex%
shift
goto NEXT
)
del gateway.pem
rem // Создаём сертификат IPFS шлюза
openssl x509 -req -sha256 -days 1096 -in gateway.csr -CAkey rootkey.pem -CA rootcert.pem -set_serial %RANDOM%%RANDOM%%RANDOM%%RANDOM% -extfile openssl.cnf -out gateway.pem
rem // Записываем ключ и сертификат в stunnel.pem, который по умолчанию используется программой stunnel
copy /b gateway.pem+gatewaykey.pem stunnel.pem
rem // Даём пользователю прочитать ошибки или информацию
pause
3.2. Запускаем
makecert.cmd ipfs.io
При первом запуске данного скрипта будет создан корневой сертификат (rootcert.pem для firefox и rootcert.crt для остальных) — ключ, которому надо задать пароль. Корневой сертификат надо добавить в хранилище доверенных корневых сертификатов в браузере и операционной системе.
Далее автоматически будет создан сертификат для шлюз, а которому надо задать домены, которые он будет обслуживать.
Перезапускаем stunnel
reload.cmd
echo off
%~d0
cd %~p0
set STUNNELBIN = ..bin
set PATH=%STUNNELBIN%;%PATH%;
stunnel -install -quiet
stunnel -start -quiet
stunnel -reload -quiet
Теперь gateway.ipfs.io будет работать на локальном шлюзе. Аналогично можно поступить с любым сайтом, который размещён в IPFS.
Сайт для теста: ivan386.tk [16]
Источники:
Другие мои статьи о "межпланетной файловой системе":
Автор: ivan386
Источник [22]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/dns-2/261487
Ссылки в тексте:
[1] хостинг: https://www.reg.ru/?rlink=reflink-717
[2] Content Delivery Network: https://ru.wikipedia.org/wiki/Content_Delivery_Network
[3] "Межпланетная файловая система IPFS": https://habrahabr.ru/post/314768/
[4] Переключаем наш сайт: https://www.pvsm.ru/post/334584/#pereklyuchaem-nash-sayt
[5] DNS: https://www.pvsm.ru/post/334584/#dns
[6] Скрипты и стили: https://www.pvsm.ru/post/334584/#skripty-i-stili
[7] Проверяем локальный шлюз и переключаемся на него скриптом: https://www.pvsm.ru/post/334584/#proveryaem-lokalnyy-shlyuz-i-pereklyuchaemsya-na-nego-skriptom
[8] Определяем рабочий шлюз при помощи CSS: https://www.pvsm.ru/post/334584/#opredelyaem-rabochiy-shlyuz-pri-pomoschi-css
[9] Локализуем глобальный шлюз или сайты в IPFS: https://www.pvsm.ru/post/334584/#lokalizuem-globalnyy-shlyuz-ili-sayty-v-ipfs
[10] Image: https://habrahabr.ru/post/334584/
[11] идентификатор контента (CID): https://habrahabr.ru/post/331010/#identifikator-kontenta-cid
[12] публикуя сайт: https://habrahabr.ru/post/316468/
[13] integrity и crossorigin: https://developer.mozilla.org/en-US/docs/Web/Security/Subresource_Integrity
[14] hosts: https://ru.wikipedia.org/wiki/Hosts
[15] Stunnel: https://en.wikipedia.org/wiki/Stunnel
[16] ivan386.tk: http://ivan386.tk
[17] Своё Certificate Authority — в 5 OpenSSL команд: https://habrahabr.ru/post/192446/
[18] A Web PKI x509 certificate primer: https://developer.mozilla.org/en-US/docs/Mozilla/Security/x509_Certificates
[19] stunnel TLS Proxy: https://www.stunnel.org/static/stunnel.html
[20] Хостим сайт в межпланетной файловой системе IPFS под Windows: https://habrahabr.ru/post/325176/
[21] Больше нет необходимости копировать в сеть: https://habrahabr.ru/post/331010/
[22] Источник: https://habrahabr.ru/post/334584/?utm_source=habrahabr&utm_medium=rss&utm_campaign=best
Нажмите здесь для печати.