Using WAF on HAProxy with ModSecurity

모드시큐리티를 이용하여 HAProxy에 WAF 달기

ModSecurity

WAF(Web Application Firewall)는 웹 어플리케이션을 보호하기 위해 필수라고 볼 수 있는 꽤나 비싼 장비 혹은 솔루션입니다. ModSecurity(이하 ModSec)은 꽤나 오래전부터 알려져 있는 오픈소스 WAF 입니다.

오픈소스이긴 하지만 상용 WAF에 비해 기능이 떨어지지도 않을 뿐더러, 오히려 많은 상용 솔루션에도 ModSec 기반으로 제작되었거나 룰셋 등 일부 시스템을 차용하기도 합니다.

초기부터 Apache를 기반으로 한 WAF로 유명했던 ModSec 외에 Nginx를 기반으로 한 NAXSI(Nginx Anti XSS & SQL Injection)라는 오픈소스 WAF도 있습니다.

ModSec은 v3 부터 아파치와의 종속성을 없애고 독립적인 모듈로써 어떤 플랫폼과도 지원할 수 있도록 변경되었습니다.

Spoa-ModSecurity

HAProxy의 공식 깃허브에서 제공되고 있는 spoa-modsecurity는 ModSec과 HAProxy를 연동할 수 있도록 개발된 모듈입니다. HAProxy에서는 외부 프로그램의 통신을 위해 SPOE(Stream Processing Offload Engine) 모듈을 개발하였습니다.

spoa-modsecurity는 바로 SPOE를 이용한 모듈이라고 보시면 됩니다.

spoa-modsecurity는 ModSec v2 기반으로 개발되었습니다. 또한 설치 및 설정 가이드는 매우 불친절하게 되어 있으며, 잘못된 내용이 많았습니다. 어렵게 모든 설정을 마무리하고 테스트를 진행했지만 remote address가 127.0.0.1로만 출력 된다거나, ModSec 룰셋 적용이 제대로 안된다거나 하는 문제점들이 있었습니다.

이러한 문제들로 인해 직접 모듈 소스를 수정해서 몇 가지 이슈는 해결되었지만  ModSec v2 기반이라는 점도 마음에 썩 들진 않았었습니다.

그러던 중 fork 된 버전이 있는지 확인하게 되었고, 그 중에서 가장 많은 커밋량과 현재까지도 커밋이 진행되고 있는 FireBurn fork를 찾게 되었습니다. 이 fork에는 제가 발견한 문제점들이 모두 수정되었으며, ModSec v3 버전으로 포팅까지 완료되어 있었습니다.

이 fork 버전 또한 설치 및 설정 가이드는 매우 불친절하고 잘못된 내용이 많았지만 결국 설치에 성공하였고 현재 테스트 중에 있습니다.

이 가이드에서는 HAProxy 설치는 다루지 않으며, spoa-haproxy의 FireBurn fork버전을 이용하여 HAProxy에 ModSec v3를 설치하여 WAF를 구성하는 방법에 대해 다루도록 하겠습니다.

HAProxy 설치는 Using HTTP/3 over QUIC on HAProxy – Umount Blog 또는 Source Install HAProxy 2.0 with TLS 1.3 on CentOS 7 – Umount Blog 문서를 참고해 주시기 바랍니다.

Enviroment

OS: Rocky Linux 9.1
HAProxy 2.6.7
ModSecurity 3.0.8
OWASP ModSecurity CRS 3.3.4

전체 종속성 패키지 설치

[root 172-19-11-6 /]# dnf -y install openssl-devel perl pcre-devel zlib-devel systemd-devel net-tools libxml2 libxml2-devel expat-devel curl-devel yajl-devel libevent libevent-devel readline-devel ssdeep ssdeep-devel lua lua-devel libtool autoconf automake pcre2-devel lmdb-devel geolite2-country geolite2-city libmaxminddb-devel libasan

ModSecurity 설치

개인적인 편의를 위해 기본적인 작업 디렉토리를 /usr/local/src/03_application/ 경로로 사용하고 있습니다.

/usr/local/modsecurity 경로에 링크를 거는 이유는 나중에 최신 버전을 설치할때 경로상 복잡함을 없애기 위함입니다. 즉, 최신버전을 설치 후 링크만 변경해 주면 되기 때문입니다.

[root 172-19-11-6 /]# cd /usr/local/src/03_application
[root 172-19-11-6 03_application]# mkdir -p /etc/modsecurity
[root 172-19-11-6 03_application]# wget https://github.com/SpiderLabs/ModSecurity/releases/download/v3.0.8/modsecurity-v3.0.8.tar.gz
[root 172-19-11-6 03_application]# tar xvzf modsecurity-v3.0.8.tar.gz
[root 172-19-11-6 03_application]# cd modsecurity-v3.0.8
[root 172-19-11-6 modsecurity-v3.0.8]# ./configure --prefix=/usr/local/modsecurity-3.0.8 --without-lua --enable-pcre-jit --with-lua --enable-lua-cache -with-pcre2 --with-lmdb
[root 172-19-11-6 modsecurity-v3.0.8]# make -j $(nproc)
[root 172-19-11-6 modsecurity-v3.0.8]# make install
[root 172-19-11-6 modsecurity-v3.0.8]# ln -s /usr/local/modsecurity-3.0.8 /usr/local/modsecurity

spoa-modsecurity 설치

아래와 같이 sed를 이용하거나 vi 편집기를 이용하여 Makefile의 내용을 ModSec을 설치한 경로로 반드시 수정해 주셔야 합니다.

[root 172-19-11-6 /]# cd /usr/local/src/03_application
[root 172-19-11-6 03_application]# git clone https://github.com/FireBurn/spoa-modsecurity.git
[root 172-19-11-6 03_application]# cd /usr/local/modsecurity/include/
[root 172-19-11-6 include]# cp -rp modsecurity /usr/local/src/03_application/spoa-modsecurity/include/
[root 172-19-11-6 include]# cd /usr/local/src/03_application/spoa-modsecurity/
[root 172-19-11-6 spoa-modsecurity]# sed -i 's/ModSecurity-v3.0.5\/INSTALL\/usr\/local\/modsecurity\/include/\/usr\/local\/modsecurity\/include\/modsecurity/g' Makefile
[root 172-19-11-6 spoa-modsecurity]# sed -i 's/ModSecurity-v3.0.5\/INSTALL\/usr\/local\/modsecurity\/lib/\/usr\/local\/modsecurity\/lib/g' Makefile
[root 172-19-11-6 spoa-modsecurity]# make
[root 172-19-11-6 spoa-modsecurity]# make install

편집기를 이용해 modsec의 systemd 파일을 만들어 줍니다.

이때 반드시 아래와 같이 LD_LIBRARY_PATH에 modsec을 설치한 library 경로를 지정해 주어야 합니다.

: vi /usr/lib/systemd/system/modsecurity.service

[Unit]
Description=Modsecurity Standalone
After=network.target

[Service]
Environment=LD_LIBRARY_PATH=/usr/local/modsecurity/lib/
Environment="CONFIG=/etc/modsecurity/modsecurity.conf" "EXTRAOPTS=-d -n 1"
ExecStart=/usr/local/bin/modsecurity $EXTRAOPTS -f $CONFIG
ExecReload=/usr/local/bin/modsecurity $EXTRAOPTS -f $CONFIG
ExecReload=/bin/kill -USR2 $MAINPID
Restart=always
Type=simple

[Install]
WantedBy=multi-user.target

OWASP ModSecurity CRS 설치

OWASP ModSecurity CRS 또한 편의를 위해 링크를 거는 과정을 추가하였습니다.

[root 172-19-11-6 spoa-modsecurity]# cd /usr/local/src/03_application
[root 172-19-11-6 03_application]# wget -O coreruleset-3.3.4.tar.gz https://github.com/coreruleset/coreruleset/archive/refs/tags/v3.3.4.tar.gz
[root 172-19-11-6 03_application]# tar xvzf coreruleset-3.3.4.tar.gz 
[root 172-19-11-6 03_application]# cp -rp coreruleset-3.3.4 /usr/local/
[root 172-19-11-6 03_application]# ln -s /usr/local/coreruleset-3.3.4/ /usr/local/coreruleset
[root 172-19-11-6 03_application]# cd /usr/local/coreruleset/
[root 172-19-11-6 coreruleset]# cp crs-setup.conf.example crs-setup.conf
[root 172-19-11-6 coreruleset]# cd rules/
[root 172-19-11-6 rules]# cp REQUEST-900-EXCLUSION-RULES-BEFORE-CRS.conf.example REQUEST-900-EXCLUSION-RULES-BEFORE-CRS.conf
[root 172-19-11-6 rules]# cp RESPONSE-999-EXCLUSION-RULES-AFTER-CRS.conf.example RESPONSE-999-EXCLUSION-RULES-AFTER-CRS.conf
[root 172-19-11-6 spoa-modsecurity]# cd /usr/local/src/03_application/modsecurity-v3.0.8/
[root 172-19-11-6 modsecurity-v3.0.8]# cp unicode.mapping /etc/modsecurity/unicode.mapping
[root 172-19-11-6 modsecurity-v3.0.8]# cp modsecurity.conf-recommended /etc/modsecurity/modsecurity.conf 
[root 172-19-11-6 modsecurity-v3.0.8]# sed -i 's/unicode.mapping/\/etc\/modsecurity\/unicode.mapping/g' /etc/modsecurity/modsecurity.conf

이제 편집기를 이용해 실질적인 룰셋 파일들을 맨 아래라인에 include 해줍니다.

: vi /etc/modsecurity/modsecurity.conf

include /usr/local/coreruleset/crs-setup.conf
include /usr/local/coreruleset/rules/REQUEST-900-EXCLUSION-RULES-BEFORE-CRS.conf
include /usr/local/coreruleset/rules/REQUEST-901-INITIALIZATION.conf
include /usr/local/coreruleset/rules/REQUEST-905-COMMON-EXCEPTIONS.conf
include /usr/local/coreruleset/rules/REQUEST-910-IP-REPUTATION.conf
include /usr/local/coreruleset/rules/REQUEST-911-METHOD-ENFORCEMENT.conf
include /usr/local/coreruleset/rules/REQUEST-912-DOS-PROTECTION.conf
include /usr/local/coreruleset/rules/REQUEST-913-SCANNER-DETECTION.conf
include /usr/local/coreruleset/rules/REQUEST-920-PROTOCOL-ENFORCEMENT.conf
include /usr/local/coreruleset/rules/REQUEST-921-PROTOCOL-ATTACK.conf
include /usr/local/coreruleset/rules/REQUEST-930-APPLICATION-ATTACK-LFI.conf
include /usr/local/coreruleset/rules/REQUEST-931-APPLICATION-ATTACK-RFI.conf
include /usr/local/coreruleset/rules/REQUEST-932-APPLICATION-ATTACK-RCE.conf
include /usr/local/coreruleset/rules/REQUEST-933-APPLICATION-ATTACK-PHP.conf
include /usr/local/coreruleset/rules/REQUEST-941-APPLICATION-ATTACK-XSS.conf
include /usr/local/coreruleset/rules/REQUEST-942-APPLICATION-ATTACK-SQLI.conf
include /usr/local/coreruleset/rules/REQUEST-943-APPLICATION-ATTACK-SESSION-FIXATION.conf
include /usr/local/coreruleset/rules/REQUEST-949-BLOCKING-EVALUATION.conf
include /usr/local/coreruleset/rules/RESPONSE-950-DATA-LEAKAGES.conf
include /usr/local/coreruleset/rules/RESPONSE-951-DATA-LEAKAGES-SQL.conf
include /usr/local/coreruleset/rules/RESPONSE-952-DATA-LEAKAGES-JAVA.conf
include /usr/local/coreruleset/rules/RESPONSE-953-DATA-LEAKAGES-PHP.conf
include /usr/local/coreruleset/rules/RESPONSE-954-DATA-LEAKAGES-IIS.conf
include /usr/local/coreruleset/rules/RESPONSE-959-BLOCKING-EVALUATION.conf
include /usr/local/coreruleset/rules/RESPONSE-980-CORRELATION.conf
include /usr/local/coreruleset/rules/RESPONSE-999-EXCLUSION-RULES-AFTER-CRS.conf

HAProxy 설정

[root 172-19-11-6 modsecurity-v3.0.8]# cd /etc/haproxy/

modsecurity 관련 설정파일을 생성합니다.

: vi spoe-modsecurity.conf

[modsecurity]
    spoe-agent modsecurity-agent
        messages    check-request
        option      var-prefix modsec
        timeout     hello      100ms
        timeout     idle       30s
        timeout     processing 15ms
        use-backend spoe-modsecurity

    spoe-message check-request
        args unique-id src src_port dst dst_port method path query req.ver req.hdrs_bin req.body_size req.body
        event on-frontend-http-request

haproxy 구성파일을 편집합니다. 아래는 아주 기본적인 haproxy.cfg 파일의 내용이며, modsecurity와 관련한 주요 설정에 하이라이트를 하였습니다.

: vi haproxy.cfg

global
    daemon
    user   haproxy
    group  haproxy
    chroot /var/lib/haproxy

    maxconn 4096

    log localhost local0
    log-send-hostname

    stats socket /run/haproxy-master.sock mode 660 level admin
    stats timeout 60s

    ssl-load-extra-del-ext
    ssl-load-extra-files key
    ssl-default-bind-options   ssl-min-ver TLSv1.2
    ssl-default-server-options ssl-min-ver TLSv1.2

defaults
    mode http
    log  global

    option httplog
    option dontlognull
    option http-server-close
    option forwardfor except 127.0.0.0/8
    option redispatch 1

    retries  3
    retry-on all-retryable-errors

    timeout http-request    180s
    timeout client          300s
    timeout queue           60s
    timeout connect         300s
    timeout server          300s
    timeout check           10s
    timeout http-keep-alive 10s

listen stats
    bind  0.0.0.0:14098
    stats enable
    stats refresh 60s
    stats uri /haproxy_status

frontend https-in
    bind 0.0.0.0:80
    bind 0.0.0.0:443 ssl crt /etc/haproxy/secure/haproxy.umount.net.pem alpn h2

    unique-id-format %{+X}o\ %ci:%cp_%fi:%fp_%Ts_%rt:%pid
    unique-id-header X-Unique-ID
    log-format "%ci:%cp [%tr] %ft %b/%s %TR/%Tw/%Tc/%Tr/%Ta %ST %B %CC %CS %tsc %ac/%fc/%bc/%sc/%rc %sq/%bq %hr %hs %{+Q}r %[unique-id]"

    http-request redirect scheme https unless { ssl_fc }
    http-request set-header X-Forwarded-Proto https if { ssl_fc }

    filter spoe engine modsecurity config /etc/haproxy/spoe-modsecurity.conf
    http-request deny if { var(txn.modsec.code) -m int gt 0 }

    http-response set-header server umount
    http-response set-header X-Frame-Options SAMEORIGIN;
    http-response set-header X-Content-Type-Options nosniff;
    http-response set-header X-XSS-Protection "1; mode=block";

    acl umount.net var(txn.txnhost) -m str -i umount.net
    http-request set-var(txn.txnhost) hdr(host)
    use_backend umount.net if umount.net

backend spoe-modsecurity
    mode    tcp
    balance roundrobin
    timeout connect 5s
    timeout server  3m
    server  modsec1 127.0.0.1:12345

backend umount.net
    option httpchk
    compression algo gzip
    compression type text/html text/plain text/css
    http-check send meth HEAD uri /health.txt ver HTTP/1.1 hdr Host umount.net
    http-check expect status 200
    balance roundrobin
    http-request disable-l7-retry if METH_POST
    cookie SERVERID insert indirect nocache
    default-server inter 5s fastinter 3s rise 3 fall 3
    server web01 10.19.11.7:8088 cookie web01 check maxconn 2048
    server web02 10.19.11.8:8088 cookie web02 check maxconn 2048

파일에 문제가 없는지 테스트 해봅니다. spoe-modsecurity 백엔드에 option forwardfor 관련 경고는 무시하셔도 됩니다.

[root 172-19-11-6 haproxy]# haproxy -c -f haproxy.cfg

[NOTICE]   (123986) : haproxy version is 2.6.7-c55bfdb
[NOTICE]   (123986) : path to executable is /usr/local/sbin/haproxy
[WARNING]  (123986) : config : 'option forwardfor' ignored for backend 'spoe-modsecurity' as it requires HTTP mode.
Warnings were found.
Configuration file is valid

문제가 없으면 modsec 및 haproxy를 systemd에 등록하고 실행합니다.

[root 172-19-11-6 haproxy]# systemctl enable --now modsecurity
[root 172-19-11-6 haproxy]# systemctl enable --now haproxy
[root 172-19-11-6 haproxy]# systemctl status modsecurity
[root 172-19-11-6 haproxy]# systemctl status haproxy

WAF 동작 확인

ModSec 및 추가한 CRS가 정상 작동하는지 보기 위해서는 audit 로그를 확인하시면 됩니다. audit 로그는 modsecurity.conf 파일에서 확인 및 수정 가능합니다.

설정파일 수정 후에는 항상 ModSec 서비스를 재시작 해주어야 합니다.

: vi /etc/modsecurity/modsecurity.conf

SecAuditLogType Serial
SecAuditLog /var/log/modsecurity/audit.log
SecAuditLogFormat JSON
[root 172-19-11-6 haproxy]# systemctl restart modsecurity

audit 로그 확인을 위해 나쁜짓을 한번 해 보겠습니다.

[root 172-19-11-6 haproxy]# curl -I https://umount.net/?../etc/passwd
[root 172-19-11-6 haproxy]# cat /var/log/modsecurity/audit.log
modsec_auditlog
ModSecurity Audit 로그

일부를 모자이크 처리 하긴 했지만 로그가 정상적으로 나오는 것 같습니다. 하지만 json 형식으로 길게 한줄로 나오기 때문에 보기 불편할 수 있습니다. jq 패키지를 이용하면 이쁘게 보실 수 있습니다.

[root 172-19-11-6 haproxy]# dnf  -y install jq
[root 172-19-11-6 haproxy]# cat /var/log/modsecurity/audit.log | jq
modsec_auditlog-jq
ModSecurity Audit 로그 with jq

지금은 기본 설정으로 되어 있기 때문에 Detected Only 모드로 동작하고 있습니다. 다음에는 ModSecurity의 설정을 통해 탐지 모드에서 차단 모드로 변경하는 방법, 예외처리, 커스텀 룰셋 등에 대해 간략하게 정리해 보도록 하겠습니다.

You may also like...

Subscribe
Notify of
guest

이 사이트는 스팸을 줄이는 아키스밋을 사용합니다. 댓글이 어떻게 처리되는지 알아보십시오.

2 Comments
Most Voted
Newest Oldest
Inline Feedbacks
View all comments
taehei
taehei
2023-01-27 11:51

안녕하세요 지금 설치는 잘 진행되었는데 modsecurity 데몬 실행이 되질 않습니다… 확인해봐야할 사항이 뭐가 있을까요

2
0
Would love your thoughts, please comment.x
()
x