Lineage 2(리니지2) 서버를 DDoS 공격으로부터 방어하기

게시일 읽는 시간 43분

Lineage 2 프리서버에 실제로 필요한 포트는 무엇인지, 왜 포트 2106의 로그인 서버가 진짜 표적인지, 공격이 왜 서버 오픈에 맞춰 계절처럼 몰리는지, 그리고 어느 공격 규모부터 서버 앞단 네트워크의 필터링만이 통하는지 정리했습니다.

저녁이면 아무도 로그인 화면을 넘어가지 못하는데 월드 안의 플레이어는 아무 방해 없이 계속 플레이하는 Lineage 2(리니지2) 프리서버는 하드웨어 문제가 아닙니다. 그것은 로그인 서버를 겨냥한 DDoS 공격의 지문이고, Lineage 2 DDoS 방어는 바로 그 지점에서 시작해야 합니다. 이 글은 먼저 추가 비용 없이 직접 보호할 수 있는 것을 보여 주고, 그다음 그 조치가 기술적으로 한계에 이르는 지점을, 마지막으로 그때 서버 앞단 네트워크에서 무엇이 이뤄져야 하는지 설명합니다.

모든 내용은 Debian 12, Debian 13, Ubuntu 22.04 LTS 또는 Ubuntu 24.04 LTS에서 운영하는 L2J와 그 파생판(L2J-Mobius, aCis), 그리고 AuthD, CacheD, L2Server로 이뤄진 L2OFF 패키지를 기준으로 합니다. 명령은 root 기준으로 적었으니, 일반 사용자라면 앞에 sudo를 붙이세요.

공격이 지금 진행 중이라면: 설정을 지금 바꾸지 마시고 로그인 서버도 게임 서버도 재시작하지 마세요. 먼저 측정값을 확보하세요(“로그 기록” 절 참고). 공격이 끝나면 그 값은 사라집니다.

Lineage 2 서버를 DDoS 공격으로부터 방어하기: 프리서버가 공격받는 이유

Lineage 2 프리서버에는 편리한 표적으로 만드는 성질이 여러 가지 겹쳐 있고, Lineage 2 DDoS 방어는 바로 이 성질들에서 출발해야 합니다. 첫째, 여러분의 주소는 처음부터 공개되어 있습니다. 플레이어는 패치된 System 폴더를 내려받고, 그 안의 l2.ini에는 여러분 로그인 서버의 IP 주소가 담긴 ServerAddr= 줄이 있습니다. 여러분의 프로젝트를 한 번이라도 설치해 본 사람은, 캐릭터를 만든 적이 있는지와 무관하게 이 주소를 알고 있습니다.

둘째, 플레이어층이 정해진 시간에 묶여 있습니다. 공성전, 에픽 레이드보스, 이벤트는 일정에 적혀 있고, 바로 그 시간의 장애는 가장 크게 눈에 띕니다. 셋째, 프로젝트끼리 직접 경쟁합니다. 서버를 여는 사람은 같은 주말에 문을 여는 다른 세 프로젝트와 똑같은 몇천 명의 플레이어를 두고 경쟁하며, 경쟁자를 눌러 놓는 것은 이 씬에서 흔한 전략입니다. 공격은 서비스로 구매되고(씬에서는 booter 또는 stresser라고 부릅니다) 주문하는 쪽에게 실력도 이렇다 할 비용도 들지 않습니다. DDoS 공격이 구체적으로 무엇인지는 DDoS 공격이란 무엇인가? 글에서 설명합니다.

포트 2106의 로그인 서버가 진짜 표적인 이유

Lineage 2는 분리된 두 프로세스로 나뉩니다. 로그인 서버 하나와, 게임 서버 하나 또는 여러 개입니다. 클라이언트는 먼저 2106 TCP로 로그인 서버에 연결해 인증을 마치고, 거기에서 게임 서버의 외부 주소와 포트가 담긴 서버 목록을 받은 뒤, 두 번째 연결을 7777 TCP로 게임 서버에 맺습니다. 두 프로세스는 각자의 설정 파일, 각자의 포트, 각자의 부하 한계를 가집니다.

여기에서 L2 운영자가 거듭 묘사하는 공격 패턴이 나옵니다. 2106을 향한 플러드는 새 로그인만 막습니다. 이미 월드에 서 있는 사람은 스스로 연결이 끊길 때까지 계속 플레이합니다. 그래서 접속자 수는 갑자기가 아니라 천천히 떨어지고, 포럼에는 “서버는 돌아가는데 들어갈 수가 없다”가 올라옵니다. 바로 이 모습이 로그인 서버를 겨냥한 공격과, 모두가 동시에 튕겨 나가는 게임 서버 공격을 구별해 줍니다.

로그인 서버는 게다가 더 값싼 표적입니다. 비용이 한쪽으로 몰려 있기 때문입니다. L2J 로그인 서버는 시작할 때 1024 비트 RSA 키 쌍 열 개와 Blowfish 키 스무 개를 미리 만들어 둡니다. 로그인 시도 하나마다 클라이언트는 패킷 하나를 보내는 비용을, 서버는 개인 RSA 키로 복호화하는 비용을 치릅니다. 그리고 절반만 완료된 세션은 내장 타이머가 버릴 때까지 자리를 차지합니다. 소스 코드의 LOGIN_TIMEOUT은 60초로 되어 있습니다. 기본값 MaxConnectionPerIP = 50은 출발지 주소마다 동시 연결 쉰 개를 허용하므로, 출발지 주소 천 개면 동시에 열린 세션 50,000개가 만들어지고, 각각 최대 1분씩 유지됩니다.

여기에 대부분의 게임 서버와 다른 이 게임만의 성질이 더해집니다. Lineage 2는 전부 TCP로 동작합니다. 제작사는 이 게임에 TCP 포트 80, 2009, 2106, 7777을 제시하고, UDP로는 이름 풀이용 포트 53만 제시합니다. 그러니까 걸러야 할 UDP 게임 트래픽이 없는 대신, 출발지 주소를 위조한 고전적인 SYN 플러드가 곧바로 효과를 내고, 커널의 연결 추적이 첫 번째 병목이 됩니다.

Lineage 2 서버 공격이 서버 오픈에 맞춰 몰리는 이유

Lineage 2 프리서버를 향한 공격은 서버 오픈 무렵에 몰립니다. 오픈 날짜와 시각이 몇 주 전부터 공개되어 있기 때문입니다. Lineage 2 프로젝트 오픈 일정표는 다가오는 오픈을 연대기(Interlude, High Five, Classic, Essence)별로, 여기에 배율과 정확한 시작 시각까지 함께 정리해 매일 갱신합니다. 공격자는 아무것도 캐낼 필요가 없습니다. 자기에게 가장 유리한 시점이 운영자의 공지에 적혀 있습니다.

두 번째 이유는 경제적입니다. Lineage 2 프리서버는 앞에서 돈을 법니다. 플레이어 기반 전체가 첫 며칠에 모이고, 후원은 첫 몇 주에 들어오며, 그다음부터 인구는 꾸준히 줄어듭니다. 첫 한 시간에 들어오지 못한 플레이어는 같은 주말에 문을 여는 프로젝트로 갈아타고, 그런 프로젝트는 언제나 있습니다. 그래서 오픈 당일의 한 시간 장애는 한 시간의 매출이 아니라 서버 수명 전체의 일부를 앗아 갑니다.

세 번째 이유는 기술적입니다. 그랜드 오픈에는 수천 명이 동시에 로그인을 시도합니다. 로그인 서버는 바로 그 순간 이미 한계에 있고, 여기에 얹힌 플러드는 정점 부하와 거의 구별되지 않습니다. 조용한 화요일이라면 아무 결과도 남기지 못할 공격이 오픈 시각에는 충분히 통합니다. 운영 중에 예고된 일정도 마찬가지입니다. 공성전과 에픽 레이드보스는 일정에 적혀 있고, 같은 이유로 인기 있는 공격 구간입니다. 오픈 러시가 지나면 동기가 다시 줄어들기 때문에, 운영자는 공격을 상시 상태가 아니라 물결처럼 겪습니다.

실제로 문제가 되는 포트

다음 표는 Lineage 2 프리서버의 포트, 해당 설정 파일, 그리고 값을 정하는 지시어를 정리한 것입니다. 기본값은 L2J에 함께 들어 있는 설정 파일, 그리고 L2OFF 패키지의 설치 안내에서 가져왔습니다.

포트와 프로토콜 서비스 파일과 지시어 공개 네트워크로?
2106 TCP 로그인 서버, 게임 클라이언트의 인증(L2J) login/config/LoginServer.properties: LoginserverPort = 2106, LoginserverHostname = * 예
7777 TCP 게임 서버, 게임 월드(L2J) game/config/Server.properties: GameserverPort = 7777, GameserverHostname = * 예
9014 TCP 로그인 서버가 게임 서버의 등록을 받는 채널 LoginServer.properties: LoginPort = 9014, LoginHostname = 127.0.0.1; Server.properties의 대응 항목: LoginHost = 127.0.0.1, LoginPort = 9014 아니요
3306 TCP MariaDB 또는 MySQL, 모든 L2J 서버의 데이터베이스 Server.properties: URL = jdbc:mysql://localhost/lineage2, Login = root 아니요
2106 TCP(L2OFF) AuthD, 공식 서버 파일의 인증 서비스 AuthD 설정: serverExPort = 2106 예
7777 TCP(L2OFF) L2Server, 공식 서버 파일의 게임 월드 l2server.ini: worldport = 7777 예
2104와 2108 TCP(L2OFF) AuthD 내부용(serverPort와 serverIntPort) AuthD 설정 아니요
2006과 2008 TCP(L2OFF) CacheD, L2Server와 데이터베이스를 잇는 다리 CacheD 설정 아니요
2002 TCP(L2OFF) L2NPC, NPC를 게임 월드에 불러들임 l2npc.ini 아니요
1433 TCP(L2OFF) Microsoft SQL Server, 공식 서버 파일의 데이터베이스 데이터베이스 설정 아니요
80과 443 TCP 가입, 후원 상점, 추천 페이지가 있는 프로젝트 웹사이트 웹 서버 예, 다만 같은 IP 주소에 두지 않음
22 TCP SSH 접속 /etc/ssh/sshd_config 본인 주소로만 제한

이 표는 두 가지 질문에도 답합니다. Lineage 2에는 쿼리 포트도 RCON 포트도 없습니다. 서버 목록에 접속자 수를 내주는 별도 서비스도 없고, Source 기반 게임과 같은 원격 제어 포트도 없습니다. 서버 목록은 로그인 서버가 직접 만들어, 인증을 마친 클라이언트에게 2106의 같은 연결로 보냅니다. 원격 제어는 L2J에서 게임 내 명령과 데이터베이스를 통해 이뤄집니다. 그래서 다른 게임이 가진 공격 경로 두 개가 사라지고, 그만큼 더 많은 것이 포트 2106에 매달립니다.

알아 두어야 할 수치

항목 값
게임의 전송 프로토콜 전부 TCP, UDP는 포트 53의 이름 풀이에만
1 Gbit/s 회선 초당 125 메가바이트
1 Gbit/s에 들어가는 64 바이트 패킷 초당 약 149만 개
일반적인 서버 커널이 처리하는 양 초당 수십만 패킷, 그 뒤로는 폐기를 시작함
출발지 주소별 동시 연결, L2J 기본값 MaxConnectionPerIP = 50
L2J에서 절반만 완료된 로그인 세션의 수명 LOGIN_TIMEOUT, 60초
차단까지의 실패 횟수, L2J 기본값 LoginTryBeforeBan = 5, 그 뒤 LoginBlockAfterBan = 900초
KernelHost에서 걸러 낸 게임 서버 대상 공격 초당 870만 패킷 이상과 함께 112.2 Gbit/s 이상
KernelHost에서 걸러 낸 음성 서버 대상 공격 초당 4,150만 패킷 이상과 함께 473.4 Gbit/s 이상

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

이 절이 가장 길고, 의도한 바입니다. 설정이 깔끔한 Lineage 2 서버는 어디에 놓여 있든 작거나 중간 규모의 공격을 자체 힘으로 견뎌 냅니다.

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

규칙을 한 줄이라도 쓰기 전에 서버가 외부에 무엇을 내주고 있는지 확인하세요. 추측하지 말고 직접 확인합니다.

ss -lntp

중요한 것은 로컬 주소 열입니다. 0.0.0.0:2106과 0.0.0.0:7777은 그 자리에 있어야 합니다. 0.0.0.0:9014와 0.0.0.0:3306은 잘못된 상태입니다. 공격자가 여러분의 서버 목록에 끼어들거나 데이터베이스를 두드릴 수 있는 두 포트가 바로 그것입니다. 반대로 127.0.0.1:3306은 “로컬에서만”을 뜻하고 방화벽 규칙이 필요하지 않습니다. 공격자의 시선은 외부에서 실행하는 포트 스캔이 보여 줍니다.

nmap -Pn -p- --min-rate 1000 YOUR.SERVER.IP.ADDRESS

2. 포트 9014와 데이터베이스를 공개 네트워크에서 빼기

포트 9014는 게임 서버가 로그인 서버에 등록하는 채널이며, 어떤 경우에도 공개 네트워크에 두어서는 안 됩니다. L2J는 이를 위해 이미 올바른 기본값을 제공합니다. LoginHostname = 127.0.0.1이 포트를 루프백 인터페이스에 바인딩하므로 외부에서는 아예 닿을 수 없습니다. 로그인 서버와 게임 서버가 서로 다른 두 머신에서 돌아간다면 * 대신 구체적인 내부 주소를 적고, 포트는 상대 쪽에만 허용하세요.

같은 규칙이 데이터베이스에도 적용됩니다. /etc/mysql/mariadb.conf.d/50-server.cnf에 다음 줄이 있는지 확인하세요.

bind-address = 127.0.0.1

그리고 데이터베이스 사용자를 바꾸세요. 함께 제공되는 Server.properties는 Login = root로 되어 있고, 파일 자체가 그것이 권장되지 않는다는 주석을 달고 있습니다. 최소 권한을 가진 전용 사용자를 만드는 방법은 MariaDB와 MySQL 보안 강화하기에 있습니다. 그다음 결과를 확인합니다.

ss -lntp | grep -E ':9014|:3306'

그 위에 얹는 방화벽은 짧게 끝납니다. Lineage 2 서버에는 외부로 두 개의 허용이면 충분하며, 스스로 접속이 막히지 않도록 반드시 이 순서를 지키세요.

ufw allow 22/tcp comment 'SSH'
ufw allow 2106/tcp comment 'L2 로그인'
ufw allow 7777/tcp comment 'L2 게임'
ufw default deny incoming
ufw default allow outgoing
ufw --force enable
ufw status verbose

복구 방법까지 포함한 전체 안내는 스스로 접속이 막히지 않게 UFW 방화벽 설정하기에 있습니다.

3. 서버가 등록되면 AcceptNewGameServer 끄기

LoginServer.properties에는 출고 상태로 AcceptNewGameServer = True가 들어 있고, 그 위의 주석이 무슨 뜻인지 정확히 설명합니다. 어떤 게임 서버든 여러분 로그인 서버의 빈자리에 등록할 수 있다는 뜻입니다. 9014가 루프백 인터페이스에만 놓여 있는 동안에는 아무 일도 생기지 않습니다. 다른 이유로 이 포트에 닿을 수 있게 되는 순간 그것은 열린 문이 됩니다. 그러므로 여러분의 게임 서버가 한 번 등록되고 자기 식별자를 받은 뒤에는 값을 False로 바꾸세요.

AcceptNewGameServer = False

게임 서버 쪽의 대응 항목에는 AcceptAlternateID = True가 있습니다. 구축 단계에서는 편리합니다. 원하는 식별자가 이미 쓰이고 있으면 로그인 서버가 다른 식별자를 내주기 때문입니다. 운영 시스템에서는 그 반대를 원하실 것입니다. 고정된 식별자, 그리고 그것이 이미 쓰이고 있을 때의 오류입니다.

4. 로그인 서버의 Flood Protection을 제대로 설정하기

L2J는 로그인 서버에 자체 연결 제동 장치를 함께 제공합니다. 이 설정은 LoginServer.properties에 있고, 시간 값은 모두 밀리초입니다.

EnableFloodProtection = True
FastConnectionLimit = 15
NormalConnectionTime = 700
FastConnectionTime = 350
MaxConnectionPerIP = 50

값들은 서로 맞물려 있습니다. 같은 출발지 주소에서 직전 연결로부터 FastConnectionTime보다 짧은 간격으로 도착한 연결은 빠른 연결로 셉니다. 그런 연결이 FastConnectionLimit개를 넘으면 그 주소는 거부됩니다. NormalConnectionTime은 카운터가 다시 줄어들기 시작하는 간격입니다. MaxConnectionPerIP는 주소별로 동시에 열려 있을 수 있는 연결의 상한입니다.

한 명의 플레이어에게 동시 연결 쉰 개는 대단히 넉넉하고, 값을 낮추면 체감할 만큼 도움이 됩니다. 그래도 여기에는 조심이 필요합니다. 같은 집의 여러 플레이어, PC방, 그리고 특히 Carrier-NAT 뒤의 회선(L2 씬에서는 튀르키예, 브라질, 동유럽 일부의 많은 플레이어가 여기에 해당합니다)은 공인 주소 하나를 함께 씁니다. 여기서 값을 3으로 두면 진짜 플레이어가 막힙니다. 먼저 평상시 운영에서 한 주를 측정하고, 그다음 단계적으로 낮추세요.

그리고 반드시 알아 두어야 할 한계가 있습니다. 이 제동 장치는 로그인 서버의 Java 프로세스 안에서 돌아갑니다. 이 장치가 판단하는 패킷은 이미 여러분의 회선을 지나왔고 이미 연산 시간을 소모했습니다. 출발지가 몇 개뿐일 때는 효과가 있지만 봇넷에는 없습니다.

5. 실패 횟수를 제한하고 banned_ip.cfg 쓰기

LoginServer.properties의 또 다른 두 지시어가 누군가 얼마나 오래 추측할 수 있는지를 정합니다.

LoginTryBeforeBan = 5
LoginBlockAfterBan = 900

LoginTryBeforeBan은 계정과 비밀번호의 잘못된 조합이 몇 번 나오면 주소를 차단할지를 정하는 횟수이고, LoginBlockAfterBan은 초 단위 차단 시간입니다(900은 15분에 해당합니다). 그 뒤에는 세기가 처음부터 다시 시작됩니다.

영구 차단은 로그인 서버의 설정 디렉터리에 있는 banned_ip.cfg 파일에 적습니다. 개별 주소, 네트워크 전체, 그리고 밀리초 단위 Unix 타임스탬프로 적는 선택적 만료 시점이 허용되며, # 뒤는 모두 주석입니다.

198.51.100.7
203.0.113.0
198.51.100.44 1789689600000

또한 AutoCreateAccounts = False로 설정하세요. 기본값 True는 알 수 없는 계정 이름으로 로그인할 때마다 계정을 자동으로 만듭니다. 구축 단계에서는 편리하고 운영에서는 선물입니다. 공격자가 이것으로 계정을 원하는 만큼 만들고, 그 계정마다 여러분 게임 서버의 주소가 담긴 서버 목록을 받아 갈 수 있습니다. 계정은 그 대신 웹사이트의 가입 절차를 통해 생기게 하세요. 그러면 누가 식별자를 받는지 여러분이 통제합니다.

6. 2106과 7777의 연결 속도를 커널에서 제한하기

Java 제동 장치가 너무 늦게 판단하는 것을 커널은 더 이르고 더 싸게 판단합니다. 작은 공격과 엉성한 봇에는 출발지 주소별 상한이 도움이 됩니다.

iptables -I INPUT -p tcp --dport 2106 --syn -m connlimit --connlimit-above 8 --connlimit-mask 32 -j DROP
iptables -I INPUT -p tcp --dport 2106 --syn -m hashlimit --hashlimit-name l2login --hashlimit-mode srcip --hashlimit-above 6/sec --hashlimit-burst 20 -j DROP
iptables -I INPUT -p tcp --dport 7777 --syn -m connlimit --connlimit-above 6 --connlimit-mask 32 -j DROP

첫 번째 규칙은 한 주소가 로그인 서버로 여덟 개를 넘는 연결을 동시에 열고 있으면 새 연결을 폐기합니다. 정상 클라이언트에는 정확히 하나가 필요합니다. 두 번째 규칙은 주소별 새 연결 속도를 초당 여섯 개로, 버스트 스무 개까지 허용하도록 제한하며, 서버 재시작 뒤의 재접속 폭주는 아직 통과시킵니다. 세 번째 규칙은 게임 서버에서 주소별 동시 연결 여섯 개를 허용합니다. Lineage 2에서는 다중 접속(듀얼박스, 트리플박스)이 평범한 일이고, 한도를 너무 좁게 잡으면 돈을 내는 플레이어가 맞기 때문입니다.

세 숫자는 모두 출발점일 뿐이고 정답이 아닙니다. 동시 접속 2,000명인 서버는 200명인 서버와 다르게 움직입니다. 먼저 측정하고, 그다음 설정하세요. iptables 규칙만 쓰면 재시작 후에 사라지므로 Debian과 Ubuntu에서는 다음과 같이 저장합니다.

apt-get install -y iptables-persistent
netfilter-persistent save

UFW를 쓴다면 이런 규칙은 /etc/ufw/before.rules에 들어가야 합니다. 그러지 않으면 다음 ufw reload에서 사라집니다.

7. SYN 플러드 막아 내기: syncookies, 백로그, 연결 추적

Lineage 2가 전부 TCP로 동작하기 때문에 SYN 플러드가 가장 자연스러운 경로입니다. SYN 플러드는 출발지 주소를 위조한 연결 요청을 보내고 확인 응답에는 끝까지 답하지 않는 공격이며, 그래서 서버는 요청마다 결코 쓰이지 않을 메모리를 예약하게 됩니다. 네 가지 설정이 이를 누그러뜨립니다.

sysctl -w net.ipv4.tcp_syncookies=1
sysctl -w net.ipv4.tcp_max_syn_backlog=8192
sysctl -w net.core.somaxconn=4096
sysctl -w net.ipv4.tcp_synack_retries=2

그중 가장 중요한 줄은 SYN 쿠키입니다. 커널이 아무것도 기억하지 않은 채 요청에 응답하고, 상대가 연결을 실제로 완료할 때에야 상태를 만듭니다. 위조된 출발지는 이렇게 허탕을 칩니다. 이 값을 영구적으로 두려면 /etc/sysctl.d/ 아래의 파일에 적고 sysctl --system으로 불러옵니다.

자주 간과되는 병목이 하나 더 있는데, 커널의 연결 추적입니다. 여기가 가득 차면 서버는 정상 패킷까지 폐기하고, 로그에는 “nf_conntrack: table full, dropping packet”이 남습니다. 현재 값과 상한은 다음 명령으로 확인합니다.

sysctl net.netfilter.nf_conntrack_count net.netfilter.nf_conntrack_max

8. 웹사이트, 로그인 서버, 게임 서버를 서로 다른 IP 주소로 분리하기

가입, 후원 상점, 추천 페이지가 있는 프로젝트 웹사이트는 여러분의 도메인을 통해 언제나 찾을 수 있습니다. 이 웹사이트가 로그인 서버와 같은 IP 주소에 놓여 있으면 웹사이트를 향한 공격이 동시에 인증까지 멈춰 세우고, 그 반대도 마찬가지입니다. 세 역할을 서로 다른 주소로 나누세요. 그러면 웹사이트를 향한 공격에도 게임에는 닿을 수 있고, 2106을 향한 공격에도 이미 접속한 플레이어는 계속 플레이합니다.

이때 DNS 항목을 깔끔하게 유지하세요. 가장 흔한 실수는 이전 주소를 가리키는 A 레코드를 잊고 방치하는 것입니다. 공격자가 여러분의 플레이어와 똑같은 이름으로 새 주소를 찾아내기 때문에, 어떤 주소 변경도 무의미해집니다.

그리고 여기에는 희망이 아니라 솔직함이 들어가야 합니다. 여러분 로그인 서버의 주소는 비밀로 유지할 수 없습니다. 그 주소는 모든 플레이어가 내려받는 System 폴더의 l2.ini에 들어 있습니다. 게임 서버의 주소는 또 로그인 서버가 직접 배포합니다. L2J에서는 ipconfig.xml의 외부 주소로(오래된 파생판에서는 Server.properties의 ExternalHostname으로) 들어 있고, 인증을 성공적으로 마친 모든 클라이언트에게 전달됩니다. 숨기기는 전략이 아니고, 필터링은 전략입니다.

9. 만일에 대비해 데이터를 남기도록 로그 기록하기

가장 중요하면서 거의 아무도 미리 하지 않는 단계가 있습니다. 모든 것이 정상으로 돌아가는 동안에 기준값을 만들어 두는 것입니다. 평상시 값이 없으면 사건이 끝난 뒤에 초당 40,000 패킷이 많았던 것인지 그냥 토요일 저녁이었던 것인지 말할 수 없습니다. apt-get install -y vnstat sysstat로 측정이 상시 돌아갑니다. 사건이 진행되는 동안에는 네 개의 명령으로 충분합니다.

sar -n DEV 1 10
ss -tn state syn-recv | wc -l
ip -s link show eth0
tcpdump -ni eth0 'tcp port 2106' -c 200 -q

Lineage 2에서 가장 많은 것을 알려 주는 줄은 두 번째입니다. 절반만 열린 연결의 수를 세기 때문입니다. 플레이어가 몇백 명인데 이 값이 다섯 자리라면 그것은 SYN 플러드이고 다른 무엇도 아닙니다. tcpdump에는 한 가지 원칙이 있습니다. 항상 -c로 개수를 제한하세요. 과부하 상태에서의 캡처는 이미 과부하인 서버에 부담을 더 얹습니다. 측정값을 해석하는 방법은 DDoS 공격 알아내기에 있습니다.

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

이제 어떤 설정 파일로도 해결할 수 없는 부분입니다. 지금까지의 모든 조치는 여러분의 서버, 즉 회선의 끝에서 동작합니다. 방화벽 규칙은 이미 케이블을 지나온 패킷을 두고 판단합니다. 그 패킷을 폐기할 수는 있지만, 보내지 않은 것으로 만들 수는 없습니다.

한번 계산해 보겠습니다. 일반적인 게임 서버는 1 Gbit/s 회선에 물려 있고, 이는 초당 125 메가바이트이며, 누군가 그보다 많이 보내는 순간 회선은 꽉 찹니다. Lineage 2 서버를 상대로는 큰 공격조차 필요하지 않습니다. 두 번째 수치가 먼저 터지기 때문입니다. 바로 패킷 전송률입니다. 64 바이트짜리 작은 패킷이라면 1 Gbit/s 회선에 초당 약 149만 개가 들어갑니다. 일반적인 서버 커널은 CPU와 네트워크 카드에 따라 그중 수십만 개를 처리한 뒤 폐기를 시작합니다.

순수 TCP 게임에서는 세 번째 한계가 더해집니다. 절반만 열린 연결은 각각 연결 추적과 백로그에서 항목 하나를 차지하고, L2J 로그인 서버는 자기 세션을 최대 60초까지 유지합니다. 그래서 여러분의 회선을 3분의 1도 채우지 못하는 초당 수십만 패킷 규모의 공격이 인증을 완전히 막을 수 있습니다. 운영자는 이것을 “사용률은 전혀 높지 않았는데 그래도 아무도 들어오지 못했다”로 경험합니다.

어느 정도 규모가 실제로 나타나는지 가늠하기 위해 덧붙입니다. KernelHost 서버에서는 그중에서도 게임 서버를 향한 초당 870만 패킷 이상과 함께 112.2 Gbit/s를 넘는 공격, 그리고 음성 서버를 향한 초당 4,150만 패킷 이상과 함께 473.4 Gbit/s를 넘는 멀티 벡터 공격이 걸러졌습니다. 여기에는 로컬 설정이 없습니다. 볼류메트릭 공격은 서버 앞단 네트워크에서 끝나야 합니다. 급한 상황에서 무엇을 해야 하는지는 심각한 DDoS 공격, 어떻게 대응하나요?에 있습니다.

KernelHost가 이에 맞서 제공하는 것

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

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

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

두 가지 특성이 결정적입니다. 이 방어는 상시 동작하므로 공격에 반응해서 비로소 시작될 필요가 없고, 그래서 초반에 서버가 사라지는 몇 분이 존재하지 않습니다. 그랜드 오픈에서는 바로 이것이 성공한 오픈과 놓친 오픈을 가릅니다. 그리고 null-routing을 쓰지 않습니다. 여러분의 IP 주소는 네트워크에 그대로 남고, 폐기되는 것은 악성 패킷뿐입니다. IP 주소를 네트워크에서 빼 버리면 여러분에게는 공격자가 노린 것과 같은 결과가 됩니다. 어떤 게임과 프로토콜이 포함되는지는 실시간 게임 서버 DDoS 방어에 정리되어 있습니다.

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

어떤 프로젝트는 어쩌다 한두 번이 아니라 표적이 되어 몇 주에 걸쳐 공격받고, Lineage 2 씬에서는 서버 목록의 상위권에 올라가는 서버라면 그것이 일상입니다. 이런 경우를 위해 Advanced DDoS Protection이 월 50.00 EUR부터, PrePaid 방식으로, 최소 이용 기간 없이 제공됩니다. 차이는 용량이 더 커지는 데 있지 않고 통제권에 있습니다.

  • 전용 방어 IP: 프랑크푸르트 코어에서 발급되며, 서버는 자체 네트워크에서 이 IP로 전환됩니다. 여러분 쪽에서 손볼 것은 없습니다.
  • 포트와 프로토콜별로 직접 관리하는 방어 규칙: 고객 포털에서 2106 TCP에 무엇을 허용할지와 7777 TCP에 무엇을 허용할지를 따로 설정합니다. Lineage 2에서는 이것이 결정적인 지점입니다. 두 포트의 트래픽 패턴이 완전히 다르기 때문입니다. 한쪽은 짧은 연결이 아주 많고, 다른 쪽은 아주 긴 연결이 적습니다.
  • 변경은 실시간으로 반영되므로 공격이 진행되는 중에도 값을 조정할 수 있고, 오픈 시각 전에 규칙을 더 엄하게 두었다가 그 뒤에 다시 느슨하게 풀 수 있습니다.
  • 애플리케이션에 맞춘 방어 프로필: 임의의 TCP 또는 UDP 포트에서 돌아가는 개조된 서버 파일과 자체 서버 파일도 다룹니다. L2J, L2J-Mobius, aCis, L2OFF 패키지 가운데 무엇을 운영하든 규칙 세트에는 상관이 없습니다. 규칙이 포트와 프로토콜에 올라서 있기 때문입니다.

두 단계 비교

항목 포함된 DDoS 상시 방어 Advanced DDoS Protection
요금 모든 서버 상품에 추가 요금 없이 포함 월 50.00 EUR부터, PrePaid
필터링 용량 17 Tbps 글로벌 스크러빙과 프랑크푸르트암마인의 3.2 Tbps Arbor 실시간 필터링 동일한 2단계 필터링
IP 주소 서버의 IP 주소 추가로 받는 전용 방어 IP
규칙 세트 자동 프로필, 설정 불필요 고객 포털에서 포트와 프로토콜별 자체 규칙, 2106과 7777을 분리
변경 자동으로 함께 적용 실시간 반영, 공격 중에도 가능
서버 파일 주요 게임에 최적화된 프로필 포트와 프로토콜별 프로필, 그래서 L2J, L2J-Mobius, aCis, L2OFF도 가능
null-routing 없음 없음
이용 기간 서버 상품에 연동 PrePaid, 최소 이용 기간 없음, 해지 통보 기간 없음, 설치비 없음

대부분의 Lineage 2 프로젝트에는 포함된 상시 방어와 깔끔한 서버 설정이면 충분합니다. Advanced DDoS Protection은 누군가 이 일을 개인적으로 받아들였을 때의 답이고, 경험상 그것은 그랜드 오픈 전 주에 일어납니다.

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

“로그인은 안 되는데 게임 서버는 정상으로 돌아갑니다”: 우연이 아니라 Lineage 2 서버를 겨냥한 공격의 일반적인 형태입니다. 로그인 서버와 게임 서버는 두 포트에 놓인 두 프로세스입니다. ss -tn state syn-recv | wc -l과 sar -n DEV 1 10을 측정하세요. 대역폭은 평범한데 절반만 열린 연결이 올라간다면 2106을 향한 연결 플러드입니다.

“IP 주소를 바꿨는데 다음 날 다시 오프라인이 되었습니다”: 공격자는 여러분의 플레이어와 똑같은 경로로 새 주소를 받습니다. 바뀐 l2.ini가 담긴 새 System 폴더, 여러분의 공지, 또는 잊고 방치한 DNS 항목입니다. 주소 변경은 시간을 벌어 주지만 해결책은 아닙니다.

“MaxConnectionPerIP를 3으로 두었더니 플레이어가 항의합니다”: 듀얼박스는 Lineage 2에서 흔한 일이고, Carrier-NAT 뒤의 플레이어는 공인 주소 하나를 수백 명과 함께 씁니다. 평상시 운영의 측정값을 덮는 값으로 되돌리고, 그 대신 새 연결 속도를 커널에서 제한하세요.

“iptables 규칙이 적용되지 않습니다”: 흔한 원인이 세 가지입니다. 규칙이 UFW 체인 뒤에 있어 전혀 도달하지 못하거나, 마지막 재시작 이후 사라졌거나(이때는 netfilter-persistent save 또는 /etc/ufw/before.rules 항목이 도움이 됩니다), 공격이 볼류메트릭이어서 규칙은 이미 꽉 찬 회선에서 제대로 동작하고 있는 경우입니다. iptables -L INPUT -n -v로 매칭 카운터가 올라가는지 확인하세요. 0에 머물러 있으면 규칙에 도달하지 못하는 것입니다.

“모든 플레이어에게 랙 스파이크가 생기는데 회선은 조용합니다”: 그렇다면 DDoS 공격이 아닙니다. Java 서버에서 흔한 용의자는 가비지 컬렉션 정지, 맞는 인덱스가 없는 데이터베이스, 그리고 루프에 빠진 스크립트나 커스텀 이벤트입니다. 먼저 sar -n DEV 1 10을 확인하세요. 패킷 전송률이 평범하게 유지된다면 원인은 네트워크가 아니라 서버 안에 있습니다.

“기존 업체가 제 IP 주소를 차단했습니다”: 그것이 null-routing입니다. 업체는 그렇게 자기 네트워크를 지키지만, 여러분에게 남는 결과는 공격이 성공한 것과 똑같고, 보통 그 뒤로 몇 시간 더 이어집니다. 확실하지 않으면 필터링을 하는지 null-routing을 하는지 물어보세요. 그 답이 어떤 하드웨어 사양보다 여러분의 가용성을 더 크게 좌우합니다.

“tcpdump에 이상한 것이 보이지 않습니다”: 트래픽이 이미 앞단 네트워크에서 걸러지고 있으면 서버에는 아무것도 도착하지 않는 것이 당연합니다. 필터링이 작동하고 있을 때의 정상적인 모습입니다. 반대로 회선이 포화되면 측정에 쓰려던 SSH 세션조차 닿지 않을 수 있습니다. 그럴 때는 게스트 시스템의 네트워크와 무관하게 동작하는 고객 포털의 VNC 콘솔을 이용하세요.

“그랜드 오픈이 2주 뒤입니다”: 그렇다면 오픈 주가 아니라 지금 옮기세요. 이전에는 플레이어를 위한 새 System 폴더, DNS 전환, 그리고 시험 운영이 듭니다. 이 모든 것을 날짜를 공지하기 전에 끝내 두어야 합니다. 공지한 순간부터 모든 경쟁자가 여러분에게 가장 불리한 시점을 알게 되기 때문입니다.

핵심 요약

  • Lineage 2 프리서버는 공개 네트워크에 정확히 두 개의 포트가 필요합니다. 로그인 서버용 2106 TCP와 게임 서버용 7777 TCP입니다. 포트 9014, 데이터베이스(L2J는 3306, L2OFF는 1433), 그리고 L2OFF 내부 포트 2002, 2006, 2008, 2104, 2108은 여기에 들어가지 않습니다.
  • Lineage 2는 전부 TCP로 동작하고 쿼리 포트도 RCON 포트도 없습니다. 그래서 전형적인 공격은 UDP 플러드가 아니라 포트 2106을 향한 SYN 플러드 또는 연결 플러드입니다.
  • 로그인 서버를 향한 공격은 새 로그인만 막습니다. 월드의 플레이어는 계속 플레이하는데 아무도 들어오지 못한다면, 원인은 7777이 아니라 포트 2106에서 찾아야 합니다.
  • EnableFloodProtection, MaxConnectionPerIP, LoginTryBeforeBan, AutoCreateAccounts를 의식적으로 설정하고, 등록이 끝난 뒤에는 AcceptNewGameServer를 False로 두며, 연결 속도는 커널에서 추가로 제한하세요. Java 제동 장치는 회선을 지나온 뒤에야 동작하기 때문입니다.
  • Lineage 2 서버를 향한 공격은 서버 오픈에 몰립니다. 날짜와 시각이 몇 주 전부터 공개되어 있고 경제적 피해가 오픈 당일에 가장 크기 때문입니다. 방어는 공지 뒤가 아니라 공지 전에 갖춰져 있어야 합니다.
  • 회선 용량을 넘어서고 초당 수십만 패킷을 넘어서면 서버 앞단 네트워크의 필터링만이 결과를 결정합니다. KernelHost에서는 그 필터링이 2단계이고, 상시 동작하며, 추가 요금이 없고, null-routing을 쓰지 않습니다.

프로젝트가 이미 KernelHost에 있다면 필터링은 여러분이 아무것도 하지 않아도 동작하고 있습니다. 그래도 이상한 점이 보이면 지원 티켓을 열어 주세요. 해당 IP 주소의 필터 규칙을 다시 맞춰 드립니다. 공격이 진행 중일 때는 WhatsApp 긴급 채팅 +43 650 8209883으로도 연락하실 수 있습니다.

자주 묻는 질문

Lineage 2 서버가 지금 오프라인입니다. DDoS 공격인지 어떻게 알 수 있나요?
CPU 부하가 아니라 패킷 전송률과 절반만 열린 연결을 보세요. sar -n DEV 1 10으로 초당 패킷과 바이트를, ss -tn state syn-recv | wc -l로 절반만 열린 TCP 연결의 수를 확인할 수 있습니다. 플레이어가 몇백 명인데 이 값이 다섯 자리라면 포트 2106을 향한 SYN 플러드입니다. 이미 접속한 플레이어는 정상으로 계속 플레이하는데 새 플레이어가 더 이상 들어오지 못한다면, 표적은 7777의 게임 서버가 아니라 로그인 서버입니다. 두 값이 모두 평범한데도 버벅인다면 원인은 서버 자체에 있습니다.
Lineage 2 서버에 열어 두어야 하는 포트는 무엇인가요?
정확히 두 개입니다. 로그인 서버용 2106 TCP와 게임 서버용 7777 TCP입니다. L2J에서는 LoginServer.properties의 LoginserverPort와 Server.properties의 GameserverPort에 들어 있습니다. 게임 서버가 로그인 서버에 등록하는 포트 9014는 127.0.0.1에 두고, 데이터베이스의 3306도 마찬가지입니다. L2OFF 패키지에서도 같습니다. 공개되는 것은 AuthD용 2106과 L2Server용 7777이며, 2002, 2006, 2008, 2104, 2108과 SQL 포트 1433은 내부 네트워크에 남습니다.
Lineage 2에서 게임 서버가 아니라 포트 2106의 로그인 서버가 공격받는 이유는 무엇인가요?
2106을 향한 플러드가 공격자에게 많은 대역폭을 요구하지 않으면서 새 플레이어의 유입을 끊어 버리기 때문입니다. 로그인 서버는 로그인 시도마다 개인 RSA 키로 접속 정보를 복호화하고, 절반만 완료된 세션은 L2J에서 최대 60초 동안 자리를 차지합니다. 기본값 MaxConnectionPerIP = 50은 출발지 주소마다 동시 연결 쉰 개를 허용하므로, 출발지 주소 천 개면 열린 세션 50,000개에 이릅니다. 월드의 플레이어는 처음에는 아무것도 느끼지 못하고, 새 플레이어는 애초에 들어오지 못합니다.
L2J의 포트 9014는 무엇에 쓰이고 외부에서 닿을 수 있어야 하나요?
포트 9014는 게임 서버가 로그인 서버에 등록하는 채널이고, 두 설정 파일에서 LoginPort로 지정합니다. 이 포트는 인터넷에서 결코 닿을 수 있어서는 안 됩니다. L2J는 이미 올바른 기본값을 제공합니다. LoginHostname = 127.0.0.1이 포트를 루프백 인터페이스에 바인딩합니다. 로그인 서버와 게임 서버가 두 머신에서 돌아간다면 구체적인 내부 주소를 적고 포트는 상대 쪽에만 허용하세요. 또한 서버가 한 번 등록된 뒤에는 AcceptNewGameServer를 False로 두세요.
Lineage 2 서버가 특히 그랜드 오픈에 공격받는 이유는 무엇인가요?
오픈 날짜와 시각이 몇 주 전부터 공개되어 있기 때문입니다. 오픈 일정표는 다가오는 Lineage 2 오픈을 연대기, 배율, 정확한 시작 시각과 함께 정리하고 매일 갱신합니다. 여기에 프리서버가 앞에서 돈을 번다는 점이 더해집니다. 플레이어 기반은 첫 며칠에 모이고, 첫 한 시간에 들어오지 못한 사람은 같은 주말에 문을 여는 프로젝트로 갈아탑니다. 기술적으로도 로그인 서버는 오픈 시각에 이미 한계에 있어, 여기에 얹힌 플러드는 정점 부하와 거의 구별되지 않습니다.
iptables나 L2J의 Flood Protection으로 DDoS 공격에 맞설 수 있나요?
작은 공격과 개별 출발지에는 맞설 수 있지만 볼류메트릭 공격에는 그렇지 못합니다. L2J의 Flood Protection은 Java 프로세스 안에서, iptables는 커널에서 돌아갑니다. 둘 다 이미 여러분의 회선을 지나온 패킷을 두고 판단합니다. 회선이 포화되면 플레이어의 패킷은 그 앞에서 이미 통과하지 못합니다. 그래도 로컬 수단은 유용합니다. 특히 포트 2106의 connlimit과 hashlimit, 그리고 위조된 출발지에 맞서는 net.ipv4.tcp_syncookies가 그렇습니다. 볼류메트릭 공격은 서버 앞단 네트워크에서 끝나야 합니다.
지금 L2 서버의 IP 주소를 빨리 바꾸면 도움이 되나요?
잠깐뿐입니다. 플레이어는 새 주소를 새 System 폴더로 받고, 그 안의 l2.ini에 ServerAddr= 줄이 있습니다. 같은 공지로 공격자도 그 주소를 받습니다. 여기에 이전 주소를 가리키는 잊힌 DNS 항목이 더해져 어떤 변경도 무의미해집니다. 게임 서버의 주소는 게다가 여러분의 로그인 서버가 인증을 마친 모든 클라이언트에게 직접 배포합니다. 주소 변경은 시간을 벌어 주지만 문제를 해결하지는 못합니다.
어느 규모부터 Lineage 2 서버가 혼자 버티지 못하나요?
일반적인 게임 서버는 1 Gbit/s 회선에 물려 있고, 이는 초당 125 메가바이트입니다. 다만 Lineage 2에서 더 중요한 것은 패킷 전송률입니다. 64 바이트짜리 패킷이라면 1 Gbit/s에 초당 약 149만 개가 들어가고, 일반적인 서버 커널은 그중 수십만 개만 처리합니다. 이 게임이 전부 TCP로 동작하기 때문에 연결 추적이 세 번째 한계로 더해집니다. 그래서 대역폭을 다 쓰지 않은 공격도 인증을 막을 수 있습니다.
KernelHost의 서버는 공격 중에 오프라인이 되나요?
아니요. null-routing을 쓰지 않습니다. 여러분의 IP 주소는 네트워크에 그대로 남고, 폐기되는 것은 악성 패킷뿐입니다. 방어는 두 단계입니다. 글로벌 스크러빙 네트워크의 17 Tbps 방어 용량과 프랑크푸르트암마인의 3.2 Tbps Arbor 실시간 필터링입니다. 이 방어는 상시 동작하며 공격에 먼저 반응할 필요가 없으므로, 초반에 서버가 사라지는 몇 분이 존재하지 않습니다. 새 서버의 오픈 시각에는 바로 이것이 결정적입니다.
KernelHost의 DDoS 방어는 추가 요금이 드나요?
아니요. 2단계 상시 방어는 모든 서버 상품에 추가 요금 없이 포함되어 있고 서버 제공 시점부터 동작합니다. 주문하거나 켜거나 설정할 필요가 없습니다. L2J, L2J-Mobius, aCis, L2OFF 패키지 가운데 무엇을 운영하든 상관이 없습니다. 필터링이 서버 파일이 아니라 포트와 프로토콜에 올라서 있기 때문입니다. 추가 비용은 자체 규칙을 쓰는 Advanced DDoS Protection을 함께 주문할 때만 발생합니다.
Lineage 2 프로젝트에 Advanced DDoS Protection이 추가로 필요한 때는 언제인가요?
프로젝트가 어쩌다 한두 번이 아니라 표적이 되어 몇 주에 걸쳐 공격받고, 필터링을 직접 조정하려 할 때입니다. 전용 방어 IP를 받고, 고객 포털에서 포트와 프로토콜별 방어 규칙을 직접 관리하므로 Lineage 2에서는 2106 TCP와 7777 TCP를 분리해 설정할 수 있습니다. 변경은 실시간으로 반영되므로 오픈 시각 전에는 규칙을 더 엄하게 두었다가 그 뒤에 다시 느슨하게 풀 수 있습니다. 요금은 월 50.00 EUR부터, PrePaid, 최소 이용 기간 없음, 설치비 없음입니다.

Lineage 2 리니지2 DDoS 방어 L2J L2OFF 게임 서버 보호 포트 2106 포트 7777 Advanced DDoS Protection 실시간 필터링