공개 IP를 붙인 서버라면 sshd 로그에 낯선 IP의 로그인 실패가 하루 종일 쌓인다. 저도 새 서버를 올리고 며칠 지나 journalctl -u sshd를 열어보면 중국, 러시아, 이름 모를 대역에서 root, admin, oracle 계정으로 초당 몇 건씩 로그인을 시도하는 기록이 늘 보인다. 키 인증만 열어두면 뚫리진 않지만 로그가 지저분해지고 리소스도 갉아먹는다. 예전에 iptables 규칙을 손으로 넣던 글을 쓴 적이 있는데, 요즘은 fail2ban에 맡기는 쪽이 훨씬 편하다.
결론부터 말하자면, Rocky/AlmaLinux 9에서는 EPEL의 fail2ban 패키지를 설치하고 fail2ban-firewalld 연동을 켠 뒤 /etc/fail2ban/jail.local에 sshd jail만 활성화하면 된다. fail2ban이 systemd 저널의 sshd 로그를 읽어 실패 횟수를 세고, 임계치를 넘긴 IP를 firewalld의 rich rule로 자동 차단한다. 아래에서 설치, 설정, 확인, 오탐 방지와 해제를 순서대로 다룬다.
1. fail2ban은 무엇을 하는가
fail2ban은 로그 파일이나 systemd 저널을 감시하다가 특정 패턴(예: 로그인 실패)이 정해진 시간 안에 정해진 횟수를 넘으면 방화벽에 차단 규칙을 넣는 데몬이다. 핵심 단위는 jail(감옥)이다. jail 하나는 "어떤 로그를(filter) 어떤 백엔드로 읽어서, 몇 번 실패하면, 어떤 방화벽 동작(action)으로 얼마나 차단할지"를 묶은 설정이다.
자주 나오는 값 세 가지만 먼저 짚는다. maxretry는 차단까지 허용하는 실패 횟수, findtime은 그 횟수를 세는 시간 창, bantime은 차단 유지 시간이다. 즉 findtime 안에 maxretry번 실패하면 bantime만큼 막는다.
2. 설치와 firewalld 연동
fail2ban은 기본 저장소에 없고 EPEL에 있다. EPEL을 먼저 붙인다.
$ sudo dnf install -y epel-release $ sudo dnf install -y fail2ban fail2ban-firewalld
fail2ban-firewalld를 함께 설치하면 /etc/fail2ban/jail.d/00-firewalld.conf가 깔린다. 이 파일이 차단 동작(banaction)을 iptables가 아니라 firewalld의 rich rule로 바꾼다. 내용은 이렇다.
$ cat /etc/fail2ban/jail.d/00-firewalld.conf [DEFAULT] banaction = firewallcmd-rich-rules[actiontype=<multiport>] banaction_allports = firewallcmd-rich-rules[actiontype=<allports>]
Rocky/AlmaLinux 9는 firewalld가 기본 방화벽이므로 이 연동을 쓰는 편이 맞다. firewalld가 켜져 있는지만 확인한다.
$ systemctl is-active firewalld active
여기서 한 번 걸리는 지점이 있다. firewalld가 꺼져 있으면 fail2ban이 차단 규칙을 넣을 대상이 없어 jail 기동 시 에러가 난다. firewalld를 안 쓰는 서버라면 00-firewalld.conf를 지우고 banaction을 iptables 계열로 되돌려야 한다. 이 글은 firewalld를 쓰는 기본 구성을 전제로 한다.
3. jail.local에 sshd jail 설정
/etc/fail2ban/jail.conf는 패키지가 관리하는 원본이라 직접 고치지 않는다. 업데이트 때 덮어써진다. 대신 /etc/fail2ban/jail.local을 새로 만들어 필요한 값만 올린다. jail.local이 jail.conf보다 우선한다.
$ sudo vi /etc/fail2ban/jail.local
[DEFAULT] # 차단에서 항상 제외할 IP (사무실 고정 IP, 모니터링 서버 등) ignoreip = 127.0.0.1/8 ::1 203.0.113.10 bantime = 1h findtime = 10m maxretry = 5 [sshd] enabled = true backend = systemd port = ssh maxretry = 4 bantime = 1h
Rocky/AlmaLinux 9의 sshd 로그는 /var/log/secure가 아니라 systemd 저널로 간다. 그래서 backend = systemd로 지정해 fail2ban이 저널을 직접 읽게 했다. 이 백엔드를 쓰면 logpath를 따로 줄 필요가 없다.
port = ssh는 /etc/services의 22번을 가리킨다. SSH 포트를 바꿨다면 여기에 실제 번호를 적는다(예: port = 2022). 포트를 안 맞추면 차단 규칙이 엉뚱한 포트에 걸려 무력해질 수 있다.
4. 서비스 기동과 상태 확인
설정을 저장했으면 서비스를 켜고 부팅 시 자동 시작으로 등록한다.
$ sudo systemctl enable --now fail2ban $ systemctl status fail2ban --no-pager
기동됐으면 fail2ban-client로 어떤 jail이 올라왔는지 본다.
$ sudo fail2ban-client status Status |- Number of jail: 1 `- Jail list: sshd
sshd jail 하나를 콕 집어 보면 감시 중인 실패 건수와 현재 차단된 IP 목록이 나온다.
$ sudo fail2ban-client status sshd Status for the jail: sshd |- Filter | |- Currently failed: 2 | |- Total failed: 37 | `- Journal matches: _SYSTEMD_UNIT=sshd.service + _COMM=sshd `- Actions |- Currently banned: 1 |- Total banned: 5 `- Banned IP list: 198.51.100.23
차단이 실제로 firewalld에 반영됐는지는 rich rule 목록으로 교차 확인한다. fail2ban이 넣은 규칙이 보인다.
$ sudo firewall-cmd --list-rich-rules rule family="ipv4" source address="198.51.100.23" port port="22" protocol="tcp" reject
차단 이벤트를 사후에 확인하려면 fail2ban 로그를 본다. 어떤 IP를 언제 Ban했는지 남는다.
$ sudo grep 'Ban' /var/log/fail2ban.log | tail 2026-09-28 11:20:41,003 fail2ban.actions [1123]: NOTICE [sshd] Ban 198.51.100.23
5. 정상 IP 오탐 방지
실무에서 가장 조심할 부분이다. 사무실이나 VPN에서 비밀번호를 몇 번 잘못 치면 나 자신이 차단돼 SSH가 끊긴다. 콘솔 접근이 없는 클라우드 인스턴스라면 곤란해진다.
가장 확실한 예방은 ignoreip에 신뢰하는 대역을 등록하는 것이다. 위 설정의 203.0.113.10 자리에 회사 고정 IP나 점프 서버 대역을 넣는다. 공백으로 여러 개를 나열하고 CIDR 표기도 된다.
ignoreip = 127.0.0.1/8 ::1 203.0.113.0/24 198.18.5.6
ignoreip에 들어간 주소는 실패를 아무리 쌓아도 차단되지 않는다. 단, 값을 바꾼 뒤에는 반드시 재적용한다.
$ sudo fail2ban-client reload sshd
차단 정책을 너무 공격적으로 잡는 것도 오탐을 늘린다. maxretry를 1~2로 두면 오타 한두 번에 바로 막힌다. 사람이 쓰는 서버라면 4~5 정도가 무난하다. 자동화 계정만 붙는 서버라면 더 낮춰도 된다.
6. 잘못 막힌 IP 해제하기
이미 차단된 주소는 unban으로 즉시 푼다. jail을 지정하지 않으면 모든 jail에서 해당 IP를 해제한다.
$ sudo fail2ban-client unban 198.51.100.23 $ sudo fail2ban-client set sshd unbanip 198.51.100.23 # sshd jail만 지정해 해제
해제 뒤에는 status sshd의 Banned IP list와 firewall-cmd --list-rich-rules에서 해당 규칙이 사라졌는지 확인한다. 두 곳 모두에서 빠져야 정상이다.
차단된 IP가 하나도 없는 초기 상태로 되돌리려면 jail을 통째로 비운다.
$ sudo fail2ban-client unban --all
7. 재범 IP를 더 오래 막기(선택)
같은 IP가 차단 해제 직후 또 들어오는 경우가 흔하다. 이럴 때는 반복 차단마다 bantime을 점점 늘리는 점진적 차단을 켠다. jail.local의 [DEFAULT]에 넣는다.
[DEFAULT] bantime.increment = true bantime.factor = 2 bantime.maxtime = 1w
bantime.increment = true면 재범할수록 차단 시간이 factor 배씩 늘어나고 maxtime에서 상한이 걸린다. 처음 1시간이던 차단이 반복되면 하루, 일주일까지 길어진다. 상습 스캐너를 걸러내는 데 효과가 있다.
설정 후에는 전체 재적용을 한다.
$ sudo fail2ban-client reload
마무리
정리하면 순서는 EPEL과 fail2ban 설치, firewalld 연동 확인, jail.local에 sshd jail 활성화, 상태 점검, 그리고 ignoreip로 오탐을 막는 것이다. 키 인증과 PermitRootLogin no 같은 sshd 기본 방어를 갖춘 위에 fail2ban을 얹으면 로그를 어지럽히는 무차별 대입 시도를 사람 손 안 대고 걸러낸다. 설정을 바꿀 때마다 reload를 잊지 않고, 원격 서버에서는 ignoreip에 자기 대역을 먼저 넣어두는 습관만 지키면 된다.