Jak przełamaliśmy Wielki Chiński Firewall (cz.2)

Cześć!

Znowu z Tobą Nikita — inżynier systemowy z firmy SEMrush. W tym artykule kontynuuję opowieść o tym, jak wymyślaliśmy rozwiązanie obejścia Chińskiego Firewalla dla naszej usługi semrush.com.

W poprzedniej części Opowiedziałem o tym,

  • jakie problemy pojawiają się po podjęciu decyzji: „Musimy sprawić, aby nasza usługa działała w Chinach”
  • jakie problemy ma chiński internet
  • dlaczego potrzebna jest licencja ICP
  • jak i dlaczego zdecydowaliśmy się testować nasze testowe środowiska przy użyciu Catchpoint
  • jaki rezultat dała nasza pierwsza wersja rozwiązania, oparta na Cloudflare China Network
  • jak znaleźliśmy błąd w DNS Cloudflare

Ta część jest moim zdaniem najciekawsza, ponieważ skupia się na konkretnych technicznych realizacjach stagingu. Zaczniemy, a właściwie kontynuujemy, od Alibaba Cloud.

Alibaba Cloud

Alibaba Cloud — dość dużego dostawcy chmurowego, który oferuje wszystkie usługi, pozwalające nazywać siebie dostawcą chmury. Dobrze, że mają możliwość rejestracji dla zagranicznych użytkowników oraz że większość strony jest przetłumaczona na angielski (to luksus dla Chin). W tej chmurze można pracować z wieloma regionami na świecie, kontynentalnymi Chinami oraz Azją Oceanu (Hongkong, Tajwan itd.).

IPSEC

Zaczęliśmy od geografii. Ponieważ nasza testowa strona znajdowała się w Google Cloud, musieliśmy „połączyć” Alibaba Cloud z GCP, dlatego otworzyliśmy listę lokalizacji, w których obecny był Google. W tamtym czasie nie mieli jeszcze własnego centrum danych w Hongkongu.
Najbliższym regionem okazał się asia-east1 (Tajwan). Najbliższym regionem kontynentalnych Chin wobec Tajwanu okazał się cn-shenzhen (Shenzhen).

Dzięki terraform Opisaliśmy i zbudowaliśmy całą infrastrukturę w GCP i Ali. Tunel 100 Mbit/s między chmurami został uruchomiony praktycznie natychmiast. Po stronie Shenzhen i Tajwanu uruchomiliśmy wirtualne maszyny proxy. W Shenzhen ruch użytkowników jest terminowany i proxowany przez tunel do Tajwanu, a stamtąd bezpośrednio na zewnętrzny IP naszej usługi w us-east (Wschodnie Wybrzeże USA). Ping między wirtualnymi maszynami przez tunel 24ms, co nie jest takie złe.

Jednocześnie uruchomiliśmy strefę testową w Alibaba Cloud DNS. Po delegacji strefy na NS Ali czas rozwiązywania DNS spadł z 470 ms do 50 ms. Wcześniej strefa była również na Cloudflare.

Równolegle z tunelami do asia-east1 uruchomiliśmy jeszcze jeden tunel z Shenzhen bezpośrednio do us-east4. Stworzono kolejne proxy wirtualne i zaczęto mierzyć oba rozwiązania, routując ruch testowy za pomocą Cookies lub DNS. Schemat testowej konfiguracji opisano na następnym rysunku:

Opóźnienie dla tuneli wyniosło:
Ali cn-shenzhen GCP asia-east1 — 24ms
Ali cn-shenzhen GCP us-east4 — 200 ms

Testy przeglądarkowe Catchpoint zgłosiły znakomitą poprawę wyników.

Porównaj wyniki testów dla dwóch rozwiązań:

Rozwiązanie
Czas pracy
Mediana
75 Percentyl
95 Percentyl

Cloudflare
86.6
18s
30s
60s

IPsec
99.79
18s
21s
30s

To dane rozwiązania, które wykorzystuje tunel IPSEC przez asia-east1. Poprzez us-east4 wyniki były gorsze, a błędów było więcej, dlatego nie podam wyników.

Na podstawie wyników tego testu dwóch tuneli, z których jeden kończy się w najbliższym regionie do Chin, a drugi w ostatecznym punkcie docelowym, stało się jasne, że ważne jest, aby jak najszybciej "wydostać się" spod chińskiej zapory, a następnie korzystać z szybkich sieci (dostawców CDN, dostawców chmurowych itp.). Nie należy próbować za jednym zamachem przejść przez zaporę i dotrzeć do punktu docelowego. To nie jest najszybsza droga.

Ogólnie wyniki są dobre, jednak mediana dla semrush.com wynosi 8.8s, a 75 percentyl 9.4s (na tym samym teście).
A zanim pójdziemy dalej, chciałbym zrobić mały dygresję.

Liryczne dygresje

Po tym, jak użytkownik wchodzi na stronę www.semrushchina.cn, która jest rozwiązywana przez "szybkie" chińskie serwery DNS, żądanie HTTP przechodzi przez nasze szybkie rozwiązanie. Odpowiedź wraca tą samą drogą, ale we wszystkich skryptach JS, stronach HTML i innych elementach strony internetowej wskazany jest domena semrush.com dla dodatkowych zasobów, które powinny zostać załadowane przy renderowaniu strony. To znaczy, klient rozwiązuje "główny" rekord A www.semrushchina.cn i wchodzi do szybkiego tunelu, szybko otrzymuje odpowiedź — stronę HTML, w której wskazane jest:

  • pobierz taki js z sso.semrush.com,
  • pobierz pliki CSS z cdn.semrush.com,
  • i jeszcze weź obrazy z dab.semrush.com
  • i tym podobne.

Przeglądarka zaczyna iść do "zewnętrznego" internetu po te zasoby, przechodząc za każdym razem przez opóźniającą czas odpowiedzi zaporę.

Jednak w poprzednim teście przedstawiono wyniki, kiedy na stronie nie ma zasobów semrush.com, tylko semrushchina.cn, a *.semrushchina.cn rozwiązuje się na adres wirtualki w Szanghaju, aby następnie trafić do tunelu.

Tylko w ten sposób, maksymalnie kierując cały możliwy ruch przez nasze rozwiązanie szybkiego przejścia przez chiński firewalla, można uzyskać akceptowalne prędkości i wskaźniki dostępności strony, a także rzetelne wyniki testów rozwiązań.
Zrealizowaliśmy to bez jedynej poprawki kodu po stronie produktów zespołów.

Subfilter

Rozwiązanie zrodziło się praktycznie natychmiast po tym, jak pojawił się ten problem. Potrzebowaliśmy PoC (Proof of Concept), aby pokazać, że nasze rozwiązania przejścia przez firewalla naprawdę dobrze działają. W tym celu należy maksymalnie kierować cały ruch strony do tego rozwiązania. I zastosowaliśmy subfilter w nginx.

Subfilter — to dość prosty moduł w nginx, który pozwala na zamianę jednego ciągu w ciele odpowiedzi na inny ciąg. Zmieniliśmy więc wszystkie wystąpienia semrush.com na semrushchina.cn we wszystkich odpowiedziach.

I… to nie zadziałało, ponieważ od backendów otrzymywaliśmy skompresowaną treść, w związku z czym subfilter nie znajdował potrzebnego ciągu. Musieliśmy dodać jeszcze jeden lokalny serwer w nginx, który rozpakowywał odpowiedź i przesyłał ją do następnego lokalnego serwera, który zajmował się zamianą ciągu, kompresją i oddawaniem jej następnemu w łańcuchu serwerowi proxy.

W rezultacie, gdzie klient by otrzymał <subdomain>.semrush.com, otrzymywał <subdomain>.semrushchina.cn i posłusznie przechodził przez nasze rozwiązanie.

Jednak wystarczy tylko zmienić domenę w jedną stronę, ponieważ backendy również nadal oczekują semrush.com w kolejnych żądaniach od klienta. W związku z tym, na tym samym serwerze, gdzie odbywa się zamiana w jedną stronę, za pomocą prostego wyrażenia regularnego uzyskujemy subdomenę z żądania, a następnie robimy proxy_pass z zmienną $host, ustawioną na $subdomain.semrush.com. Może się to wydawać skomplikowane, ale to działa. I działa dobrze. Dla poszczególnych domen, które wymagają innej logiki, po prostu tworzone są własne bloki serwerowe i przygotowywana jest oddzielna konfiguracja. Poniżej przedstawiono skrócone konfiguracje nginx dla jasności i demonstracji tego schematu.

Następna konfiguracja obsługuje wszystkie żądania z Chin na .semrushchina.cn:

    słuchaj 80;

    server_name ~^(?<subdomain>[w-]+).semrushchina.cn$;

    sub_filter '.semrush.com' '.semrushchina.cn';
    sub_filter_last_modified on;
    sub_filter_once off;
    sub_filter_types *;

    gzip on;
    gzip_proxied any;
    gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript application/javascript;

    location / {
        proxy_pass http://127.0.0.1:8083;
        proxy_set_header Accept-Encoding "";
        proxy_set_header Host $subdomain.semrush.com;
        proxy_set_header X-Accept-Encoding $http_accept_encoding;
    }
}

Ta konfiguracja przekierowuje na localhost na porcie 83, a tam czeka następna konfiguracja:

    słuchaj 127.0.0.1:8083;

    server_name *.semrush.com;

    location / {
        resolver 8.8.8.8 ipv6=off;
        gunzip on;
        proxy_pass https://$host;
        proxy_set_header Accept-Encoding gzip;
    }
}

Powtarzam, to są skrócone konfiguracje.

Mniej więcej tak. Może wyglądać skomplikowanie, ale to w słowach. W rzeczywistości jest prościej niż się wydaje 🙂

Koniec lirycznego dygresji

Przez jakiś czas byliśmy szczęśliwi, ponieważ mit o upadających tunelach IPSEC nie został potwierdzony. Ale potem tunel zaczął padać. Kilka razy dziennie przez kilka minut. Trochę, ale nam to nie odpowiadało. Ponieważ oba tunel były zakończone po stronie Ali na jednym routerze, postanowiliśmy, że może to być problem regionalny i trzeba zwiększyć zapasowy region.

Wznieśliśmy. Tunel zaczęły padać w różnych momentach, ale nasz failover działał doskonale na poziomie upstream w nginx. Ale potem tunel zaczęły padać mniej więcej jednocześnie 🙂 I znów zaczęły się 502 i 504. Uptime zaczął się pogarszać, więc zaczęliśmy opracowywać opcję z Alibaba CEN (Cloud Enterprise Network).

CEN

CEN — to łączność dwóch VPC z różnych regionów w Alibaba Cloud, to znaczy można połączyć sieci prywatne z dowolnych regionów w chmurze. I co najważniejsze: ten kanał ma dość surowe SLA. Jest bardzo stabilny zarówno pod względem prędkości, jak i uptime. Ale nigdy nie jest tak prosto:

  • bardzo trudno go uzyskać, jeśli nie jesteś obywatelami lub osobami prawnymi,
  • trzeba płacić za każdy megabit przepustowości kanału.

Uzyskując możliwość połączenia Mainland China i Overseas, stworzyliśmy CEN między dwoma regionami Ali: cn-shenzhen i us-east-1 (najbliższym punktem do us-east4). W Ali us-east-1 wznieśliśmy jeszcze jedną wirtualkę, aby mieć jeszcze jeden hop.

Wyszło tak:

Wyniki testów przeglądarkowych poniżej:

Rozwiązanie
Czas pracy
Mediana
75 Percentyl
95 Percentyl

Cloudflare
86.6
18s
30s
60s

IPsec
99.79
18s
21s
30s

CEN
99.75
16s
21s
27s

Wyniki są nieco lepsze niż w przypadku IPSEC. Ale przez IPSEC można potencjalnie pobierać z prędkością 100 Mbit/s, podczas gdy przez CEN tylko z prędkością 5 Mbit/s i drożej.

Sugeruje się hybrydę, prawda? Połączyć prędkość IPSEC i stabilność CEN.

Tak zrobiliśmy, przepuszczając ruch zarówno przez IPSEC, jak i przez CEN w przypadku awarii tunelu IPSEC. Czas działania znacznie się poprawił, ale prędkość ładowania strony nadal pozostawia wiele do życzenia. Wtedy naszkicowałem wszystkie schematy, które już wykorzystaliśmy i przetestowaliśmy, i postanowiłem spróbować dodać do tej schemy trochę GCP, a mianowicie GLB.

GLB

GLB — to Global Load Balancer (lub Google Cloud Load Balancer). Ma kluczową dla nas zaletę: w kontekście CDN posiada IP anycast, co pozwala na kierowanie ruchu do najbliższego centrum danych dla klienta, dzięki czemu ruch szybciej trafia do szybkiej sieci Google i mniej podróżuje po „zwykłym” internecie.

Nie zastanawiając się zbytnio, uruchomiliśmy HTTP/HTTPS LB w GCP i backendem postawiliśmy nasze wirtualne maszyny z subfilter.

Było kilka schematów:

  • Użyć Cloudflare China Network, ale tym razem jako Origin wskazano globalny IP GLB.
  • Terminować klientów w cn-shenzhen, a stamtąd proxować ruch od razu do GLB.
  • Iść od razu z Chin do GLB.
  • Terminować klientów w cn-shenzhen, a stamtąd proxować do asia-east1 przez IPSEC (w us-east4 przez CEN), skąd ruch przechodzi już do GLB (spokojnie, na dole będzie obrazek i wyjaśnienie)

Przetestowaliśmy wszystkie te opcje i jeszcze kilka hybrydowych:

  • Cloudflare + GLB

Ten schemat nie zadowolił nas pod względem uptime i błędów DNS. Ale test przeprowadzono przed poprawką błędu po stronie CF, być może teraz jest lepiej (jednak to nie wyklucza timeoutów HTTP).

  • Ali + GLB

Ten schemat również nie zadowolił nas pod względem uptime, ponieważ GLB często wypadał z upstreamu z powodu niemożności nawiązania połączenia w akceptowalnym czasie lub timeoutu, ponieważ dla serwera w Chinach adres GLB pozostaje na zewnątrz, a więc za chińskim firewallem. Magii nie było.

  • GLB tylko

Opcja podobna do poprzedniej, tylko nie używano serwerów w samych Chinach: ruch szedł od razu do GLB (zmieniliśmy rekordy DNS). W związku z tym wyniki nas nie zadowoliły, ponieważ w przypadku zwykłych klientów z Chin korzystających z usług zwykłych dostawców internetowych sytuacja z przejściem przez firewall była znacznie gorsza niż w Ali Cloud.

  • Shenzhen -> (CEN/IPSEC) -> Proxy -> GLB

Tutaj postanowiliśmy wykorzystać to, co najlepsze ze wszystkich rozwiązań:

  • stabilność i gwarantowany SLA od CEN
  • wysoką prędkość od IPSEC
  • szybką sieć Google i jego anycast.

Schemat wygląda mniej więcej tak: ruch użytkowników terminowany jest na wirtualnej maszynie w ch-shenzhen. Ustawiono upstreamy nginx, z których część odnosi się do prywatnych serwerów IP znajdujących się na drugim końcu tunelu IPSEC, a część upstreamów — do prywatnych adresów serwerów po drugiej stronie CEN. IPSEC został skonfigurowany przed regionem asia-east1 w GCP (był to najbliższy region do Chin w momencie tworzenia rozwiązania. Teraz GCP ma również obecność w Hongkongu). CEN — przed regionem us-east1 w Ali Cloud.

Następnie ruch z obu końców kierowano na anycast IP GLB, czyli do najbliższego punktu obecności Google'a, i przesyłano przez jego sieci do regionu us-east4 w GCP, w którym znajdowały się serwery wirtualne z podmianą (z subfilter w nginx).

To hybrydowe rozwiązanie, jak się spodziewaliśmy, pozwoliło skorzystać z zalet każdej technologii. Ogólnie, ruch przechodzi przez szybki IPSEC, ale gdy pojawiają się problemy, szybko i na kilka minut wykluczamy te serwery z upstreamów i kierujemy ruch tylko przez CEN, aż tunel się ustabilizuje.

Wdrażając czwarte rozwiązanie z powyższej listy, osiągnęliśmy to, czego chcieliśmy, i co biznes wymagał od nas w tamtym czasie.

Wyniki testów przeglądarkowych dla nowego rozwiązania w porównaniu do poprzednich:

Rozwiązanie
Czas pracy
Mediana
75 Percentyl
95 Percentyl

Cloudflare
86.6
18s
30s
60s

IPsec
99.79
18s
21s
30s

CEN
99.75
16s
21s
27s

CEN/IPsec + GLB
99.79
13s
16s
25s

CDN

W wprowadzonym przez nas rozwiązaniu wszystko jest w porządku, tylko brakuje CDN, które mogłoby przyspieszyć ruch na poziomie regionów i nawet miast. Zasadniczo powinno to przyspieszyć działanie strony dla użytkowników końcowych dzięki wykorzystaniu szybkich kanałów do CDN-prowadzącego. I cały czas o tym myśleliśmy. Nadszedł czas na następną iterację projektu: poszukiwanie i testowanie dostawców CDN w Chinach.

I o tym opowiem Wam w następnej, końcowej części 🙂

Źródło: habr.com

Kup solidny hosting stron z ochroną przed DDoS, serwery VPS VDS 🔥 Kup solidny hosting stron z ochroną przed DDoS, serwery VPS VDS | ProHoster