저는 실수를 반복하는 것도 습관이라고 생각합니다. 이는 반대로 습관을 고치면 실수를 하지 않을 수 있는 것이죠.
습관을 바꾸면 실수를 없앨 수 있다는 희망을 가지고 30대의 저에게 좋은 습관을 물려주기 위해
오늘도 열심히 블로그를 작성해보겠습니다.
목차
3-way-handshake
DDoS, SYN_Flooding
OS 커널 파라미터 튜닝
1. 3-way-handshake
일반적인 통신의 경우 3-way-handshake를 통해 TCP 연결을 맺은 이후 연결된 통로를 통해 통신을 시작한다. 3-way-handshake를 흐름은 아래와 같다.
1). Client가 통신하기 위해 SYN 패킷을 Server에 요청하는 것으로 handshake가 시작된다.
2). SYN 패킷을 받은 Server는 Client에게 SYN + ACK 패킷을 보내고 Client의 요청을 SYN_RECV 상태로 설정한 후 SYN Queue에 대기시킨다.
3). Client는 SYN + ACK 패킷을 받은 후 Server로 ACK 패킷을 보낸다.
4). Server는 ACK 패킷을 받은 후 Accept Queue의 공간을 확인하고 자리가 있으면 SYN Queue에서 SYN_RECV 상태로 대기 하던 요청을 ESTABLISHED 상태로 전환하며 Accept Queue에 대기시킨다.
5). 응용 애플리케이션(WEB, WAS 등)에서 accept() 메소드를 호출해 Accept Queue에서 대기하던 TCP Connection을 사용해 사용자의 요청을 처리한다.
서버에 별 다른 부하가 없다면 위 과정을 통해 클라이언트와 서버 간 TCP 네트워킹이 가능한데, 위 2) 과정에서 SYN_RECV가 머무르는 SYN Queue가 가득찬다면? 다음과 같은 에러 로그를 마주할 수 있다.
TCP: request_sock_TCP: Possible SYN flooding on port 80. Sending cookies. Check SNMP counters
로그 확인 명령어: dmesg T
위 에러를 마주했다면 DDos 공격 or SYN_Flooding 공격을 의심해볼 필요가 있다.
2. DDoS, SYN_Flooding
DDoS 공격은 엄청난 양의 트래픽을 통해 서버에 부하를 주어 서버 자원을 고갈시키는 공격이다. 위에서 나온 SYN Queue, Accept Queue, CPU, MEM 등 무수히 많은 서버 자원들이 부하를 받아 서비스 처리 속도가 느려지거나 서비스가 다운될 수 있다. 이는 마치 예전 고속도로 톨게이트에서 일 하시는 직원분들을 대상으로 무수히 많은 차가 몰려와 모든 직원분들이 빠르게 업무를 처리함에도 불구하고 교동 체증이 발생해 꽉 막힌 고속도로가 되는 모습과 같다고 필자는 생각한다.
SYN flooding은 DDoS(서비스 거부 공격)의 한 형태로, 네트워크 연결에서 발생하는 TCP 3-way handshake의 특성을 악용하여 서버의 리소스를 고갈시키고, 정상적인 연결을 막는 공격 방식이다.
대량의 트래픽 부하 + TCP 3-way handshake 특성 악용 = SYN_Flooding 이라고 할 수 있다.
TCP 3-way handshake의 어떤 특성을 악용한 것일까?
A SYN flood attack works by not responding to the server with the expected ACK code.
-참조:https://en.wikipedia.org/wiki/SYN_flood-
위키피디아에서 찾아보니 Client가 마지막으로 보내는 ACK 패킷을 의도적으로 보내지 않는 행위를 한다고 나와있다. 이는 무슨 뜻이냐, Server입장에서 해당 통신은 SYN_RECV 상태에서 ACK 패킷이 오기만을 기다리는 상태에 빠지게 되고 SYN Queue에서 머무르게 된다. 이때, SYN Queue의 max size보다 더 많은 SYN_RECV가 생성되어 ACK 패킷이 오기만을 기다리게 된다면 SYN Queue 자원이 고갈되어 SYN_Flooding이 발생하게 된다.
물론 malicious 공격자의 행위도 중요하지만 필자는 결과적으로 SYN Queue의 자원이 고갈되었다는 것에 중점을 둘 필요가 있다고 생각한다. 왜냐하면 위와 같이 의도적으로 패킷을 보내지 않는 행위 이외에도 SYN Queue의 자원이 고갈되도록 할 수 있는 여러 가지 경로가 있다고 생각하기 때문이다.
맨 위 3-way-handshake를 통해 예를 들어 보자, Accept Queue에서 대기하는 connection을 Application이 호출하여 사용한다고 필자가 설명했다. 이때, DDos 공격으로 인해 대량의 트래픽이 들어오면 어떻게 될까? Application이 client의 request를 처리하기 위해 accept Queue에서 ESTABLISHED된 connection을 사용함에도 불구하고 Accept Queue에는 대량의 요청으로 인한 connection들이 엄청나게 많이 쌓여있을 것이다. 그러다 Accept Queue 자원이 고갈되면 3-way-handshake를 통해 client가 ACK 요청을 보내도 server는 accpet Queue에 ESTABLISHED된 connection을 대기시킬 수 없기 때문에 완전한 통신 상태를 성립시킬 수 없다. 즉, 더 이상 ESTABLISHED 한 상태로 될 수 없다는 뜻이다. 그럼 자연스레 SYN_RECV 상태로 SYN Queue에 머무르게 되고, SYN Queue 자원 또한 같은 메커니즘으로 고갈이 된다면 SYN Flooding이 발생하게 된다.
위 예시처럼, 공격자가 ACK 패킷을 보내더라도 대량의 트래픽을 통해서 SYN Flooding이 발생할 수 있다. 그래서 필자는 SYN Flooding을 야기시킬 수 있는 자원을 튜닝시키는 것 만으로도 어느정도 SYN Flooding을 예방할 수 있다고 생각한다. 그것이 바로 OS 커널 파라미터 튜닝이다.
3. OS 커널 파라미터 튜닝
리눅스 환경에 Apache(port: 80)으로 서비스를 사용한다는 가정 하에 아래와 같이 튜닝을 했다.
# 리눅스 커널 파라미터를 영구 설정하는 sysctl.conf 파일 수정
vi /etc/sysctl.conf
#아래 내용 추가 및 저장
net.ipv4.tcp_max_syn_backlog = 1024
net.core.somaxconn = 1024
kernel.threads-max = 253438
#수정된 sysctl.conf 파일 적용
sysctl -p
SYN Queue 크기 확장을 위해 net.ipv4.tcp_max_syn_backlog 값 수정.
Accept Queue 크기 확장을 위해 net.core.somaxconn 값 수정.
통신 대기량이 많아짐에 따라 해당 통신들에 대한 요청을 빨리 처리할 수 있도록 kernel.threads-max 값 수정.
#httpd.conf 파일 수정
vi /etc/httpd/conf/httpd.conf
#ListenBackLog 설정 추가
ListenBackLog 1024
#ss -tnlp -> send-Q 열에서 Application이 확보한 accept queue 크기가 확장됨을 확인 가능.
ss -tnlp
Apache가 사용하는 80포트에 대한 Accept Queue는 min(net.core.somaxconn, ListenBacklog) 공식을 통해 정해지기 때문에 net.core,somaxconn 값만 늘려서는 Apache가 사용할 Accept Queue크기를 확장시킬 수 없다. Apache의 httpd.conf 파일을 통해 ListenBackLog 값도 같이 늘려줘야 한다.
#셸에서 실행시키는 프로세스의 파일 디스크립터 크기 설정
ulimits -n 100000
#systemd를 통해 프로세스가 실행되는 apache는 별도로 수정
systemctl edit httpd
#아래와 같이 추가 및 저장
[Service]
LimitNOFILE=100000
#아파치 재기동
systemctl restart httpd
파일 디스크립터 또한 값을 늘려줘야 Too many open files 같은 에러가 발생하지 않는다. 위 주석에 설명을 적어놨지만 한 번더 정리하자면, 셸에서 실행시키는 프로세스의 파일 디스크립터 크기는 ulimit -n 명령어를 통해 설정 가능하지만 systemd를 통해 실행되는 프로세스는 위와 같이 systemctl edit 명령어를 통해 값을 수정하고 restart 해줘야 반영이 가능하다.
반영 여부 확인은 cat /proc/{apache pid}/limits 명령어를 통해 가능하다. open files 행의 값을 확인하면 된다.
이번 포스팅에서는 네트워크에 대한 트러블슈팅을 정리하게 된 것 같습니다. 해당 분야에 대한 지식이 많지 않아서 정리를 할까 말까 고민을 엄청 많이 했습니다.... 왜냐하면 잘못된 정보를 공유하게 될 수 있기 때문이죠.. 그럼에도 불구하고 최대한 찾아보며 정리를 하는 것이 저에게도 큰 도움이 될 것이라 생각했기에 열심히 작성해 보았습니다. 글 내용 중 shit한 내용이 있었다면 질타보단 올바른 내용을 공유함으로써 보듬어주시면 감사하겠습니다.