지역기반 IP로 방화벽 강화하기
지역기반 IP를 활용하여 iptables 방화벽 규칙을 적용시킨 작업기록입니다.
iptables
리눅스를 설치하면 네트워크 패킷 관리자로 iptables
패키지를 만나볼 수 있다. 여러가지 규칙들을 선언하여 커널단위에서 패킷을 차단하거나 허용할 수 있어서 어플리케이션까지 패킷이 전달되어 컴퓨터 자원이 소모되는 것을 최소화할 수 있다. 지역기반 IP 정보로 Nginx 필터 설정하기를 참고하면 DB-IP에서 제공하는 지역기반 IP(이하 GeoIP) 데이터베이스를 활용하여 Nginx 트래픽 차단을 설정한 내역을 확인해볼 수 있다. 이 문서에서는 이와 동일한 데이터 베이스를 활용하여 iptables에 적용하는 방법을 소개하고자 한다.
xt_geoip
sudo apt install xtables-addons-common libtext-csv-xs-perl unzip
iptables의 애드온으로 간단한 명령을 통하여 설치할 수 있다. 다른 라이브러리는 이후에 빌드를 위해 필요한 라이브러리다. 우선 앞서 소개한 DB-IP 홈페이지로 들어가서 Lite Country 버전의 데이터베이스를 'csv'형식으로 다운로드 받는다. 적당한 위치에 위치시킨 후 xt_geoip가 사용할 수 있는 형태로 빌드를 해야 한다.
sudo /usr/libexec/xtables-addons/xt_geoip_build -D /usr/share/xt_geoip
sudo modprobe xt_geoip
# 활성화 확인
sudo lsmod | grep xt_geoip
# 재부팅 시에도 자동으로 로드시키려면
echo "xt_geoip" | sudo tee -a /etc/modules-load.d/xt_geoip
국가 코드별로 v4, v6에 해당하는 IP 대역이 모두 변환되어 파일로 저장되는데 상당히 많은 파일이 생성되어 오래걸릴 수도 있다. 완료되고 나면 커널모듈로 xt_geoip를 사용할 수 있게 활성화 해주어야 한다. lsmod | grep xt_geoip
를 통해서 활성화 되었는지 확인할 수 있다.
sudo iptables -A INPUT -p tcp --dport 22 -m geoip ! --src-cc KR -j DROP
사전 작업이 모두 끝났으니 실제로 방화벽 규칙을 추가하여 트래픽을 제어할 수 있다. 한국 이외 지역에서 들어오는 ssh 포트 패킷을 모두 버리는 규칙이다. iptables의 규칙은 순서대로 작동하며 -A
옵션으로 가장 마지막 규칙으로 추가할 수 있다. 설정이 잘못되면 ssh 접속이 불가능한 경우가 생길 수 있으므로 주의하고 콘솔로 접속할 수 있는 대책을 준비하는 것이 좋다.
웹 접근 제어
이 방법을 활용하면 Nginx와 mmdb를 연동한 접근제어보다 더 효율적인 지역별 접근 제어를 할 수 있다. 두 가지 모두 동일한 데이터베이스를 사용하며 동일한 결과를 얻기 위한 작업이기 때문에 iptables를 활용한다면 패킷이 어플리케이션까지 도달하는데 필요한 OSI 7계층 작업들을 일부 건너뛰고도 동일한 결과를 얻을 수 있는 셈이다.
이 말은 곧 Nginx나 fail2ban과 같은 어플리케이션이 전송받기 위해서 필요한 일부 과정을 목표로하는 공격이나 예기치 못한 문제들 또한 사전에 차단할 수 있다는 이야기이다. iptables 패키지는 커널 단위에서 작동하면서 모든 OSI 7계층에 관여할 수 있다. 우리가 지금 설정하는 것은 네트워크 계층(src, ipset)과 전송 계층(IP, PORT)에서 작동한다. 네트워크 계층에서 차단할 수 있다면 이후 4단계를 모두 실행할 필요가 없고 패킷은 바로 폐기된다. 이러한 이유로 iptables를 사용한 GeoIP 차단이 보안과 성능면에서 더 좋은 효과를 얻을 수 있다.
ipset
시중에 유통되는 GeoIP 데이터베이스는 일반적으로 큰 용량을 차지한다. v4대역은 물론 v6대역도 기술되어 있기 때문인데 이 문서에서는 v4에 대해서만 작성하겠다. 애초에 VM을 구매할 때 v6는 추가 요금이 붙어서 비활성화 했기 때문이기도 하니 v6에 대한 설정이 더 필요하다면 내용을 잘 수정하거나 다른 문서를 참조하는 것이 도움이 될 것이다.
sudo apt install ipset
ipset은 이런 광범위한 대역 정보를 그루핑하여 효율적으로 관리할 수 있도록 도와주는 툴이다. 커널 모듈로 작동하여 iptables와 함께 구동할 수 있어서 많은 대역정보를 핸들링할 때 유용하게 사용할 수 있다. 단지 국내 IP만 허용하겠다고 한다면 앞서 소개한 ssh 포트 22를 http, https 포트인 80, 443으로 변경하면 해결되니 참고하길 바란다.
IP 대역 집합 등록
#!/bin/bash
# 오류 발생 시 즉시 중단
set -e
# 다운로드 받은 csv 형식의 Geo IP 데이터베이스
CSV_FILE_PATH="/path/to/dbip-country-lite.csv"
if [ -z "$CSV_FILE_PATH" ]; then
echo "오류: IPv4 CSV 파일을 찾을 수 없습니다. 경로와 파일명을 확인하세요."
exit 1
fi
# 허용할 국가 코드 목록
COUNTRIES=(KR US JP)
# ipset 집합명
IPSET_NAME_V4="allowed_countries_v4"
# 그룹이 이미 있다면 제거한 후 등록
# iptables에서 이미 사용하고 있다면 커널모듈에 등록되어서 삭제가 불가능함
# 삭제하려면 반드시 iptables 규칙에서 그룹을 사용하고 있는 규칙을 제거한 후 실행해야함
echo "새로운 ipset 집합 '$IPSET_NAME_V4'을(를) 생성합니다."
ipset destroy "$IPSET_NAME_V4" || true
# 환경에 맞게 그룹 최대 길이를 설정
# maxelem 524288 로 기본값의 8배로 설정
ipset create "$IPSET_NAME_V4" hash:net maxelem 524288
echo "CSV 파일에서 허용된 국가들의 IPv4 대역을 ipset에 추가합니다..."
# 추출된 IP 대역을 ipset에 바로 추가
for COUNTRY in "${COUNTRIES[@]}"; do
# IPv4 주소만 필터링하도록 awk에 조건 추가
awk -F, '$3 == "'"$COUNTRY"'" && $1 !~ ":" {print $1 "-" $2}' "$CSV_FILE_PATH" | while read -r IPRANGE; do
if [ -n "$IPRANGE" ]; then
ipset add "$IPSET_NAME_V4" "$IPRANGE" || echo "Error adding $IPRANGE to ipset."
fi
done
done
# ipset 집합 파일로 저장
ipset save > /etc/ipset.rules
echo "ipset 업데이트 완료. 규칙이 /etc/ipset.rules에 저장되었습니다."
ipset을 실행하기 위해서 해당 쉘 파일은 root
권한으로 실행해야 한다. 위 코드는 예시로 '한미일' 세 국가만 지정했지만 더 많은 국가를 지정하려면 그룹 생성 옵션인 maxelem
을 반드시 확인해야한다. 기본값은 65536
으로 대역에 대한 정보가 너무 많으면 입력이 실패하는 경우가 있을 수 있기 때문이다. 예시에서 지정한 값은 8배에 해당하며 이 부분은 자신의 환경에 맞는 사이즈로 적절하게 조절하면 된다.
sudo iptables-save > /path/to/iptables.save
sudo iptables -I INPUT -p tcp -m multiport --dports 80,443 -m set --match-set allowed_countries_v4 src -j ACCEPT
sudo iptables -A INPUT -p tcp -m multiport --dports 80,443 -j DROP
# 복구 필요시
# sudo iptables-restore < /path/to/iptables.save
서버 내부 동작코드가 도메인이나 IP를 사용하여 자신에게 Ajax 호출하는 경우 등
만약 웹 서비스를 도커 컨테이너에서 작동시키고 있는 경우라면 설정이 하나 더 필요하다. 예를들어 서버 내부에서 자기 자신에게 구현된 API Ajax 호출을 도메인이나 IP 등 호스트에 설정된 네트워크 인터페이스를 거치게 된다면 마찬가지로 패킷이 드랍된다. 이는 도커 컨테이너가 도커 네트워크 인터페이스를 통해 네트워크 통신을 하게 되기 때문인데 컨테이너에 연결된 네트워크 인터페이스 명을 찾아 ACCEPT
설정을 해주면 된다. ifconfig
또는 도커에서 제공해주는 network
명령을 통하여 확인할 수 있다.
iptables의 규칙은 등록된 순서대로 실행되기 때문에 잘 확인하면서 수행해야 한다. iptables -L INPUT --line-numbers
명령을 사용하면 현재 등록된 들어오는 패킷에 대한 규칙들을 순서대로 확인할 수도 있다. fail2ban 처럼 작업이 실행되기 전까지 패킷이 살아있는 것이 아니라 규칙이 적용되면 바로 작동하기 때문에 반드시 대체할 수 있는 방안을 마련해두어야 한다. 또한 규칙을 변경하기 전에 항상 iptables-save
를 통해 백업본을 만들어 두고 잘 못 되었을 경우 iptables-restore
를 통해 복구할 수 있다.
네트워크 차단
Nginx에서 처리한 것과는 다르게 브라우저가 아예 응답을 받지 못하는 것을 확인할 수 있었다. 커널 단위에서 패킷을 조용히 버려버리기 때문에 좀 더 효율적으로 컴퓨터 자원을 사용할 수 있다. 계속되는 공격을 Nginx가 처리할 필요도 없으며 공개된 IP 대역으로는 더 원할한 서비스를 제공할 수 있다. 공개 지역에서도 공격이 있기는 하지만 어차피 같은 효과를 얻을 수 있기 때문에 이전 보다 더 나은 선택이라고 생각한다.
설정 유지하기
sudo apt install iptables-persistent
# 등록된 데몬들이 재부팅시에 자동으로 실행되도록
# 'enable'을 다시 한 번 확인한다.
sudo systemctl enable iptables.service
sudo systemctl enable ip6tables.service
netfilter-persistent
커널에 설정된 값들은 재부팅 후에는 복원되지 않는데 이를 위해서 iptables-persistent
를 적용해야한다. 이 패키지는 구형 패키지로 netfilter-persistent
가 최신 패키지이다. 또한 구형 패키지명을 사용하더라도 실제로 설치되는 것은 netfilter-persistent임을 확인할 수 있다. 설치를 진행하면 v4, v6에 대한 룰 저장을 물어보는데 예를 선택하면 /etc/iptables/rules.v4
, /etc/iptables/rules.v6
가 자동으로 생성된다. rules.v4
파일을 확인해보면 설정했던 규칙들이 포함된 것을 확인할 수 있다.
[Unit]
Description=Restore ipset rules
Before=iptables-persistent.service
After=network-online.target
[Service]
Type=oneshot
ExecStart=/usr/sbin/ipset restore -file /etc/ipset.rules
RemainAfterExit=yes
[Install]
WantedBy=multi-user.target
iptables-persistent는 iptables와 관련된 작업만 복원하므로 ipset에 대해서는 복원되지 않는다. 따라서 재부팅시에 iptables가 작동하기 전에 ipset을 복원시켜줘야 한다. 이를 위한 시스템 데몬을 등록하고 마찬가지로 enable
해주면 끝이다. 만약 Nginx + fail2ban 조합으로 GeoIP를 적용한 경우라면 Nginx 설정에서 잊지 말고 geoip2 모듈을 제거해주자.
초판: 2025. 10. 11. 21:20:27
수정판: 2025. 10. 11. 22:43:30
© 2025 이 문서는 "CC BY 4.0 국제규약" 라이선스로 배포 되었습니다. 모든 권리는 저자에게 있습니다.