오픈소스 IDS_IPS Suricata
지난번 AWS Network Firewall 서비스에 대해 소개해드리면서 AWS Network Firewall이 오픈소스 IDS/IPS인 Suricata를 기반으로 동작한다는 내용을 언급한 적이 있습니다.
이번에는 EC2 인스턴스에 직접 Suricata를 설치하여 Suricata를 어떻게 설치 및 설정하고, 어떻게 작동하는지 직접 확인한 내용을 바탕으로 공유드리려고 합니다.
테스트 환경
테스트 환경에서는 Suricata 인스턴스를 웹서버가 위치한 서브넷과 다른 별도의 서브넷에 구성하고 VPC 내/외부에서 발생하는 모든 트래픽이 Suricata 인스턴스를 거쳐 통신하도록 구성하여 Suricata 인스턴스에 설정한 룰셋을 통해 트래픽을 필터링하여 웹 서버로 전달하도록 아래와 같이 구성하였습니다.
위의 그림은 간단해보이지만 VPC 네트워크 상에서 위와 같이 세팅하려면 아래와 같이 Suricata를 위한 별도의 서브넷과 라우팅 테이블, 인터넷 게이트웨이를 위한 별도의 라우팅 테이블 구성이 필요합니다. 아래 그림은 AWS Network Firewall 서비스 적용 시 네트워크 구성이지만 Suricata를 EC2 인스턴스에 세팅하는 경우에도 방식은 동일합니다. (테스트 환경에서는 가용 영역 이중화를 적용하지 않았습니다.)
웹 서버 세팅
퍼블릭 서브넷(10.0.10.0/24)에 웹서버 인스턴스를 생성합니다. 웹 서버는 간단하게 Apache 설치 후 index.html 파일을 생성하였습니다.
[root@ip-10-0-10-192 html]# tree /var/www/html/
/var/www/html/
├── admin
│ └── index.html
├── board
│ └── index.html
└── index.html
2 directories, 3 files
현재 퍼블릭 서브넷의 라우팅 테이블은 인터넷 게이트웨이를 향하고 있습니다. Suricata 인스턴스의 IP 라우팅 기능과 IGW 라우팅 테이블 등이 세팅되지 않은 상태에서 라우팅 경로를 변경하면 웹서버 인스턴스로 접속이 불가해지기 때문에 라우팅 테이블 수정은 이후 단계에서 진행하도록 하겠습니다.
VPC 네트워크 세팅1 - Suricata 서브넷 및 라우팅 테이블 추가
Suricata 인스턴스가 위치할 서브넷(10.0.30.0/24)을 추가합니다.(IP 주소 자동 할당 활성화) 위의 그림에서는 Firewall Subnet과 같은 역할을 할 것입니다.
Suricata 서브넷을 위한 별도의 라우팅 테이블을 새로 생성하고 0.0.0.0/0 -> IGW 로 설정된 룰을 아래와 같이 추가합니다.
Suricata 인스턴스 세팅1 - IP 라우팅 설정
Suricata 인스턴스 생성
Suricata 서브넷(10.0.30.0/24)에 인스턴스를 생성합니다. 테스트 환경에서는 CentOS7 AMI를 사용하여 인스턴스를 생성하였습니다.
ENI 추가
인스턴스 생성이 완료되면 퍼블릭 서브넷(10.0.10.0/24)에 새로운 ENI를 생성하여 suricata 인스턴스에 연결합니다. ENI 추가가 완료되면 아래와 같이 Suricata 서브넷(10.0.30.0/24)의 ENI를 메인 인터페이스로 가지고, 퍼블릭 서브넷(10.0.10.0/24)의 ENI를 서브 인터페이스로 가지는 인스턴스가 세팅됩니다.
이 때, 반드시 Suricata 인스턴스의 두 ENI는 작업 -> 소스/대상 확인 변경 버튼을 눌러 소스/대상 확인을 비활성화해줘야합니다.
SSH를 통해 Suricata 인스턴스로 접속 후 인터페이스의 상태를 확인하면 아래와 같이 보여집니다.(OS의 버전 및 종류에 따라 인터페이스 이름은 다를 수 있습니다.)
현재 보조 인터페이스인 ens6 인터페이스가 활성화되지 않았기 때문에 /etc/sysconfig/network-scripts/ifcfg-ens5 파일을 복사하여 ifcfg-ens6 파일을 생성 후 ens6 인터페이스의 mac 주소에 맞게 수정해줍니다. 또한 재부팅 시 디폴트 라우팅을 잡아주기 위해 rc.local 파일 수정합니다.
chmod 755 /etc/rc.d/rc.local
vim /etc/rc.d/rc.local
route del default gw 10.0.10.1
route add default gw 10.0.30.1 metric 0 ens5
systemctl restart network
재부팅 후 다시 SSH 접속하여 인터페이스가 정상적으로 보여지는지 확인합니다. 또한 외부와의 통신도 정상적인지 확인합니다.
만약 인터페이스 설정이 정상적으로 되지 않은 경우 외부와의 통신이 불가하거나, SSH 접속이 불가할 것입니다. 설정 방법을 재확인 후 인스턴스를 새로 만들거나 루트 볼륨 대체를 통해 초기 상태의 인스턴스로 복구한 뒤 다시 세팅합니다. 인터페이스 설정이 정상적으로 되었다면 아래 명령어를 통해 인스턴스의 두 인터페이스 간의 IP 라우팅 기능을 활성화합니다.
echo "net.ipv4.ip_forward = 1" >> /etc/sysctl.conf
sysctl -p
echo 1 > /proc/sys/net/ipv4/ip_forward
cat /proc/sys/net/ipv4/ip_forward
VPC 네트워크 세팅2 - 인터넷 게이트웨이 전용 라우팅 테이블 추가 및 엣지 연결 설정
인터넷 게이트웨이 전용 라우팅 테이블 추가 및 엣지 연결 설정
인터넷 게이트웨이를 통해 유입되는 모든 트래픽이 Suricata 인스턴스의 메인 ENI로 라우팅될 수 있도록 인터넷 게이트웨이 전용 라우팅 테이블을 설정하고 엣지 연결을 설정합니다. 인터넷 게이트웨이 라우팅 테이블에 서브넷은 연결할 필요가 없으며, 라우팅 룰은 10.0.0.0/16(로컬 대역) -> Suricata 인스턴스의 메인 ENI(10.0.30.XXX )로 라우팅되도록 룰을 구성합니다. 또한 엣지 연결 탭에서 현재 VPC의 인터넷 게이트웨이를 추가합니다.
퍼블릭 라우팅 테이블의 라우팅 룰 수정
이제 퍼블릭 서브넷의 모든 트래픽이 Suricata 인스턴스를 거쳐 아웃바운드될 수 있도록 퍼블릭 라우팅 테이블의 "0.0.0.0/0 -> IGW" 룰을 0.0.0.0/0 -> Suricata 인스턴스의 서브 ENI(10.0.10.XXX )로 라우팅되도록 룰을 구성합니다.
라우팅 설정 확인
라우팅 테이블 수정까지 완료되었다면 클라이언트에서 웹서버로 향하는 요청 트래픽과 웹서버에서 클라이언트로 발생하는 응답 트래픽이 Suricata 인스턴스를 거쳐 통신하는지 확인합니다. Suricata 인스턴스에서 tcpdump 를 통해 클라이언트와 웹서버 간 트래픽이 오고 가는 것을 확인합니다.
여기까지 설정되었다면 VPC로 유입되는 모든 트래픽과 VPC 외부로 나가는 모든 트래픽이 Suricata 인스턴스를 거쳐서 In/Out Bound 되도록 설정되었습니다. 이제 본격적으로 Suricata 를 설치 및 설정합니다.
Suricata 인스턴스 세팅2 - Suricata 설치 및 IDS 모드로 동작 확인
Suricata 는 IDS(위협 탐지 시스템) 모드와 IPS(위협 방지 시스템) 모드로 동작하며, 설치 시 기본으로 탐지 모드로 동작합니다. 먼저 Suricata 를 설치하고 IDS 모드에서 어떻게 탐지가 되는지 확인해봅니다.
Suricata 설치
설치 가이드
https://suricata.readthedocs.io/en/latest/install.html
yum install -y epel-release yum-plugin-copr
yum copr enable -y @oisf/suricata-6.0
yum install -y suricata
suricata -V
# 시그니쳐 업데이트
suricata-update
차단 모드로 적용해보기 전에 기본 값인 탐지 모드로 사용해보겠습니다.
# 탐지 모드로 설정
vim /etc/sysconfig/suricata
OPTIONS="-i eth0 --user suricata " -> "eth0"을 현재 서버의 메인 인터페이스(ens5 등)로 수정
Suricata 는 기본적으로 /var/lib/suricata/rules/suricata.rules 경로에 기본 룰 파일을 생성하지만 우리는 "local.rules"라는 이름의 별도의 rule 파일을 구성할 것이기 때문에 설정 파일에서 rule 파일 리스트에 추가해줍니다.
# Suricata 설정 파일 수정
vim /etc/suricata/suricata.yaml
12 vars:
13 # more specific is better for alert accuracy and performance
14 address-groups:
15 HOME_NET: "[192.168.0.0/16, 10.0.0.0/8, 172.16.0.0/12]" -> "[10.0.0.0/16]" 등 내부 네트워크(vpc) 대역으로 수정(기본 값으로 사설 아이피 대역이 추가되어 있기 때문에 수정하지 않고 사용해도 정상 작동)
1870 default-rule-path: /var/lib/suricata/rules
1871
1872 rule-files:
1873 - suricata.rules
1874 - local.rules -> 추가할 룰셋 파일의 이름을 새 라인에 추가
이제 경로에 맞게 테스트 룰셋 파일의 생성합니다. 룰을 작성하는 문법은 아래 가이드를 참고합니다.
https://suricata.readthedocs.io/en/latest/rules/index.html
아래 룰은 외부 IP 대역에서 VPC(사설 네트워크) 대역으로 80 포트를 통해 유입되는 트래픽에 대해 alert(차단은 하지 않고 로그를 생성)하는 룰입니다.
vim /var/lib/suricata/rules/local.rules
alert tcp $EXTERNAL_NET any -> $HOME_NET 80 (msg:"http access"; sid:10002; rev:1;)
이제 Suricata 서비스를 구동합니다. 실행으로부터 엔진 구동 완료까지 20초정도 소요됩니다.
# 서비스 시작
systemctl start suricata && systemctl enable suricata
# 서비스 구동 로그 확인
tail -f /var/log/suricata/suricata.log
아래와 같이 "engine started" 라고 로그가 확인되면 정상적으로 서비스가 구동되어 룰을 로드한 것입니다.
접속 테스트 및 이벤트 로그 확인
이제 웹서버를 통해 접속하여 정상적으로 웹페이지가 열리는지 확인하고 suricata 에서 어떻게 로그를 발생시키는지 확인합니다.
# 이벤트(alert) 로그 확인
tail -f /var/log/suricata/fast.log
아래와 같이 Suricata 인스턴스를 거쳐 웹서버 인스턴스로 유입되는 트래픽이 위에서 설정한 룰에 매치되기 때문에 alert를 발생시켜 로그가 확인됩니다.
URL 경로 기반으로 트래픽 필터링하기 예제
HTTP URI 구문 사용 가이드
https://suricata.readthedocs.io/en/latest/rules/http-keywords.html#http-uri-and-http-uri-raw
Suricata는 단순히 IP, Port, 프로토콜 뿐만 아니라 패킷의 메시지를 확인하여 필터링하는 것도 가능합니다. 그 중 HTTP 트래픽의 URL 경로(URI)를 기반으로 필터링하는 예제입니다. 위에서 구성한 룰셋 파일을 수정하여 아래와 같이 작성합니다.
vim /var/lib/suricata/rules/local.rules
alert http $EXTERNAL_NET any -> $HOME_NET 80 (msg:"path board"; content:"/board"; http_uri; sid:10001; rev:1;)
alert http $EXTERNAL_NET any -> $HOME_NET 80 (msg:"path admin"; content:"/admin"; http_uri; sid:10002; rev:1;)
룰셋 파일 작성을 완료하였다면 suricata가 수정된 룰셋 파일을 로드하도록 서비스를 리로드합니다. 차단 모드에서 systemctl restart 명령어로 서비스를 재시작하는 경우 서비스가 재시작되는 동안 외부에서 커넥션 불가하기 때문에 이 점에 유의하여 룰셋을 리로드하는 경우에는 systemctl reload 명령어를 사용합니다. (systemctl reload 명령어를 사용하는 경우 서비스가 리로드되는 동안에도 외부에서 커넥션이 정상적으로 가능합니다.)
# 룰셋 리로드
systemctl reload suricata
아래와 같이 접속한 URI 경로에 따라 alert 로그가 발생하는 것을 확인할 수 있습니다.
GeoIP 를 통해 국가별 IP 기반으로 트래픽 필터링 예제
GeoIP 활성화 가이드
https://suricata.readthedocs.io/en/latest/rules/header-keywords.html#geoip
Suricata는 GeoIP 데이터베이스를 통해 국가별 IP를 기반으로 트래픽을 제한할 수 있습니다.
Maxmind 사이트(https://dev.maxmind.com/geoip/geolite2-free-geolocation-data )를 통해 GeoIP 데이터베이스 파일(.mmdb)이 포함된 "GeoLite2-Country_날짜.tar.gz" 압축 파일을 내려받아 Suricata 인스턴스로 업로드 후 "/usr/local/share/GeoLite2/" 경로에 압축을 풀어줍니다.
GeoIP를 활성화하기 위해 Suricata 설정 파일을 수정합니다.
# GeoIP 활성화
vim /etc/suricata/suricata.yaml
1077 # GeoIP2 database file. Specify path and filename of GeoIP2 database
1078 # if using rules with "geoip" rule option.
1079 geoip-database: /usr/local/share/GeoLite2/GeoLite2-Country.mmdb
이제 기존 룰셋 파일을 수정하여 한국 아이피로부터 HTTP 액세스가 발생할 때와 한국이 아닌 아이피로부터 HTTP 액세스가 발생할 때 alert를 발생시키는 룰셋을 작성합니다.
vim /var/lib/suricata/rules/local.rules
alert tcp $EXTERNAL_NET any -> $HOME_NET 80 (msg:"http access from Korean"; geoip:any,KR; sid:10002; rev:1;)
alert tcp $EXTERNAL_NET any -> $HOME_NET 80 (msg:"http access from not Korean"; geoip:any,!KR; sid:10003; rev:1;)
저장 후 닫기
# Suricata 서비스 재시작(/etc/suricata/suricata.yaml 파일을 수정했기 때문에 서비스 재시작 필요)
systemctl restart suricata
서비스 재시작 완료 후 웹서버 인스터스로 HTTP 접속 테스트 시 아래와 같이 한국 아이피에 대한 룰이 매칭되어 alert 가 발생합니다.
아래는 웹 사이트의 압축 여부를 확인해주는 사이트인데 해외에서 HTTP 액세스를 발생시켜보기 위해 웹서버 인스턴스의 URL을 입력하여 해당 해외 서버에서 테스트 환경의 웹서버 인스턴스로 HTTP 액세스를 발생시켜보았습니다.
Suricata 인스턴스 세팅3 - IPS 모드 및 iptables 설정
아래와 같은 룰셋 예제에서 액션을 정의하는 alert 부분을 트래픽을 거부하거나 차단하는 reject, drop 액션으로 수정하여도 실제로 트래픽의 차단이 발생하지는 않습니다.
vim /var/lib/suricata/rules/local.rules
alert tcp $EXTERNAL_NET any -> $HOME_NET 80 (msg:"http access from Korean"; geoip:any,KR; sid:10002; rev:1;)
alert tcp $EXTERNAL_NET any -> $HOME_NET 80 (msg:"http access from not Korean"; geoip:any,!KR; sid:10003; rev:1;)
그 이유는 Suricata 가 현재 IDS 모드로 동작하고 있어 트래픽을 탐지만 하기 때문입니다. Suricata 가 차단 모드로 동작하도록 설정하기 위해서는 Suricata 실행 옵션을 변경하고 iptables 의 룰을 추가하여 실제로 인스턴스에 유입되는 트래픽을 NFQUEUE(NetFilter Queue)로 보내 Suricata 에 의해 필터링되도록 설정하는 작업이 필요합니다.
IPS 모드로 Suricata 실행
먼저 Suricata를 차단 모드로 실행하도록 옵션을 변경해줍니다.
# 차단 모드로 실행 옵션 추가
vim /etc/sysconfig/suricata
#OPTIONS="-i ens5 --user suricata " -> 주석 후 아래 라인 추가
OPTIONS="-q 0 "
저장 후 닫기
# NFQ(NetFilter Queue) 모드가 repeat 모드로 동작하도록 설정 수정
vim /etc/suricata/suricata.yaml
nfq:
# mode: accept -> 주석 해제 후 repeat 로 수정
# repeat-mark: 1 -> 주석 해제
# repeat-mask: 1 -> 주석 해제
저장 후 닫기
위에서 nfq 설정을 기본 값인 accept 에서 repeat 로 변경하는 경우 Suricata 에 의해 트래픽 필터링 후 다시 iptables 로 트래픽을 돌려보낼지(repeat) 아니면 Suricata 에 의해 처리되어 iptables 를 통과할지(accept)를 결정하는 옵션으로 상세 내용은 조금 복잡하니 아래 가이드를 참고합니다.
https://suricata.readthedocs.io/en/latest/configuration/suricata-yaml.html#nfq
Suricata 설정 파일을 수정했으니 서비스를 재시작해줍니다.
systemctl restart suricata
iptables 설정 추가
FORWARD 체인을 통과하는 모든 트래픽 중 1/1 mark가 없는 모든 트래픽을 NFQUEUE 로 보내 Suricata가 필터링하도록 하는 iptables 룰입니다. 여기서 mark는 NFQ 모드가 repeat 일 때에 Suricata를 거친 트래픽과 아직 거치지 않은 트래픽을 구분하기 위해 사용하는 것으로 상세 내용은 위의 가이드(https://suricata.readthedocs.io/en/latest/configuration/suricata-yaml.html#nfq)를 참고합니다.
# 1/1 mark가 없는 모든 트래픽 suricata 필터링
iptables -I FORWARD -m mark ! --mark 1/1 -j NFQUEUE
iptables 룰 적용 시 아래와 같이 보여집니다.
필요에 따라 해당 룰셋은 재부팅이 되어도 자동으로 설정되도록 구성합니다. 다만, 해당 iptables 룰이 세팅된 상태에서 Suricata 서비스가 구동 중이지 않는다면 모든 FORWARD 트래픽이 통신 불가하기 때문에 주의가 필요합니다.
테스트 룰셋 파일 수정
Suricata 가 IPS 모드로 동작하고 있기 때문에 룰셋 파일의 액션(가장 앞) 부분을 reject(거부 메시지를 보내고 차단 및 로깅), drop(차단 및 로깅)으로 설정하면 실제로 Suricata 를 통해 Forward 되는 트래픽이 차단됩니다. 기존 룰셋 파일을 아래와 같은 내용으로 수정해봅니다. "/board" 경로는 로깅, "/admin" 경로는 차단, 한국 아이피는 로깅, 한국 외의 아이피는 차단하도록 구성한 예제입니다.
alert http $EXTERNAL_NET any -> $HOME_NET 80 (msg:"path board"; content:"/board"; http_uri; sid:10001; rev:1;)
drop http $EXTERNAL_NET any -> $HOME_NET 80 (msg:"path admin"; content:"/admin"; http_uri; sid:10002; rev:1;)
alert tcp $EXTERNAL_NET any -> $HOME_NET 80 (msg:"http access from Korean"; geoip:any,KR; sid:10002; rev:1;)
drop tcp $EXTERNAL_NET any -> $HOME_NET 80 (msg:"http access from not Korean"; geoip:any,!KR; sid:10003; rev:1;)
결론
여기까지 오픈소스 IDS/IPS 솔루션인 Suricata 에 대해 알아보고 간단한 예제를 통해 룰셋을 구성하여 VPC 내/외부로 흐르는 트래픽을 제어하는 내용으로 테스트를 진행해봤습니다. 위에서 소개한 예제 외에도 아주 다양한 룰 설정 방법이 있기 때문에 흥미가 있으신 분들은 직접 테스트 환경을 구성해보시는 것도 좋을 것 같습니다.
https://suricata.readthedocs.io/en/latest/rules/index.html
또한 AWS Network Firewall이 Suricata를 기반으로 동작하기 때문에 AWS Network Firewall의 고급 규칙을 구성할 때에도 Suricata 룰셋 문법을 이해하고 있는 것은 많은 도움이 될 것으로 생각합니다.