Бұғаттауды айналып өту үшін BGP орнату немесе «Қалай мен қорқуды тоқтаттым және RKN-ге ғашық болдым»

Жарайды, «сүйетіндер» туралы әсірелеу. Керісінше, «бірге өмір сүре алды».

Баршаңызға белгілі, 16 жылдың 2018 сәуірінен бастап Роскомнадзор «Домендік атаулардың Бірыңғай тізілімін, Интернеттегі сайттардың бет индекстерін және сайттарды анықтауға мүмкіндік беретін желілік мекенжайларды» қосып, Интернеттегі ресурстарға қол жеткізуді өте кең ауқымда бұғаттауда. Интернетте «Ресей Федерациясында таратуға тыйым салынған ақпаратты қамтитын» (мәтінінде - жай ғана реестр) /10 кейде. Нәтижесінде Ресей Федерациясының азаматтары мен кәсіпкерлер өздеріне қажетті толық заңды ресурстарға қол жеткізе алмай, зардап шегуде.

Мен Хабредегі мақалалардың біріне түсініктемелерде зардап шеккендерге айналып өту схемасын орнатуға көмектесуге дайын екенімді айтқаннан кейін, бірнеше адам маған осындай көмек сұрап келді. Барлығы олар үшін жұмыс істегенде, олардың бірі техниканы мақалада сипаттауды ұсынды. Біраз ойланып, мен сайттағы үнсіздігімді бұзып, жоба мен Facebook жазбасының арасында бірдеңе жазуға тырыстым, яғни. хабрапост. Нәтижесі сіздің алдыңызда.

Жауапкершіліктен бас тарту

Ресей Федерациясының аумағында тыйым салынған ақпаратқа қол жеткізуді бұғаттауды айналып өту жолдарын жариялау өте заңды емес болғандықтан, осы мақаланың мақсаты рұқсат етілген ресурстарға қол жеткізуді автоматтандыруға мүмкіндік беретін әдіс туралы айту болады. Ресей Федерациясының аумағында, бірақ басқа біреудің әрекеттеріне байланысты провайдеріңіз арқылы тікелей қол жеткізу мүмкін емес. Мақаладағы әрекеттер нәтижесінде алынған басқа ресурстарға қол жеткізу жағымсыз жанама әсер болып табылады және бұл мақаланың мақсаты болып табылмайды.

Сондай-ақ, мен негізінен мамандығым, мамандығым және өмір жолы бойынша желі сәулетшісі болғандықтан, бағдарламалау және Linux менің күшті жақтарым емес. Сондықтан, әрине, сценарийлерді жақсырақ жазуға болады, VPS-тегі қауіпсіздік мәселелерін тереңірек өңдеуге болады және т.б. Сіздің ұсыныстарыңыз ризашылықпен қабылданады, егер олар жеткілікті түрде егжей-тегжейлі болса - мен оларды мақаланың мәтініне қосуға қуаныштымын.

TL; DR

Біз тізілімнің көшірмесін және BGP протоколын пайдалана отырып, бар туннель арқылы ресурстарға қол жеткізуді автоматтандырамыз. Мақсат - туннельдегі блокталған ресурстарға бағытталған барлық трафикті жою. Минималды түсініктемелер, негізінен қадамдық нұсқаулар.

Бұл үшін сізге не керек?

Өкінішке орай, бұл пост бәріне бірдей арналмаған. Бұл техниканы пайдалану үшін сізге бірнеше элементтерді біріктіру қажет:

  1. Сізде блоктау өрісінен тыс жерде Linux сервері болуы керек. Немесе, кем дегенде, мұндай серверге ие болғысы келетіні - бақытымызға орай, оның құны жылына $ 9, және, мүмкін, одан да төмен. Егер сізде бөлек VPN туннелі болса, бұл әдіс қолайлы, содан кейін сервер блоктау өрісінің ішінде орналасуы мүмкін.
  2. Сіздің маршрутизаторыңыз қабілетті болу үшін жеткілікті ақылды болуы керек
    • сізге ұнайтын кез келген VPN клиенті (мен OpenVPN-ді қалаймын, бірақ ол PPTP, L2TP, GRE+IPSec немесе туннель интерфейсін жасайтын кез келген басқа опция болуы мүмкін);
    • BGPv4 протоколы. Бұл SOHO үшін бұл Mikrotik немесе Quagga немесе Bird орнатуға мүмкіндік беретін OpenWRT/LEDE/ұқсас арнайы микробағдарламасы бар кез келген маршрутизатор болуы мүмкін дегенді білдіреді. ДК маршрутизаторын пайдалануға да тыйым салынбайды. Кәсіпорын жағдайында шекаралық маршрутизатордың құжаттамасынан BGP қолдауын іздеңіз.
  3. Сізде Linux пайдалану және желілік технологиялар, соның ішінде BGP протоколы туралы түсінік болуы керек. Немесе кем дегенде осындай идеяны алғыңыз келеді. Мен бұл жолы ауқымдылықты қабылдауға дайын болмағандықтан, сізге түсініксіз кейбір аспектілерді өз бетіңізше зерттеуге тура келеді. Дегенмен, мен, әрине, түсініктемелерде нақты сұрақтарға жауап беремін және мен жалғыз жауап беруім екіталай, сондықтан сұраудан тартынбаңыз.

Мысалда не қолданылады

  • Тізілімнің көшірмесі – бастап https://github.com/zapret-info/z-i 
  • VPS - Ubuntu 16.04
  • Маршруттау қызметі - құс 1.6.3   
  • Маршрутизатор - Микротик hAP ак
  • Жұмыс қалталары - біз түбір ретінде жұмыс істеп жатқандықтан, барлығының көпшілігі түбірдің негізгі қалтасында орналасады. Сәйкесінше:
    • /root/blacklist - компиляция сценарийі бар жұмыс қалтасы
    • /root/zi - github тізілімінің көшірмесі
    • /etc/bird - құстарға қызмет көрсету параметрлеріне арналған стандартты қалта
  • Маршруттау сервері және туннельді аяқтау нүктесі бар VPS сыртқы IP мекенжайы 194.165.22.146, ASN 64998; маршрутизатордың сыртқы IP мекенжайы - 81.177.103.94, ASN 64999
  • Туннель ішіндегі IP мекенжайлары сәйкесінше 172.30.1.1 және 172.30.1.2.

Бұғаттауды айналып өту үшін BGP орнату немесе «Қалай мен қорқуды тоқтаттым және RKN-ге ғашық болдым»

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

Қысқаша – шешімнің логикасы

  1. Дайындық әрекеттері
    1. VPS алу
    2. Маршрутизатордан VPS-ке туннельді көтеру
  2. Біз тізілімнің көшірмесін аламыз және үнемі жаңартып отырамыз
  3. Маршруттау қызметін орнату және конфигурациялау
  4. Біз тізілім негізінде маршруттау қызметі үшін статикалық маршруттар тізімін жасаймыз
  5. Біз маршрутизаторды қызметке қосамыз және барлық трафикті туннель арқылы жіберуді конфигурациялаймыз.

Нақты шешім

Дайындық әрекеттері

Интернетте өте қолайлы бағамен VPS ұсынатын көптеген қызметтер бар. Осы уақытқа дейін мен жылына $9 үшін опцияны таптым және қолданамын, бірақ сіз тым көп алаңдамасаңыз да, әр бұрышта 1E/ай үшін көптеген нұсқалар бар. VPS таңдау мәселесі осы мақаланың ауқымынан тыс, сондықтан біреу бұл туралы бірдеңені түсінбесе, түсініктемелерде сұраңыз.

VPS-ті тек маршруттау қызметі үшін ғана емес, сонымен қатар ондағы туннельді тоқтату үшін де пайдалансаңыз, бұл туннельді көтеру керек және ол үшін NAT конфигурациялау керек. Интернетте бұл әрекеттер туралы көптеген нұсқаулар бар, мен оларды мұнда қайталамаймын. Мұндай туннельге қойылатын негізгі талап - ол VPS бағытында туннельді қолдайтын маршрутизаторда бөлек интерфейс жасауы керек. Көптеген қолданылатын VPN технологиялары осы талапқа сәйкес келеді - мысалы, tun режиміндегі OpenVPN тамаша.

Тізілімнің көшірмесін алу

Жәбірейіл айтқандай: «Бізге кім кедергі болса, бізге көмектеседі». ҚРН тыйым салынған ресурстар тізілімін жасап жатқандықтан, біздің мәселемізді шешу үшін бұл тізілімді қолданбау күнә болар еді. Біз github сайтынан тізілімнің көшірмесін аламыз.

Біз сіздің Linux серверіңізге барамыз, түбірлік контекстке түсеміз (sudo su —) және әлі орнатылмаған болса, git орнатыңыз.

apt install git

Үй каталогына өтіп, тізілімнің көшірмесін шығарыңыз.

cd ~ && git clone --depth=1 https://github.com/zapret-info/z-i 

Біз cron жаңартуын орнаттық (мен мұны 20 минут сайын бір рет жасаймын, бірақ сізді қызықтыратын кез келген аралықты таңдауға болады). Мұны істеу үшін біз іске қосамыз crontab -e және оған келесі жолды қосыңыз:

*/20 * * * * cd ~/z-i && git pull && git gc

Біз тізілімді жаңартқаннан кейін маршруттау қызметі үшін файлдарды жасайтын ілгекті қосамыз. Ол үшін файл жасаңыз /root/zi/.git/hooks/post-merge келесі мазмұнмен:

#!/usr/bin/env bash
changed_files="$(git diff-tree -r --name-only --no-commit-id ORIG_HEAD HEAD)"
check_run() {
    echo "$changed_files" | grep --quiet "$1" && eval "$2"
}
check_run dump.csv "/root/blacklist/makebgp"

және оны орындалатын етіп жасауды ұмытпаңыз

chmod +x /root/z-i/.git/hooks/post-merge

Біз ілмекке сілтеме жасайтын makebgp сценарийін сәл кейінірек жасаймыз.

Маршруттау қызметін орнату және конфигурациялау

Құс орнатыңыз. Өкінішке орай, қазіргі уақытта Ubuntu репозиторийлерінде жарияланған құс нұсқасы балғындығы бойынша Archaeopteryx нәжісімен салыстырылады, сондықтан алдымен жүйеге бағдарламалық жасақтаманы әзірлеушілердің ресми PPA қосу керек.

add-apt-repository ppa:cz.nic-labs/bird
apt update
apt install bird

Осыдан кейін біз IPv6 үшін құсты бірден өшіреміз - бұл орнатуда бізге қажет болмайды.

systemctl stop bird6
systemctl disable bird6

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

log syslog all;
router id 172.30.1.1;

protocol kernel {
        scan time 60;
        import none;
#       export all;   # Actually insert routes into the kernel routing table
}

protocol device {
        scan time 60;
}

protocol direct {
        interface "venet*", "tun*"; # Restrict network interfaces it works with
}

protocol static static_bgp {
        import all;
        include "pfxlist.txt";
        #include "iplist.txt";
}

protocol bgp OurRouter {
        description "Our Router";
        neighbor 81.177.103.94 as 64999;
        import none;
        export where proto = "static_bgp";
        local as 64998;
        passive off;
        multihop;
}

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

Direct протоколы маршруттау процесімен қандай интерфейстер жұмыс істейтінін анықтайды. Мысал бірнеше мысал атауларын береді, басқаларын қосуға болады. Сіз жай ғана жолды жоя аласыз, бұл жағдайда сервер IPv4 мекенжайы бар барлық қолжетімді интерфейстерді тыңдайды.

protocol static — кейінгі хабарландырулар үшін файлдардан префикстер мен IP мекенжайларының (әрине, шын мәнінде /32 префикстер) тізімдерін жүктейтін сиқырымыз. Бұл тізімдердің қайдан алынғаны төменде талқыланады. IP мекенжайларын жүктеу әдепкі бойынша түсіндірілетінін ескеріңіз, мұның себебі жүктеп салудың үлкен көлемі болып табылады. Салыстыру үшін, жазу кезінде префикстер тізімінде 78 жол және IP мекенжайлар тізімінде 85898 жол бар. Мен тек префикстер тізімінен бастауды және жөндеуді және IP жүктеуді қосу-қосуды ұсынамын. Маршрутизатормен тәжірибе жасағаннан кейін болашақты өзіңіз шешесіз. Олардың әрқайсысы маршруттық кестедегі 85 мың жазбаны оңай сіңіре алмайды.

bgp протоколы, шын мәнінде, сіздің маршрутизаторыңызбен bgp теңдеуін орнатады. IP мекенжайы – маршрутизатордың сыртқы интерфейсінің мекенжайы (немесе маршрутизатор жағындағы туннель интерфейсінің мекенжайы), 64998 және 64999 – автономды жүйелердің нөмірлері. Бұл жағдайда оларды кез келген 16 биттік сандар түрінде тағайындауға болады, бірақ RFC6996 - 64512-65534 қоса алғанда анықталған жеке ауқымдағы AS нөмірлерін пайдалану жақсы тәжірибе болып табылады (32 биттік ASN үшін пішім бар, бірақ біздің жағдайда бұл сөзсіз артық). Сипатталған конфигурацияда маршруттау қызметі мен маршрутизатордың автономды жүйелерінің нөмірлері әртүрлі болуы керек eBGP пирингін пайдаланады.

Көріп отырғаныңыздай, қызмет маршрутизатордың IP мекенжайын білуі керек, сондықтан сізде динамикалық немесе бағытталмайтын жеке (RFC1918) немесе ортақ (RFC6598) мекенжайыңыз болса, сізде сыртқы желіде пирингті арттыру опциясы жоқ. интерфейс, бірақ қызмет әлі де туннель ішінде жұмыс істейді.

Сондай-ақ, бір қызметтен бірнеше түрлі маршрутизаторларға бағыттарды ұсына алатыныңыз анық - bgp протоколының бөлімін көшіріп, көршінің IP мекенжайын өзгерту арқылы олар үшін параметрлерді көшіріңіз. Сондықтан мысал туннельден тыс қарауға арналған параметрлерді ең әмбебап ретінде көрсетеді. Параметрлердегі IP мекенжайларын сәйкесінше өзгерту арқылы оларды туннельге алып тастау оңай.

Маршруттау қызметі үшін тізілімді өңдеу

Енді бізге алдыңғы кезеңде статикалық хаттамада айтылған префикстер мен IP мекенжайларының тізімдерін жасау керек. Ол үшін біз тізілім файлын алып, одан қажетті файлдарды келесі сценарий арқылы жасаймыз, /root/blacklist/makebgp

#!/bin/bash
cut -d";" -f1 /root/z-i/dump.csv| tr '|' 'n' |  tr -d ' ' > /root/blacklist/tmpaddr.txt
cat /root/blacklist/tmpaddr.txt | grep / | sed 's_.*_route & reject;_' > /etc/bird/pfxlist.txt
cat /root/blacklist/tmpaddr.txt | sort | uniq | grep -Eo "([0-9]{1,3}[.]){3}[0-9]{1,3}" | sed 's_.*_route &/32 reject;_' > /etc/bird/iplist.txt
/etc/init.d/bird reload
logger 'bgp list compiled'

Оны орындалатын етіп жасауды ұмытпаңыз

chmod +x /root/blacklist/makebgp

Енді сіз оны қолмен іске қосып, /etc/bird ішіндегі файлдардың көрінісін бақылай аласыз.

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

systemctl start bird
birdc show route

Екінші пәрменнің шығысы шамамен 80 жазбаны көрсетуі керек (бұл қазір, бірақ сіз оны орнатқан кезде бәрі РКН-ның желілерді блоктаудағы құлшынысына байланысты болады) келесідей нәрсе:

54.160.0.0/12      unreachable [static_bgp 2018-04-19] * (200)

команда

birdc show protocol

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

BIRD 1.6.3 ready.
name     proto    table    state  since       info
kernel1  Kernel   master   up     2018-04-19
device1  Device   master   up     2018-04-19
static_bgp Static   master   up     2018-04-19
direct1  Direct   master   up     2018-04-19
RXXXXXx1 BGP      master   up     13:10:22    Established
RXXXXXx2 BGP      master   up     2018-04-24  Established
RXXXXXx3 BGP      master   start  2018-04-22  Connect       Socket: Connection timed out
RXXXXXx4 BGP      master   up     2018-04-24  Established
RXXXXXx5 BGP      master   start  2018-04-24  Passive

Маршрутизаторды қосу

Бұл аяқ киімдерді оқудан бәрі шаршаған шығар, бірақ жүректі қабылдаңыз - ақырзаман жақын. Сонымен қатар, бұл бөлімде мен қадамдық нұсқауларды бере алмаймын - бұл әр өндіруші үшін әртүрлі болады.

Дегенмен, мен сізге бірнеше мысал келтіре аламын. Негізгі логика - BGP теңдеуін жоғарылату және біздің туннельді (егер бізге p2p интерфейсі арқылы трафик жіберу керек болса) немесе трафик Ethernet желісіне өтетін болса, nexthop IP мекенжайын көрсете отырып, барлық алынған префикстерге nexthop тағайындау.

Мысалы, RouterOS жүйесіндегі Mikrotik-те бұл келесідей шешіледі

/routing bgp instance set default as=64999 ignore-as-path-len=yes router-id=172.30.1.2
/routing bgp peer add in-filter=dynamic-in multihop=yes name=VPS remote-address=194.165.22.146 remote-as=64998 ttl=default
/routing filter add action=accept chain=dynamic-in protocol=bgp comment="Set nexthop" set-in-nexthop=172.30.1.1

және Cisco IOS жүйесінде - осылай

router bgp 64999
  neighbor 194.165.22.146 remote-as 64998
  neighbor 194.165.22.146 route-map BGP_NEXT_HOP in
  neighbor 194.165.22.146 ebgp-multihop 250
!
route-map BGP_NEXT_HOP permit 10
  set ip next-hop 172.30.1.1

Егер бірдей туннель BGP пирингі үшін де, пайдалы трафикті жіберу үшін де пайдаланылса, келесі жолды орнату қажет емес, ол протокол арқылы дұрыс орнатылады. Бірақ егер сіз оны қолмен орнатсаңыз, ол да оны нашарлатпайды.

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

После того, как у вас поднялась BGP-сессия, прилетели и установились в таблицу маршруты на крупные сети, трафик на адреса из них пошел и счастье близко, вы можете вернуться к сервису bird и попробовать раскомментировать там запись, подключающую список ip-адресов, выполнить Осыдан кейін

systemctl reload bird

және сіздің маршрутизаторыңыз осы 85 мың маршрутты қалай тасымалдағанын қараңыз. Электр желісінен ажыратуға дайын болыңыз және онымен не істеу керектігін ойлаңыз :)

Барлығы

Таза теориялық тұрғыдан алғанда, жоғарыда сипатталған қадамдарды орындағаннан кейін сізде енді сүзгілеу жүйесінен өткен Ресей Федерациясында тыйым салынған IP мекенжайларына трафикті автоматты түрде қайта бағыттайтын қызмет бар.

Оны, әрине, жақсартуға болады. Мысалы, perl немесе python шешімдерінің көмегімен IP мекенжайларының тізімін қорытындылау өте оңай. Мұны Net::CIDR::Lite көмегімен орындайтын қарапайым Perl сценарийі 85 мың префиксті 60-ға (мың емес) айналдырады, бірақ, әрине, бұғатталғаннан әлдеқайда көп мекенжайлар ауқымын қамтиды.

Қызмет ISO/OSI үлгісінің үшінші деңгейінде жұмыс істейтіндіктен, ол тізілімде жазылған қате мекенжайды шешсе, ол сізді сайтты/бетті блоктаудан құтқармайды. Бірақ тізіліммен бірге nxdomain.txt файлы github-тан келеді, ол сценарийдің бірнеше штрихтары арқылы оңай мекенжайлар көзіне айналады, мысалы, Chrome жүйесіндегі SwitchyOmega плагині.

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

Егер сізде сұрақтар болса, сұраңыз, мен жауап беруге дайынмын.

UPD. Рақмет сізге навигация и ТерАнЮ жүктеу көлемін азайтуға мүмкіндік беретін git параметрлері үшін.

UPD2. Әріптестер, мақалаға VPS және маршрутизатор арасында туннель орнату нұсқауларын қоспай қателескен сияқтымын. Осыған байланысты көптеген сұрақтар туындайды.
Бұл нұсқаулықты бастамас бұрын, сіз VPN туннелін қажетті бағытта конфигурациялағаныңызды және оның функционалдығын тексергеніңізді (мысалы, трафикті әдепкі бойынша немесе статикалық түрде бұру арқылы) тағы бір рет ескертемін. Егер сіз осы кезеңді әлі аяқтамаған болсаңыз, мақаладағы қадамдарды орындаудың мағынасы жоқ. Бұл туралы менің жеке мәтінім әлі жоқ, бірақ Google-да VPS жүйесінде орнатылған операциялық жүйенің атымен бірге «OpenVPN серверін орнату» және маршрутизатордың атымен «OpenVPN клиентін орнату» деп жазсаңыз , сіз осы тақырып бойынша көптеген мақалаларды таба аласыз, соның ішінде Хабре.

UPD3. Құрбандықсыз Мен dump.csv файлын IP мекенжайларының қосымша қорытындылауымен құсқа арналған нәтиже файлына айналдыратын код жаздым. Сондықтан «Маршруттау қызметінің тізілімін өңдеу» бөлімін оның бағдарламасына қоңырау шалу арқылы ауыстыруға болады. https://habr.com/post/354282/#comment_10782712

UPD4. Қателер бойынша кішкене жұмыс (мен оларды мәтінге қоспадым):
1) орнына systemctl қайта жүктеу құс пәрменін пайдалану мағынасы бар birdc конфигурациялау.
2) туннельдің екінші жағындағы IP-ге келесі жолды өзгертудің орнына Mikrotik маршрутизаторында /бағыттау сүзгісі қосу әрекеті=қабылдау тізбегі=динамикалық кіріс протоколы=bgp comment=»Nexthop орнату» set-in-nexthop=172.30.1.1 мекенжайсыз туннель интерфейсіне тікелей маршрутты көрсету мағынасы бар /бағыттау сүзгісін қосу әрекеті=қабылдау тізбегі=динамикалық-ин протоколы=bgp comment=»Nexthop орнату» set-in-nexthop-direct=<интерфейс атауы>

UPD5. Жаңа қызмет пайда болды https://antifilter.download, ол жерден IP мекенжайларының дайын тізімдерін алуға болады. Әр жарты сағат сайын жаңартылады. Клиент жағында сәйкес «бағыт... қабылдамау» бар жазбаларды жақтау ғана қалады.
Осы кезде, әжеңді шүберекке түйіп, мақаланы жаңартып отыру жеткілікті болар.

UPD6. Оны түсінгісі келмейтін, бірақ бастағысы келетіндер үшін мақаланың қайта қаралған нұсқасы - осында.

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

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