Pehea mākou ma ZeroTech i hoʻopili ai iā Apple Safari a me nā palapala hōʻoia o nā mea kūʻai aku me nā websockets

Pono ka ʻatikala i ka poʻe:

  • ʻike i ke ʻano o ka Client Cert a maopopo i ke kumu e pono ai ia i nā websockets ma Safari mobile;
  • Makemake au e hoʻolaha i nā lawelawe pūnaewele i kahi pōʻai palena o nā kānaka a i ʻole iaʻu wale nō;
  • ke manaʻo nei ua hana ʻia nā mea a pau e kekahi, a makemake ʻo ia e hoʻolilo i ka honua i mea maʻalahi a palekana.

Ua hoʻomaka ka mōʻaukala o nā websockets ma kahi o 8 mau makahiki i hala. Ma mua, ua hoʻohanaʻia nāʻano ma keʻano o nā noi http lōʻihi (nā pane maoli): ua hoʻouna ka mea hoʻohana i kahi noi i ke kikowaena a kali iā ia e pane i kekahi mea, ma hope o ka paneʻana ua pili hou a kali. Akā, ua puka mai nā pūnaewele pūnaewele.

Pehea mākou ma ZeroTech i hoʻopili ai iā Apple Safari a me nā palapala hōʻoia o nā mea kūʻai aku me nā websockets

I kekahi mau makahiki i hala aku nei, ua hoʻomohala mākou i kā mākou hoʻokō ponoʻī i ka PHP maʻemaʻe, ʻaʻole hiki ke hoʻohana i nā noi https, no ka mea ʻo kēia ka loulou loulou. ʻAʻole i liʻuliʻu i hala, kokoke i nā kikowaena pūnaewele āpau i aʻo i nā noi proxy ma luna o https a me ke kākoʻo pili: hoʻonui.

I ka wā i loaʻa ai kēia, ua lilo nā websockets i ka lawelawe paʻamau no nā noi SPA, no ka mea, pehea ka maʻalahi o ka hāʻawi ʻana i ka ʻike i ka mea hoʻohana ma ka hoʻomaka ʻana o ka server (hoʻouna i kahi leka mai kahi mea hoʻohana ʻē aʻe a hoʻoiho i kahi mana hou o kahi kiʻi, palapala, hōʻike. ke hoʻoponopono nei kekahi i kēia manawa).

ʻOiai ua puni ʻo Client Certificate no ka manawa lōʻihi, ʻaʻole i kākoʻo maikaʻi ʻia, no ka mea he nui nā pilikia i ka wā e hoʻāʻo ai e kāpae. A (malia paha :slightly_smiling_face: ) ʻo ia ke kumu ʻaʻole makemake nā mea hoʻokele IOS (koe wale ʻo Safari) e hoʻohana a noi mai ia mai ka hale kūʻai palapala kūloko. He nui nā pono o nā palapala hōʻoia i ka hoʻohālikelike ʻana i ka login/pass a i ʻole ssh kī a i ʻole ke pani ʻana i nā awa kūpono ma o kahi pā ahi. Akā ʻaʻole ia ka mea e pili ana i kēia.

Ma iOS, maʻalahi ke kaʻina hana no ka hoʻokomo ʻana i kahi palapala hōʻoia (ʻaʻole me ka ʻole o nā kikoʻī), akā ma ke ʻano maʻamau e hana ʻia e like me nā ʻōlelo aʻoaʻo, kahi nui ma ka Pūnaewele a loaʻa wale no ka polokalamu Safari. ʻO ka mea pōʻino, ʻaʻole ʻike ʻo Safari i ka hoʻohana ʻana iā Client Сert no nā kumu pūnaewele, akā nui nā ʻōlelo aʻoaʻo ma ka Pūnaewele e pili ana i ka hana ʻana i kahi palapala hōʻoia, akā ma ka hoʻomaʻamaʻa ʻaʻole hiki ke loaʻa kēia.

Pehea mākou ma ZeroTech i hoʻopili ai iā Apple Safari a me nā palapala hōʻoia o nā mea kūʻai aku me nā websockets

No ka hoʻomaopopo ʻana i nā websockets, ua hoʻohana mākou i kēia hoʻolālā: pilikia / kuhiakau / hoʻonā.

ʻO ka pilikia: ʻaʻohe kākoʻo no nā kumu punaewele i ka wā e hoʻololi ai i nā noi i nā kumuwaiwai i pale ʻia e ka palapala mea kūʻai aku ma ka polokalamu kele pūnaewele Safari no IOS a me nā noi ʻē aʻe i hiki ai ke kākoʻo palapala.

Kuhiakau:

  1. Hiki ke hoʻonohonoho i kēlā ʻokoʻa no ka hoʻohana ʻana i nā palapala hōʻoia (me ka ʻike ʻaʻole e loaʻa kekahi) i nā punawelewele o nā kumuwaiwai kūloko / waho.
  2. No nā websockets, hiki iā ʻoe ke hana i kahi pilina kūʻokoʻa, paʻa a pale ʻia me ka hoʻohana ʻana i nā manawa pōkole i hana ʻia i ka wā o kahi noi polokalamu maʻamau (non-websocket).
  3. Hiki ke hoʻokō ʻia nā kau manawa me ka hoʻohana ʻana i hoʻokahi kikowaena pūnaewele proxy (nā modules i kūkulu ʻia a me nā hana wale nō).
  4. Ua hoʻokō ʻia nā hōʻailona kau manawa e like me nā modula Apache i mākaukau.
  5. Hiki ke hoʻokō ʻia nā hōʻailona kau manawa ma ka hoʻolālā ʻana i ka hoʻolālā pili.

ʻIke ʻia ma hope o ka hoʻokō ʻana.

Pahuhopu o ka hana: pono e ʻike ʻia ka mālama ʻana i nā lawelawe a me nā ʻōnaehana mai ke kelepona paʻa ma IOS me ka ʻole o nā polokalamu hou (e like me VPN), i hui pū ʻia a paʻa.

Pahuhopu hou: ka mālama ʻana i ka manawa a me nā kumuwaiwai/kelepona (kekahi mau lawelawe me ka ʻole o nā kumu punaewele e hoʻopuka i nā noi pono ʻole) me ka wikiwiki o ka lawe ʻana i nā ʻike ma ka pūnaewele paʻa.

Pehea e nānā ai?

1. Nā ʻaoʻao wehe:

— например, https://teamcity.yourdomain.com в мобильном браузере Safari (доступен также в десктопной версии) — вызывает успешное подключение к веб-сокетам.
— например, https://teamcity.yourdomain.com/admin/admin.html?item=diagnostics&tab=webS…— показывает ping/pong.
— например, https://rancher.yourdomain.com/p/c-84bnv:p-vkszd/workload/deployment:danidb:ph…-> viewlogs — показывает логи контейнера.

2. A i ʻole ma ka console developer:

Pehea mākou ma ZeroTech i hoʻopili ai iā Apple Safari a me nā palapala hōʻoia o nā mea kūʻai aku me nā websockets

Hoʻāʻo kuhiakau:

1. Hiki ke hoʻonohonoho i kēlā ʻokoʻa no ka hoʻohana ʻana i nā palapala hōʻoia (me ka ʻike ʻaʻole ʻaʻohe) i nā kumu punaewele o nā kumuwaiwai kūloko / waho.

Ua loaʻa nā haʻina 2 ma aneʻi:

a) Ma ka pae

<Location sock*> SSLVerifyClient optional </Location>
<Location /> SSLVerifyClient require </Location>

hoʻololi i ka pae komo.

Loaʻa i kēia ʻano nā nuances:

  • Loaʻa ka hōʻoia hōʻoia ma hope o ke noi ʻana i ke kumuwaiwai i hoʻopili ʻia, ʻo ia hoʻi, hoʻopaʻa lima lima. ʻO ia ke kumu e hoʻouka mua ai ka mea koho a laila ʻoki i ka noi i ka lawelawe mālama ʻia. He ʻino kēia, akā ʻaʻole koʻikoʻi;
  • Ma ka protocol http2. Aia ia i loko o ke kikoo, a ʻaʻole ʻike nā mea hana polokalamu kele i ka hoʻokō ʻana #info e pili ana i ka tls1.3 http2 post handshake (ʻaʻole hana i kēia manawa) E hoʻokō i ka RFC 8740 "E hoʻohana ana i ka TLS 1.3 me HTTP/2";
  • ʻAʻole maopopo pehea e hoʻohui ai i kēia hana.

b) Ma kahi pae kumu, e ʻae iā ssl me ka ʻole o kahi palapala.

Pono ʻo SSLVerifyClient => SSLVerifyClient koho, akā hoʻemi kēia i ka pae palekana o ke kikowaena proxy, no ka mea, e hoʻopili ʻia kēlā pilina me ka ʻole o kahi palapala. Eia nō naʻe, hiki iā ʻoe ke hōʻole hou i ke komo ʻana i nā lawelawe proxied me kēia kuhikuhi aʻe:

RewriteEngine        on
RewriteCond     %{SSL:SSL_CLIENT_VERIFY} !=SUCCESS
RewriteRule     .? - [F]
ErrorDocument 403 "You need a client side certificate issued by CAcert to access this site"

Hiki ke loaʻa ka ʻike kikoʻī hou aku ma ka ʻatikala e pili ana i ssl: ʻO Apache Server Client Certificate Authentication

Ua hoʻāʻo ʻia nā koho ʻelua, koho ʻia ke koho "b" no kāna versatility a me ka hoʻohālikelike ʻana me ka protocol http2.

No ka hoʻopau ʻana i ka hōʻoia o kēia kuhiakau, ua nui nā hoʻokolohua me ka hoʻonohonoho;

inā = koi = kākau hou

ʻO ka hopena ka hoʻolālā kumu:

SSLVerifyClient optional
RewriteEngine on
RewriteCond %{SSL:SSL_CLIENT_VERIFY} !=SUCCESS
RewriteCond %{HTTP:Upgrade} !=websocket [NC]
RewriteRule     .? - [F]
#ErrorDocument 403 "You need a client side certificate issued by CAcert to access this site"

#websocket for safari without cert auth
<If "%{SSL:SSL_CLIENT_VERIFY} != 'SUCCESS'">
<If "%{HTTP:Upgrade} = 'websocket'">
...
    #замещаем авторизацию по владельцу сертификата на авторизацию по номеру протокола
    SSLUserName SSl_PROTOCOL
</If>
</If>

I ka noʻonoʻo ʻana i ka ʻae ʻia e ka mea nona ka palapala hōʻoia, akā me kahi palapala e nalowale ana, pono wau e hoʻohui i kahi mea nona ka palapala i loaʻa ʻole ma ke ʻano o kekahi o nā mea hoʻololi i loaʻa SSl_PROTOCOL (ma kahi o SSL_CLIENT_S_DN_CN), nā kikoʻī hou aku i ka palapala:

Apache Module mod_ssl

Pehea mākou ma ZeroTech i hoʻopili ai iā Apple Safari a me nā palapala hōʻoia o nā mea kūʻai aku me nā websockets

2. No nā websockets, hiki iā ʻoe ke hana i kahi pilina kūʻokoʻa, paʻa a hoʻopaʻa ʻia me ka hoʻohana ʻana i nā manawa pōkole i hana ʻia i ka wā o kahi noi polokalamu maʻamau (non-websocket).

Ma muli o ka ʻike ma mua, pono ʻoe e hoʻohui i kahi ʻāpana ʻē aʻe i ka hoʻonohonoho ʻana i mea e hoʻomākaukau ai i nā hōʻailona pōkole no nā pilina pūnaewele i ka wā o kahi noi maʻamau (non-web socket).

#подготовка передача себе Сookie через пользовательский браузер
<If "%{SSL:SSL_CLIENT_VERIFY} = 'SUCCESS'">
<If "%{HTTP:Upgrade} != 'websocket'">
Header set Set-Cookie "websocket-allowed=true; path=/; Max-Age=100"
</If>
</If>

#проверка Cookie для установления веб-сокет соединения
<source lang="javascript">
<If "%{SSL:SSL_CLIENT_VERIFY} != 'SUCCESS'">
<If "%{HTTP:Upgrade} = 'websocket'">
#check for exists cookie

#get and check
SetEnvIf Cookie "websocket-allowed=(.*)" env-var-name=$1

#or rewrite rule
RewriteCond %{HTTP_COOKIE} !^.*mycookie.*$

#or if
<If "%{HTTP_COOKIE} =~ /(^|; )cookie-names*=s*some-val(;|$)/ >
</If

</If>
</If>

Ua hōʻike ʻia ka hoʻāʻo ʻana. Hiki ke hoʻoili i nā Kuki iā ʻoe iho ma o ka polokalamu kele o ka mea hoʻohana.

3. Hiki ke hoʻokō ʻia nā kau manawa me ka hoʻohana ʻana i hoʻokahi kikowaena pūnaewele proxy (nā modules i kūkulu ʻia a me nā hana wale nō).

E like me kā mākou i ʻike ai ma mua, he nui nā hana koʻikoʻi ʻo Apache e hiki ai iā ʻoe ke hana i nā kūkulu kūlana. Eia nō naʻe, pono mākou i mea e pale ai i kā mākou ʻike ʻoiai aia ma ka polokalamu kele o ka mea hoʻohana, no laila mākou e hoʻokumu i ka mea e mālama ai a no ke aha, a me nā hana i kūkulu ʻia e hoʻohana ai mākou:

  • Pono mākou i kahi hōʻailona hiki ʻole ke hoʻololi maʻalahi.
  • Pono mākou i kahi hōʻailona i kūkulu ʻia ka obsolescence a me ka hiki ke nānā i ka obsolescence ma ke kikowaena.
  • Pono mākou i kahi hōʻailona e pili pū me ka mea nona ka palapala.

Pono kēia i kahi hana hashing, kahi paʻakai, a me kahi lā i ka makahiki o ka hōʻailona. Ma muli o ka palapala Nā ʻōlelo ma Apache HTTP Server Loaʻa iā mākou nā mea a pau ma waho o ka pahu sha1 a me %{TIME}.

ʻO ka hopena kēia hoʻolālā:

#нет сертификата, и обращение к websocket
<If "%{SSL:SSL_CLIENT_VERIFY} != 'SUCCESS'">
<If "%{HTTP:Upgrade} = 'websocket'">
    SetEnvIf Cookie "zt-cert-sha1=([^;]+)" zt-cert-sha1=$1
    SetEnvIf Cookie "zt-cert-uid=([^;]+)" zt-cert-uid=$1
    SetEnvIf Cookie "zt-cert-date=([^;]+)" zt-cert-date=$1

#только так можно работать с переменными, полученными в env-ах в этот момент времени, более они нигде не доступны для функции хеширования (по отдельности можно, но не вместе, да и ещё с хешированием)
    <RequireAll>
        Require expr %{sha1:salt1%{env:zt-cert-date}salt3%{env:zt-cert-uid}salt2} == %{env:zt-cert-sha1}
        Require expr %{env:zt-cert-sha1} =~ /^.{40}$/
    </RequireAll>
</If>
</If>

#есть сертификат, запрашивается не websocket
<If "%{SSL:SSL_CLIENT_VERIFY} = 'SUCCESS'">
<If "%{HTTP:Upgrade} != 'websocket'">
    SetEnvIf Cookie "zt-cert-sha1=([^;]+)" HAVE_zt-cert-sha1=$1

    SetEnv zt_cert "path=/; HttpOnly;Secure;SameSite=Strict"
#Новые куки ставятся, если старых нет
    Header add Set-Cookie "expr=zt-cert-sha1=%{sha1:salt1%{TIME}salt3%{SSL_CLIENT_S_DN_CN}salt2};%{env:zt_cert}" env=!HAVE_zt-cert-sha1
    Header add Set-Cookie "expr=zt-cert-uid=%{SSL_CLIENT_S_DN_CN};%{env:zt_cert}" env=!HAVE_zt-cert-sha1
    Header add Set-Cookie "expr=zt-cert-date=%{TIME};%{env:zt_cert}" env=!HAVE_zt-cert-sha1
</If>
</If>

Ua hoʻokō ʻia ka pahuhopu, akā aia nā pilikia me ka obsolescence server (hiki iā ʻoe ke hoʻohana i kahi Kuki makahiki makahiki), ʻo ia hoʻi ʻo nā hōʻailona, ​​ʻoiai he palekana no ka hoʻohana ʻana i loko, ʻaʻole palekana no ka hoʻohana ʻana i ka ʻoihana (nui).

Pehea mākou ma ZeroTech i hoʻopili ai iā Apple Safari a me nā palapala hōʻoia o nā mea kūʻai aku me nā websockets

4. Ua hoʻokō ʻia nā hōʻailona kau manawa ma ke ʻano he mau modula Apache i mākaukau.

Hoʻokahi pilikia koʻikoʻi i koe mai ka hoʻomaʻamaʻa mua - ʻo ka hiki ʻole ke kāohi i ka ʻelemakule hōʻailona.

Ke ʻimi nei mākou i kahi module i mākaukau e hana i kēia, e like me nā huaʻōlelo: apache token json two factor auth

ʻAe, aia nā modula i hoʻomākaukau ʻia, akā pili lākou a pau i nā hana kikoʻī a loaʻa nā artifacts i ke ʻano o ka hoʻomaka ʻana i kahi hālāwai a me nā Kuki hou. ʻO ia hoʻi, ʻaʻole no kekahi manawa.
He ʻelima mau hola mākou e ʻimi ai, ʻaʻole i hāʻawi i kahi hopena paʻa.

5. Hiki ke hoʻokō ʻia nā hōʻailona kau manawa ma ka hoʻolālā ʻana i ke ʻano o nā pilina.

He paʻakikī loa nā modula i hoʻomākaukau ʻia, no ka mea pono mākou i ʻelua mau hana.

ʻO ka mea i ʻōlelo ʻia, ʻo ka pilikia me ka lā ʻaʻole ʻae nā hana i kūkulu ʻia e Apache i ka hana ʻana i kahi lā mai ka wā e hiki mai ana, a ʻaʻohe hoʻohui / unuhi makemakika i loko o nā hana i kūkulu ʻia i ka wā e nānā ai no ka obsolescence.

ʻO ia hoʻi, ʻaʻole hiki iā ʻoe ke kākau:

(%{env:zt-cert-date} + 30) > %{DATE}

Hiki iā ʻoe ke hoʻohālikelike i ʻelua mau helu.

ʻOiai e ʻimi ana i kahi hoʻoponopono no ka pilikia Safari, loaʻa iaʻu kahi ʻatikala hoihoi: Mālama i ka HomeAssistant me nā palapala hōʻoia o ka mea kūʻai aku (hana me Safari/iOS)
Hōʻike ia i kahi hiʻohiʻona o ke code ma Lua no Nginx, a ʻo ia hoʻi, e like me ka mea i ʻike ʻia, e hoʻihoʻi hou i ka loiloi o kēlā ʻāpana o ka hoʻonohonoho ʻana a mākou i hoʻokō ai, koe wale nō ka hoʻohana ʻana i ke ʻano hmac salting no ka hashing ( ʻAʻole i loaʻa kēia ma Apache).

Ua maopopo he ʻōlelo ʻo Lua me ka noʻonoʻo maopopo, a hiki ke hana i kahi mea maʻalahi no Apache:

Ke aʻo ʻana i ka ʻokoʻa me Nginx a me Apache:

A loaʻa nā hana mai ka mea hana ʻōlelo Lua:
22.1 – La a me ka manawa

Ua loaʻa iā mākou kahi ala e hoʻonohonoho ai i nā mea hoʻololi env i kahi faila Lua liʻiliʻi i mea e hoʻonohonoho ai i kahi lā mai ka wā e hiki mai ana e hoʻohālikelike me ka mea o kēia manawa.

ʻO kēia ke ʻano o kahi palapala Lua maʻalahi:

require 'apache2'

function handler(r)
    local fmt = '%Y%m%d%H%M%S'
    local timeout = 3600 -- 1 hour

    r.notes['zt-cert-timeout'] = timeout
    r.notes['zt-cert-date-next'] = os.date(fmt,os.time()+timeout)
    r.notes['zt-cert-date-halfnext'] = os.date(fmt,os.time()+ (timeout/2))
    r.notes['zt-cert-date-now'] = os.date(fmt,os.time())

    return apache2.OK
end

A penei ka hana a pau, me ka hoʻonui ʻana i ka helu o nā Kuki a me ka hoʻololi ʻana i ka hōʻailona ke hiki mai ka hapalua o ka manawa ma mua o ka pau ʻana o ka Kuki kahiko (token):

SSLVerifyClient optional

#LuaScope thread
#generate event variables zt-cert-date-next
LuaHookAccessChecker /usr/local/etc/apache24/sslincludes/websocket_token.lua handler early

#запрещаем без сертификата что-то ещё, кроме webscoket
RewriteEngine on
RewriteCond %{SSL:SSL_CLIENT_VERIFY} !=SUCCESS
RewriteCond %{HTTP:Upgrade} !=websocket [NC]
RewriteRule     .? - [F]
#ErrorDocument 403 "You need a client side certificate issued by CAcert to access this site"

#websocket for safari without certauth
<If "%{SSL:SSL_CLIENT_VERIFY} != 'SUCCESS'">
<If "%{HTTP:Upgrade} = 'websocket'">
    SetEnvIf Cookie "zt-cert=([^,;]+),([^,;]+),[^,;]+,([^,;]+)" zt-cert-sha1=$1 zt-cert-date=$2 zt-cert-uid=$3

    <RequireAll>
        Require expr %{sha1:salt1%{env:zt-cert-date}salt3%{env:zt-cert-uid}salt2} == %{env:zt-cert-sha1}
        Require expr %{env:zt-cert-sha1} =~ /^.{40}$/
        Require expr %{env:zt-cert-date} -ge %{env:zt-cert-date-now}
    </RequireAll>
   
    #замещаем авторизацию по владельцу сертификата на авторизацию по номеру протокола
    SSLUserName SSl_PROTOCOL
    SSLOptions -FakeBasicAuth
</If>
</If>

<If "%{SSL:SSL_CLIENT_VERIFY} = 'SUCCESS'">
<If "%{HTTP:Upgrade} != 'websocket'">
    SetEnvIf Cookie "zt-cert=([^,;]+),[^,;]+,([^,;]+)" HAVE_zt-cert-sha1=$1 HAVE_zt-cert-date-halfnow=$2
    SetEnvIfExpr "env('HAVE_zt-cert-date-halfnow') -ge %{TIME} && env('HAVE_zt-cert-sha1')=~/.{40}/" HAVE_zt-cert-sha1-found=1

    Define zt-cert "path=/;Max-Age=%{env:zt-cert-timeout};HttpOnly;Secure;SameSite=Strict"
    Define dates_user "%{env:zt-cert-date-next},%{env:zt-cert-date-halfnext},%{SSL_CLIENT_S_DN_CN}"
    Header set Set-Cookie "expr=zt-cert=%{sha1:salt1%{env:zt-cert-date-next}sal3%{SSL_CLIENT_S_DN_CN}salt2},${dates_user};${zt-cert}" env=!HAVE_zt-cert-sha1-found
</If>
</If>

SetEnvIfExpr "env('HAVE_zt-cert-date-halfnow') -ge %{TIME} && env('HAVE_zt-cert-sha1')=~/.{40}/" HAVE_zt-cert-sha1-found=1
работает,

а так работать не будет
SetEnvIfExpr "env('HAVE_zt-cert-date-halfnow') -ge  env('zt-cert-date-now') && env('HAVE_zt-cert-sha1')=~/.{40}/" HAVE_zt-cert-sha1-found=1 

No ka mea, e ho'ā wale ʻia ʻo LuaHookAccessChecker ma hope o ka loaʻa ʻana o nā loiloi e pili ana i kēia ʻike mai Nginx.

Pehea mākou ma ZeroTech i hoʻopili ai iā Apple Safari a me nā palapala hōʻoia o nā mea kūʻai aku me nā websockets

loulou i ke kumu Nā Kiʻi.

Hoʻokahi mea hou.

Ma keʻano holoʻokoʻa, ʻaʻole ia he mea nui i ke ʻano o nā kuhikuhi i kākau ʻia i ka hoʻonohonoho Apache (ʻo Nginx paha), no ka mea ma ka hopena e hoʻonohonoho ʻia nā mea āpau e pili ana i ke ʻano o ka noi mai ka mea hoʻohana, e pili ana i ka hoʻolālā no ka hana ʻana. Palapala lua.

Hoʻopau:

ʻIke ʻia ma hope o ka hoʻokō ʻana (pahuhopu):
Loaʻa ka mālama ʻana i nā lawelawe a me nā ʻoihana mai ke kelepona paʻa ma IOS me ka ʻole o nā polokalamu hou (VPN), i hui pū ʻia a paʻa.

Ua hoʻokō ʻia ka pahuhopu, hana nā kumu pūnaewele a loaʻa kahi pae palekana ʻaʻole i emi iho ma mua o kahi palapala.

Pehea mākou ma ZeroTech i hoʻopili ai iā Apple Safari a me nā palapala hōʻoia o nā mea kūʻai aku me nā websockets

Source: www.habr.com

Pākuʻi i ka manaʻo hoʻopuka