SSH 보안 단단하게: Fail2ban + UFW로 무차별 대입 차단(필터/액션/알림/점검 루틴)
패스워드 로그인만 열어두면 SSH는 몇 분도 안 되어 무차별 대입(Brute-force) 시도로 가득 찹니다. 공개키 로그인 + 비번 금지가 1순위지만, 네트워크 차단 계층도 함께 두면 훨씬 안전합니다. 이 글은 Ubuntu 24.04 기준으로 Fail2ban을 설치·튜닝하여 UFW와 연동하고, 메일/Slack 알림, 화이트리스트, 퀵 테스트, 주간 리포트까지 한 번에 세팅합니다.
1) 선행 보안(필수)
# 1) 공개키 로그인만 허용
sudo nano /etc/ssh/sshd_config
# PasswordAuthentication no
# PubkeyAuthentication yes
# PermitRootLogin prohibit-password (root 직접 로그인 금지)
sudo systemctl restart ssh
# 2) 방화벽(기본)
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow OpenSSH
sudo ufw enable
여기에 실패 시도 IP를 자동 차단하는 Fail2ban을 얹습니다.
2) 설치와 서비스 상태
sudo apt update
sudo apt install -y fail2ban
sudo systemctl enable --now fail2ban
sudo systemctl status fail2ban --no-pager
Fail2ban은 journald나 텍스트 로그를 읽어 정규식으로 실패 패턴을 잡고,
액션(action)으로 UFW/iptables에 차단 규칙을 넣습니다.
3) 기본 개념: jail · filter · action
- jail: “무엇을 감시하고, 얼마나 실패하면, 얼마나 차단할지” 규칙의 집합
- filter: 로그에서 실패 패턴을 찾는 정규식(서비스별 존재)
- action: 차단 방식(UFW, iptables, firewalld)과 알림 훅
4) SSH 감시(jail.local) 만들기
sudo cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local
sudo nano /etc/fail2ban/jail.local
[DEFAULT]
# 차단 대상 제외(사내망/관리IP 등)
ignoreip = 127.0.0.1/8 ::1 10.10.0.0/16 192.168.0.0/16
bantime = 1h # 차단 유지 시간(예: 1시간)
findtime = 10m # 관찰 윈도우(10분)
maxretry = 5 # 실패 5회면 차단
backend = systemd # journald 사용 시
# UFW로 차단(기본 액션)
banaction = ufw
action = %(action_)s
[sshd]
enabled = true
port = ssh
logpath = %(sshd_log)s
backend = systemd
maxretry = 5
참고: Ubuntu의 기본 필터(/etc/fail2ban/filter.d/sshd.conf)는 대부분의 실패 패턴을 커버합니다.
5) 알림(메일 또는 Slack)
5-1) 메일 알림(간단)
# postfix 등 MTA 설치(간단 발송)
sudo apt install -y postfix mailutils
sudo nano /etc/fail2ban/jail.local
[DEFAULT]
destemail = ops@example.com
sender = fail2ban@$(hostname -f)
mta = mail
action = %(action_mwl)s # whois 포함 메일 전송
5-2) Slack Webhook(선호 시)
sudo tee /etc/fail2ban/action.d/slack-notify.conf >/dev/null <<'AC'
[Definition]
actionstart = /usr/bin/curl -X POST -H 'Content-type: application/json' \
--data '{"text":"Fail2ban started on <HOST>"}' <WEBHOOK>
actionstop = /usr/bin/curl -X POST -H 'Content-type: application/json' \
--data '{"text":"Fail2ban stopped on <HOST>"}' <WEBHOOK>
actionban = /usr/bin/curl -X POST -H 'Content-type: application/json' \
--data '{"text":"BANNED <IP> in <name> (failures: <failures>)"}' <WEBHOOK>
actionunban = /usr/bin/curl -X POST -H 'Content-type: application/json' \
--data '{"text":"UNBANNED <IP> in <name>"}' <WEBHOOK>
AC
# jail.local에서 교체
[DEFAULT]
action = slack-notify[WEBHOOK=https://hooks.slack.com/services/XXX/YYY/ZZZ]
banaction = ufw
6) 적용/상태 확인/테스트
sudo fail2ban-client reload
sudo fail2ban-client status
sudo fail2ban-client status sshd
퀵 테스트(다른 터미널/호스트에서):
# 비번 로그인 시도(연속 5회 실패)
ssh -o PreferredAuthentications=password -o PubkeyAuthentication=no user@your.server
# 잠시 후 차단 → 다시 접속 시 connection closed/timeout
해제/화이트리스트:
sudo fail2ban-client set sshd unbanip 203.0.113.10
# 영구 제외는 jail.local 의 ignoreip 에 추가
7) 고급 튜닝: 공격 빈도별 차단 강화
공격이 거센 경우 증가형 bantime을 추천합니다.
[DEFAULT]
bantime = 1h
bantime.increment = true
bantime.factor = 2
bantime.overalljails = true
bantime.maxtime = 24h
자격증명 스프레이처럼 느리게 시도하면 findtime을 늘리고 maxretry를 낮춰 센서티브하게 대응합니다(운영자 본인 차단 주의).
8) 다른 서비스도 보호(Nginx/택틱)
관리 콘솔이나 로그인 엔드포인트를 Nginx에서 보호 중이라면 다음 jails를 추가하세요.
[nginx-http-auth]
enabled = true
port = http,https
filter = nginx-http-auth
logpath = /var/log/nginx/error.log
maxretry = 10
[nginx-botsearch]
enabled = true
port = http,https
filter = nginx-botsearch
logpath = /var/log/nginx/access.log
maxretry = 20
필터는 /etc/fail2ban/filter.d/*.conf 안의 정규식을 활용/수정합니다.
9) 주간 리포트/헬스체크 자동화
# /usr/local/sbin/f2b-report.sh
#!/usr/bin/env bash
set -e
echo "== Fail2ban Summary $(date) =="
fail2ban-client status sshd
zgrep -a "Ban " /var/log/fail2ban.log* | tail -200
sudo chmod +x /usr/local/sbin/f2b-report.sh
echo "0 9 * * 1 root /usr/local/sbin/f2b-report.sh | mail -s 'Fail2ban Weekly' ops@example.com" | \
sudo tee /etc/cron.d/f2b-report
10) 트러블슈팅
- 차단이 안 됨:
fail2ban-client status sshd에서 Failures가 증가하는지,banaction이 UFW인지 확인.sudo ufw status에 f2b-sshd 규칙이 생겼는지 체크. - 관리자 본인 차단: 콘솔 접속이 막히면 콘솔/클라우드 패널에서 들어가
ignoreip에 관리망을 추가하고unbanip. - 성능/로그 누락:
backend=systemd로 journald 직접 읽기가 안정적. 텍스트 로그면logpath경로·권한을 재확인. - 과잉 차단:
maxretry·findtime완화, 사내 NAT 등에서 다수 사용자가 한 IP로 보이는 환경 고려.
11) 최종 체크리스트
- SSH: 공개키 전용,
PasswordAuthentication no, 루트 직접 로그인 금지 - UFW: 기본 deny, SSH만 허용(또는 Bastion)
- Fail2ban:
jail.local적용, 증가형 차단 설정,ignoreip에 관리망 추가 - 알림: 메일 또는 Slack Webhook 연동
- 정기 리포트: 주 1회 차단 현황 메일
여기까지 구성하면 SSH 노이즈가 크게 줄고, 비밀번호 로그인이 실수로 열려도 2중 방어가 됩니다. 다음 글에서는 포트 포워딩/리버스 터널(OpenSSH)을 안전하게 운영하는 패턴과 기록/감사 팁을 다루겠습니다.
'서버 인프라 실무' 카테고리의 다른 글
| Fail2ban 고급편 + UFW 튜닝, 그리고 Nginx/HAProxy와 SSH 터널의 역할 분리 전략 (0) | 2025.10.26 |
|---|---|
| SSH 포트 포워딩·리버스 터널 완전 정복: 개발/운영 실전 보안 가이드(OpenSSH) (0) | 2025.10.26 |
| Nginx 리버스 프록시 + Spring Boot 실전 운영: CORS, 타임아웃, 업로드 제한, 보안 헤더 한 번에 정리 (0) | 2025.10.24 |
| 로그 보존 자동화: logrotate + systemd-journald로 용량·보존·압축 완전 정복(Ubuntu 24.04) (0) | 2025.10.24 |
| 백업은 존재할 때만 복구된다: rsync/cron으로 무중단 증분 백업(로컬·원격·보안·검증 루틴) (0) | 2025.10.23 |