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

해결책:

  1. 클라우드 보안 그룹에서 필요한 포트만 열기 (가장 확실)
  2. Docker 컨테이너를 127.0.0.1에만 바인딩
  3. 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건의 차단이라는 숫자가 이를 증명합니다.

하지만 기본적인 보안 설정만 잘 해두면 걱정할 필요 없습니다:

  1. SSH 키 인증만 사용 (비밀번호 인증 비활성화)
  2. fail2ban으로 반복 공격 차단 (bantime 24시간 + recidive)
  3. 불필요한 서비스 비활성화
  4. 자동 보안 업데이트 활성화

이 네 가지만 지켜도 대부분의 자동화된 공격은 막을 수 있습니다.