In this guide, we are going to learn how to Install Modsecurity with Nginx on Rocky Linux 8. LibMosecurity is the newest version of ModSecurity. It is therefore known as ModSecurity version 3.
Installing Modsecurity with Nginx on Rocky Linux 8
Run System Update
Begin by updating your system packages.
dnf update
Install Required Build Tools and Dependencies
Both Nginx and LibModsecurity are going to be compiled from the source and thus a number of build tools and dependencies are required. Run the command below to install them.
dnf install gcc-c++ flex bison yajl curl-devel curl zlib-devel pcre-devel autoconf automake git curl make libxml2-devel pkgconfig libtool httpd-devel redhat-rpm-config git wget openssl openssl-devel vim
dnf --enablerepo=powertools install doxygen yajl-devel -y
Install GeoIP Libraries
dnf install epel-release https://rpms.remirepo.net/enterprise/remi-release-8.rpm -y
dnf --enablerepo=remi install GeoIP-devel -y
Download Modsecurity Source Code
Create a temporary directory to store the source tarballs.
mkdir /tmp/modsec
Run the command below to clone the latest LibModsecurity GitHub repository.
cd /tmp/modsec
git clone --depth 1 -b v3/master --single-branch https://github.com/SpiderLabs/ModSecurity
Compile and Install Modsecurity on Rocky Linux 8
Navigate to the Modsecurity source directory, configure, compile and install it
cd ModSecurity
Download libInjection code which is available as part of ModSecurity source code in a format of a git-submodule
git submodule init
git submodule update
Configure Modsecurity to adapt it to your system and check if any required dependency is missing.
./build.sh
./configure
The fatal: No names found, cannot describe anything can be safely ignored.
Fix any dependency issue just in case there is any before you can proceed.
Compile and install ModSecurity.
make
make install
Install Nginx with LibModsecurity Support
To setup Nginx with LibModsecurity, you need to compile Nginx with support for LibModsecurity.
Therefore, download the ModSecurity-nginx connector which provides a communication channel between Nginx and LibModsecurity by cloning its git repository.
cd /tmp/modsec
git clone https://github.com/SpiderLabs/ModSecurity-nginx.git
Next, download the latest staple version of Nginx from Nginx downloads. The latest version as of this writing is version nginx-1.19.10.tar.gz.
wget http://nginx.org/download/nginx-1.19.10.tar.gz
Extract the archive.
tar xzf nginx-1.19.10.tar.gz
Create a non-privileged Nginx system user and group.
useradd -r -M -s /sbin/nologin -d /usr/local/nginx nginx
Navigate to Nginx source directory and configure it.
cd nginx-1.19.10
./configure --user=nginx --group=nginx --with-pcre-jit --with-debug --with-http_ssl_module --with-http_realip_module --add-module=/tmp/modsec/ModSecurity-nginx
Configuration summary
Configuration summary
+ using system PCRE library
+ using system OpenSSL library
+ using system zlib library
nginx path prefix: "/usr/local/nginx"
nginx binary file: "/usr/local/nginx/sbin/nginx"
nginx modules path: "/usr/local/nginx/modules"
nginx configuration prefix: "/usr/local/nginx/conf"
nginx configuration file: "/usr/local/nginx/conf/nginx.conf"
nginx pid file: "/usr/local/nginx/logs/nginx.pid"
nginx error log file: "/usr/local/nginx/logs/error.log"
nginx http access log file: "/usr/local/nginx/logs/access.log"
nginx http client request body temporary files: "client_body_temp"
nginx http proxy temporary files: "proxy_temp"
nginx http fastcgi temporary files: "fastcgi_temp"
nginx http uwsgi temporary files: "uwsgi_temp"
nginx http scgi temporary files: "scgi_temp"
Compile and install Nginx on Rocky Linux 8.
make
make install
Confgure Nginx with ModSecurity on Rocky Linux 8
To begin with, copy the sample ModSecurity configuration file on the source directory to Nginx configuration directory.
cp /tmp/modsec/ModSecurity/modsecurity.conf-recommended /usr/local/nginx/conf/modsecurity.conf
Copy the unicode.mapping
file from ModSecurity source directory to Nginx configuration directory.
cp /tmp/modsec/ModSecurity/unicode.mapping /usr/local/nginx/conf/
Next, edit Nginx configuration file and make the changes as shown below.
Create a backup of the Nginx configuration file.
cp /usr/local/nginx/conf/nginx.conf{,.bak}
Open the configuration file for editing.
vim /usr/local/nginx/conf/nginx.conf
Configure Nginx such that your configuration may look like;
user nginx;
worker_processes 1;
pid /run/nginx.pid;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
sendfile on;
keepalive_timeout 65;
server {
listen 80;
server_name nginx.kifarunix-demo.com;
modsecurity on;
modsecurity_rules_file /usr/local/nginx/conf/modsecurity.conf;
access_log /var/log/nginx/access_kifarunix-demo.log;
error_log /var/log/nginx/error_kifarunix-demo.log;
location / {
root html;
index index.html index.htm;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}
}
Note that the line;
modsecurity on;
modsecurity_rules_file /usr/local/nginx/conf/modsecurity.conf;
Turns on Modsecurity and specifies the location of the Modsecurity rules.
Note that ModSecurity 3 can be turned on per directory basis.
Create Nginx log directory.
mkdir /var/log/nginx
Create Nginx Systemd Service
To be able to run Nginx as a service, create a systemd service as shown below;
cat > /etc/systemd/system/nginx.service << 'EOL'
[Unit]
Description=The nginx HTTP and reverse proxy server
After=network.target remote-fs.target nss-lookup.target
[Service]
Type=forking
PIDFile=/run/nginx.pid
ExecStartPre=/usr/bin/rm -f /run/nginx.pid
ExecStartPre=/usr/sbin/nginx -t
ExecStart=/usr/sbin/nginx
ExecReload=/bin/kill -s HUP $MAINPID
KillSignal=SIGQUIT
TimeoutStopSec=5
KillMode=mixed
PrivateTmp=true
[Install]
WantedBy=multi-user.target
EOL
Create a symbolic link of Nginx binary to /usr/sbin/
path.
ln -s /usr/local/nginx/sbin/nginx /usr/sbin/
Reload systemd configurations.
systemctl daemon-reload
Run Nginx configuration syntax error verification.
nginx -t
nginx: the configuration file /usr/local/nginx/conf/nginx.conf syntax is ok
nginx: configuration file /usr/local/nginx/conf/nginx.conf test is successful
If all is well, start and enable Nginx to run on system boot.
systemctl enable --now nginx
Check the status;
systemctl status nginx
● nginx.service - The nginx HTTP and reverse proxy server
Loaded: loaded (/etc/systemd/system/nginx.service; disabled; vendor preset: disabled)
Active: active (running) since Thu 2021-08-19 20:46:33 EAT; 11s ago
Process: 31623 ExecStart=/usr/sbin/nginx (code=exited, status=0/SUCCESS)
Process: 31621 ExecStartPre=/usr/sbin/nginx -t (code=exited, status=0/SUCCESS)
Process: 31620 ExecStartPre=/usr/bin/rm -f /run/nginx.pid (code=exited, status=0/SUCCESS)
Main PID: 31625 (nginx)
Tasks: 2 (limit: 4938)
Memory: 3.2M
CGroup: /system.slice/nginx.service
├─31625 nginx: master process /usr/sbin/nginx
└─31626 nginx: worker process
Aug 19 20:46:33 rocky8 systemd[1]: Starting The nginx HTTP and reverse proxy server...
Aug 19 20:46:33 rocky8 nginx[31621]: nginx: the configuration file /usr/local/nginx/conf/nginx.conf syntax is ok
Aug 19 20:46:33 rocky8 nginx[31621]: nginx: configuration file /usr/local/nginx/conf/nginx.conf test is successful
Aug 19 20:46:33 rocky8 systemd[1]: nginx.service: Failed to parse PID from file /run/nginx.pid: Invalid argument
Aug 19 20:46:33 rocky8 systemd[1]: Started The nginx HTTP and reverse proxy server.
Turn on ModSecurity Rule Engine
By default, ModSecurity is set on detection only mode where it only logs the requests based on the triggered rules without blocking anything. This can be changed by setting the value of SecRuleEngine
to On
.
sed -i 's/SecRuleEngine DetectionOnly/SecRuleEngine On/' /usr/local/nginx/conf/modsecurity.conf
You can also change default log directory for Modsecurity
sed -i 's#/var/log/modsec_audit.log#/var/log/nginx/modsec_audit.log#' /usr/local/nginx/conf/modsecurity.conf
Install OWASP ModSecurity Core Rule Set (CRS)
The OWASP ModSecurity Core Rule Set (CRS) is a set of generic attack detection rules for use with ModSecurity. It aims at protecting the web applications from a wide range of attacks, including the OWASP Top Ten, minimum of false alerts.
Clone the CRS from GitHub repository to /usr/local/nginx/conf/
as shown below;
git clone https://github.com/SpiderLabs/owasp-modsecurity-crs.git /usr/local/nginx/conf/owasp-crs
Next, rename crs-setup.conf.example
to crs-setup.conf
.
sudo cp /usr/local/nginx/conf/owasp-crs/crs-setup.conf{.example,}
Once the OWASP rules are in place, configure ModSecurity to use these rules. You therefore need to enter the following lines on the ModSecurity configuration file to tell it where to find the rules.
echo -e "Include owasp-crs/crs-setup.conf\nInclude owasp-crs/rules/*.conf" >> /usr/local/nginx/conf/modsecurity.conf
This command will append the lines below on /usr/local/nginx/conf/modsecurity.conf
.
Include owasp-crs/crs-setup.conf
Include owasp-crs/rules/*.conf
Verify Nginx configuration file again.
nginx -t
Next, restart Nginx if everything is Okay.
systemctl restart nginx
Testing ModSecurity on Nginx
Next, you can test the effectiveness of Modsecurity with OWASP rules, for example, using the command injection. Run the command below;
curl localhost/index.html?exec=/bin/bash
You should get 403 Forbidden
if the rules are file.
<html>
<head><title>403 Forbidden</title></head>
<body>
<center><h1>403 Forbidden</h1></center>
<hr><center>nginx/1.19.10</center>
</body>
</html>
Check the logs file.
tail -100 /var/log/nginx/modsec_audit.log
---iQWU4H4y---A--
[19/Aug/2021:20:48:26 +0300] 1629395306 127.0.0.1 37240 127.0.0.1 80
---iQWU4H4y---B--
GET /index.html?exec=/bin/bash HTTP/1.1
Host: localhost
User-Agent: curl/7.61.1
Accept: */*
---iQWU4H4y---D--
---iQWU4H4y---E--
<html>\x0d\x0a<head><title>403 Forbidden</title></head>\x0d\x0a<body>\x0d\x0a<center><h1>403 Forbidden</h1></center>\x0d\x0a<hr><center>nginx/1.19.10</center>\x0d\x0a</body>\x0d\x0a</html>\x0d\x0a
---iQWU4H4y---F--
HTTP/1.1 403
Server: nginx/1.19.10
Date: Thu, 19 Aug 2021 17:48:26 GMT
Content-Length: 154
Content-Type: text/html
Connection: keep-alive
---iQWU4H4y---H--
ModSecurity: Warning. Matched "Operator `PmFromFile' with parameter `unix-shell.data' against variable `ARGS:exec' (Value: `/bin/bash' ) [file "/usr/local/nginx/conf/owasp-crs/rules/REQUEST-932-APPLICATION-ATTACK-RCE.conf"]
[line "496"] [id "932160"] [rev ""] [msg "Remote Command Execution: Unix Shell Code Found"] [data "Matched Data: bin/bash found within ARGS:exec: /bin/bash"] [severity "2"] [ver "OWASP_CRS/3.2.0"] [maturity "0"] [accuracy "0"] [tag "application-multi"] [tag "language-shell"]
[tag "platform-unix"] [tag "attack-rce"] [tag "paranoia-level/1"] [tag "OWASP_CRS"] [tag "OWASP_CRS/WEB_ATTACK/COMMAND_INJECTION"] [tag "WASCTC/WASC-31"] [tag "OWASP_TOP_10/A1"] [tag "PCI/6.5.2"] [hostname "127.0.0.1"] [uri "/index.html"] [unique_id "1629395306"] [ref "o1,8v21,9t:urlDecodeUni,t:cmdLine,t:normalizePath,t:lowercase"]
ModSecurity: Access denied with code 403 (phase 2). Matched "Operator `Ge' with parameter `5' against variable `TX:ANOMALY_SCORE' (Value: `5' )
[file "/usr/local/nginx/conf/owasp-crs/rules/REQUEST-949-BLOCKING-EVALUATION.conf"] [line "80"] [id "949110"] [rev ""] [msg "Inbound Anomaly Score Exceeded (Total Score: 5)"]
[data ""] [severity "2"] [ver "OWASP_CRS/3.2.0"] [maturity "0"] [accuracy "0"] [tag "application-multi"] [tag "language-multi"] [tag "platform-multi"] [tag "attack-generic"] [hostname "127.0.0.1"] [uri "/index.html"] [unique_id "1629395306"] [ref ""]
---iQWU4H4y---I--
---iQWU4H4y---J--
---iQWU4H4y---Z--
Similarly, the logs are written to Nginx error logs file.
You can also create your own test rules. For example, append the line below on your /usr/local/nginx/conf/modsecurity.conf
.
SecRule ARGS "@streq test" "id:1,phase:1,deny,msg:\'Mytest Rule\'"
Save the configuration file.
You can also create your custom rules file and make sure it is included on the main ModSecurity configuration file.
Restart Nginx.
systemctl restart nginx
Test the rule.
curl localhost/index.html?a=test
<html>
<head><title>403 Forbidden</title></head>
<body>
<center><h1>403 Forbidden</h1></center>
<hr><center>nginx/1.19.10</center>
</body>
</html>
Check the log file;
tail /var/log/nginx/error_kifarunix-demo.log
2021/08/19 20:48:26 [error] 31687#0: *1 [client 127.0.0.1] ModSecurity: Access denied with code 403 (phase 2). Matched "Operator `Ge' with parameter `5' against variable `TX:ANOMALY_SCORE' (Value: `5' ) [file "/usr/local/nginx/conf/owasp-crs/rules/REQUEST-949-BLOCKING-EVALUATION.conf"] [line "80"] [id "949110"] [rev ""] [msg "Inbound Anomaly Score Exceeded (Total Score: 5)"] [data ""] [severity "2"] [ver "OWASP_CRS/3.2.0"] [maturity "0"] [accuracy "0"] [tag "application-multi"] [tag "language-multi"] [tag "platform-multi"] [tag "attack-generic"] [hostname "127.0.0.1"] [uri "/index.html"] [unique_id "1629395306"] [ref ""], client: 127.0.0.1, server: nginx.kifarunix-demo.com, request: "GET /index.html?exec=/bin/bash HTTP/1.1", host: "localhost"
2021/08/19 20:54:03 [error] 31765#0: *1 [client 127.0.0.1] ModSecurity: Access denied with code 403 (phase 1). Matched "Operator `StrEq' with parameter `test' against variable `ARGS:a' (Value: `test' ) [file "/usr/local/nginx/conf/modsecurity.conf"] [line "261"] [id "1"] [rev ""] [msg "\'Mytest Rule\'"] [data ""] [severity "0"] [ver ""] [maturity "0"] [accuracy "0"] [hostname "127.0.0.1"] [uri "/index.html"] [unique_id "1629395643"] [ref "v18,4"], client: 127.0.0.1, server: nginx.kifarunix-demo.com, request: "GET /index.html?a=test HTTP/1.1", host: "localhost"
The ModSecurity is now running and protecting your web server.
You can check our other guides on ModSecurity by following the links below;
Process and Visualize ModSecurity Logs on ELK Stack
Create Kibana Visualization Dashboards for ModSecurity Logs
Restrict Access to WordPress Login Page to Specific IPs with libModSecurity