Jak nainstalovat ModSecurity pro Nginx

26. června 2019

V tomto článku si představíme web aplikační firewall ModSecurity a ukážeme si jeho zprovoznění na webovém serveru Nginx.

Předpokládané znalosti

Očekává se od čtenáře alespoň základní znalost práce s UNIXovým prostředím a webovým serverem Nginx včetně úpravy jeho konfiguračních souborů.

Úvod

V tomto článku čtenáře seznámíme s nejpopulárnějším open source web aplikačním firewallem ModSecurity verze 3 a ukážeme si, jak ho zprovoznit na webovém serveru Nginx. Projdeme si instalaci na nejrozšířenějších volně dostupných Linuxových distribucích pro servery (Ubuntu Server, CentOS) a na oblíbené distribuci Alpine, která je hojně využívána jako základ pro Dockerové kontejnery. Všechna nastavení budeme dělat ze stavu, ve kterém se Nginx nachází po instalaci, tedy obsahující pouze výchozí konfiguraci.

Web aplikační firewall

Bežný firewall zpravidla pracuje s provozem na úrovni síťové či transportní. Zjednodušené řečeno zkoumá věci jako IP adresu odesílatele a příjemce, port, případně transportní protokol (TCP, UDP, ..) apod. Aplikační firewall, jak název napovídá, provádí hlubší inspekci komunikace - na úrovni aplikace. A protože mluvíme konkrétně o webovém aplikačním firewallu (WAF), bude se jednat o HTTP provoz. Tedy nám umožní na úrovni HTTP komunikace definovat pravidla, za kterých smí či nesmí požadavek dosáhnout samotné webové aplikace. Samozřejmě v ideálním případě by naše webová aplikace nesprávný či škodlivý požadavek rozpoznala a nevykonala, vždyť pečlivá kontrola vnějších vstupů není nikdy zanedbána. Takže by žádný web aplikační firewall nebyl vlastně třeba. Bohužel nežijeme v ideálním světě a i v těch nejlepších webových aplikacích se objevují chyby. Protože upravit/opravit webovou aplikaci není vždy možné řešení, nasazení web aplikačního firewallu nám může pomoci aplikaci ochránit.

ModSecurity

Jedná se o v současnosti asi nejrozšířenější open source WAF. Původní verze byly vytvořeny pro použití s webovým serverem Apache a proto je v článku zdůrazněno, že se zaměříme na verzi ModSecurity 3, která byla kompletně přepsána a umožňuje použítí i v kombinaci s jinými webovými servery jako například právě Nginx.

Instalace

Pusťme se do práce. Pro zprovoznění ModSecurity na Nginxu budeme potřebovat nainstalovat tyto součásti:
- Nginx - tedy samotný webserver
- libmodsecurity - toto je samotná knihovna ModSecurity, která načítá a interpretuje pravidla, a následně je aplikuje na HTTP provoz
- ngx_http_modsecurity_module.so - dynamický modul pro Nginx (z pohledu ModSecurity se jedná o tzv. Connector), který umožní komunikaci mezi web serverem a knihovnou libmodsecurity
- (volitelně) Core Rule Set - soubor generických pravidel (vytvořených organizací OWASP), která se snaží zamezit najčastějším typům webových útoků (SQL Injection, Cross Site Scripting a další) a minimalizovat počet false positives

Instalace Nginx

Instalace samotného Nginx je přímočará neboť je k dispozici ve standartních distribučních repozitářích. Pro někoho pak může být vhodnější přidat repozitáře spravované přímo Nginxem, ve kterých vždy nalezneme nejnovější stable verzi.

Instalace libmodsecurity

Co se libmodsecurity týče, pro CentOS je situace taky poměrně přívětivá, po nainstalování EPEL (Extra Packages for Enterprise Linux) repozitáře (yum install epel-release) zde najdeme balík libmodsecurity, který snadno nainstalujeme (yum install libmodsecurity). V době psaní tohoto článku je balík libmodsecurity ve verzi 3.0.2 zatímco oficiální stable verze je 3.0.3.
V případě, že si chceme knihovnu ručně sestavit nebo pracujeme s Ubuntu či Alpinem, na kterých není knihovna dostupná v podobě balíčku, budeme postupovat následovně:

Instalace závislostí pro Ubuntu:
apt-get install -y apt-utils autoconf automake build-essential git libcurl4-openssl-dev libgeoip-dev liblmdb-dev libpcre++-dev libtool libxml2-dev libyajl-dev pkgconf wget zlib1g-dev

Instalace závislostí pro CentOS:
yum install gcc-c++ flex bison yajl yajl-devel curl-devel curl GeoIP-devel doxygen zlib-devel automake autoconf libtool git pcre pcre-devel lmdb lmdb-devel ssdeep ssdeep-devel libxml2 libxml2-devel lua lua-devel

Zklonujeme si zdrojové kódu z githubu do adresáře /src/ModSecurity, zkompilujeme a nainstalujeme. Celý proces bude trvat několik minut, případnou hlášku (fatal: No names found, cannot describe anything.) můžeme bezpečně ignorovat.

git clone --depth 1 -b v3/master --single-branch https://github.com/SpiderLabs/ModSecurity /src/ModSecurity
cd /src/ModSecurity
git submodule init
git submodule update
./build.sh
./configure
make
make install

Tím jsme dokončili instalaci libmodsecurity. Instalaci pro Alpine Linux uvedeme na konci v rámci komentovaného Dockerfilu.

Instalace dynamického modulu ngx_http_modsecurity_module.so

Dalším krokem je kompilace a instalace dynamického modulu pro Nginx ngx_http_modsecurity_module.so. K tomu budeme potřebovat stáhnout zdrojové kódy jak tohoto modulu, tak celého Nginxu ve verzi, kterou máme nainstalovanou.
nginx -v
nginx version: nginx/1.16.0

Stáhneme zdrojové kódy dynamického modulu a web serveru:
git clone --depth 1 https://github.com/SpiderLabs/ModSecurity-nginx.git /src/ModSecurity-nginx
wget -qO - https://nginx.org/download/nginx-1.16.0.tar.gz | tar xzfv - -C /src

Z adresáře se zdrojovými kódy Nginxu zkompilujeme dynamický modul (přepínač --add-dynamic-module musí odkazovat na kořenovou složku, ve které jsou zdrojové kódy modulu). Výsledek přesuneme do adresáře /etc/nginx/modules:
cd /src/nginx-1.16.0
./configure --with-compat --add-dynamic-module=/src/ModSecurity-nginx
make modules
mv objs/ngx_http_modsecurity_module.so /etc/nginx/modules

Aktivaci modulu pak provedeme přidáním následující direktivy na začátek konfiguračního souboru /etc/nginx/nginx.conf:
load_module /etc/nginx/modules/ngx_http_modsecurity_module.so;

Otestování funkčnosti ModSecurity

Pro otestování funkčnosti ModSecurity vytvoříme adresář /etc/nginx/modescurity a v něm soubor /etc/nginx/modsecurity/test.conf s následujícím obsahem:
SecRuleEngine On
SecRule ARGS:testparam "@contains test" "id:1234,deny,status:403"

První řádek povolí aplikování pravidel a druhý řádek je testovacím pravidlem, které v případě, že url má parametr "testparam" s obsahujícím řetězcem "test", pak vrátí 403 Access Denied.
Do server bloku konfigurace (výchozí k nalezení v /etc/nginx/conf.d/default.conf) přidáme direktivy modsecurity a modsecurity_rules_file do této podoby:
server {
      listen 80;
      server_name localhost;
      modsecurity on;
      modsecurity_rules_file /etc/nginx/modsec/test.conf;
      location / {
            root /usr/share/nginx/html;
            index index.html index.htm;
      }
}

Provedeme restart/reload Nginxu a otestujeme v prohlížeči na url http://localhost/?testparam=test, na což by nám měl server odpovědět očekávaným kódem 403 Access Denied. Tím jsme ověřili, že ModSecurity funguje, nicméně nemáme zatím žádná vhodná pravidla, podle kterých by mohl požadavky vyhodnocovat.

Nasazení Core Rule Setu

V tento okamžik přichází na scénu Core Rule Set od organizace OWASP, který taková generická pravidla obsahuje. V ideálním případě bychom tato pravidla použili pouze jako odrazový můstek pro vytvoření pravidel vlastních. To je však mimo rozsah tohoto článku, zde si pouze ukážeme, jak nasadit Core Rule Set.
Jako hlavní součást konfigurace ModSecurity použijeme soubor modsecurity.conf-recommended, který jsme stáhli v rámci prvního repozitáře (github.com/SpiderLabs/ModSecurity) a umístíme ho do /etc/nginx/modsecurity/modsecurity.conf.
cp /src/ModSecurity/modsecurity.conf-recommended /etc/nginx/modsecurity/modsecurity.conf

Dále stáhneme Core Rule Set v našem případě jako archiv přímo z githubu (a rozbalíme do /src):
wget -qO - https://github.com/SpiderLabs/owasp-modsecurity-crs/archive/v3.1.0.tar.gz | tar xzf - -C /src

Vytvoříme adresář /etc/nginx/modsecurity/crs, do kterého zkopírujeme soubor crs-setup.conf-example jako crs-setup.conf a celý adresář rules.
cp /src/owasp-modsecurity-crs-3.1.0/crs-setup.conf-example /etc/nginx/modsecurity/crs/crs-setup.conf
cp -r /src/owasp-modsecurity-crs-3.1.0/rules /etc/nginx/modsecurity/crs

V adresáři /etc/nginx/modsecurity/crs/rules najdeme mnoho .conf.example souborů. U těch dle potřeby odstraníme příponu .example. Nyní máme všechny konfigurační soubory na svých místech, ještě je však musíme vzájemně naincludovat. Tedy celková posloupnost konfiguračních souborů vypadá následovně:
1. V server bloku Nginx konfigurace (viz ukázkový /etc/nginx/conf.d/default.conf) nastavíme direktivu:
modsecurity_rules_file /etc/nginx/modsecurity/modsecurity.conf
2. Do souboru /etc/nginx/modsecurity/modsecurity.conf přidáme na konec dvě direktivy:
Include /etc/nginx/modsecurity/crs/crs-setup.conf
Include /etc/nginx/modsecurity/crs/rules/*.conf
Toto řetězení includů je nutné, protože konfigurace Nginx umožňuje načtení pouze jednoho konfiguračního souboru pro ModSecurity (viz předchozí server blok s direktivou modsecurity_rules_file). Nic nám ale nebrání z tohoto souboru provést include dalších konfigurací, díky čemuž nemusíme mít všechna nastavení a pravidla zapsána v jednom souboru.
3. (volitelně) V souboru /etc/nginx/modsecurity/modsecurity.conf najdeme direktivu SecRuleEngine, která má výchozí hodnotu DetectionOnly, kdy jsou HTTP požadavky vůči pravidlům vyhodnocovány, ale pouze zalogovány, a změníme na On.
SecRuleEngine On

Otestování Core Rule Setu

Opět provedeme restart/reload Nginxu a pravidla Core Rule Setu by měla být aktivní. Můžeme to otestovat například jednoduchým požadavkem na url http://localhost/?testparam=/etc/passwd, který by nám opět měl vrátit kód 403 Access Denied.
Audit log ModSecurity najdeme ve výchozím nastavení v /var/log/modsec_audit.log (definované v /etc/nginx/modsecurity/modsecurity.conf direktivou SecAuditLog).

Alpine (Docker)

Neboť je Alpine Linux využíván zejména pro minimalistické Dockerové kontejnery, ukážeme si celý postup v podobě komentovaného Dockerfilu pro vytvoření kontejneru:
# sestaveni libmodsecurity a dynamickeho modulu ngx_http_modsecurity_module.so
ARG NGINX_VERSION="1.16.0"

FROM nginx:${NGINX_VERSION}-alpine as build

ARG NGINX_VERSION
ARG CRS_VERSION="3.1.0"

# instalace zavislosti pro sestaveni libmodsecurity
RUN build_pkgs="alpine-sdk apr-dev apr-util-dev autoconf automake binutils-gold curl curl-dev g++ gcc geoip-dev git gnupg icu-dev libcurl libffi-dev libjpeg-turbo-dev libstdc++ libtool libxml2-dev linux-headers lmdb-dev m4 make openssh-client pcre-dev pcre2-dev perl pkgconf wget yajl-dev zlib-dev" && \
     apk add --update --no-cache ${build_pkgs}

# stazeni zdrojovych kodu a core rule setu
RUN mkdir -p /src && \
     git clone --depth 1 -b v3/master --single-branch https://github.com/SpiderLabs/ModSecurity /src/ModSecurity && \
     git clone --depth 1 https://github.com/SpiderLabs/ModSecurity-nginx.git /src/ModSecurity-nginx && \
     wget -qO - https://nginx.org/download/nginx-${NGINX_VERSION}.tar.gz | tar xzf - -C /src && \
     wget -qO - https://github.com/SpiderLabs/owasp-modsecurity-crs/archive/v${CRS_VERSION}.tar.gz | tar xzf - -C /src

# sestaveni libmodsecurity
RUN cd /src/ModSecurity && \
     git submodule init && \
     git submodule update && \
     ./build.sh && \
     ./configure && \
     make && \
     make install

# sestaveni dynamickeho modulu
RUN cd /src/nginx-${NGINX_VERSION} && \
     ./configure --with-compat --add-dynamic-module=../ModSecurity-nginx && \
     make modules

# pro samotne nasazeni jiz nepotrebujeme instalovane zavislosti a zdrojove kody
FROM nginx:${NGINX_VERSION}-alpine

RUN apk add --update --no-cache libcurl yajl libstdc++

# kopirovani sestavenych souboru, vychozich konfiguraci a core rule setu
COPY --from=build /usr/local/modsecurity /usr/local/modsecurity
COPY --from=build /src/nginx-${NGINX_VERSION}/objs/ngx_http_modsecurity_module.so /etc/nginx/modules
COPY --from=build /src/ModSecurity/modsecurity.conf-recommended /etc/nginx/modsecurity/modsecurity.conf
COPY --from=build /src/ModSecurity/unicode.mapping /etc/nginx/modsecurity/unicode.mapping
COPY --from=build /src/owasp-modsecurity-crs-${CRS_VERSION}/crs-setup.conf.example /etc/nginx/modsecurity/crs/crs-setup.conf
COPY --from=build /src/owasp-modsecurity-crs-${CRS_VERSION}/rules /etc/nginx/modsecurity/crs/rules

# uprava konfiguracnich souboru
RUN echo -e "load_module modules/ngx_http_modsecurity_module.so;\n$(cat /etc/nginx/nginx.conf)" > /etc/nginx/nginx.conf && \
     sed -i 's/SecRuleEngine DetectionOnly/SecRuleEngine On/' /etc/nginx/modsecurity/modsecurity.conf && \
     echo "Include /etc/nginx/modsecurity/crs/crs-setup.conf" >> /etc/nginx/modsecurity/modsecurity.conf && \
     echo "Include /etc/nginx/modsecurity/crs/rules/*.conf" >> /etc/nginx/modsecurity/modsecurity.conf && \
     for filename in /etc/nginx/modsecurity/crs/rules/*.example; do mv $filename ${filename:0:-8}; done

Vznik tohoto textu byl podpořen Nástrojem Evropské unie pro propojení Evropy.
drawing