
최근 한 기업이 랜섬웨어 공격으로 인해 업무에 큰 차질을 겪었다는 보안 기사를 접했습니다. 해킹 그룹이 Brute Force 기법을 이용해 SSL-VPN에 무차별 로그인 시도를 한 끝에 내부망을 장악한 것이 원인이었습니다. 이 기사를 보고 저희가 운영 중인 VPN도 동일한 위협에 노출될 수 있다는 우려가 생겼고, 이에 따라 로그인 시도를 제한하는 방안을 검토하게 되었습니다.
Fail2Ban은 서버 보안을 강화하기 위해 널리 사용되는 침입 방지 도구(IPS, Intrusion Prevention System)입니다. 주로 SSH, FTP, SMTP, Apache, Nginx, VPN 등 외부 접근이 가능한 서비스에서 발생하는 비정상적인 로그인 시도를 자동으로 탐지하고 차단하는 데 사용합니다.
fail2ban의 주요 기능
| 기능 | 설명 |
|---|---|
| 로그 분석 | /var/log/secure, /var/log/auth.log 등 로그 파일을 실시간 감시 |
| IP 차단 | 공격으로 의심되는 IP를 iptables, firewalld, nftables, 또는 route 명령어로 차단 |
| 다중 jail 지원 | 각 서비스별로 감시 규칙(jail)을 개별 설정 가능 |
| 차단 해제 | 일정 시간이 지나면 자동으로 차단 해제 (또는 수동 해제 가능) |
| Slack, 이메일 알림 | 공격 탐지 시 관리자에게 알림 전송 가능 |
apt-get install fail2ban -ysystemctl enable fail2ban
systemctl start fail2bansystemctl status fail2ban.service
먼저 fail2ban의 기본 구조를 살펴 보겠습니다.
가장 중점적으로 볼 부분은
입니다. 이 3가지의 구조로 기본적인 IP 차단 룰을 구현 할 수 있기 때문에 나머지 부분은 참고해주시면 됩니다.
/etc/fail2ban
├── action.d/ # 액션 정의 파일들 (예: IP 차단 방식)
├── fail2ban.conf # fail2ban 기본 설정 파일
├── fail2ban.d/ # fail2ban 설정 조각(디렉토리 override용)
├── filter.d/ # 로그 필터 정의 (regex 기반)
├── jail.conf # 기본 jail 설정 파일
├── jail.d/ # 개별 jail 설정 파일 모음
├── jail.local # 사용자 정의 jail 설정 파일 (우선 적용됨)
├── paths-arch.conf # Arch 리눅스용 로그 경로 설정
├── paths-common.conf # 공통 로그 경로 설정
├── paths-debian.conf # Debian 계열용 로그 경로 설정
├── paths-opensuse.conf # OpenSUSE용 로그 경로 설정
기존에 운영하던 VPN에 해당 프로세스를 적용한 뒤, 고도화로 내용으로 Slack과 연동을 진행해 보겠습니다
VPN 구축이 궁금하신 분은 아래의 링크 참고 부탁드립니다
fail2ban 정책 생성
cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local
[vpn]
enabled = true
filter = vpn-auth
port = 1111
protocol = udp # 프로토콜 default는 TCP
logpath = /var/log/로그경로.log
maxretry = 5 # 최대 시도 횟수
bantime = 600 # 차단 시간
findtime = 300 # 탐지 범위
action = %(action_)s # 차단 후 Action
slack-notify 
기존 sshd fail2ban disable
vi /etc/fail2ban/jaid.d/defaults-debian.conf
[sshd]
enabled = false
sshd도 같이 차단 정책을 걸고 싶으면 해당 부분은 true로 설정하시면 됩니다.
로그 포맷 설정
vi /etc/fail2ban/filter.d/vpn-auth.conf
[Definition]
maxlines = 10
prefregex = ^\s*(?:(?:[A-Za-z]{3}\s+\d+\s+\d{2}:\d{2}:\d{2}\s+[\w\d\.-]+\s+)?(?:vpn\[\d+\]:)?\s*)PLUGIN AUTH-PAM: BACKGROUND: user '(?P<user>[^']+)' failed to authenticate: Authentication failure
failregex = ^(?:<F-NOFAIL>IP:Port SENT CONTROL \[UNDEF\]: 'AUTH_FAILED' \(status=1\)</F-NOFAIL>\s+)?<HOST>:\d+ SENT CONTROL \[UNDEF\]: 'AUTH_FAILED' \(status=1\)$
ignoreregex =
저는 로그인을 시도한 IP와 user명을 Slack으로 알림 보내기 위해 failregex와 같이 prefregex를 사용하였습니다. 각 환경에 맞게 정규식을 조정하시면 됩니다.
IP 차단 후 Action 설정 편집
vi /etc/fail2ban/action.d/slack-notify.conf
[Definition]
actionstart =
actionstop =
actioncheck =
# 'actionban'은 IP가 차단될 때 실행될 명령어
# 스크립트에 다음 인자들을 전달합니다:
# 1. <ip>: 차단될 IP 주소 (Fail2Ban이 자동으로 제공)
# 2. BANNED: 조치 타입
# 3. <name>: 조치를 유발한 Jail 이름 (Fail2Ban이 자동으로 제공)
# 4. <F-USER>: 필터에서 캡처된 사용자 ID
actionban = /etc/fail2ban/slack-notify.sh <ip> BANNED <name> <F-USER>
# 'actionunban'은 IP 차단이 해제될 때 실행될 명령어
# 동일한 인자들을 전달합니다.
actionunban = /etc/fail2ban/slack-notify.sh <ip> UNBANNED <name> <F-USER>
Slack 알림 스크립트 생성
vi /etc/fail2ban/slack-notify.sh
#!/bin/bash
# Slack 웹훅 URL WEBHOOK_URL="https://hooks.slack.com/test" # 이 부분을 본인의 웹훅 URL로 변경해주세요!
# Fail2Ban으로부터 전달받는 인자들
HOST="$1" # 차단되거나 해제된 IP 주소 (Fail2Ban이 차단하는 대상)
ACTION="$2" # 'BANNED' 또는 'UNBANNED'
JAIL="$3" # 조치가 발생한 Fail2Ban Jail 이름
USERID="$4" # 필터에서 캡처된 사용자 ID
# Slack 알림을 보낼 채널 설정
CHANNEL="#test"
if [[ "$ACTION" == "BANNED" ]]; then
COLOR="#ff0000"
elif [[ "$ACTION" == "UNBANNED" ]]; then
COLOR="#36a64f"
else
COLOR="#cccccc"
fi
# 메시지 내용
TEXT="*IP 차단 알림*
*조치:* ${ACTION}
*IP 주소:* ${HOST}
*사용자 ID:* ${USERID}
*시간:* $(date +'%Y-%m-%d %H:%M:%S %Z')"
# JSON 페이로드 생성
PAYLOAD=$(cat <<EOF
{
"channel": "${CHANNEL}",
"username": "test",
"icon_emoji": ":happy:",
"attachments": [
{
"color": "${COLOR}",
"text": "${TEXT}"
}
]
}
EOF
)
# Slack으로 메시지 전송
curl -X POST -H "Content-type: application/json" --data "${PAYLOAD}" "${WEBHOOK_URL}"
fail2ban 재시작 및 로그 확인
systemctl restart fail2ban.service
tail -50 /var/log/fail2ban.log

Jail List 확인
fail2ban-client status

IP 차단 테스트
fail2ban-client status {JAIL-NAME}

차단 되었을 경우


차단 해제
fail2ban-client set <jail-name> unbanip <IP>
