Welcome to our guide on how to install ModSecurity 3 with Apache in a Docker container. Libmodsecurity (Modsecurity v3), is an open source, cross platform web application firewall (WAF) developed by Trustwave’s SpiderLabs. It is a complete rewrite of ModSecurity v2 and it provides a robust event-based programming language which protects web applications against a wide range of attacks such as SQL injection, Cross-site Scripting (XSS), Local File Include, Remote File Include e.tc. It also allows for HTTP traffic monitoring, logging and real-time analysis.
Table of Contents
Installing ModSecurity 3 with Apache in a Docker Container
In order to install ModSecurity 3 in a Docker container, we will create our own Docker image based on our previous tutorials on how to install ModSecurity 3, links given below;
Install LibModsecurity with Apache on Debian 10
Install LibModsecurity with Apache on Ubuntu 20.04
Configure LibModsecurity with Apache on CentOS 8
Configure LibModsecurity with Nginx on CentOS 8
Install Docker
In your respective base OS, you need to have Docker installed. In our guide, we are using Ubuntu 20.04 server to host the Docker containers. As such, run the command below to install Docker on Ubuntu 20.04.
apt install apt-transport-https ca-certificates curl gnupg-agent software-properties-common
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
echo "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -sc) stable" > /etc/apt/sources.list.d/docker-ce.list
apt update
Install Docker CE and other tools including containerd.io
, An open and reliable container runtime.
apt install docker-ce docker-ce-cli containerd.io
Create Dockerfile
Since we will be building our Modsecurity container based on the Modsecurity installation commands, you need to create a Dockerfile. Dockerfile is a text document that contains all the commands a user could call on the command line to assemble an image.
vim Dockerfile
Paste the content below into the Dockerfile.
We will be using an Ubuntu image to create our Modsecurity container, hence, install commands from the guide, Install LibModsecurity with Apache on Ubuntu 20.04, are used.
# Running Modsecurity in a Docker container;
FROM ubuntu:latest
ARG DEBIAN_FRONTEND=noninteractive
# Run system update/upgrade
RUN apt update -y && apt upgrade -y
# Install Required Build Tools and Dependencies
RUN apt install -y g++ flex bison curl apache2-dev \
doxygen libyajl-dev ssdeep liblua5.2-dev \
libgeoip-dev libtool dh-autoreconf \
libcurl4-gnutls-dev libxml2 libpcre++-dev \
libxml2-dev git wget tar apache2
# Download LibModsecurity Source Code
RUN wget https://github.com/SpiderLabs/ModSecurity/releases/download/v3.0.4/modsecurity-v3.0.4.tar.gz
# Extract the ModSecurity source code.
RUN tar xzf modsecurity-v3.0.4.tar.gz && rm -rf modsecurity-v3.0.4.tar.gz
# Compile and Install LibModsecurity
RUN cd modsecurity-v3.0.4 && \
./build.sh && ./configure && \
make && make install
# Install ModSecurity-Apache Connector
RUN cd ~ && git clone https://github.com/SpiderLabs/ModSecurity-apache
RUN cd ~/ModSecurity-apache && \
./autogen.sh && \
./configure --with-libmodsecurity=/usr/local/modsecurity/ && \
make && \
make install
# Load the Apache ModSecurity Connector Module
RUN echo "LoadModule security3_module /usr/lib/apache2/modules/mod_security3.so" >> /etc/apache2/apache2.conf
# Configure ModSecurity
RUN mkdir /etc/apache2/modsecurity.d && \
cp modsecurity-v3.0.4/modsecurity.conf-recommended /etc/apache2/modsecurity.d/modsecurity.conf && \
cp modsecurity-v3.0.4/unicode.mapping /etc/apache2/modsecurity.d/ && \
sed -i 's/SecRuleEngine DetectionOnly/SecRuleEngine On/' /etc/apache2/modsecurity.d/modsecurity.conf
ADD modsec_rules.conf /etc/apache2/modsecurity.d/
# Install OWASP ModSecurity Core Rule Set (CRS) on Ubuntu
RUN git clone https://github.com/SpiderLabs/owasp-modsecurity-crs.git /etc/apache2/modsecurity.d/owasp-crs && \
cp /etc/apache2/modsecurity.d/owasp-crs/crs-setup.conf.example /etc/apache2/modsecurity.d/owasp-crs/crs-setup.conf
# Activate ModSecurity
RUN mv /etc/apache2/sites-available/000-default.conf /etc/apache2/sites-available/000-default.conf.old
ADD 000-default.conf /etc/apache2/sites-available/
EXPOSE 80
CMD apachectl -D FOREGROUND
Define the base image for the Docker container. This can be done using the FROM
instruction command. The image will be pulled from the Public Repositories.
Running ModSecurity 3 with Apache in a Docker Container
Build ModSecurity 3 with Apache in Docker Image
Once you have setup your Dockerfile, you can now build an image out of it.
Ensure docker service is running;
systemctl status docker
● docker.service - Docker Application Container Engine
Loaded: loaded (/lib/systemd/system/docker.service; enabled; vendor preset: enabled)
Active: active (running) since Thu 2021-05-06 05:19:12 UTC; 1s ago
TriggeredBy: ● docker.socket
Docs: https://docs.docker.com
Main PID: 8542 (dockerd)
Tasks: 8
Memory: 40.5M
CGroup: /system.slice/docker.service
└─8542 /usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock
May 06 05:19:08 kifarunix.com dockerd[8542]: time="2021-05-06T05:19:08.455064769Z" level=warning msg="Your kernel does not support CPU realtime scheduler"
May 06 05:19:08 kifarunix.com dockerd[8542]: time="2021-05-06T05:19:08.455517885Z" level=warning msg="Your kernel does not support cgroup blkio weight"
May 06 05:19:08 kifarunix.com dockerd[8542]: time="2021-05-06T05:19:08.455936526Z" level=warning msg="Your kernel does not support cgroup blkio weight_device"
May 06 05:19:08 kifarunix.com dockerd[8542]: time="2021-05-06T05:19:08.456858801Z" level=info msg="Loading containers: start."
May 06 05:19:09 kifarunix.com dockerd[8542]: time="2021-05-06T05:19:09.970095995Z" level=info msg="Default bridge (docker0) is assigned with an IP address 172.17.0.0/16. D>
May 06 05:19:10 kifarunix.com dockerd[8542]: time="2021-05-06T05:19:10.589619449Z" level=info msg="Loading containers: done."
May 06 05:19:11 kifarunix.com dockerd[8542]: time="2021-05-06T05:19:11.360156761Z" level=info msg="Docker daemon" commit=8728dd2 graphdriver(s)=overlay2 version=20.10.6
May 06 05:19:11 kifarunix.com dockerd[8542]: time="2021-05-06T05:19:11.362448498Z" level=info msg="Daemon has completed initialization"
May 06 05:19:12 kifarunix.com systemd[1]: Started Docker Application Container Engine.
May 06 05:19:12 kifarunix.com dockerd[8542]: time="2021-05-06T05:19:12.141070774Z" level=info msg="API listen on /run/docker.sock"
If not, then start Docker service using;
systemctl start docker
Before you can build the image, there are files that needs to be copied from the host as per out Dockerfile. These are the modsecuriry rules and Apache site configuration file.
Thus create these files;
cat > modsec_rules.conf << 'EOL'
Include "/etc/apache2/modsecurity.d/modsecurity.conf"
Include "/etc/apache2/modsecurity.d/owasp-crs/crs-setup.conf"
Include "/etc/apache2/modsecurity.d/owasp-crs/rules/*.conf"
EOL
cat > 000-default.conf << 'EOL'
<VirtualHost *:80>
modsecurity on
modsecurity_rules_file /etc/apache2/modsecurity.d/modsec_rules.conf
ServerAdmin webmaster@localhost
DocumentRoot /var/www/html
ErrorLog ${APACHE_LOG_DIR}/error.log
CustomLog ${APACHE_LOG_DIR}/access.log combined
</VirtualHost>
EOL
You can now proceed to build the docker image.
To build a Docker image using a Dockerfile, simply use the docker build <path to Dockerfile>
.
docker build .
I used the dot (.)
to signify current location of my Dockerfile.
If it is not in the current working directory, then use the -f
option to specify the path:
docker build -f /path/to/a/Dockerfile .
Sample output of the build command;
...
Step 14/18 : RUN mv /etc/apache2/sites-available/000-default.conf /etc/apache2/sites-available/000-default.conf.old
---> Running in ac6525e24f7d
Removing intermediate container ac6525e24f7d
---> ec6d4457b765
Step 15/18 : ADD 000-default.conf /etc/apache2/sites-available/
---> 7c4201ccfd92
Step 16/18 : VOLUME /var/log/apache2
---> Running in 9919d9cf570d
Removing intermediate container 9919d9cf570d
---> aa45b6406512
Step 17/18 : EXPOSE 80
---> Running in 368fc959c99d
Removing intermediate container 368fc959c99d
---> 210d4c2df36e
Step 18/18 : CMD apachectl -D FOREGROUND
---> Running in dfea7e1352ee
Removing intermediate container dfea7e1352ee
---> 229edcf62162
Successfully built 229edcf62162
You have successfully build Modsecurity 3 with Apache on Docker Image.
Listing the currently available images;
docker image ls
REPOSITORY TAG IMAGE ID CREATED SIZE
<none> <none> 229edcf62162 2 hours ago 2.48GB
...
Our ModSecurity 3 docker image ID is, 229edcf62162
.
Running ModSecurity 3 with Apache Docker Container
You can now create a ModSecurity Docker container based on the image created above using the docker run
command;
docker run [OPTIONS] IMAGE [COMMAND] [ARG...]
For example, we can launch our Apache Server with ModSecurity container by running the command below;
docker run --name modsec3-apache -dp 80:80 229edcf62162
The above command starts an Apache with ModSecurity container called modsec3-apache in the background (-b) based on the image created. It also exposes the container port 80 to port 80 on the host server.
Listing the containers;
docker container ls
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
ae4017bdaf23 229edcf62162 "/bin/sh -c 'apachec…" About a minute ago Up About a minute 0.0.0.0:80->80/tcp, :::80->80/tcp modsec3-apache
You can also list running containers using the docker ps
command.
And that is it. Your Apache with ModSecurity running as a docker container is setup.
You can test whether ModSecurity is now protecting Apache running in a docker container as follows.
Open port 80/tcp on firewall on the host;
ufw allow 80/tcp
Now, on the host, run the command below;
ss -altnp | grep :80
LISTEN 0 4096 0.0.0.0:80 0.0.0.0:* users:(("docker-proxy",pid=29372,fd=4))
LISTEN 0 4096 [::]:80 [::]:* users:(("docker-proxy",pid=29377,fd=4))
So, on the host, we can access our Apache/Modsecurity docker container using any address;
hence, to test the effectiveness of the ModSecurity in the container;
curl localhost?doc=/bin/ls
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>403 Forbidden</title>
</head><body>
<h1>Forbidden</h1>
<p>You don't have permission to access this resource.</p>
<hr>
<address>Apache/2.4.41 (Ubuntu) Server at localhost Port 80</address>
</body></html>
You can login to the container and check the logs;
docker exec -it modsec3-apache /bin/bash
tail /var/log/apache2/error.log
[Thu May 06 17:12:15.526844 2021] [:notice] [pid 16:tid 139891014069312] ModSecurity: ModSecurity-Apache v0.1.1-beta configured.
AH00558: apache2: Could not reliably determine the server's fully qualified domain name, using 172.17.0.2. Set the 'ServerName' directive globally to suppress this message
[Thu May 06 17:12:15.592630 2021] [mpm_event:notice] [pid 16:tid 139891014069312] AH00489: Apache/2.4.41 (Ubuntu) configured -- resuming normal operations
[Thu May 06 17:12:15.592655 2021] [core:notice] [pid 16:tid 139891014069312] AH00094: Command line: '/usr/sbin/apache2 -D FOREGROUND'
[Thu May 06 17:32:37.690656 2021] [:error] [pid 17:tid 139890944558848] [client 172.17.0.1:60688] ModSecurity: Warning. Matched "Operator `PmFromFile' with parameter `unix-shell.data' against variable `ARGS:doc' (Value: `/bin/ls' ) [file "/etc/apache2/modsecurity.d/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/ls found within ARGS:doc: /bin/ls"] [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 "172.17.0.2"] [uri "/"] [unique_id "162032235753.217056"] [ref "o1,6v10,7t:urlDecodeUni,t:cmdLine,t:normalizePath,t:lowercase"]
[Thu May 06 17:35:30.014353 2021] [:error] [pid 17:tid 139890927757056] [client 172.17.0.1:60692] ModSecurity: Warning. Matched "Operator `PmFromFile' with parameter `unix-shell.data' against variable `ARGS:doc' (Value: `/bin/ls' ) [file "/etc/apache2/modsecurity.d/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/ls found within ARGS:doc: /bin/ls"] [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 "172.17.0.2"] [uri "/"] [unique_id "16203225307.958576"] [ref "o1,6v10,7t:urlDecodeUni,t:cmdLine,t:normalizePath,t:lowercase"]
...
You can also check how to view the logs using the docker logs
command.
You can as well set the container to store the logs on the host using the docker run
--volume/-v
option.
docker run --name modsec3-apache -v /var/log/apache2:/var/log/apache2 -dp 80:80 229edcf62162
The logs should now be written to the /var/log/apache2
on the host.
tail -f /var/log/apache2/error.log
[Thu May 06 17:55:35.007467 2021] [:notice] [pid 16:tid 139799054105664] ModSecurity: ModSecurity-Apache v0.1.1-beta configured.
AH00558: apache2: Could not reliably determine the server's fully qualified domain name, using 172.17.0.2. Set the 'ServerName' directive globally to suppress this message
[Thu May 06 17:55:35.066755 2021] [mpm_event:notice] [pid 16:tid 139799054105664] AH00489: Apache/2.4.41 (Ubuntu) configured -- resuming normal operations
[Thu May 06 17:55:35.066783 2021] [core:notice] [pid 16:tid 139799054105664] AH00094: Command line: '/usr/sbin/apache2 -D FOREGROUND'
[Thu May 06 17:55:56.640042 2021] [:error] [pid 18:tid 139798870210304] [client 172.17.0.1:60700] ModSecurity: Warning. Matched "Operator `PmFromFile' with parameter `unix-shell.data' against variable `ARGS:doc' (Value: `/bin/ls' ) [file "/etc/apache2/modsecurity.d/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/ls found within ARGS:doc: /bin/ls"] [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 "172.17.0.2"] [uri "/"] [unique_id "162032375634.996239"] [ref "o1,6v10,7t:urlDecodeUni,t:cmdLine,t:normalizePath,t:lowercase"]