Left 4 Dead 2 서버를 DDoS 공격으로부터 방어하기

게시일 읽는 시간 42분

Left 4 Dead 2 서버에 실제로 필요한 포트는 무엇인지, 27015/UDP의 A2S 조회를 자기 플레이어를 내쫓지 않으면서 어떻게 제한하는지, 로비 시스템이 접속 필터로 무엇을 해 주는지, 그리고 어느 공격 규모부터 서버 앞단 네트워크의 필터링만이 통하는지 정리했습니다.

Left 4 Dead 2 서버는 편한 시점에 멈추는 일이 드뭅니다. 캠페인의 마지막 구간에서 멈추고, Versus 대전의 두 번째 라운드에서 멈추고, 차단된 플레이어가 세 번째로 거부된 바로 그 순간에 멈춥니다. 지금 공격을 받고 있는 운영자에게 필요한 것은 네트워크 기술에 관한 원론이 아니라 순서입니다. 이 글은 먼저 기본 도구로 아직 대응이 가능한 동안 Left 4 Dead 2 서버를 DDoS 공격으로부터 방어하는 방법을 보여 주고, 그다음 그 방법이 물리적으로 끝나는 지점을, 마지막으로 그 앞단 네트워크에서 무엇이 이뤄져야 하는지 설명합니다.

모든 내용은 SteamCMD의 애플리케이션 ID 222860으로 설치한 전용 서버(srcds)를 기준으로 하며, Debian 12, Debian 13, Ubuntu 22.04 LTS 또는 Ubuntu 24.04 LTS에서 운영하는 경우를 다룹니다. 명령은 root 기준으로 적었으니, 일반 사용자라면 앞에 sudo를 붙이세요. 순서를 정하는 한 가지를 먼저 말씀드립니다. 공격이 진행되는 중에 무엇이든 무턱대고 바꾸지 마시고, 측정값을 확보하기 전에는 서버를 재시작하지 마세요. 공격이 끝나면 그 값은 사라집니다.

Left 4 Dead 2 서버가 DDoS 공격의 표적으로 노려지는 이유

슬롯이 64개인 슈팅 게임과 다른 점은 한 판의 크기에 있습니다. 협동 캠페인에는 생존자 슬롯이 네 개, Versus 대전에는 양 진영을 합쳐 여덟 개가 있습니다. 그래서 장애는 개별 플레이어가 아니라 언제나 판 전체를 덮칩니다. 다섯 구간 가운데 세 번째에서 캠페인이 끊기면 참가자 모두의 저녁이 끝납니다. 바로 이 점이 공격을 일으키는 쪽에게 매력적으로 작용합니다. 실력도 이렇다 할 비용도 들이지 않으면서 상대편의 한 시간을 없애 버리기 때문입니다.

여기에 구조가 더해집니다. Left 4 Dead 2는 Source 엔진에서 동작하고, Source 서버는 IP 주소와 포트로 공개적으로 찾을 수 있습니다. 이것은 실수가 아니라 전제입니다. 조회에 응답하지 않는 서버는 어떤 목록에도 오르지 않고 어떤 로비도 찾아내지 못합니다. 그래서 질문은 공격자가 여러분의 주소를 아는지가 아니라, 그 주소로 쏘았을 때 무슨 일이 벌어지는지입니다. 게임 트래픽은 UDP로 흐르고, UDP에는 요구할 수 있는 연결 수립 절차가 없으며, 출발지 주소는 위조할 수 있습니다. 이때 기술적으로 무엇이 일어나는지는 DDoS 공격이란 무엇인가? 글에서 설명합니다.

세 번째 요소는 Left 4 Dead 2에만 있고 Counter-Strike, Garry's Mod, Team Fortress 2에는 대응물이 없습니다. 대부분의 플레이어가 서버 브라우저가 아니라 로비 시스템을 통해 들어온다는 점입니다. 최대 네 명으로 이뤄진 로비가 Steam 매치메이킹을 통해 전용 서버로 배정되고, 그 서버는 이를 위해 예약을 받습니다. 이 방식은 여러분이 가진 가장 효과적인 접속 필터이면서 동시에 추가 공격 표면이기도 합니다. 둘 다 아래에서 자세히 다룹니다.

실제로 문제가 되는 포트

Left 4 Dead 2 서버는 게임을 이루는 모든 것에 UDP 포트를 정확히 하나만 씁니다. 기본값은 27015이고, 시작 줄의 -port 또는 +hostport로 정합니다.

./srcds_run -game left4dead2 -console -nohltv \
  -port 27015 \
  +ip 203.0.113.10 \
  +maxplayers 4 \
  +exec server.cfg \
  +map c1m1_hotel
포트와 프로토콜 용도 공개 네트워크에 열어야 하나요
27015/UDP 같은 포트에서 게임 트래픽과 A2S 서버 조회 예, 이 포트가 없으면 게임이 성립하지 않음
27015/TCP rcon_password가 설정된 경우의 RCON 아니요, 본인 주소만 허용
27005/UDP 클라이언트 포트, 플레이어 쪽에서 나감 아니요, 서버에서는 열 필요 없음
27020/UDP SourceTV, -hltv 또는 +tv_enable 1을 쓸 때만 실제로 중계할 때만
27016, 27017 및 그 이후 같은 호스트의 추가 인스턴스 인스턴스별로 하나씩, 구간으로 열지 않음
80/TCP와 443/TCP 빠른 다운로드(sv_downloadurl), 같은 호스트에 있는 경우 그 호스트에서 웹 서버가 돌아갈 때만
22/TCP SSH 접속 아니요, 본인 주소로 제한

이 표의 첫 줄이 문제의 핵심입니다. 게임 트래픽과 서버 조회가 27015/UDP를 함께 쓰며, Left 4 Dead 2에는 분리된 쿼리 포트가 없습니다. 이 포트를 일괄 차단하거나 거칠게 속도 제한하는 운영자는 같은 동작으로 자기 플레이어를 내쫓고, 공격자가 원하는 방향으로 공격을 끝맺어 줍니다.

A2S 요청은 수십 바이트짜리 패킷 하나이고, 응답은 그보다 몇 배 큽니다. UDP에서는 출발지 주소를 위조할 수 있으므로 여러분의 서버는 피해자에 그치지 않고 증폭기가 됩니다. 공격자가 표적의 주소를 실어 남의 게임 서버에 조회를 보내고, 그 응답을 표적으로 몰아가는 것입니다. Valve는 2020년 12월 A2S_INFO에 선행 요구 절차(S2C_CHALLENGE)를 추가했고, 조회하는 쪽은 응답을 받기 전에 이 값을 되돌려 보내야 합니다. 이것으로 반사 공격은 누그러졌지만 끝나지는 않았습니다. 오래된 조회 프로그램은 여전히 응답을 받기 때문입니다.

돈을 쓰기 전에 직접 할 수 있는 일

이어지는 부분은 비용이 들지 않고, 서버가 어디에 놓여 있든 해 둘 만합니다. 볼류메트릭 공격을 대신 막아 주지는 않지만 작고 중간 규모의 공격을 허탕으로 만들고, DDoS 공격으로 잘못 신고되는 장애를 없애 줍니다.

1. 현황 파악: 실제로 무엇이 대기하고 있나요?

규칙을 한 줄이라도 쓰기 전에 어떤 서비스가 외부에서 닿는지 확인하세요. 오래 운영된 Left 4 Dead 2 서버에서는 거의 언제나 예상보다 많습니다. srcds 외에 캠페인용 웹 서버, 통계 데이터베이스, 때로는 Versus용 두 번째 서버가 함께 돌아가기 때문입니다.

ss -lntup

127.0.0.1이나 ::1에 바인딩된 것은 방화벽 규칙이 필요하지 않습니다. 0.0.0.0이나 [::]에서 대기하는 것은 인터넷에서 접근할 수 있습니다. 공격자의 시선은 외부에서 실행하는 포트 스캔이 보여 주며, 경험상 그 결과는 본인의 예상과 어긋납니다.

nmap -Pn -sU -sT -p 27000-27050,80,443,3306 YOUR.SERVER.IP.ADDRESS

기반을 새로 구축했거나 그 과정을 되짚어 보고 싶다면 SteamCMD로 게임 서버 설치하기 글이 SteamCMD에서 srcds 구동까지의 길을 설명합니다.

2. srcds가 실제로 필요한 포트만 열어 두기

외부로 UDP 포트 하나, 본인 주소를 위한 TCP 포트 하나, 그 이상은 없습니다. RCON은 열린 인터넷에 있을 것이 아닙니다. RCON을 가진 사람은 맵을 바꾸고, 모든 플레이어를 차단하고, 서버를 멈춥니다.

ufw allow 27015/udp comment "L4D2 게임 포트와 A2S"
ufw allow from 203.0.113.10 to any port 27015 proto tcp comment "RCON"
ufw allow from 203.0.113.10 to any port 22 proto tcp comment "SSH"
ufw default deny incoming
ufw default allow outgoing
ufw --force enable

203.0.113.10은 본인 주소로 바꾸세요. 이 주소가 자주 바뀐다면 상시 허용이 아니라 SSH 포워딩이 답입니다. 방화벽을 켜는 순서가 스스로 접속이 막히는지를 결정합니다. 복구 방법까지 포함한 순서는 스스로 접속이 막히지 않게 UFW 방화벽 설정하기에 있습니다. 그래도 막혔다면, KernelHost의 KVM 루트 서버와 Dedicated Server에는 IPMI도 iDRAC도 없고, 고객 포털의 VNC 콘솔로 서버에 접근하며, 이 콘솔은 게스트 시스템의 네트워크 스택에 매달려 있지 않습니다.

3. 로비 검색에서 빠지지 않으면서 A2S 쿼리 부담 덜기

이 주제에서 가장 비싼 실수가 여기 있습니다. 게임 트래픽과 서버 조회가 같은 포트를 쓰기 때문에, 제동은 포트가 아니라 두 패킷 부류를 구별해야 합니다.

Steam 게임 서버 기반은 2020년 12월의 변경 이후 자체 제한을 함께 제공하며, 이 값은 서버를 띄우기 전에 환경 변수로 설정합니다. STEAM_GAMESERVER_RATE_LIMIT_200MS=N은 한 출발지 주소에서 200밀리초 구간 안에 N개를 넘는 연결 없는 패킷(A2S_INFO, A2S_RULES, A2S_PLAYERS)이 들어오면 그것을 폐기합니다. Valve는 쓸 만한 범위로 25에서 75를 제시하며, 기본값은 제한이 꺼진 상태입니다.

export STEAM_GAMESERVER_RATE_LIMIT_200MS=50
./srcds_run -game left4dead2 -console -port 27015 +exec server.cfg +map c1m1_hotel

systemd 유닛에서는 같은 값을 [Service] 절에 Environment=STEAM_GAMESERVER_RATE_LIMIT_200MS=50으로 넣어야 하며, 그러지 않으면 다음 재시작 뒤에 사라집니다. 이 제동은 서버 빌드가 최신 Steamworks 기반을 포함할 때만 동작하고, 여러분의 회선이 아니라 서버의 연산 시간을 지켜 줍니다. 패킷은 이미 도착해 있기 때문입니다.

한 단계 아래에서는 같은 트래픽을 커널에서 분리할 수 있습니다. Source 엔진의 연결 없는 패킷, 곧 서버 조회와 연결 수립은 모두 네 바이트가 전부 1로 채워진 값(0xffffffff)으로 시작하고, 이미 접속한 플레이어의 트래픽에는 이 머리가 없습니다. 여기에 nftables로 출발지 주소별 속도 제한을 걸 수 있습니다.

table inet l4d2 {
    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
    }
}

이 파일은 nft -f로 불러옵니다. 우선순위 -10은 이 규칙이 UFW의 필터 체인보다 먼저 동작하게 하고, @th,64,32는 UDP 헤더 뒤의 첫 네 바이트를 읽습니다. 처음에는 넉넉하게 시작하고, 정상 조회가 확실히 통과하는 것을 확인한 뒤에 한도를 좁히세요. 서버 목록에 올라가는 여러분의 항목이 여기에 매달려 있습니다.

4. 로비 시스템을 접속 필터로 쓰기

이것은 Left 4 Dead 2와 그 전작만 가진 지렛대입니다. 매치메이킹을 거치지 않은 연결을 아예 받을지 서버가 직접 결정합니다. server.cfg의 지시어 네 개가 이를 정합니다.

sv_allow_lobby_connect_only 1
sv_search_key "your-own-key"
sv_steamgroup "103582791400000000"
sv_steamgroup_exclusive 2
  • sv_allow_lobby_connect_only 1은 매치메이킹 로비에서 오는 접속만 허용합니다. 개발자 콘솔의 connect 203.0.113.10:27015와 Steam 초대는 거부됩니다. 값 0은 둘 다 허용합니다.
  • sv_search_key는 자유롭게 정하는 검색 키입니다. 같은 키가 설정된 로비만 매치메이킹으로 이 서버를 찾습니다. 이 키가 없으면 공개 검색에는 나타나지 않습니다.
  • sv_steamgroup은 서버를 Steam 그룹에 연결하고, 그 그룹의 그룹 서버 목록에 나타나게 합니다.
  • sv_steamgroup_exclusive에는 세 단계가 있습니다. 0은 누구나 허용하고, 1은 0과 같게 동작하지만 로비를 통한 접속을 요구하며, 2는 그룹 구성원과 IP 주소를 통한 직접 접근만 통과시킵니다.

고정된 커뮤니티라면 검색 키와 sv_steamgroup_exclusive 2의 조합이 이 게임이 가진 가장 효과적인 무료 접속 필터입니다. 공개 서버는 이를 쓸 수 없습니다. 아무도 찾지 못하는 서버는 오프라인인 서버와 똑같이 비어 있기 때문입니다.

그리고 광고 문구가 즐겨 빼놓는 부분입니다. 이 지시어들은 여러분의 게임 로직을 지키고 회선은 지키지 않습니다. 27015/UDP를 플러딩하는 공격자는 애초에 들어올 생각이 없습니다. 그 패킷은 거부되지만 그래도 이미 도착했고, 대역폭을 소모했고, 네트워크 스택을 한 번 통과하는 비용을 치르게 했습니다. 버리는 계정으로 몰려오는 접속 플러드에는 sv_allow_lobby_connect_only 1이 훌륭하게 작동하고, booter에는 전혀 작동하지 않습니다.

5. 로비 예약, 그리고 sv_force_unreserved가 더 나은 선택인 경우

로비 예약은 매치메이킹 로비가 여러분의 서버를 일정 시간 동안 점유하는 것입니다. 예약이 유지되는 동안 그 서버는 다른 로비에게 이미 배정된 것으로 취급되고, 예약은 한동안 지난 뒤에야 스스로 풀립니다. 슬롯이 네 개인 서버에서 이것은 빠듯한 자원입니다. 슬롯이 32개나 64개인 슈팅 게임과 달리, 한 판을 막는 데 아주 적은 양이면 충분합니다.

서버를 매치메이킹으로 운영하지 않는다면 이 표면을 통째로 없앨 수 있습니다.

sv_force_unreserved 1
sv_allow_lobby_connect_only 0

sv_force_unreserved 1은 서버가 로비 시스템의 예약 요청에 더 이상 응답하지 않고, 예약 표시가 붙은 접속을 거부하게 만듭니다. L4DToolZ로 협동 슬롯을 네 개보다 많이 운영한다면 이 설정은 어차피 필수입니다. 그러지 않으면 처음 네 슬롯이 채워지는 순간 로비가 예약을 받고, 남은 슬롯에는 아무도 닿을 수 없습니다. 반대급부는 분명합니다. 그 뒤로 플레이어는 서버 브라우저나 connect로만 들어올 수 있습니다.

두 운영 방식 가운데 하나를 의식적으로 고르세요. 반쯤 열린 매치메이킹과 반쯤 열린 직접 접속의 혼합은 두 단점을 합친 형태입니다.

6. RCON 보호하기

약한 비밀번호가 걸린 열린 RCON 포트는 DDoS 문제가 아니라 탈취입니다. rcon_password를 비운 채로도, 추측할 수 있는 값으로도 두지 마세요. openssl rand -base64 32의 결과면 충분합니다. Source 타이틀은 로그인 시도에 맞서는 제동 장치도 함께 제공합니다.

rcon_password "A_RANDOM_VALUE_HERE"
sv_rcon_minfailures 3
sv_rcon_maxfailures 5
sv_rcon_minfailuretime 30
sv_rcon_banpenalty 1440

이렇게 하면 30초 안에 세 번 실패한 주소가 하루 동안 차단됩니다. 서버 콘솔에서 find sv_rcon을 실행하면 여러분의 빌드가 이 변수들 가운데 어떤 것을 아는지 보여 줍니다. 그래도 2절의 방화벽 제한이 더 효과적입니다. 시도 자체가 애플리케이션까지 닿지 못하게 막기 때문입니다. RCON이 필요하지 않다면 비밀번호를 비워 두세요. 그러면 27015의 TCP 쪽은 대기하지 않습니다.

7. 커스텀 캠페인을 게임 포트로 내보내지 말고 밖으로 옮기기

커스텀 캠페인은 Left 4 Dead 2가 열다섯 해가 지나서도 계속 플레이되는 이유이면서, 동시에 Counter-Strike에는 이런 형태로 없는 부하의 원천입니다. 캠페인 하나는 맵, 모델, 텍스처, 사운드가 담긴 VPK 패키지이고, 경기용 맵 하나의 무게와는 비교가 되지 않습니다.

플레이어에게 편한 길은 Steam Workshop입니다. 그러면 패키지가 여러분의 서버가 아니라 Steam에서 오고, 여러분의 대역폭은 전혀 들지 않습니다. 개별 파일을 직접 내보낸다면, 그 배포는 게임 포트가 아니라 웹 서버가 맡아야 합니다.

sv_allowdownload 1
sv_allowupload 0
sv_downloadurl "https://cdn.example.org/l4d2/"
sv_consistency 1

sv_downloadurl에 쓸 파일은 bzip2 아카이브로 웹 서버에 올려야 하므로, mymap.bsp는 mymap.bsp.bz2가 됩니다. sv_downloadurl이 없으면 srcds가 게임 연결을 통해 파일을 직접 보내고, 그러면 새 플레이어의 연결 시도 한 번마다 다운로드 한 벌이 온전히 들고, 다운로드 도중의 중단도 마찬가지입니다. 이것은 회선을 채우는 대단히 값싼 방법이고, 어떤 통계에서도 공격처럼 보이지 않습니다.

실무에서 아픈 세 가지를 덧붙입니다. sv_allowupload 0은 설정해 두어야 합니다. 클라이언트에서 서버로 올리는 업로드는 필요하지 않기 때문입니다. sv_downloadurl용 웹 서버가 게임과 같은 호스트에 있으면 다운로드와 게임 트래픽이 같은 회선과 같은 IP 주소를 나눠 쓰게 되고, 443/TCP를 향한 공격이 진행 중인 여러분의 판까지 때립니다. 그리고 sv_consistency 1은 공격에 맞서는 방어가 아니라 어긋난 클라이언트 파일에 맞서는 장치입니다. 어떤 캠페인이 이것 때문에 확실히 시작되지 않을 때만 끄세요.

8. SourceMod, Metamod, 그리고 확장 기능

DDoS로 신고되는 장애 가운데 상당수는 DDoS가 아닙니다. 서버 바이너리나 확장 기능에 구멍이 열려 있어서 한 명의 클라이언트가 일으키는 크래시와 부하 급증입니다. 여기에는 대역폭이 아니라 관리가 답입니다.

  • Metamod:Source와 SourceMod를 엔진 버전에 맞게 유지하세요. Left 4 Dead 2는 계속 업데이트를 받고, 맞지 않는 확장 기능은 업데이트 직후 크래시의 가장 흔한 원인입니다.
  • 직접 손대는 대신 Left4DHooks를 쓰세요. L4D2 특유의 이벤트가 이 확장 기능에 한데 묶여 제공됩니다. 같은 함수에 직접 손을 대는 것은 특정 패킷 순서에서 멈춰 버리는 서버 바이너리로 가는 가장 빠른 길입니다.
  • L4DToolZ는 의식적으로만 쓰세요. 이 확장 기능은 고정되어 있는 슬롯 상한을 올립니다. 슬롯이 하나 늘어날 때마다 연산 시간을 만드는 플레이어가 하나 늘고, 로비 시스템과 함께 쓸 때는 sv_force_unreserved 1이 필요합니다.
  • 확장 기능은 적게. 플러그인은 모두 같은 프로세스 안의 코드입니다. 자체 웹 서비스를 가진 확장 기능은 포트를 추가로 열고, 여러분이 지키려는 바로 그 주소를 공개하는 일이 잦습니다.

차단은 영구적으로 저장해야 하며, 그러지 않으면 재시작 뒤에 사라집니다. Source 타이틀은 이를 위해 banid와 writeid, 그리고 addip와 writeip를 제공하고, 생성된 파일은 exec banned_user.cfg와 exec banned_ip.cfg로 다시 읽어 들입니다.

9. 연결 추적과 수신 버퍼 부담 덜기

이 항목은 자주 간과되고, 볼륨 공격처럼 보이지만 실은 아닌 장애를 설명해 줍니다. 커널은 UDP 트래픽에 대해서도 연결 추적(conntrack)에 항목을 만들고, 출발지 주소가 위조되어 있으면 주소마다 새 항목이 생깁니다. 테이블이 가득 차면 커널은 구별 없이 패킷을 폐기하고, 공격과 여러분의 플레이어가 함께 튕겨 나갑니다. 현재 값과 상한은 한 번 보면 알 수 있습니다.

sysctl net.netfilter.nf_conntrack_count net.netfilter.nf_conntrack_max

가장 효과적인 조치는 게임 트래픽을 애초에 추적하지 않게 하는 것입니다. 엔진이 자기 세션을 스스로 관리하기 때문입니다.

table inet raw {
    chain prerouting {
        type filter hook prerouting priority raw; policy accept;
        udp dport 27015 notrack
    }
    chain output {
        type filter hook output priority raw; policy accept;
        udp sport 27015 notrack
    }
}

iptables에서는 iptables -t raw -A PREROUTING -p udp --dport 27015 -j NOTRACK이 이에 해당하고, OUTPUT에는 같은 줄을 --sport로 씁니다. 그 뒤로 이 포트에는 명시적인 허용이 필요합니다. 추적이 없으면 기존 상태를 확인하는 규칙은 더 이상 동작하지 않기 때문입니다. srcds가 가져가는 속도보다 패킷이 빨리 도착하면 수신 버퍼까지 넘치고, 플레이어에게는 비어 있는 회선에서 패킷 손실이 일어난 것처럼 보입니다.

net.core.rmem_max = 16777216
net.core.rmem_default = 1048576
net.core.netdev_max_backlog = 16384

이 파일은 /etc/sysctl.d/에 두고 sysctl -p로 적용합니다. 이 값들이 정말 필요한지는 커널이 직접 알려 줍니다. nstat -az의 UdpRcvbufErrors가 올라간다면 값이 효과를 냅니다. 카운터가 0에 머물러 있으면 이 조정은 아무것도 바꾸지 않습니다. 이것은 여유분이고 방어가 아닙니다.

10. 공격 중에 추측하지 않도록 측정하기

공격 중에 가장 중요한 질문은 이것입니다. 얼마나 도착하는지, 어느 포트로 오는지, 조회 트래픽인지 게임 트래픽인지. 명령 네 개로 충분합니다.

ip -s link show eth0
nstat -az | grep -i udp
sar -n DEV 1 10
tcpdump -ni eth0 -c 200 "udp port 27015 and udp[8:4] = 0xffffffff"

첫 번째 명령은 10초 간격으로 두 번 실행하세요. 그러면 절대값이 아니라 전송률을 얻습니다. 마지막 줄은 연결 없는 패킷만, 곧 쿼리 플러드가 악용하는 바로 그 부류만 보여 줍니다. 접속한 사람이 거의 없는데 카운터가 몇 초 만에 다 차면 답을 얻은 것입니다. 캡처는 짧게 유지하세요. 부하 상태에서는 캡처 자체가 연산 시간을 먹습니다. 측정값을 해석하는 방법은 서버에서 DDoS 공격 알아내기에 있습니다.

다만 가장 중요한 단계는 거의 아무도 미리 하지 않는 것입니다. 모든 것이 정상으로 돌아가는 동안 기준값을 만들어 두는 것입니다. 평상시 값이 없으면 사건이 끝난 뒤에 초당 40,000 패킷이 많았던 것인지 그냥 Versus 서버가 꽉 찬 금요일 저녁이었던 것인지 말할 수 없습니다.

이 조치들이 한계에 이르는 지점

이제 솔직한 부분입니다. 지금까지 설명한 모든 것은 패킷이 여러분의 네트워크 카드에 도착한 뒤에야 동작합니다. 방화벽 규칙은 이미 케이블을 지나온 패킷을 두고 판단합니다. 그 패킷을 폐기할 수는 있지만, 보내지 않은 것으로 만들 수는 없습니다.

한번 계산해 보겠습니다. 일반적인 게임 서버는 1 Gbit/s 회선에 물려 있고, 이는 초당 125 메가바이트입니다. 패킷 크기가 가장 작을 때 이 회선은 초당 약 149만 개를, 10 Gbit/s 회선은 약 1,488만 개를 나릅니다. 이것이 CPU와 커널과 방화벽과 무관한 물리적 상한입니다. 일반적인 서버 커널은 프로세서와 네트워크 카드에 따라 초당 수십만 패킷을 처리한 뒤 폐기를 시작합니다. 그래서 회선을 3분의 1도 채우지 못하는 공격이 여러분의 서버를 멈춰 세울 수 있습니다. 폐기에 연산 시간이 다 들어가기 때문입니다. 운영자는 이것을 “사용률은 전혀 높지 않았는데 그래도 전부 죽었다”로 경험합니다.

이에 맞서는 것은 실제 공격입니다. KernelHost 운영에서 나온 두 사례이고, 둘 다 실시간으로 걸러 냈습니다. 7777/UDP의 게임 서버를 겨냥한 UDP 플러드가 112.2 Gbit/s를 넘고 초당 870만 패킷을 넘었으며, 9987/UDP의 음성 서버를 겨냥한 멀티 벡터 공격이 473.4 Gbit/s를 넘고 초당 4,150만 패킷을 넘었습니다. 이것을 여러분의 회선과 견줘 보세요. 473.4 Gbit/s는 1 Gbit/s 회선의 약 470배이고, 10 Gbit/s 회선으로 봐도 여전히 약 47배입니다.

그래서 널리 쓰이는 두 가지 비상 제동은 만족스럽지 않습니다. null-routing(블랙홀링)은 공격받는 IP 주소를 네트워크에서 빼내어 공격을 끝내지만 여러분의 서버도 함께 끝냅니다. 플레이어에게 그 결과는 공격이 성공한 것과 똑같습니다. 공격을 감지한 뒤에 경로를 돌리는 방식은 전환 시간 동안 캠페인의 승패가 갈리는 바로 그 몇 분을 잡아먹습니다. 효과가 있는 것은 서버 앞단 네트워크에서 상시 돌아가는 필터링뿐입니다.

KernelHost가 이에 맞서 제공하는 것

모든 서버에 포함된 상시 방어

KernelHost의 DDoS 방어는 두 단계로 구성되어 상시 동작하며, 여러분이 무엇을 켜거나 주문하거나 설정할 필요가 없습니다.

  • 1단계: 글로벌 스크러빙 네트워크의 17 Tbps 방어 용량. 볼류메트릭 공격은 발생지에 가까운 곳에서 정화되며, 데이터센터에 닿기 훨씬 전에 걸러집니다.
  • 2단계: 프랑크푸르트암마인의 3.2 Tbps Arbor 실시간 필터링. 서버 바로 앞에서 Layer 3부터 7까지의 프로토콜별 패턴을 찾아내 패킷 하나하나를 폐기합니다.

두 가지 특성이 결정적입니다. 첫째, 필터링이 상시 돌아가므로 플레이어가 튕겨 나가는 전환 시간이 존재하지 않습니다. 둘째, null-routing을 쓰지 않습니다. 공격받는 IP 주소는 네트워크에 그대로 남고, 폐기되는 것은 악성 패킷뿐입니다. 이 방어는 모든 서버 상품에 추가 요금 없이, 별도의 방어 상품 없이, 설치 작업 없이 포함되어 있고 서버 제공 시점부터 동작합니다. 서버는 프랑크푸르트암마인의 maincubes Premium Datacenter에 있습니다. 어떤 게임과 프로토콜이 포함되는지는 실시간 게임 서버 DDoS 방어에 정리되어 있습니다.

계속 공격받는 프로젝트를 위한 Advanced DDoS Protection

어떤 프로젝트는 어쩌다 한두 번이 아니라 표적이 되어 몇 주에 걸쳐, 바뀌는 패턴으로, 그것도 늘 약속된 캠페인 저녁에 맞춰 공격받습니다. 이런 경우를 위해 Advanced DDoS Protection이 월 50.00 EUR부터, PrePaid 방식으로, 최소 이용 기간 없이 제공됩니다. 차이는 용량이 더 커지는 데 있지 않고 통제권에 있습니다.

  • 전용 방어 IP 하나. 여러분의 서버는 자체 네트워크에서 이 주소로 전환되며, 여러분 쪽에서 손볼 것은 없습니다.
  • 포트와 프로토콜별로 직접 관리하는 방어 규칙. 어느 포트를 어떤 프로필로 필터링할지 고객 포털에서 정하므로, 27015/UDP를 캠페인을 내보내는 웹 서버와 다르게 다룰 수 있습니다.
  • 변경은 실시간으로 반영되며, 티켓도 대기 시간도 없습니다. 공격이 진행되는 중에도 값을 조정할 수 있습니다.
  • 해당 게임에 맞춘 방어 프로필. Left 4 Dead 2와 나머지 Source 타이틀은 물론 40종이 넘는 다른 게임과 프로토콜에도 준비되어 있고, 개조된 서버를 위한 자유 TCP 및 UDP 프로필도 함께 제공됩니다.

여기에도 PrePaid 모델이 적용됩니다. 최소 이용 기간도, 해지 통보 기간도, 계약도, 설치비도 없습니다. 공격의 물결이 지나가면 그냥 연장하지 않으시면 됩니다.

두 단계 비교

항목 포함된 상시 방어 Advanced DDoS Protection
요금 모든 서버 상품에 추가 요금 없이 포함 월 50.00 EUR부터, PrePaid, 최소 이용 기간 없음
활성화 서버 제공 시점부터 동작, 설정할 것 없음 주문하고 방어 IP를 받으면 서버가 전환됨
필터링 용량 17 Tbps 글로벌 스크러빙과 프랑크푸르트암마인의 3.2 Tbps Arbor 실시간 필터링 동일한 2단계 필터링에 자체 규칙까지
IP 주소 서버의 IP 주소 추가로 받는 전용 방어 IP
규칙 변경 KernelHost가 관리, 세부 조정은 티켓으로 고객 포털에서 직접, 실시간 반영
게임 프로필 Source 타이틀을 포함한 40종이 넘는 게임과 프로토콜 포트별로 프로필 선택, 개조된 서버도 가능
공격 중 null-routing 없음 없음
적합한 대상 첫 캠페인부터 모든 서버 표적이 되어 지속적으로 공격받는 프로젝트

대부분의 Left 4 Dead 2 프로젝트에는 포함된 상시 방어와 깔끔한 서버 설정이면 충분합니다. Advanced DDoS Protection은 누군가 이 일을 개인적으로 받아들였을 때의 답입니다.

자주 나오는 실수와 해결 방법

서버가 로비 검색에서 사라졌는데 서버 자체는 계속 돌아갑니다: 대개 27015/UDP를 일괄 차단했거나 속도 제한을 너무 좁게 잡은 경우입니다. 게임 트래픽과 조회가 같은 포트를 나눠 쓰므로 거친 규칙은 양쪽을 함께 때립니다. 대신 연결 없는 패킷에 대한 매칭으로 작업하세요. 포트에 닿을 수 있는데도 서버가 보이지 않는다면 sv_search_key, sv_steamgroup_exclusive, sv_lan 0, sv_region 255를 확인하고, 시작할 때 실수로 -nomaster가 붙지 않았는지도 보세요.

서버 콘솔에 “Invalid split packet length”가 계속 찍힙니다: 이것은 볼류메트릭 공격이 아니라 잘못 조립된 네트워크 패킷이 빠르게 연이어 도착하는 상황입니다. 트래픽은 아주 작게 유지되는데도 서버는 버벅입니다. 먼저 대역폭이 실제로 이상한지 확인하고, 서버 바이너리와 확장 기능을 최신 상태로 맞추세요. 여기에는 대역폭이 도움이 되지 않습니다.

모든 플레이어의 핑이 높은데 회선은 꽉 차지 않았습니다: 이것은 볼륨이 아니라 패킷 전송률을 가리킵니다. ip -s link show의 폐기된 패킷과 nstat -az의 UDP 카운터를 보세요. 시스템 로그에 nf_conntrack: table full이 있으면 게임 포트를 notrack으로 빼내세요.

방화벽 규칙이 올바른데도 효과가 없습니다: iptables -L INPUT -n -v로 매칭 카운터가 올라가는지 확인하세요. 0에 머물러 있으면 규칙이 UFW 체인 뒤에 있어 도달하지 못하거나 마지막 재시작 이후 사라진 것입니다. 카운터가 올라가는데도 아무것도 달라지지 않으면 서버 앞단 회선이 포화된 상태이고, 그 지점부터는 네트워크에서의 필터링만이 통합니다.

슬롯이 비어 있는데도 서버가 플레이어를 더 받지 않습니다: 대개 로비 예약이 걸려 있습니다. 서버를 매치메이킹으로 일관되게 운영하든지, sv_force_unreserved 1을 설정하고 플레이어를 서버 브라우저로 들어오게 하세요. L4DToolZ로 협동 슬롯을 네 개보다 많이 운영한다면 이 설정은 어차피 필수입니다.

새 플레이어의 로딩이 끝없이 이어지고 그동안 회선이 꽉 찹니다: 그렇다면 srcds가 캠페인 파일을 게임 포트로 직접 내보내고 있습니다. sv_downloadurl을 웹 서버로 설정하고 파일을 그곳에 bzip2 아카이브로 올리거나, 플레이어를 Steam Workshop으로 안내하세요.

IP 주소를 바꾼 뒤 공격이 멈췄다가 하루나 이틀 뒤에 돌아옵니다: 그것이 정상입니다. 서버가 다시 등록되는 순간 새 주소를 스스로 공개하고, 잊고 방치한 DNS 항목이나 상태를 표시하는 Discord 봇이 나머지를 해 줍니다. IP 주소 변경은 몇 시간을 벌어 주지만 해결책은 아닙니다.

서버에서 남이 보낸 관리 명령이 실행됩니다: DDoS 공격이 아니라 RCON 접속이 탈취된 상황입니다. 비밀번호를 즉시 바꾸고 27015의 TCP 쪽을 본인 주소로 제한하세요.

핵심 요약

  • Left 4 Dead 2 서버는 외부로 열린 포트가 정확히 하나, 27015/UDP만 필요합니다. 게임 트래픽과 A2S 조회가 이 포트를 나눠 쓰며, 분리된 쿼리 포트는 없습니다.
  • 27015/TCP는 RCON이고 본인 주소만 허용해야 합니다. RCON이 필요하지 않다면 rcon_password를 비워 두세요.
  • 로비 시스템은 이 게임이 가진 가장 효과적인 무료 접속 필터입니다. sv_allow_lobby_connect_only 1, 자체 sv_search_key, sv_steamgroup_exclusive 2가 매치메이킹을 거치지 않은 모든 것을 막습니다. 다만 걸러 내는 대상은 접속이고 패킷이 아닙니다.
  • 커스텀 캠페인은 Steam Workshop이나 sv_downloadurl 뒤에 두어야 하고, 게임 포트로는 내보내지 않습니다. 그러지 않으면 중단된 연결 시도마다 여러분의 대역폭으로 값을 치릅니다.
  • 속도 제한은 연결 없는 패킷(0xffffffff로 시작)과 게임 트래픽을 구별해야 합니다. 27015/UDP에 거친 규칙을 걸면 자기 플레이어가 튕겨 나갑니다.
  • 64 바이트짜리 패킷이라면 1 Gbit/s 회선은 초당 약 149만 개를 나릅니다. 그 위에서는 서버 앞단 네트워크만이 결과를 결정하고, 서버 자체의 어떤 설정도 아닙니다.
  • KernelHost에서는 17 Tbps 글로벌 스크러빙과 프랑크푸르트암마인의 3.2 Tbps Arbor 실시간 필터링이 추가 요금 없이 상시 걸러 내며, null-routing도 전환 시간도 없습니다.

프로젝트가 이미 KernelHost에 있다면 필터링은 여러분이 아무것도 하지 않아도 동작하고 있습니다. 그래도 이상한 점이 보이면 지원 티켓을 열어 주세요. 담당 팀이 해당 IP 주소의 필터 규칙을 다시 맞춰 드립니다. 공격이 진행 중일 때는 WhatsApp 긴급 채팅 +43 650 8209883으로도 연락하실 수 있습니다. 그때는 네 가지를 함께 알려 주세요. IP 주소, 포트, 여러분 시간대 기준의 발생 구간, 그리고 보이는 현상(플레이어가 튕겨 나감, 로비 검색에 서버가 없음, 핑이 높음)입니다. 이렇게 하면 되묻는 과정을 한 번 줄일 수 있고, 캠페인이 진행되는 중에는 그 한 번이 중요합니다.

다른 곳에서 호스팅하면서 정기적으로 공격을 받고 있다면, 회선이 그 앞에서 끝나는 서버에 규칙을 하나 더 얹는 것보다 KernelHost로 옮기는 편이 짧은 길입니다. 상시 방어는 모든 서버 상품의 일부이고, 상황이 나빠졌을 때 비로소 주문하는 부가 상품이 아닙니다.

자주 묻는 질문

Left 4 Dead 2 서버에 열어 두어야 하는 포트는 무엇인가요?
정확히 하나, 27015/UDP입니다. 이 포트로 게임 트래픽과 A2S 서버 조회가 함께 흐르고, Left 4 Dead 2에는 분리된 쿼리 포트가 없습니다. 27015/TCP는 RCON이며 본인 주소에만 허용해야 합니다. 27005/UDP는 클라이언트 포트로 플레이어 쪽에서 나가므로 서버에서는 열 필요가 없습니다. 27020/UDP는 SourceTV이고 -hltv 또는 tv_enable 1로 실제 중계를 할 때만 필요합니다. 같은 호스트의 추가 인스턴스는 27016, 27017처럼 하나씩 올라갑니다.
L4D2 서버가 버벅이는데 회선은 한가합니다. DDoS 공격인가요?
아마 아닙니다. 먼저 서버 콘솔을 보세요. Invalid split packet length 줄이 반복된다면 잘못 조립된 네트워크 패킷이 도착하고 있고, 그것은 아주 적은 양으로도 충분합니다. 트래픽은 아주 작은데도 서버는 버벅입니다. 동시에 sar -n DEV 1 10으로 패킷 전송률을, ip -s link show eth0으로 폐기 카운터를 확인하세요. 두 값이 모두 평범하다면 볼류메트릭 공격이 아니라 엔진이나 SourceMod 확장 기능의 크래시 또는 부하 패턴이었던 것입니다. 여기에는 대역폭이 아니라 최신 서버 바이너리가 답입니다.
sv_allow_lobby_connect_only 1이 DDoS 공격을 막아 주나요?
아니요. 이 지시어는 패킷이 아니라 접속을 걸러 냅니다. sv_allow_lobby_connect_only 1을 켜면 Steam 매치메이킹 로비를 통해 배정된 플레이어만 들어오고, 개발자 콘솔에서 입력한 connect와 Steam 초대는 거부됩니다. 트롤, 버리는 계정, 접속 플러드에는 매우 효과적입니다. 하지만 27015/UDP를 플러딩하는 공격자는 애초에 들어올 생각이 없습니다. 그 패킷은 거부되지만 그래도 이미 도착했고, 대역폭을 소모했고, 연산 시간을 먹었습니다. 볼류메트릭 공격에는 이 설정이 통하지 않습니다.
서버가 공격받을 때 포트 27015에 그냥 속도 제한을 걸어도 되나요?
일괄로는 안 됩니다. 게임 트래픽과 서버 조회가 27015/UDP를 나눠 쓰기 때문에, 거친 속도 제한은 여러분의 플레이어를 내쫓고 공격자가 원하는 방향으로 공격을 끝맺어 줍니다. 제동은 패킷 부류를 구별해야 합니다. Source 엔진의 연결 없는 패킷은 모두 네 바이트가 전부 1로 채워진 값(0xffffffff)으로 시작하고, 이미 접속한 플레이어의 트래픽은 그렇지 않습니다. 바로 이 지점에 nftables나 iptables로 출발지 주소별 한도를 걸 수 있습니다. 여기에 더해 환경 변수 STEAM_GAMESERVER_RATE_LIMIT_200MS는 한 주소에서 200밀리초 안에 설정값을 넘는 연결 없는 패킷이 들어오면 그것을 폐기합니다.
로비 예약이란 무엇이고 왜 제 서버를 막나요?
로비 예약은 매치메이킹 로비가 여러분의 서버를 일정 시간 동안 점유하는 것입니다. 예약이 유지되는 동안 그 서버는 다른 로비에게 이미 배정된 것으로 취급되고, 한동안 지난 뒤에야 스스로 풀립니다. 협동 슬롯이 네 개라면 이것은 빠듯한 자원입니다. 서버를 매치메이킹으로 운영하지 않는다면 sv_force_unreserved 1을 설정하세요. 그러면 서버가 예약 요청에 응답하지 않고 예약 표시가 붙은 접속을 거부합니다. L4DToolZ로 협동 슬롯을 네 개보다 많이 운영한다면 이 설정은 어차피 필수이며, 그러지 않으면 추가 슬롯에 아무도 닿을 수 없습니다.
커스텀 캠페인이 제 서버를 공격에 취약하게 만드나요?
취약하게 만드는 것이 아니라 비싸게 만듭니다. 커스텀 캠페인은 맵, 모델, 텍스처, 사운드가 담긴 VPK 패키지이고, 맵 하나의 몇 배입니다. srcds가 이 파일을 게임 포트로 직접 내보내면 새 플레이어의 연결 시도 한 번마다 다운로드 한 벌이 온전히 들고, 다운로드 도중의 중단도 마찬가지입니다. 이것은 회선을 채우는 값싼 방법이고, 어떤 통계에서도 공격처럼 보이지 않습니다. 플레이어를 Steam Workshop으로 안내하거나, sv_downloadurl로 파일을 웹 서버에 두고 그곳에 bzip2 아카이브로 올리세요.
어느 공격 규모부터 방화벽 규칙이 더는 통하지 않나요?
서버 앞단 회선이 포화되는 순간부터입니다. 일반적인 게임 서버는 1 Gbit/s에 물려 있고, 이는 초당 125 메가바이트이며, 64 바이트짜리 패킷이라면 초당 약 149만 개입니다. 일반적인 서버 커널은 그중 수십만 개를 처리한 뒤 폐기를 시작합니다. 여러분의 규칙은 언제나 이미 케이블을 지나온 패킷을 두고 판단합니다. 그 패킷을 폐기할 수는 있지만 보내지 않은 것으로 만들 수는 없습니다. 이 한계를 넘어서면 서버 앞단 네트워크에서 상시 돌아가는 필터링만이 통합니다.
KernelHost의 서버는 공격 중에 오프라인이 되나요?
아니요. null-routing을 쓰지 않습니다. 여러분의 IP 주소는 네트워크에 그대로 남고, 폐기되는 것은 악성 패킷뿐입니다. 방어는 두 단계입니다. 글로벌 스크러빙 네트워크의 17 Tbps 방어 용량이 볼류메트릭 공격을 발생지에 가까운 곳에서 걸러 내고, 프랑크푸르트암마인의 3.2 Tbps Arbor 실시간 필터링이 서버 바로 앞에서 프로토콜별 패턴을 폐기합니다. 두 단계 모두 상시 동작하며 공격에 먼저 반응할 필요가 없으므로, 플레이어가 튕겨 나가는 전환 시간이 존재하지 않습니다.
KernelHost의 DDoS 방어는 추가 요금이 드나요?
아니요. 2단계 상시 방어는 모든 서버 상품에 추가 요금 없이 포함되어 있고 서버 제공 시점부터 동작합니다. 주문하거나 켜거나 설정할 필요가 없고, 별도의 방어 상품도 없습니다. 서버는 프랑크푸르트암마인의 maincubes Premium Datacenter에 있습니다. Left 4 Dead 2 서버를 지금까지 다른 곳에서 운영하면서 정기적으로 공격받고 있다면, 회선이 그 앞에서 끝나는 서버에 규칙을 하나 더 얹는 것으로는 해결되지 않고 이전으로 해결됩니다.
Advanced DDoS Protection이 추가로 필요한 때는 언제인가요?
프로젝트가 어쩌다 한두 번이 아니라 표적이 되어 몇 주에 걸쳐 공격받고, 필터링을 직접 조정하려 할 때입니다. 전용 방어 IP를 받고, 고객 포털에서 포트와 프로토콜별 방어 규칙을 직접 관리하므로 27015/UDP를 캠페인을 내보내는 웹 서버와 다르게 다룰 수 있습니다. 변경은 실시간으로 반영되므로 공격이 진행되는 중에도 값을 조정할 수 있습니다. 요금은 월 50.00 EUR부터, PrePaid, 최소 이용 기간 없음, 설치비 없음입니다.

Left 4 Dead 2 L4D2 DDoS 방어 Source 엔진 srcds 로비 시스템 포트 27015 게임 서버 보호 Advanced DDoS Protection