Ubuntu 서버 보안 강화 완전 가이드: SSH부터 fail2ban까지
Oracle Cloud 서버에 한 달간 7,600건의 SSH 브루트포스 공격이 들어왔습니다. 공격 데이터를 분석하고, SSH 하드닝과 fail2ban 강화로 효과적으로 방어한 경험을 공유합니다.
1. 문제 상황
사이드 프로젝트용 서버를 Oracle Cloud에 올린 지 한 달. 별 문제 없이 잘 돌아가는 줄 알았는데, 어느 날 fail2ban 상태를 확인해보니 충격적인 숫자가 눈에 들어왔습니다.
$ sudo fail2ban-client status sshd
Status for the jail: sshd
|- Filter
| |- Currently failed: 1
| |- Total failed: 18769
| `- Journal matches: _SYSTEMD_UNIT=sshd.service + _COMM=sshd
`- Actions
|- Currently banned: 2
|- Total banned: 3048
`- Banned IP list: 178.62.252.32 209.38.36.112
총 18,769번의 로그인 실패, 3,048개의 IP 차단.
서버를 띄워놓기만 해도 이렇게 많은 공격이 들어온다는 사실에 놀랐습니다. 이 글에서는 한 달간의 공격 데이터를 분석하고, 실제로 적용한 보안 강화 설정을 공유합니다.
공격 규모 요약
| 항목 | 값 |
|---|---|
| 분석 기간 | 2025-12-21 ~ 2026-01-22 (약 1개월) |
| 총 차단 건수 | ~7,600건 |
| 일평균 차단 | ~250건 |
| 최다 차단일 | 513건 (2026-01-19) |
2. 원인 분석: 누가 왜 공격하는가?
공격 출처 분석
fail2ban 로그 전체를 분석해서 어떤 IP 대역에서 공격이 오는지 확인해봤습니다.
# 전체 로그에서 차단된 IP 대역 분석
$ { zcat /var/log/fail2ban.log.*.gz; cat /var/log/fail2ban.log.1 /var/log/fail2ban.log; } \
| grep 'Ban' | awk '{print $NF}' | cut -d. -f1-2 | sort | uniq -c | sort -rn | head -10
결과:
| IP 대역 | 차단 횟수 | 호스팅 업체 |
|---|---|---|
| 45.78.x.x | 738 | IT7 Networks |
| 159.223.x.x | 317 | DigitalOcean |
| 101.47.x.x | 317 | BytePlus (ByteDance) |
| 209.38.x.x | 305 | DigitalOcean |
| 167.99.x.x | 285 | DigitalOcean |
| 161.35.x.x | 258 | DigitalOcean |
| 167.71.x.x | 244 | DigitalOcean |
| 146.190.x.x | 244 | DigitalOcean |
| 164.92.x.x | 237 | DigitalOcean |
| 188.166.x.x | 233 | DigitalOcean |
대부분 DigitalOcean에서 호스팅된 서버들입니다. 공격자들이 저렴한 클라우드 서버를 대량으로 만들어서 봇넷처럼 운영하는 것으로 보입니다.
공격에 사용된 사용자명
어떤 사용자명으로 로그인을 시도하는지 분석해봤습니다.
$ sudo journalctl -u ssh --since '7 days ago' | grep 'Invalid user' \
| awk '{print $8}' | sort | uniq -c | sort -rn | head -10
결과:
| 사용자명 | 시도 횟수 | 비고 |
|---|---|---|
| admin | 1,632 | 가장 흔한 관리자 계정명 |
| user | 762 | 일반적인 사용자명 |
| test | 578 | 테스트 계정 |
| postgres | 531 | PostgreSQL 기본 계정 |
| oracle | 400 | Oracle DB 계정 |
| mysql | 302 | MySQL 계정 |
| guest | 286 | 게스트 계정 |
| git | 201 | Git 서버 계정 |
| debian | 192 | Debian 기본 계정 |
| pi | 185 | Raspberry Pi 기본 계정 |
패턴이 보이시나요? 공격자들은 흔히 사용되는 기본 계정명을 사전(dictionary)으로 만들어서 무차별 대입 공격을 합니다.
시간대별 공격 분포
$ sudo journalctl -u ssh --since '24 hours ago' | grep -E '(Invalid user|Failed password)' \
| awk '{print $3}' | cut -d: -f1 | sort | uniq -c
특정 시간대에 집중되지 않고 24시간 내내 꾸준히 공격이 들어옵니다. 자동화된 봇이라는 증거입니다.
일별 차단 추이
2025-12-21 302건
2025-12-25 256건
2026-01-01 324건
2026-01-05 443건
2026-01-11 446건
2026-01-19 513건 ← 최다
시간이 지날수록 공격이 늘어나는 추세입니다. 서버가 인터넷에 노출되어 있으면 봇들의 스캔 목록에 추가되고, 점점 더 많은 봇이 시도하게 됩니다.
3. 해결 방법: 단계별 보안 강화
3.1 현재 보안 상태 점검
먼저 현재 서버의 보안 상태를 점검합니다.
# SSH 설정 확인
$ sudo sshd -T | grep -E '^(passwordauthentication|permitrootlogin|maxauthtries|pubkeyauthentication)'
# 결과 예시 (기본값)
maxauthtries 6
permitrootlogin without-password
pubkeyauthentication yes
passwordauthentication no
# UFW 상태 확인
$ sudo ufw status verbose
# 결과 예시
Status: active
Default: deny (incoming), allow (outgoing)
To Action From
-- ------ ----
22/tcp ALLOW IN Anywhere
80/tcp ALLOW IN Anywhere
443/tcp ALLOW IN Anywhere
# fail2ban 상태 확인
$ sudo fail2ban-client status sshd
# 열린 포트 확인
$ sudo ss -tulpn | grep LISTEN
3.2 SSH 하드닝
SSH 설정을 강화합니다. Ubuntu에서는 /etc/ssh/sshd_config.d/ 디렉토리에 별도 파일을 만드는 것이 권장됩니다.
$ sudo tee /etc/ssh/sshd_config.d/99-security-hardening.conf << 'EOF'
# Security Hardening
PermitRootLogin no
MaxAuthTries 3
X11Forwarding no
ClientAliveInterval 300
ClientAliveCountMax 2
EOF
각 설정 설명:
| 설정 | 값 | 설명 |
|---|---|---|
PermitRootLogin |
no |
root 계정 SSH 접속 차단 |
MaxAuthTries |
3 |
3회 실패 시 연결 종료 (기본값 6) |
X11Forwarding |
no |
GUI 포워딩 비활성화 (서버에서 불필요) |
ClientAliveInterval |
300 |
5분마다 연결 확인 |
ClientAliveCountMax |
2 |
2회 응답 없으면 종료 (약 10분) |
# 설정 문법 검증
$ sudo sshd -t
# 설정 적용 (Ubuntu는 ssh, CentOS는 sshd)
$ sudo systemctl reload ssh
# 적용 확인
$ sudo sshd -T | grep -E '^(permitrootlogin|maxauthtries|x11forwarding)'
permitrootlogin no
maxauthtries 3
x11forwarding no
3.3 fail2ban 강화
기본 fail2ban 설정은 10분 차단(bantime)에 5회 실패(maxretry)입니다. 이러면 같은 IP가 하루에 수십 번 차단-해제를 반복합니다.
# 현재 설정으로 같은 IP가 반복 차단되는 모습
$ sudo grep 'Ban 178.62.252.32' /var/log/fail2ban.log | tail -5
2026-01-22 21:37:23 Ban 178.62.252.32
2026-01-22 21:53:55 Ban 178.62.252.32
2026-01-22 22:06:02 Ban 178.62.252.32
2026-01-22 22:18:03 Ban 178.62.252.32
2026-01-22 22:29:51 Ban 178.62.252.32
강화된 설정 적용:
$ sudo tee /etc/fail2ban/jail.d/sshd-hardening.conf << 'EOF'
# SSH Hardening
[sshd]
enabled = true
maxretry = 3
findtime = 1d
bantime = 1d
# Recidive - 반복 공격자 장기 차단
[recidive]
enabled = true
logpath = /var/log/fail2ban.log
banaction = %(banaction_allports)s
bantime = 4w
findtime = 1w
maxretry = 3
EOF
설정 설명:
| Jail | 설정 | 값 | 설명 |
|---|---|---|---|
| sshd | maxretry | 3 | 3회 실패 시 차단 |
| findtime | 1d | 24시간 내 시도 기준 | |
| bantime | 1d | 24시간 차단 | |
| recidive | maxretry | 3 | 1주일 내 3번 차단되면 |
| findtime | 1w | 1주일 기준 | |
| bantime | 4w | 1달 차단 |
recidive jail의 핵심: 24시간 차단 후 풀려서 다시 공격 → 또 차단 → 1주일 내 3번 반복되면 1달 차단. 집요한 공격자를 효과적으로 막습니다.
주의: recidive의 findtime은 sshd bantime보다 길어야 합니다. sshd bantime이 24시간이면 findtime을 1주일 이상으로 설정해야 실제로 작동합니다.
# fail2ban 재시작
$ sudo systemctl restart fail2ban
# jail 목록 확인
$ sudo fail2ban-client status
Status
|- Number of jail: 2
`- Jail list: recidive, sshd
# 각 jail 상태 확인
$ sudo fail2ban-client status sshd
$ sudo fail2ban-client status recidive
3.4 불필요한 서비스 비활성화
열린 포트를 확인하면 불필요한 서비스가 실행 중일 수 있습니다.
$ sudo ss -tulpn | grep LISTEN
tcp LISTEN 0.0.0.0:111 rpcbind
tcp LISTEN 0.0.0.0:22 sshd
tcp LISTEN 0.0.0.0:80 docker-proxy
tcp LISTEN 0.0.0.0:443 docker-proxy
rpcbind (포트 111): NFS에 필요한 서비스인데, NFS를 사용하지 않으면 불필요합니다. 과거에 보안 취약점이 많았던 서비스입니다.
# rpcbind 비활성화
$ sudo systemctl stop rpcbind rpcbind.socket
$ sudo systemctl disable rpcbind rpcbind.socket
# 확인
$ sudo ss -tulpn | grep 111
# (출력 없음 = 포트 닫힘)
3.5 자동 보안 업데이트 설정
보안 패치는 자동으로 적용되도록 설정합니다.
# 설치 확인
$ dpkg -l | grep unattended-upgrades
ii unattended-upgrades 2.9.1+nmu4ubuntu1 all automatic installation of security upgrades
# 설정 확인
$ cat /etc/apt/apt.conf.d/20auto-upgrades
APT::Periodic::Update-Package-Lists "1";
APT::Periodic::Unattended-Upgrade "1";
설치되어 있지 않다면:
$ sudo apt install unattended-upgrades
$ sudo dpkg-reconfigure unattended-upgrades
4. 핵심 개념 정리
보안 레이어 구성
| 레이어 | 도구 | 역할 | 특징 |
|---|---|---|---|
| 네트워크 | 클라우드 보안 그룹 | 포트 차단 | 가장 바깥쪽, 가장 강력 |
| OS | UFW | 포트 차단 | 서버 레벨 방화벽 |
| 애플리케이션 | fail2ban | IP 차단 | 로그 기반 동적 차단 |
| 서비스 | SSH 설정 | 인증 강화 | 마지막 방어선 |
fail2ban vs UFW 차이점
| 항목 | UFW | fail2ban |
|---|---|---|
| 차단 방식 | 포트 기반 | IP 기반 |
| 규칙 | 정적 | 동적 (로그 분석) |
| 용도 | 불필요한 포트 차단 | 악성 IP 차단 |
| 예시 | "포트 3306은 닫아" | "이 IP는 5번 실패했으니 차단" |
Docker와 UFW 주의사항
Docker는 iptables를 직접 조작하기 때문에 UFW 규칙을 우회할 수 있습니다.
# Docker 컨테이너가 0.0.0.0에 바인딩되어 있으면 주의
$ sudo ss -tulpn | grep docker-proxy
tcp LISTEN 0.0.0.0:2368 docker-proxy # ← Ghost
tcp LISTEN 0.0.0.0:3000 docker-proxy # ← Analytics
해결책:
- 클라우드 보안 그룹에서 필요한 포트만 열기 (가장 확실)
- Docker 컨테이너를 127.0.0.1에만 바인딩
DOCKER_OPTS에서 iptables 비활성화 (권장하지 않음)
Oracle Cloud, AWS, GCP 등 클라우드를 사용한다면 클라우드 보안 그룹이 가장 바깥쪽 방어선이므로, 여기서 22, 80, 443만 열어두면 Docker 포트 노출 문제를 해결할 수 있습니다.
5. 베스트 프랙티스
서버 보안 체크리스트
SSH 설정:
- [ ] 비밀번호 인증 비활성화 (
PasswordAuthentication no) - [ ] root 로그인 비활성화 (
PermitRootLogin no) - [ ] 인증 시도 횟수 제한 (
MaxAuthTries 3) - [ ] 유휴 세션 타임아웃 설정
방화벽:
- [ ] UFW 활성화
- [ ] 필요한 포트만 개방 (22, 80, 443)
- [ ] 클라우드 보안 그룹 설정 확인
침입 방지:
- [ ] fail2ban 설치 및 활성화
- [ ] findtime/bantime 24시간으로 설정 (느린 공격도 차단)
- [ ] recidive jail 활성화 (1주일 내 3번 차단 시 1달 차단)
유지보수:
- [ ] 자동 보안 업데이트 활성화
- [ ] 불필요한 서비스 비활성화 (rpcbind 등)
- [ ] 정기적인 로그 점검
정기 점검 명령어
# fail2ban 현황
$ sudo fail2ban-client status sshd
# 최근 차단된 IP
$ sudo grep 'Ban' /var/log/fail2ban.log | tail -10
# 실패한 SSH 시도
$ sudo journalctl -u ssh | grep 'Failed' | tail -10
# 열린 포트 확인
$ sudo ss -tulpn | grep LISTEN
# UFW 상태
$ sudo ufw status
6. FAQ
Q: 비밀번호 인증을 비활성화하면 SSH 키를 잃어버리면 어떡하나요?
A: 클라우드 서비스의 콘솔 접근 기능을 사용하세요. Oracle Cloud, AWS, GCP 모두 웹 콘솔에서 직접 서버에 접근하는 기능을 제공합니다. SSH 키를 잃어버려도 복구할 수 있습니다.
Q: fail2ban이 정상 사용자를 차단하면 어떡하나요?
A: ignoreip 설정으로 화이트리스트를 추가할 수 있습니다.
# /etc/fail2ban/jail.d/sshd-hardening.conf
[sshd]
ignoreip = 127.0.0.1/8 ::1 your.home.ip.address
Q: PermitRootLogin이 without-password면 안전한가요?
A: SSH 키로만 root 로그인이 가능한 상태입니다. 키가 유출되지 않으면 안전하지만, no로 설정하고 일반 사용자로 접속 후 sudo를 사용하는 것이 더 안전합니다.
Q: recidive jail이 작동하는지 어떻게 확인하나요?
A: 시간이 지나면 반복 공격자가 recidive jail에 추가됩니다.
$ sudo fail2ban-client status recidive
Status for the jail: recidive
|- Currently banned: 5
`- Banned IP list: 45.78.xx.xx 159.223.xx.xx ...
Q: 공격이 너무 많은데 더 강력한 방법은 없나요?
A: 몇 가지 추가 옵션이 있습니다:
- SSH 포트 변경: 22 → 비표준 포트 (스캔 회피, 완전한 해결책은 아님)
- Port Knocking: 특정 시퀀스로 포트를 두드려야 SSH 포트 오픈
- Cloudflare Tunnel: SSH 포트를 아예 닫고 Cloudflare를 통해서만 접근
- VPN: 서버를 VPN 뒤에 숨기기
7. 참고 자료
8. 마무리
서버를 인터넷에 올려놓으면 24시간 자동화된 공격에 노출됩니다. 한 달 만에 7,600건의 차단이라는 숫자가 이를 증명합니다.
하지만 기본적인 보안 설정만 잘 해두면 걱정할 필요 없습니다:
- SSH 키 인증만 사용 (비밀번호 인증 비활성화)
- fail2ban으로 반복 공격 차단 (bantime 24시간 + recidive)
- 불필요한 서비스 비활성화
- 자동 보안 업데이트 활성화
이 네 가지만 지켜도 대부분의 자동화된 공격은 막을 수 있습니다.