ZeroTech-те Apple Safari және клиент сертификаттарын веб-розеткалар арқылы қалай байланыстырдық

Мақала келесі адамдарға пайдалы болады:

  • Client Cert не екенін біледі және мобильді Safari-де веб-розеткалар не үшін қажет екенін түсінеді;
  • Мен веб-қызметтерді шектеулі адамдар тобына немесе тек өзіме жариялағым келеді;
  • барлығын әлдеқашан жасап қойған деп ойлайды және әлемді біршама ыңғайлырақ және қауіпсіз еткісі келеді.

Websockets тарихы шамамен 8 жыл бұрын басталды. Бұрын әдістер ұзақ http сұраулары (шын мәнінде жауаптар) түрінде қолданылған: пайдаланушы браузері серверге сұрау жіберіп, оның бірдеңе жауап беруін күтті, жауаптан кейін ол қайтадан қосылып, күтті. Бірақ содан кейін веб-розеткалар пайда болды.

ZeroTech-те Apple Safari және клиент сертификаттарын веб-розеткалар арқылы қалай байланыстырдық

Бірнеше жыл бұрын біз https сұрауларын пайдалана алмайтын таза PHP-де өз енгізуімізді әзірледік, өйткені бұл сілтеме қабаты. Жақында дерлік барлық веб-серверлер https арқылы прокси сұрауларын және қосылымды қолдауды үйренді: жаңарту.

Бұл орын алған кезде, websockets SPA қолданбалары үшін әдепкі қызметке айналды, өйткені сервердің бастамасы бойынша пайдаланушыға мазмұнды беру қаншалықты ыңғайлы (басқа пайдаланушыдан хабарлама жіберу немесе суреттің, құжаттың, презентацияның жаңа нұсқасын жүктеп алу) басқа біреу қазір өңдеп жатқаны).

Клиент сертификатының пайда болғанына біраз уақыт болғанымен, ол әлі де нашар қолдау көрсетеді, өйткені ол оны айналып өту кезінде көптеген мәселелерді тудырады. Және (мүмкін :slightly_smiling_face: ) сондықтан IOS браузерлері (Safariден басқа барлығы) оны пайдаланғысы келмейді және оны жергілікті сертификаттар дүкенінен сұрайды. Сертификаттар кіру/өту немесе ssh кілттерімен немесе брандмауэр арқылы қажетті порттарды жабумен салыстырғанда көптеген артықшылықтарға ие. Бірақ бұл әңгіме емес.

IOS жүйесінде сертификатты орнату процедурасы өте қарапайым (ерекшеліктерсіз емес), бірақ тұтастай алғанда ол Интернетте көп нәрсе бар және тек Safari браузерінде қол жетімді болатын нұсқауларға сәйкес жасалады. Өкінішке орай, Safari веб-розеткалар үшін Client Сert қалай пайдалану керектігін білмейді, бірақ Интернетте мұндай сертификатты жасау туралы көптеген нұсқаулар бар, бірақ іс жүзінде бұл мүмкін емес.

ZeroTech-те Apple Safari және клиент сертификаттарын веб-розеткалар арқылы қалай байланыстырдық

Веб-розеткаларды түсіну үшін біз келесі жоспарды қолдандық: мәселе/гипотеза/шешу.

Мәселе: IOS жүйесіне арналған Safari мобильді браузерінде және сертификаттық қолдауды қосқан басқа қолданбаларда клиент сертификатымен қорғалған ресурстарға сұрауларды прокси арқылы жіберу кезінде веб-розеткаларға қолдау көрсетілмейді.

Гипотезалар:

  1. Ішкі/сыртқы прокси-ресурстардың веб-розеткаларына сертификаттарды (болмайтынын біле отырып) пайдалану үшін мұндай ерекшелікті теңшеуге болады.
  2. Websockets үшін кәдімгі (websocket емес) шолғыш сұрауы кезінде жасалған уақытша сеанстар арқылы бірегей, қауіпсіз және қорғалатын қосылым жасауға болады.
  3. Уақытша сеанстарды бір прокси веб-сервердің көмегімен жүзеге асыруға болады (тек кірістірілген модульдер мен функциялар).
  4. Уақытша сеанс токендері дайын Apache модульдері ретінде іске асырылған.
  5. Уақытша сеанс таңбалауыштарын өзара әрекеттесу құрылымын логикалық жобалау арқылы жүзеге асыруға болады.

Орындаудан кейінгі көрінетін күй.

Жұмыс мақсаты: қызметтер мен инфрақұрылымды басқаруға IOS жүйесінде ұялы телефоннан қосымша бағдарламаларсыз (VPN сияқты), бірыңғай және қауіпсіз қолжетімді болуы керек.

Қосымша мақсат: мобильді Интернетте мазмұнды жылдам жеткізу арқылы уақыт пен ресурстарды/телефон трафигін үнемдеу (веб-розеткалары жоқ кейбір қызметтер қажетсіз сұрауларды тудырады).

Қалай тексеруге болады?

1. Беттерді ашу:

— например, 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. Немесе әзірлеуші ​​​​консолінде:

ZeroTech-те Apple Safari және клиент сертификаттарын веб-розеткалар арқылы қалай байланыстырдық

Гипотезаны тексеру:

1. Ішкі/сыртқы прокси-ресурстардың веб-розеткаларына сертификаттарды (болмайтынын біле отырып) пайдалану үшін мұндай ерекшелікті конфигурациялауға болады.

Мұнда 2 шешім табылды:

а) деңгейде

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

қол жеткізу деңгейін өзгерту.

Бұл әдіс келесі нюанстарға ие:

  • Сертификатты тексеру прокси-ресурсқа сұраудан кейін, яғни сұрауды жібергеннен кейін қол алысу арқылы жүзеге асады. Бұл прокси алдымен жүктелетінін, содан кейін қорғалған қызметке сұрауды өшіретінін білдіреді. Бұл жаман, бірақ сыни емес;
  • http2 протоколында. Ол әлі де жобада, ал браузер өндірушілері оны қалай жүзеге асыру керектігін білмейді. RFC 8740 "HTTP/1.3 көмегімен TLS 2 пайдалану" енгізу;
  • Бұл өңдеуді қалай біріктіру керектігі белгісіз.

b) Негізгі деңгейде ssl-ге сертификатсыз рұқсат беріңіз.

SSLVerifyClient талап етеді => SSLVerifyClient міндетті емес, бірақ бұл прокси сервердің қауіпсіздік деңгейін төмендетеді, өйткені мұндай қосылым сертификатсыз өңделеді. Дегенмен, келесі директива арқылы прокси қызметтерге қол жеткізуден бас тартуға болады:

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"

Толығырақ ақпаратты ssl туралы мақаладан табуға болады: Apache серверінің клиенттік сертификатының аутентификациясы

Екі нұсқа да сынақтан өтті, әмбебаптығы мен http2 протоколымен үйлесімділігі үшін «b» нұсқасы таңдалды.

Бұл гипотезаны тексеруді аяқтау үшін конфигурациямен көптеген эксперименттер қажет болды, келесі конструкциялар сынақтан өтті:

егер = талап ету = қайта жазу

Нәтиже келесі негізгі дизайн болып табылады:

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>

Куәлік иесінің бар рұқсатын ескере отырып, бірақ сертификаты жоқ, мен жоқ куәлік иесін SSl_PROTOCOL қол жетімді айнымалы мәндерінің бірі түрінде қосуға тура келді (SSL_CLIENT_S_DN_CN орнына), құжаттамада толығырақ:

Apache модулі mod_ssl

ZeroTech-те Apple Safari және клиент сертификаттарын веб-розеткалар арқылы қалай байланыстырдық

2. Websockets үшін кәдімгі (websocket емес) шолғыш сұрауы кезінде жасалған уақытша сеанстарды пайдаланып бірегей, қауіпсіз және қорғалған қосылым жасауға болады.

Алдыңғы тәжірибеге сүйене отырып, кәдімгі (веб-розеткадан тыс) сұрау кезінде веб-розетка қосылымдары үшін уақытша таңбалауыштарды дайындау үшін конфигурацияға қосымша бөлім қосу керек.

#подготовка передача себе С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>

Тестілеу оның жұмыс істейтінін көрсетті. Cookie файлдарын пайдаланушы браузері арқылы өзіңізге тасымалдауға болады.

3. Уақытша сеанстарды бір прокси веб-сервердің көмегімен жүзеге асыруға болады (тек кірістірілген модульдер мен функциялар).

Бұрын білгеніміздей, Apache шартты конструкцияларды жасауға мүмкіндік беретін көптеген негізгі функционалдылыққа ие. Дегенмен, бізге ақпарат пайдаланушы браузерінде болған кезде қорғау құралдары қажет, сондықтан біз нені және не үшін сақтау керектігін және қандай кірістірілген функцияларды қолданатынымызды анықтаймыз:

  • Бізге оңай декодтау мүмкін емес таңбалауыш қажет.
  • Бізге ескіргендігі бар және сервердегі ескіруді тексеру мүмкіндігі бар таңбалауыш қажет.
  • Бізге сертификат иесімен байланыстырылатын белгі қажет.

Бұл хэштеу функциясын, тұзды және таңбалауыштың ескіру күнін қажет етеді. Құжаттама негізінде Apache HTTP серверіндегі өрнектер бізде барлығы sha1 және %{TIME} жоқ.

Нәтиже келесі дизайн болды:

#нет сертификата, и обращение к 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>

Мақсатқа қол жеткізілді, бірақ сервердің ескіруімен байланысты проблемалар бар (бір жылдық Cookie файлын пайдалануға болады), бұл таңбалауыштардың ішкі пайдалану үшін қауіпсіз болғанымен, өнеркәсіптік (жаппай) пайдалану үшін қауіпті екенін білдіреді.

ZeroTech-те Apple Safari және клиент сертификаттарын веб-розеткалар арқылы қалай байланыстырдық

4. Уақытша сеанс токендері дайын Apache модульдері ретінде іске асырылған.

Алдыңғы итерациядан бір маңызды мәселе қалды - токендердің қартаюын бақылау мүмкін емес.

Біз мына сөздерге сәйкес мұны жасайтын дайын модульді іздейміз: apache token json two factor auth

Иә, дайын модульдер бар, бірақ олардың барлығы нақты әрекеттерге байланысты және сеанс пен қосымша Cookie файлдарын бастау түріндегі артефактілер бар. Яғни, біраз уақытқа емес.
Іздеуге бес сағат кетті, ол нақты нәтиже бермеді.

5. Уақытша сеанс таңбалауыштарын өзара әрекеттесу құрылымын логикалық жобалау арқылы жүзеге асыруға болады.

Дайын модульдер тым күрделі, өйткені бізге тек бірнеше функция қажет.

Айтуынша, күнге қатысты мәселе Apache-дің кірістірілген функциялары болашақта күнді жасауға мүмкіндік бермейді және ескіргенді тексеру кезінде кірістірілген функцияларда математикалық қосу/азайту жоқ.

Яғни, сіз жаза алмайсыз:

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

Сіз тек екі санды салыстыра аласыз.

Safari мәселесінің шешімін іздеу кезінде мен қызықты мақала таптым: HomeAssistant қолданбасын клиент сертификаттарымен қорғау (Safari/iOS жүйесінде жұмыс істейді)
Ол Nginx үшін Lua кодының мысалын сипаттайды және ол хэштеу үшін hmac тұздау әдісін қолдануды қоспағанда, біз енгізген конфигурацияның сол бөлігінің логикасын өте қайталайды ( бұл Apache-де табылмады).

Луа - бұл анық логикасы бар тіл және Apache үшін қарапайым нәрсені жасауға болатыны белгілі болды:

Nginx және Apache арасындағы айырмашылықты зерттегеннен кейін:

Және Lua тілінің өндірушісінің қол жетімді функциялары:
22.1 – Күні мен уақыты

Ағымдағымен салыстыру үшін болашақта күнді орнату үшін шағын Lua файлында env айнымалы мәндерін орнатудың жолын таптық.

Қарапайым Lua сценарийі осылай көрінеді:

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

Барлығы осылай жұмыс істейді, Cookie файлдарының санын оңтайландыру және ескі Cookie (токен) мерзімі біткенге дейін уақыттың жартысы келгенде таңбалауышты ауыстыру:

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 

Өйткені LuaHookAccessChecker тек Nginx-тен осы ақпаратқа негізделген кіруді тексеруден кейін ғана іске қосылады.

ZeroTech-те Apple Safari және клиент сертификаттарын веб-розеткалар арқылы қалай байланыстырдық

Дереккөзге сілтеме бейне.

Тағы бір нәрсе.

Тұтастай алғанда, директивалардың Apache (мүмкін Nginx) конфигурациясында қандай ретпен жазылғаны маңызды емес, өйткені соңында бәрі өңдеу схемасына сәйкес келетін пайдаланушының сұрауының реті негізінде сұрыпталады. Луа сценарийлері.

Аяқталу:

Орындаудан кейінгі көрінетін күй (мақсат):
қызметтер мен инфрақұрылымды басқару ұялы телефоннан IOS жүйесінде қосымша бағдарламаларсыз (VPN), бірыңғай және қауіпсіз.

Мақсатқа қол жеткізілді, веб-розеткалар жұмыс істейді және сертификаттан кем емес қауіпсіздік деңгейіне ие.

ZeroTech-те Apple Safari және клиент сертификаттарын веб-розеткалар арқылы қалай байланыстырдық

Ақпарат көзі: www.habr.com

пікір қалдыру