Fail2ban 설정: SSH를 무차별 대입 공격으로부터 보호하기
Fail2ban은 서비스 로그를 읽어 반복된 로그인 실패를 찾아내고, 공격자의 IP 주소를 방화벽을 통해 일정 시간 차단합니다. SSH 보호는 패키지를 설치하고 [sshd] 섹션이 있는 /etc/fail2ban/jail.local을 만든 뒤(enabled = true, maxretry, findtime, bantime) 서비스를 재시작하면 됩니다. 상태는 fail2ban-client status sshd로 확인합니다.
Fail2ban은 서비스 로그를 감시하다가 반복되는 로그인 실패를 발견하면, 그 IP 주소를 일정 시간 차단하는 데몬입니다. SSH에서는 비밀번호 무차별 대입을 막는 가장 간단한 방법입니다. 몇 번 실패하면 봇은 더 이상 서버를 두드리지 않습니다. 아래에서는 설치부터 차단 확인, 흔한 오류까지 fail2ban 설정 전 과정을 다룹니다.
SSH 키를 쓰는데도 fail2ban이 필요한 이유
22번 포트가 열린 서버는 실행 직후부터 몇 분 안에 봇의 로그인 시도를 받기 시작합니다. 직접 확인해 보세요.
$ sudo journalctl -u ssh --since "1 hour ago" | grep -c "Failed password"
412
비밀번호 로그인을 끄고 키만 사용한다면 접근을 뚫기는 사실상 불가능합니다. 그래도 fail2ban은 유용합니다. 로그에서 잡음을 줄이고, 수천 건의 연결로 인한 부하를 줄이며, 실수로 비밀번호 로그인이 다시 켜졌을 때도 보호해 주기 때문입니다. 이것은 키를 대신하는 것이 아니라 두 번째 방어선입니다.
fail2ban의 동작 원리
구조는 세 부분으로 이루어집니다.
- 필터(filter) — 로그에서 로그인 실패 줄을 찾고 IP를 추출하는 정규식 집합입니다. 필터는
/etc/fail2ban/filter.d/에 있습니다. - 액션(action) — 위반자에게 무엇을 할지 정합니다. iptables, nftables, ufw, firewalld에 규칙을 추가하는 식입니다. 액션은
/etc/fail2ban/action.d/에 있습니다. - 감옥(jail) — 필터, 액션, 로그 출처, 임계값을 묶은 단위입니다. 예를 들어
sshdjail은 SSH 로그를 감시합니다.
같은 IP에서 findtime 기간 안에 maxretry번 일치하면, 그 주소는 bantime 동안 차단됩니다. 기간이 지나면 차단은 자동으로 풀립니다.
fail2ban 설치
데비안과 우분투에서는 다음과 같이 설치합니다.
sudo apt update
sudo apt install fail2ban
RHEL, Rocky, AlmaLinux에서는 패키지가 EPEL 저장소에 있습니다.
sudo dnf install epel-release
sudo dnf install fail2ban
자동 시작을 켜고 서비스 상태를 확인하세요.
sudo systemctl enable --now fail2ban
sudo systemctl status fail2ban
데비안과 우분투에서는 SSH jail이 /etc/fail2ban/jail.d/defaults-debian.conf를 통해 기본으로 켜져 있습니다. RHEL 계열에서는 설치 후 아무것도 켜져 있지 않으므로 jail을 직접 활성화해야 합니다.
설정 파일 위치: jail.conf와 jail.local
기본 파일은 /etc/fail2ban/jail.conf이지만 직접 수정하면 안 됩니다. 패키지를 업데이트하면 덮어쓰이기 때문입니다. 사용자 설정은 /etc/fail2ban/jail.local이나 /etc/fail2ban/jail.d/*.local에 둡니다. fail2ban은 다음 순서로 파일을 읽으며, 뒤에 읽는 설정이 앞의 설정을 덮어씁니다.
jail.confjail.d/*.confjail.localjail.d/*.local
jail.conf 전체를 jail.local로 복사하지 마세요. 오래된 기본값이 고정되어 버립니다. jail.local에는 바꾸는 항목만 적으세요.
SSH용 fail2ban 설정: 최소 jail.local
파일을 만듭니다.
sudo nano /etc/fail2ban/jail.local
다음 내용을 추가합니다.
[DEFAULT]
# 절대 차단하지 않을 주소
ignoreip = 127.0.0.1/8 ::1 203.0.113.10
bantime = 1h
findtime = 10m
maxretry = 5
# 로그 출처: systemd 저널 또는 파일
backend = systemd
[sshd]
enabled = true
port = ssh
maxretry = 3
서비스를 재시작하고 jail이 올라왔는지 확인하세요.
$ sudo systemctl restart fail2ban
$ sudo fail2ban-client status
Status
|- Number of jail: 1
`- Jail list: sshd
옵션의 의미
| 옵션 | 의미 | 예시 |
|---|---|---|
enabled | jail 활성화 여부 | true |
port | 위반자에 대해 차단할 포트 | ssh, 2222, ssh,2222 |
maxretry | 차단 전까지 허용되는 실패 횟수 | 3 |
findtime | 실패를 세는 시간 창 | 10m |
bantime | 차단 기간, -1이면 영구 | 1h, 1d |
ignoreip | 차단하지 않을 주소와 서브넷 | 10.0.0.0/8 |
backend | 로그 출처 | systemd, auto |
logpath | 파일 백엔드를 쓸 때 로그 파일 경로 | /var/log/auth.log |
banaction | 차단에 쓸 방화벽 | nftables-multiport, ufw |
시간은 s, m, h, d, w 접미사와 함께 쓸 수 있습니다. 접미사가 없는 숫자는 초 단위로 해석됩니다.
backend: systemd 또는 파일
최신 데비안 12나 우분투 24.04에서는 rsyslog를 설치하지 않으면 /var/log/auth.log 파일이 없을 수 있습니다. SSH 로그는 systemd 저널에만 남습니다. 이때는 backend = systemd가 필요합니다. 그렇지 않으면 fail2ban이 로그 파일을 찾을 수 없다는 오류와 함께 시작되지 않습니다. rsyslog가 있다면 파일 방식을 그대로 사용할 수 있습니다.
[sshd]
enabled = true
backend = auto
logpath = /var/log/auth.log
RHEL 계열에서는 같은 역할의 파일이 /var/log/secure입니다.
비표준 포트의 SSH
sshd가 예를 들어 2222번 포트에서 수신한다면, jail에 그 포트를 적어야 합니다. 그렇지 않으면 fail2ban은 22번 포트만 차단하고, 공격은 2222번 포트에서 계속됩니다.
[sshd]
enabled = true
port = 2222
위반자를 모든 포트에서 차단하려면 banaction 매개변수로 iptables-allports 또는 nftables-allports 액션을 지정하세요.
방화벽 선택: iptables, nftables, ufw, firewalld
데비안의 기본값은 banaction = nftables(예전 버전에서는 iptables)이고, RHEL에서는 firewalld입니다. 서버에서 ufw를 사용 중이라면 차단 목록도 ufw에서 보이도록 설정하는 것이 편리합니다.
[DEFAULT]
banaction = ufw
firewalld를 쓴다면:
[DEFAULT]
banaction = firewallcmd-rich-rules
규칙이 실제로 추가되었는지는 sudo nft list ruleset | grep f2b 또는 sudo iptables -L -n | grep f2b로 확인할 수 있습니다. fail2ban은 f2b- 접두사를 붙인 체인을 만듭니다.
동작 확인: fail2ban-client status
주요 관리 도구는 fail2ban-client입니다. 특정 jail의 상태는 다음과 같이 봅니다.
$ sudo fail2ban-client status sshd
Status for the jail: sshd
|- Filter
| |- Currently failed: 2
| |- Total failed: 147
| `- Journal matches: _SYSTEMD_UNIT=sshd.service + _COMM=sshd
`- Actions
|- Currently banned: 4
|- Total banned: 38
`- Banned IP list: 45.148.10.12 92.118.39.7 185.224.128.40 218.92.0.112
여기서 현재 시간 창 안의 실패 수, 차단된 IP 수와 목록을 볼 수 있습니다. 실제로 테스트하려면 ignoreip에 없는 다른 장비에서 잘못된 비밀번호로 여러 번 로그인해 보세요. 세 번째 시도 이후에는 연결이 성립하지 않아야 합니다.
fail2ban 자체의 로그는 /var/log/fail2ban.log에 있습니다.
$ sudo tail -n 3 /var/log/fail2ban.log
2026-10-14 09:12:41,118 fail2ban.filter [812]: INFO [sshd] Found 218.92.0.112
2026-10-14 09:12:44,302 fail2ban.filter [812]: INFO [sshd] Found 218.92.0.112
2026-10-14 09:12:47,551 fail2ban.actions [812]: NOTICE [sshd] Ban 218.92.0.112
fail2ban에서 IP 차단 해제
자기 자신이나 동료를 차단했다면 서버 콘솔(호스팅 패널이나 다른 IP)에서 차단을 해제하세요.
sudo fail2ban-client set sshd unbanip 203.0.113.25
모든 jail의 차단을 한 번에 해제하려면:
sudo fail2ban-client unban --all
필터가 발동하기를 기다리지 않고 주소를 수동으로 차단하려면:
sudo fail2ban-client set sshd banip 198.51.100.7
같은 일이 되풀이되지 않도록, 고정 IP나 사무실 서브넷은 ignoreip에 추가하세요.
재범자에 대한 점진적 차단
많은 봇은 차단이 풀리자마자 다시 돌아옵니다. fail2ban 0.11 이후 버전에서는 재범자의 차단 기간을 늘릴 수 있습니다.
[DEFAULT]
bantime = 1h
bantime.increment = true
bantime.factor = 2
bantime.maxtime = 4w
같은 IP가 다시 차단될 때마다 차단 기간이 이전보다 길어지며, 최대 4주까지 늘어납니다. 구버전을 쓴다면 대안은 recidive jail입니다. 이 jail은 fail2ban 자체의 로그를 읽어서 여러 번 걸린 주소를 오랫동안 차단합니다.
[recidive]
enabled = true
logpath = /var/log/fail2ban.log
bantime = 1w
findtime = 1d
maxretry = 5
필터 점검: fail2ban-regex
jail은 동작하는데 아무도 차단되지 않는다면, 필터가 로그에서 줄을 실제로 찾고 있는지 확인하세요. 파일 로그의 경우:
sudo fail2ban-regex /var/log/auth.log /etc/fail2ban/filter.d/sshd.conf
systemd 저널의 경우:
sudo fail2ban-regex systemd-journal sshd
출력에서 Lines: ... matched 줄을 보세요. 일치 수가 0이면 필터가 로그 형식과 맞지 않거나 잘못된 출처를 지정한 것입니다. 재시작 전에 전체 설정의 문법도 검사하세요.
sudo fail2ban-client -t
자주 하는 실수
- 서비스가 시작되지 않음: «Have not found any log file for sshd jail».
/var/log/auth.log가 없습니다.backend = systemd로 설정하거나 rsyslog를 설치하세요. - 차단 목록에는 IP가 있는데 공격은 계속됨. 포트가 잘못되었거나(SSH 포트를 옮긴 경우), 더 앞에 있는 허용 규칙이 방화벽 차단을 가리고 있습니다.
port값과nft list ruleset출력을 확인하세요. - 변경 사항이 적용되지 않음.
jail.conf를 고쳤지만jail.local이나jail.d/*.local에 다른 값이 있습니다. 최종 값을 확인하세요:sudo fail2ban-client get sshd maxretry. - 자기 자신을 차단함. 콘솔에서 차단을 해제하고, IP를
ignoreip에 추가하세요. - Docker 안의 fail2ban이 공격을 보지 못함. 컨테이너 포트는 INPUT을 우회하는 DOCKER 체인을 통해 게시됩니다. 이런 서비스에는 액션에서
chain = DOCKER-USER를 사용하세요.
SSH 보호를 위해 추가로 할 일
fail2ban은 /etc/ssh/sshd_config의 기본 설정과 함께 쓸 때 가장 잘 동작합니다.
PasswordAuthentication no
PermitRootLogin no
MaxAuthTries 3
수정한 뒤에는 sudo sshd -t로 설정을 검사하고 서비스를 재시작하세요. 새 세션에서 키 로그인이 되는 것을 확인하기 전에는 현재 세션을 닫지 마세요. 방화벽에서 SSH 접근을 필요한 주소로만 제한하는 것도 좋은 방법입니다.
정리
fail2ban 설정은 몇 단계로 끝납니다. 패키지를 설치하고, [sshd] 섹션이 있는 jail.local을 만들고, 올바른 backend와 포트를 고르고, maxretry, findtime, bantime을 정하고, 자신의 주소를 ignoreip에 넣은 뒤, fail2ban-client status sshd로 확인하면 됩니다. 키만 사용하는 로그인과 함께 쓰면 SSH를 비밀번호 무차별 대입으로부터 안정적으로 보호할 수 있습니다. 서버 보안과 fail2ban은 리눅스 과정에서 실습으로 다룹니다.
자주 묻는 질문
비밀번호 로그인을 껐다면 fail2ban이 꼭 필요한가요?
필수는 아니지만 유용합니다. 로그에서 수천 건의 불필요한 시도를 줄여 주고, 누군가 비밀번호 로그인을 실수로 다시 켜도 보호해 줍니다.
모든 jail에서 차단된 IP를 한 번에 보려면 어떻게 하나요?
sudo fail2ban-client banned 명령을 실행하면 jail별 차단 목록이 나옵니다. 한 jail만 보려면 fail2ban-client status sshd를 사용하세요.
서버를 재부팅해도 차단이 유지되나요?
네. fail2ban은 차단 정보를 SQLite 데이터베이스(/var/lib/fail2ban/fail2ban.sqlite3)에 저장하고 시작할 때 유효한 차단을 복원합니다. 다만 dbpurgeage 설정에 따라 일찍 삭제될 수 있습니다.
SSH 외의 서비스도 fail2ban으로 보호할 수 있나요?
네. nginx, Apache, Postfix, Dovecot 등 여러 서비스용 필터가 포함되어 있습니다. jail.local에서 해당 jail을 활성화하면 됩니다. 예를 들어 [nginx-http-auth] 섹션에 enabled = true를 적습니다.
fail2ban과 방화벽의 허용 규칙은 어떻게 다른가요?
방화벽은 주소를 정적으로 허용하거나 거부하고, fail2ban은 로그에서 드러난 행동에 따라 동적으로 차단합니다. 두 방식은 흔히 함께 씁니다. 신뢰하는 주소는 방화벽 허용 목록에, 나머지는 fail2ban이 관리합니다.