

TL;DR
- 운영 중인 서버에서 SSH 브루트포스 공격을 탐지했다.
- 1월 13일 발견 당시 1,312건이었던 공격이 7일 후 5,227건으로 증가.
- fail2ban + SSH 포트 변경 + Private Subnet 배치로 대응한 결과,
GPU 서버 공격 0건 달성.
MovieSir 서비스를 운영하던 중 정기 보안 점검에서 이상 징후를 발견했다.
$ sudo lastb | wc -l
1107
lastb 명령어는 실패한 로그인 시도를 보여준다.
운영 2주 만에 1,107건의 SSH 로그인 실패 기록이 쌓여 있었다.

$ sudo lastb | head -20
monitor ssh:notty 192.32.162.151 Tue Jan 13 04:27 - 04:27 (00:00)
config ssh:notty 162.214.115.13 Tue Jan 13 04:23 - 04:23 (00:00)
public ssh:notty 192.32.162.151 Tue Jan 13 04:23 - 04:23 (00:00)
...
동일한 IP에서 짧은 시간 동안 반복적으로 로그인을 시도하고 있었다.
전형적인 브루트포스(무차별 대입) 공격 패턴이다.
$ sudo lastb | awk '{print $3}' | sort | uniq -c | sort -rn | head -10
423 45.148.10.121
312 143.110.174.161
156 164.92.161.167
...
상위 공격 IP들을 분석한 결과:
| IP | 국가 | 시도 횟수 | 시도한 계정 |
|---|---|---|---|
| 45.148.10.121 | 네덜란드 | 423회 | admin, AdminGPO |
| 143.110.174.161 | 미국 (DigitalOcean) | 312회 | user |
| 164.92.161.167 | 미국 (DigitalOcean) | 156회 | solv |
| 3.37.6.179 | 한국 (AWS) | 89회 | admin |
공격자들이 시도한 계정명을 분석했다.
$ sudo lastb | awk '{print $1}' | sort | uniq -c | sort -rn | head -10
523 admin
312 user
187 root
89 test
67 ubuntu
45 ftpuser
32 debian
28 student
21 operator
15 guest
| 유형 | 시도한 계정 | 특징 |
|---|---|---|
| 기본 계정 | root, ubuntu, admin | 리눅스 기본 계정 |
| 서비스 계정 | ftpuser, mysql, postgres | 서비스 기본 계정 |
| 일반 계정 | user, test, student, guest | 흔한 이름 |
자동화된 봇이 사전에 정의된 계정 목록으로 무차별 시도하는 것을 확인했다.
App Server만 확인하면 안 될 것 같아서 GPU Server도 점검했다.
$ sudo lastb | wc -l
205
GPU Server에도 205건의 공격이 들어오고 있었다.
이 서버에는 PostgreSQL과 AI 모델이 있어서 더 위험했다.
공격이 성공했다면 어떤 피해가 발생할 수 있었을까?
| 시나리오 | 피해 유형 | 심각도 |
|---|---|---|
| DB 접근 | 사용자 데이터 유출 | 🔴 높음 |
| 랜섬웨어 | 서버 암호화, 금전 요구 | 🔴 높음 |
| 크립토마이너 | GPU 자원 탈취, 전기료 폭증 | 🟠 중간 |
| 봇넷 편입 | DDoS 공격 가담 | 🟠 중간 |
특히 GPU 서버에는 Tesla T4 GPU와 PostgreSQL이 있어서,
침입 시 크립토마이닝 + 데이터 유출의 이중 피해가 우려됐다.
공격을 발견한 당일부터 바로 대응에 들어갔다.
먼저 기존 보안 도구의 상태를 확인했다.

$ sudo fail2ban-client status sshd
Status for the jail: sshd
|- Filter
| |- Currently failed: 4
| |- Total failed: 1107
| `- File list: /var/log/auth.log
`- Actions
|- Currently banned: 1
|- Total banned: 118
`- Banned IP list: 216.180.127.200
| 항목 | 값 | 의미 |
|---|---|---|
| Total failed | 1,107 | 총 실패한 로그인 시도 |
| Total banned | 118 | 차단된 IP 수 |
| Currently banned | 1 | 현재 차단 중인 IP (만료됨) |
fail2ban이 작동하고 있었지만,
차단 후에도 새로운 IP에서 계속 공격이 들어오고 있었다.
# /etc/fail2ban/jail.local
[sshd]
enabled = true
port = 52222
filter = sshd
logpath = /var/log/auth.log
maxretry = 3
bantime = 3600
findtime = 600
| 파라미터 | 값 | 설명 |
|---|---|---|
| maxretry | 3 | 3회 실패 시 차단 (기존 5회) |
| bantime | 3600 | 1시간 동안 차단 |
| findtime | 600 | 10분 내 실패 횟수 카운트 |
가장 효과적인 조치는 SSH 포트 변경이었다.
# /etc/ssh/sshd_config
Port 52222
PermitRootLogin no
PasswordAuthentication no
PubkeyAuthentication yes
| 설정 | 변경 | 효과 |
|---|---|---|
| Port | 22 → 52222 | 자동화된 스캔 회피 |
| PermitRootLogin | no | root 직접 로그인 차단 |
| PasswordAuthentication | no | 키 인증만 허용 |
포트 22는 SSH의 기본 포트로, 자동화된 봇들이 가장 먼저 스캔하는 대상이다.
비표준 포트로 변경하면 대부분의 자동 공격을 회피할 수 있다.
| 포트 | 출발지 | 용도 |
|---|---|---|
| 52222 | 0.0.0.0/0 | SSH (변경된 포트) |
| 443 | 0.0.0.0/0 | HTTPS |
| 80 | 0.0.0.0/0 | HTTP → HTTPS 리다이렉트 |
기본 정책 설정:
# 모든 인바운드 차단, 아웃바운드 허용 (화이트리스트 방식)
sudo ufw default deny incoming
sudo ufw default allow outgoing
| 정책 | 설정 | 이유 |
|---|---|---|
| incoming | deny | 허용된 포트만 열기 |
| outgoing | allow | 서버에서 외부 통신 허용 (apt, curl 등) |
포트 허용:
sudo ufw allow 52222/tcp # SSH (변경된 포트)
sudo ufw allow 443/tcp # HTTPS
sudo ufw allow 80/tcp # HTTP
sudo ufw enable
상태 확인:

$ sudo ufw status verbose
Status: active
Logging: on (low)
Default: deny (incoming), allow (outgoing), deny (routed)
To Action From
-- ------ ----
80/tcp ALLOW IN Anywhere
443/tcp ALLOW IN Anywhere
52222/tcp ALLOW IN Anywhere
| 계층 | 도구 | 위치 | 특징 |
|---|---|---|---|
| 클라우드 | 보안그룹 | VM 외부 | 네트워크 레벨에서 필터링 |
| OS | ufw (iptables) | VM 내부 | 커널 레벨에서 필터링 |
이중 방어의 이점:
GPU 서버에는 DB와 AI 모델이 있어서 더 강력한 보호가 필요했다.

GPU 서버 보안그룹:
| 포트 | 출발지 | 용도 |
|---|---|---|
| 22 | 10.0.1.117/32 | SSH (App Server만) |
| 5432 | 10.0.1.117/32 | PostgreSQL |
| 8001 | 10.0.1.117/32 | AI Service |
모든 포트를 App Server IP에서만 접근 가능하도록 제한했다.
GPU 서버에 접속할 때는 App Server를 Bastion Host로 사용한다.
# ~/.ssh/config
Host moviesir-app
HostName <App-Server-IP>
User ubuntu
IdentityFile ~/.ssh/your-key.pem
Port 52222
Host moviesir-gpu
HostName 10.0.35.62
User ubuntu
IdentityFile ~/.ssh/your-key.pem
ProxyJump moviesir-app
# 한 번의 명령으로 GPU 서버 접속
$ ssh moviesir-gpu
ProxyJump를 사용하면 App Server를 경유해서
Private Subnet의 GPU 서버에 접속할 수 있다.
대응 조치를 완료하고 7일이 지났다. 결과를 확인해보자.

$ sudo fail2ban-client status sshd
Status for the jail: sshd
|- Filter
| |- Currently failed: 2
| |- Total failed: 2680
| `- File list: /var/log/auth.log
`- Actions
|- Currently banned: 0
|- Total banned: 209
`- Banned IP list:
| 지표 | 1월 13일 | 1월 20일 (7일 후) | 변화 |
|---|---|---|---|
| 총 공격 시도 | 1,107건 | 5,227건 | +4,120건 |
| 차단된 IP | 118개 | 299개 | +181개 |

$ sudo lastb | awk '{print $3}' | sort | uniq -c | sort -rn | head -10
655 130.12.181.24
357 176.120.22.13
328 176.120.22.47
315 216.180.127.200
216 143.110.174.161
186 91.202.233.33
116 80.94.92.164
112 3.37.6.179
공격은 계속되고 있지만, fail2ban이 299개 IP를 차단하며 방어 중이다.

$ sudo lastb | head -5
user ssh:notty 45.148.10.121 Tue Jan 13 01:22 - 01:22 (00:00)
AdminGPO ssh:notty 45.148.10.121 Tue Jan 13 00:47 - 00:47 (00:00)
admin ssh:notty 45.148.10.121 Tue Jan 13 00:12 - 00:12 (00:00)
admin ssh:notty 3.37.6.179 Tue Jan 13 00:04 - 00:04 (00:00)
admin ssh:notty 3.37.6.179 Tue Jan 13 00:04 - 00:04 (00:00)
| 지표 | 1월 13일 | 1월 20일 (7일 후) | 변화 |
|---|---|---|---|
| 마지막 공격 | 1월 13일 | 1월 13일 | 7일간 신규 공격 0건 |
📌 1월 13일 이후로 새로운 공격이 단 한 건도 없다.
GPU Server에서 Public IP를 제거하고 Private Subnet으로 옮긴 것이 결정적이었다.
외부에서 접근 자체가 불가능해졌기 때문이다.

문제: 포트를 52222로 변경했는데 SSH 접속이 안 됨
원인: 보안그룹에서 새 포트를 열지 않고 sshd만 재시작
해결:
# 1. 보안그룹에서 52222 포트 먼저 열기
# 2. ufw에서 새 포트 허용
sudo ufw allow 52222/tcp
# 3. 기존 22 포트 삭제
sudo ufw delete allow 22/tcp
# 4. sshd 재시작
sudo systemctl restart sshd
교훈:
| 순서 | 작업 |
|---|---|
| 1 | 보안그룹에서 새 포트 열기 |
| 2 | ufw에서 새 포트 허용 |
| 3 | sshd_config 수정 |
| 4 | sshd 재시작 |
| 5 | 새 포트로 접속 테스트 |
| 6 | 성공 후 기존 포트 닫기 |
문제: 포트 변경 후 fail2ban이 공격을 차단 안 함
원인: jail.local에서 port 설정이 기존 22로 되어 있음
해결:
# /etc/fail2ban/jail.local
[sshd]
port = 52222 # 변경된 포트로 수정
sudo systemctl restart fail2ban
문제: ssh moviesir-gpu 실행 시 타임아웃
원인: App Server 방화벽에서 내부 SSH(22) 아웃바운드 차단
해결:
# App Server에서
sudo ufw allow out 22/tcp
| 도구 | 유형 | 가격 | 특징 |
|---|---|---|---|
| fail2ban | 호스트 기반 | 무료 | 로그 분석 후 IP 차단 |
| CrowdSec | 호스트 + 커뮤니티 | 무료 | 글로벌 위협 공유 |
| Cloudflare | 클라우드 WAF | 무료/유료 | DNS 기반 프록시 |
| AWS WAF | 클라우드 WAF | 유료 | AWS 네이티브 |
| 항목 | fail2ban | CrowdSec | Cloudflare |
|---|---|---|---|
| 설치 난이도 | 쉬움 | 중간 | 쉬움 (DNS만 변경) |
| SSH 보호 | O | O | X (HTTP만) |
| 리소스 사용 | 낮음 | 중간 | 없음 (클라우드) |
| 위협 인텔리전스 | X (로컬만) | O (커뮤니티) | O (유료) |
| 실시간 차단 | O | O | O |
| 기준 | 판단 |
|---|---|
| SSH 보호 필요 | Cloudflare는 HTTP만 → 탈락 |
| 비용 | 무료 필수 → fail2ban, CrowdSec |
| 단순성 | 서버 2대에 CrowdSec은 오버스펙 |
| 검증됨 | fail2ban은 10년+ 사용된 안정적 도구 |
| 상황 | 추천 도구 |
|---|---|
| 서버 1~2대, SSH 방어 | fail2ban ✅ |
| 서버 10대+, 위협 공유 필요 | CrowdSec |
| 웹 서비스 DDoS 방어 | Cloudflare |
| AWS 환경, 예산 있음 | AWS WAF + Shield |
현재: fail2ban (단일 서버 방어)
↓
확장 시: CrowdSec (다중 서버 위협 공유)
↓
대규모: Cloudflare + AWS WAF (다계층 방어)
| 항목 | 교훈 |
|---|---|
| 기본 포트 | SSH 22번 포트는 자동 공격의 첫 번째 타겟 |
| 공격 속도 | 서버 오픈 후 수 시간 내에 공격 시작 |
| 자동화 | 대부분의 공격은 자동화된 봇에 의한 것 |
| 다층 방어 | 단일 보안 조치로는 부족, 여러 계층 필요 |
| Private Subnet | 외부 노출이 필요 없는 서버는 격리 |
서버 운영 시 최소한의 보안 조치:
# 1. SSH 포트 변경
sudo sed -i 's/#Port 22/Port 52222/' /etc/ssh/sshd_config
# 2. 비밀번호 인증 비활성화
sudo sed -i 's/#PasswordAuthentication yes/PasswordAuthentication no/' /etc/ssh/sshd_config
# 3. fail2ban 설치
sudo apt install fail2ban -y
# 4. 방화벽 설정
sudo ufw allow 52222/tcp
sudo ufw enable
# 5. SSH 재시작
sudo systemctl restart sshd
정기적으로 확인해야 할 명령어:
# 실패한 로그인 시도
sudo lastb | head -20
# fail2ban 상태
sudo fail2ban-client status sshd
# 현재 접속 중인 사용자
who
# 열린 포트 확인
sudo netstat -tlnp
| 날짜 | 상황 |
|---|---|
| 1월 13일 | 공격 발견 (App 1,107건 / GPU 205건) |
| 1월 13일 | fail2ban 설정 강화, SSH 포트 변경 (22→52222) |
| 1월 14일 | GPU Server Public IP 제거, Private Subnet 배치 |
| 1월 20일 | 결과 확인 (App 5,227건 누적, 299 IP 차단 / GPU 7일간 0건) |
| 항목 | 결과 |
|---|---|
| 탐지 | 5,227건 공격 시도 발견 |
| 차단 | 299개 IP 자동 차단 |
| 방어 | GPU 서버 7일간 공격 0건 달성 |
| 구축 | 5계층 보안 아키텍처 |
| 영역 | 개선 가능한 점 |
|---|---|
| 모니터링 | Prometheus + Grafana로 실시간 대시보드 |
| 알림 | Slack/Discord 연동으로 공격 알림 |
| 로그 | ELK Stack으로 중앙 로그 관리 |
| 감사 | CloudTrail 같은 감사 로그 활성화 |
이 글은 스나이퍼팩토리 카카오클라우드 AIaaS 마스터 클래스 2기 프로젝트 경험을 바탕으로 작성되었습니다.