Jak zabezpieczyć serwer CS2 i Source przed atakami DDoS
W Counter-Strike 2 i tytułach na silniku Source ruch gry oraz zapytania o status idą przez ten sam port 27015. Co zabezpieczysz sam i od jakiej skali ataku pomaga już tylko filtrowanie w sieci przed serwerem.
Serwer Counter-Strike rzadko pada w przypadkowym momencie. Awaria przychodzi w decydującej rundzie, tuż przed finałem turnieju albo dokładnie wtedy, gdy zbanowany gracz został odrzucony po raz drugi. Kto jest właśnie pod ostrzałem, nie potrzebuje dyskusji o zasadach, tylko kolejności działań. Ten artykuł pokazuje najpierw, co możesz zmienić na samym serwerze, potem, gdzie te możliwości się kończą, a na koniec, co musi zadziać się przed nim w sieci.
Dlaczego serwery CS2 i Source są atakowane tak często
Counter-Strike to gra na czas. Runda trwa krócej niż dwie minuty, mecz niecałą godzinę, a awaria w tej właśnie godzinie rozstrzyga o wyniku. Przestój nie jest więc tylko irytujący, staje się narzędziem: kto przegrywa, zyskuje na przerwaniu meczu czas, a kto prowadzi konkurencyjną społeczność, wie, że wieczór pełen timeoutów wypycha stałych graczy gdzie indziej.
Do tego dochodzi budowa silnika. Serwer Source jest publicznie odnajdywalny po adresie IP i porcie, i jest to warunek działania, a nie przeoczenie: bez odpowiedzi na zapytanie o status nie pojawi się w żadnej przeglądarce serwerów. Pytanie nigdy nie brzmi więc, czy atakujący znajdzie twój adres, tylko co się stanie, kiedy zacznie w niego strzelać.
Porty, o które chodzi
Counter-Strike 2, CS:GO i Garry's Mod dzielą tę samą logikę portów, a kryje się w niej szczegół, który odróżnia je od Minecrafta czy Rusta:
- 27015/UDP, jednocześnie port gry i zapytań o status (
-port). Przez ten jeden port idzie ruch gry, a dodatkowo zapytanie A2S, którym Steam i każda strona z listą serwerów odczytują twój serwer. Osobnego portu zapytań tutaj nie ma. - 27015/TCP, RCON. Ten sam numer, inny protokół. Idą tędy polecenia administracyjne, o ile ustawione jest
rcon_password. - 27020/UDP, GOTV, czyli SourceTV (
tv_port). Potrzebny tylko wtedy, gdy faktycznie transmitujesz. - 27005/UDP, port klienta. Wychodzi od gracza i nie wymaga otwierania na serwerze.
- Przy kilku instancjach numery rosną (27016, 27017 oraz 27021, 27022 dla GOTV). Jeśli szybkie pobieranie map (
sv_downloadurl) leży na tym samym hoście, dochodzi jeszcze 80/TCP albo 443/TCP.
Wspólny port jest sednem problemu. Zapytanie A2S to pakiet o wielkości kilkudziesięciu bajtów, a odpowiedź jest wielokrotnie większa, przy czym w UDP adres nadawcy da się podrobić. Atakujący może odpytywać cudze serwery i kierować odpowiedzi na swój właściwy cel: twój serwer jest wtedy nie tylko ofiarą, ale i wzmacniaczem. Dlatego Valve dołożyło do A2S_INFO poprzedzające wyzwanie (challenge), co złagodziło sprawę, ale jej nie zakończyło. Po czym poznasz trwający atak, opisuje wpis Jak rozpoznać atak DDoS na serwerze.
Co możesz zrobić sam, zanim wydasz pieniądze
Ta część nic nie kosztuje i opłaca się niezależnie od tego, gdzie stoi twój serwer. Nie zdejmie z ciebie ataku wolumetrycznego, ale sprawi, że małe i średnie ataki spełzną na niczym.
1. Inwentaryzacja: co naprawdę nasłuchuje
Zanim napiszesz jakąkolwiek regułę, ustal, które usługi są osiągalne. Na serwerze gry, który rozrastał się przez lata, jest ich prawie zawsze więcej, niż się spodziewasz:
ss -lntup
Wszystko, co jest przypięte do 127.0.0.1 albo ::1, nie wymaga otwierania portu. Wszystko, co nasłuchuje na 0.0.0.0 albo [::], jest osiągalne z internetu, łącznie z bazą danych, którą przyniósł ze sobą dodatek statystyczny. Porównaj to ze swoją linią startową:
./game/bin/linuxsteamrt64/cs2 -dedicated \
-port 27015 \
-maxplayers_override 12 \
+game_alias competitive \
+map de_dust2 \
+sv_setsteamaccount TWOJ_TOKEN_GSLT
Przy CS:GO i Garry's Mod to samo zadanie przejmuje srcds_run. Jeśli fundament jest postawiony przez SteamCMD, pomoże wpis Instalacja serwera gry przez SteamCMD.
2. Zostaw otwarte tylko te porty, których serwer naprawdę potrzebuje
Dwa porty UDP i jeden ograniczony port TCP, nic więcej. RCON nie ma czego szukać w otwartym internecie:
ufw allow 27015/udp comment "CS2 port gry i A2S"
ufw allow 27020/udp comment "GOTV"
ufw allow from 203.0.113.10 to any port 27015 proto tcp comment "RCON"
Zamień 203.0.113.10 na swój własny adres. Jeśli zmienia się on regularnie, lepszą drogą jest tunel SSH niż trwale otwarty port.
Uwaga, która co roku kosztuje wiele serwerów: o tym, czy zamkniesz dostęp samemu sobie, decyduje kolejność przy uruchamianiu firewalla. Opisuje ją razem z drogą powrotną wpis Konfiguracja firewalla UFW. Gdyby jednak do tego doszło: serwery root KVM i serwery dedykowane w KernelHoście nie mają IPMI ani iDRAC, do serwera dostaniesz się przez konsolę VNC w panelu klienta. Ta nie wisi na stosie sieciowym systemu gościa.
3. Ogranicz ruch zapytań, nie wypadając z listy serwerów
Tutaj kryje się najdroższy błąd w całym temacie. Ponieważ ruch gry i zapytania o status zajmują ten sam port, najbardziej oczywista reakcja jest błędna: kto zablokuje 27015/UDP albo obłoży go ogólnym limitem, tym samym ruchem wyrzuci własnych graczy i dokończy atak za atakującego.
Właściwym punktem zaczepienia jest rozróżnienie między pakietami zapytań a pakietami gry. Silnik widzi zawartość i ma do tego trzy zmienne konsolowe:
sv_max_queries_sec 3
sv_max_queries_sec_global 60
sv_max_queries_window 30
Pierwsza ogranicza liczbę obsłużonych zapytań na adres nadawcy, druga nakłada limit na sumę ze wszystkich adresów, a trzecia ustala okno uśredniania w sekundach; find sv_max_queries pokaże, czy twoja wersja serwera je zna. Chronią procesor przed generowaniem bezsensownych odpowiedzi, ale nie sprawią, że pakiety przestaną docierać.
Poziom niżej ruch zapytań da się czysto oddzielić. Wszystkie bezpołączeniowe pakiety silnika Source, czyli zapytania o status i nawiązywanie połączenia, zaczynają się od czterech ustawionych bajtów (0xffffffff), a ruch graczy już połączonych tego nagłówka nie ma. Dokładnie na tym można oprzeć limit w nftables:
table inet cs2 {
chain input {
type filter hook input priority -10; policy accept;
udp dport 27015 @th,64,32 0xffffffff \
meter a2sflood { ip saddr limit rate over 10/second burst 20 packets } drop
}
}
Plik ładujesz przez nft -f. Priorytet -10 sprawia, że reguła zadziała przed łańcuchem filtrującym UFW, a @th,64,32 odczytuje pierwsze cztery bajty za nagłówkiem UDP. W klasycznym iptables to samo rozdzielenie osiąga dopasowanie do sygnatury A2S_INFO:
iptables -A INPUT -p udp --dport 27015 \
-m string --algo bm --hex-string "|ffffffff54536f7572636520456e67696e6520517565727900|" \
-m hashlimit --hashlimit-name a2sflood --hashlimit-mode srcip \
--hashlimit-above 10/sec --hashlimit-burst 20 -j DROP
Zacznij od hojnego limitu i zaciskaj go dopiero wtedy, gdy masz pewność, że legalne zapytania przechodzą.
4. Zabezpiecz RCON
Otwarty port RCON ze słabym hasłem to nie problem DDoS, tylko przejęcie serwera: kto ma RCON, zmienia mapę, banuje wszystkich graczy i zatrzymuje serwer. Nigdy nie zostawiaj rcon_password pustego ani zgadywalnego, w zupełności wystarczy wartość z openssl rand -base64 32. Tytuły na silniku Source mają dodatkowo hamulec na próby logowania:
sv_rcon_minfailures 3
sv_rcon_maxfailures 5
sv_rcon_minfailuretime 30
sv_rcon_banpenalty 1440
sv_rcon_whitelist_address "203.0.113.10"
Adres zostaje w ten sposób zablokowany na dobę po trzech nieudanych próbach w ciągu 30 sekund, a twój własny pozostaje wyłączony spod tej reguły; find sv_rcon pokaże, które zmienne zna twoja wersja serwera. Ograniczenie na firewallu z kroku 2 i tak działa skuteczniej, bo nie przepuszcza próby aż do aplikacji.
5. Odciąż śledzenie połączeń
Ten punkt bywa pomijany, a wyjaśnia awarie, które wyglądają jak atak wolumetryczny, choć nim nie są. Kernel zakłada dla ruchu UDP wpisy w śledzeniu połączeń (conntrack), a przy podrobionych adresach nadawcy każdy adres oznacza nowy wpis. Kiedy tablica jest pełna, kernel odrzuca pakiety bez różnicy: atak i twoi gracze wylatują razem. Najskuteczniejszym krokiem jest w ogóle nie śledzić ruchu gry, bo silnik zarządza swoimi sesjami sam:
table inet raw {
chain prerouting {
type filter hook prerouting priority raw; policy accept;
udp dport { 27015, 27020 } notrack
}
chain output {
type filter hook output priority raw; policy accept;
udp sport { 27015, 27020 } notrack
}
}
W iptables odpowiednikiem jest iptables -t raw -A PREROUTING -p udp --dport 27015 -j NOTRACK oraz ta sama linia dla OUTPUT z --sport. Port potrzebuje potem wyraźnego otwarcia, bo bez śledzenia nie zadziała już żadna reguła sprawdzająca istniejący stan połączenia.
6. Bufor odbiorczy i parametry kernela
Jeśli pakiety docierają szybciej, niż proces serwera je odbiera, bufor odbiorczy się przepełnia. Dla graczy wygląda to jak utrata pakietów, mimo że łącze jest wolne. Trochę powietrza da plik w /etc/sysctl.d/, aktywowany przez sysctl -p:
net.core.rmem_max = 16777216
net.core.rmem_default = 1048576
net.core.netdev_max_backlog = 16384
Czy te wartości są potrzebne, zdradzi sam kernel: jeśli UdpRcvbufErrors w nstat -az rośnie, to znaczy, że działają. Jeśli licznik stoi na zerze, zmiana niczego nie da. To zapas, a nie ochrona.
7. Środki po stronie anti-cheata i pluginów
Sporą część awarii zgłaszanych jako DDoS wcale nimi nie jest. To wywalanie się procesu, które pojedynczy klient wywołuje kilkuset pakietami, bo w binarce serwera albo w jakimś rozszerzeniu stoi otworem luka. Nie pomoże na to żadna przepustowość, tylko utrzymanie:
- Trzymaj binarkę serwera w aktualnej wersji. Aktualizacje poza zawartością gry łatają także błędy sieciowe. Serwer, który został dwie wersje w tyle, stoi otworem dla znanych wzorców wywalania procesu.
- Dopasuj rozszerzenia do wersji silnika. Dla CS:GO i Garry's Mod typowym fundamentem są Metamod:Source oraz SourceMod; dla Counter-Strike 2 SourceMod nie osiągnął jeszcze tej samej dojrzałości, w użyciu są tam Metamod:Source w wersjach rozwojowych oraz CounterStrikeSharp. Niedopasowane rozszerzenie to najczęstsza przyczyna awarii po aktualizacji.
- Mniej rozszerzeń. Każdy plugin to kod w tym samym procesie, a rozszerzenia z własnymi usługami webowymi otwierają kolejne porty i często publikują dokładnie ten adres, który chcesz chronić.
- W Garry's Mod ogranicz wiadomości sieciowe. Najbardziej znanym strzałem we własną stopę jest menu, które bez żadnego limitu nasłuchuje na
net.Receive: klient wysyła wiadomość w pętli i sam jeden zatyka serwer.
local last = {}
net.Receive("moje_menu", function(len, ply)
if last[ply] and CurTime() - last[ply] < 0.5 then return end
last[ply] = CurTime()
end)
hook.Add("PlayerDisconnected", "moje_menu_cleanup", function(ply)
last[ply] = nil
end)
Również przy Garry's Mod: sv_allowcslua 0 uniemożliwia klientom wykonywanie własnego kodu Lua. Bany trzeba zapisywać trwale, inaczej po restarcie ich nie ma: tytuły na silniku Source mają do tego banid i writeid oraz addip i writeip; co daje twoja wersja serwera, pokaże find ban.
8. Lista serwerów, whitelista i własny adres
Publiczny serwer Counter-Strike potrzebuje Game Server Login Token, ustawianego przez sv_setsteamaccount. Bez tego tokenu pozostaje niezarejestrowany i nie pojawia się na żadnej publicznej liście. Dla zamkniętej grupy jest to akurat skuteczne rozwiązanie: ustaw sv_password, zrezygnuj z rejestracji i przekaż adres wyłącznie własnym graczom. Dla serwera publicznego to żadna opcja: serwer, którego nikt nie znajdzie, świeci taką samą pustką jak serwer offline. Prawdziwej whitelisty silnik nie ma, dokłada się ją rozszerzeniami.
Adresu serwera gry zmienić się nie da, za to wszystkiego obok już tak: atakujący często znajduje od razu całe otoczenie, od serwera WWW przez hosta bota Discord aż po dostęp do panelu. Te adresy nie powinny trafiać do tego samego ogłoszenia co adres serwera ani zostawać w starych wpisach DNS.
9. Zbieraj dane, żeby w trakcie ataku nie zgadywać
W trakcie ataku najważniejsze pytanie brzmi: ile tego dociera i na którym porcie. Wystarczą trzy polecenia:
ip -s link show eth0
nstat -az | grep -i udp
tcpdump -ni eth0 -c 200 "udp port 27015 and udp[8:4] = 0xffffffff"
Pierwsze polecenie pokazuje pakiety, błędy i odrzucenia dla każdego interfejsu; wykonaj je dwa razy w odstępie dziesięciu sekund, a dostaniesz tempo zamiast wartości bezwzględnej. Trzecia linia pokazuje wyłącznie pakiety bezpołączeniowe, czyli tę klasę, którą wykorzystuje zalew zapytań. Trzymaj taki zrzut krótko, bo pod obciążeniem sam kosztuje czas procesora. Jeśli licznik zapełnia się w kilka sekund, choć prawie nikt nie jest połączony, masz swoją odpowiedź.
Gdzie kończy się ochrona własnymi siłami
Teraz część uczciwa. Wszystko opisane do tej pory zadziała dopiero wtedy, gdy pakiety dotarły już na twoją kartę sieciową. Przy najmniejszym możliwym rozmiarze pakietu łącze 1 Gbit/s przenosi około 1,49 miliona pakietów na sekundę, a łącze 10 Gbit/s około 14,88 miliona. To granica fizyczna, niezależna od procesora, kernela i firewalla.
Po drugiej stronie stoją realne ataki. Dwa przykłady z ruchu produkcyjnego w KernelHoście, oba odfiltrowane w czasie rzeczywistym: flood UDP na serwer gry na porcie 7777/UDP z ponad 112,2 Gbit/s i ponad 8,7 miliona pakietów na sekundę oraz atak wielowektorowy na serwer głosowy na porcie 9987/UDP z ponad 473,4 Gbit/s i ponad 41,5 miliona pakietów na sekundę.
Przelicz to na swoje łącze: 473,4 Gbit/s to około 470-krotność łącza 1 Gbit/s i wciąż około 47-krotność łącza 10 Gbit/s. Twoja reguła może być całkowicie poprawna, a mimo to nigdy nie zostanie wykonana, bo straty powstają wcześniej, na routerze przed serwerem. Na długo zanim łącze się zapcha, kończy się zresztą procesor, bo każdy pakiet kosztuje przebieg przez stos sieciowy, nawet jeśli zaraz potem zostaje odrzucony.
Dlatego dwa rozpowszechnione hamulce awaryjne nie zadowalają. Null-routing (blackholing) zdejmuje atakowany adres IP z sieci i kończy wprawdzie atak, ale razem z nim twój serwer. Reaktywne przekierowanie ruchu kosztuje w czasie przełączania dokładnie te minuty, w których rozstrzyga się mecz. Skuteczne jest wyłącznie filtrowanie, które działa nieprzerwanie w sieci przed serwerem.
Co KernelHost stawia przed serwerem
Stała ochrona, która działa na każdym serwerze
Ochrona DDoS w KernelHoście jest zbudowana dwustopniowo i działa nieprzerwanie, bez włączania czegokolwiek z twojej strony. Pierwszy stopień to globalna sieć scrubbingowa o pojemności mitygacji 17 Tbps, która przechwytuje ataki wolumetryczne blisko ich źródła, na długo zanim dotrą do centrum danych. Drugi stopień to filtrowanie Arbor w czasie rzeczywistym o wydajności 3,2 Tbps bezpośrednio na miejscu we Frankfurcie nad Menem, które przejmuje pracę precyzyjną i odrzuca złożone wzorce na warstwach od 3 do 7.
Decydujące są dwie rzeczy. Po pierwsze, filtrowanie działa bez przerwy, nie ma więc czasu przełączania, w którym twoi gracze wylatują. Po drugie, nie stosuje się null-routingu: atakowany adres IP zostaje w sieci, odpadają wyłącznie szkodliwe pakiety. Ochrona jest zawarta w każdym pakiecie serwerowym bez dopłaty, bez osobnego pakietu ochronnego i bez konfiguracji. Serwery stoją w maincubes Premium Datacenter we Frankfurcie nad Menem (Niemcy), a dostawcą jest KernelHost GmbH z siedzibą w Wiedniu (Austria). Które gry i protokoły są objęte ochroną, wylicza wpis Ochrona DDoS serwerów gier w czasie rzeczywistym.
Advanced DDoS Protection dla projektów pod stałym ostrzałem
Niektóre projekty są atakowane celowo przez całe tygodnie, zmiennymi wzorcami i zawsze dokładnie w terminie meczu. Dla takich przypadków jest Advanced DDoS Protection od 50,00 € miesięcznie, w modelu PrePaid i bez minimalnego okresu umowy. Daje trzy rzeczy, których wliczona stała ochrona nie oferuje:
- Dedykowany chroniony adres IP. Twój serwer zostaje na niego przełączony w naszej sieci, po twojej stronie nie trzeba niczego przebudowywać.
- Samodzielnie zarządzane reguły ochrony na port i protokół. W panelu klienta ustalasz, który port ma być filtrowany jakim profilem, na przykład 27015/UDP inaczej niż 27020/UDP. Zmiany działają w czasie rzeczywistym, bez zgłoszenia i bez czekania.
- Profil ochrony dopasowany do konkretnej gry. Zarówno dla Counter-Strike 2 i tytułów na silniku Source, jak i dla ponad 40 innych gier oraz protokołów, do tego swobodne profile TCP i UDP dla zmodyfikowanych serwerów.
Także tutaj obowiązuje model PrePaid: bez minimalnego okresu umowy, bez okresu wypowiedzenia, bez umowy i bez opłaty aktywacyjnej. Kiedy fala ataków minie, po prostu nie przedłużasz.
Porównanie obu stopni ochrony
| Cecha | Wliczona stała ochrona | Advanced DDoS Protection |
|---|---|---|
| Cena | zawarta w każdym pakiecie serwerowym, bez dopłaty | od 50,00 € miesięcznie, PrePaid bez minimalnego okresu umowy |
| Aktywacja | aktywna od pierwszej minuty, nic do skonfigurowania | zamawiasz, dostajesz chroniony adres IP, serwer zostaje przełączony |
| Adres IP | adres IP serwera z frankfurckiej sieci | dodatkowy dedykowany chroniony adres IP |
| Filtrowanie | 17 Tbps globalnego scrubbingu, do tego 3,2 Tbps filtrowania Arbor w czasie rzeczywistym we Frankfurcie nad Menem | to samo filtrowanie, do tego własne reguły na port i protokół |
| Zmiana reguł | utrzymywane przez KernelHost, dostrajanie przez zgłoszenie | samodzielnie w panelu klienta, skutkuje w czasie rzeczywistym |
| Profile gier | ponad 40 gier i protokołów | profil wybierany osobno dla każdego portu, także dla zmodyfikowanych serwerów |
| Null-routing podczas ataku | nie | nie |
| Pasuje do | każdego serwera, od pierwszego meczu | projektów ostrzeliwanych stale i celowo |
Typowe błędy i ich rozwiązania
Serwer zniknął z przeglądarki serwerów, ale działa dalej: najczęściej 27015/UDP został zablokowany ogólnie albo obłożony zbyt ciasnym limitem, a ponieważ ruch gry i zapytania dzielą ten sam port, zgrubna reguła trafia w jedno i drugie. Pracuj zamiast tego z dopasowaniem do pakietów bezpołączeniowych. Jeśli serwera brakuje mimo osiągalnego portu, sprawdź sv_setsteamaccount.
Wszyscy gracze mają wysoki ping, a łącze nie jest wysycone: to wskazuje na liczbę pakietów, a nie na wolumen. Obejrzyj odrzucone pakiety w ip -s link show oraz liczniki UDP w nstat -az. Jeśli w logu systemowym stoi nf_conntrack: table full, wyjmij port gry przez notrack.
Reguła firewalla jest poprawna, a mimo to nie działa: wtedy wysycone jest łącze przed serwerem. Reguła, która nigdy nie zostaje wykonana, bo pakiet odpadł już na routerze przed nią, nie zdziała nic. Od tego miejsca pomaga wyłącznie filtrowanie w sieci.
Po włączeniu firewalla nie ma już dostępu przez SSH: zaloguj się przez konsolę VNC w panelu klienta (IPMI ani iDRAC nie ma) i wyłącz tam firewalla.
Atak milknie po zmianie adresu IP i wraca po jednym, dwóch dniach: to normalny przebieg, bo twój serwer sam publikuje nowy adres, gdy tylko znów się zarejestruje. Zmiana adresu IP daje godziny, a nie rozwiązanie.
Serwer wywala się w sposób powtarzalny, a przepustowość nie zwraca uwagi: najczęściej to nie DDoS, tylko błąd w jakimś rozszerzeniu albo przestarzała wersja serwera.
Na serwerze wykonują się cudze polecenia administracyjne: to nie DDoS, tylko przejęty dostęp RCON. Natychmiast zmień hasło i ogranicz port.
Jeśli właśnie jesteś atakowany
Jeśli twój serwer działa już w KernelHoście, filtrowanie jest stale aktywne. Gdybyś mimo to zauważył coś nietypowego, załóż zgłoszenie wsparcia, żeby nasz zespół dostroił reguły filtrowania dla twojego adresu IP. Przy trwającym ataku dotrzesz do nas dodatkowo przez awaryjny czat WhatsApp pod numerem +43 650 8209883.
Podaj od razu cztery informacje: adres IP, port, przedział czasu w twojej strefie czasowej oraz to, co widzisz (gracze wylatują, serwera nie ma w przeglądarce, wysoki ping). To oszczędza jedną rundę dopytywania, a ona liczy się wtedy, gdy trwa mecz.
Najczęstsze pytania
Mój serwer CS2 nagle zniknął. Czy to atak DDoS?
Czy mogę po prostu zablokować port zapytań?
Których portów naprawdę potrzebuje serwer CS2 albo Source?
Czy zmiana adresu IP pomaga przeciwko atakowi?
Dlaczego moja reguła firewalla nic nie daje?
Zamknąłem sobie dostęp firewallem. Jak wrócić na serwer?
Czy KernelHost zdejmuje mój adres IP z sieci podczas ataku?
Kiedy potrzebuję Advanced DDoS Protection?
2026 KernelHost GmbH. Wszelkie prawa zastrzeżone. Ten poradnik jest chroniony prawem autorskim. Publikowanie go w innych serwisach, w całości, we fragmentach lub w zmienionej formie, wymaga naszej pisemnej zgody. Cytaty z podaniem źródła i z linkiem są jak najbardziej mile widziane.

