Install ModSecurity with Apache in a Docker Container
ModSecurity is a free, open-source, and most popular web application firewall (WAF) that protects your web application against a wide range of Layer 7 attacks. It was designed for Apache web server monitoring, logging, and filtering requests. It comes with a Core Rule Set that detects and stops several attacks including, SQL injection, cross-site scripting, Trojans, bad user agents, session hijacking, and more.
In this tutorial, I will show you how to install ModSecurity 3 with Apache inside a Docker container.
Requirements
- A server running Ubuntu 20.04.
- A root password is configured on your server.
Getting Started
First, it is recommended to update your system to the latest version. You can update it with the following command:
apt-get update -y
Once your system is up-to-date, you will need to install some dependencies to your system. You can install all of them with the following command:
apt-get install apt-transport-https ca-certificates curl gnupg2 software-properties-common -y
Once all the dependencies are installed, you can proceed to the next step.
Install Docker
Next, you will need to install the Docker CE to your system. By default, the latest version of Docker is not included in the Ubuntu default repository. So you will need to add the Docker official repository to the APT.
First, download and add the Docker GPG key with the following command:
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | apt-key add -
Next, add the Docker CE repository to the APT source list with the following command:
echo "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -sc) stable" > /etc/apt/sources.list.d/docker-ce.list
Once the repository is added, update the repository with the following command:
apt-get update -y
Once the repository is updated, install the latest version of Docker CE with the following command:
apt-get install docker-ce -y
After installing Docker CE, verify the installed version of Docker CE with the following command:
docker --version
You should get the following output:
Docker version 20.10.6, build 370c289
You can also check the status of the Docker service with the following command:
systemctl status docker
You should get the following output:
? docker.service - Docker Application Container Engine Loaded: loaded (/lib/systemd/system/docker.service; enabled; vendor preset: enabled) Active: active (running) since Sun 2021-05-16 06:49:29 UTC; 38s ago TriggeredBy: ? docker.socket Docs: https://docs.docker.com Main PID: 8964 (dockerd) Tasks: 8 Memory: 40.6M CGroup: /system.slice/docker.service ??8964 /usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock May 16 06:49:29 ubuntu2004 dockerd[8964]: time="2021-05-16T06:49:29.365433228Z" level=warning msg="Your kernel does not support swap memory li> May 16 06:49:29 ubuntu2004 dockerd[8964]: time="2021-05-16T06:49:29.365916961Z" level=warning msg="Your kernel does not support cgroup blkio w> May 16 06:49:29 ubuntu2004 dockerd[8964]: time="2021-05-16T06:49:29.366112111Z" level=warning msg="Your kernel does not support cgroup blkio w> May 16 06:49:29 ubuntu2004 dockerd[8964]: time="2021-05-16T06:49:29.366653374Z" level=info msg="Loading containers: start." May 16 06:49:29 ubuntu2004 dockerd[8964]: time="2021-05-16T06:49:29.498790388Z" level=info msg="Default bridge (docker0) is assigned with an I> May 16 06:49:29 ubuntu2004 dockerd[8964]: time="2021-05-16T06:49:29.576691602Z" level=info msg="Loading containers: done." May 16 06:49:29 ubuntu2004 dockerd[8964]: time="2021-05-16T06:49:29.610542206Z" level=info msg="Docker daemon" commit=8728dd2 graphdriver(s)=o> May 16 06:49:29 ubuntu2004 dockerd[8964]: time="2021-05-16T06:49:29.611668583Z" level=info msg="Daemon has completed initialization" May 16 06:49:29 ubuntu2004 systemd[1]: Started Docker Application Container Engine. May 16 06:49:29 ubuntu2004 dockerd[8964]: time="2021-05-16T06:49:29.690496888Z" level=info msg="API listen on /run/docker.sock" lines 1-21/21 (END)
Once you are finished, you can proceed to the next step.
Create a Dockerfile for ModSecurity
Next, you will need to create a Dockerfile to install the ModSecurity inside the Ubuntu container.
First, change the directory to the /opt and create a modsec_rules.conf file with the following command:
cd /opt
nano modsec_rules.conf
Add the following lines:
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"
Save and close the file then create another file with the following command:
nano 000-default.conf
Add the following lines:
<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>
Save and close the file when you are finished. Dockerfile will copy the above files to the Docker container during the build process.
Finally, create a Dockerfile with the following command:
nano Dockerfile
Add the following lines:
# Install Modsecurity in a Docker container; FROM ubuntu:latest ARG DEBIAN_FRONTEND=noninteractive # update/upgrade your system RUN apt-get update -y # Install Required Dependencies RUN apt-get 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 RUN wget https://github.com/SpiderLabs/ModSecurity/releases/download/v3.0.4/modsecurity-v3.0.4.tar.gz # Extract the Downloaded File 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
Save and close the file when you are finished.
The above file will download the Ubuntu image, install all the dependencies, download the ModSecurity, compile it, and configure Apache to work with ModSecurity.
At this point, Dockerfile is ready. You can now proceed to the next step.
Build the Apache ModSecurity Image and Start the Container
Now, change the directory to the /opt and build the Docker image for Apache ModSecurity with the following command:
cd /opt
docker build .
Once the build process has been completed, you should get the following output:
Step 13/17 : 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 ---> Running in 00dfa2a5cd23 Cloning into '/etc/apache2/modsecurity.d/owasp-crs'... Removing intermediate container 00dfa2a5cd23 ---> b38c1d874d2f Step 14/17 : RUN mv /etc/apache2/sites-available/000-default.conf /etc/apache2/sites-available/000-default.conf.old ---> Running in 12c9e6d2c559 Removing intermediate container 12c9e6d2c559 ---> 899e26019297 Step 15/17 : ADD 000-default.conf /etc/apache2/sites-available/ ---> eb11751afd6c Step 16/17 : EXPOSE 80 ---> Running in 2f4ba47e2b66 Removing intermediate container 2f4ba47e2b66 ---> dd59b0ac7c7c Step 17/17 : CMD apachectl -D FOREGROUND ---> Running in 98b8cc77df0f Removing intermediate container 98b8cc77df0f ---> f603dbc38018 Successfully built f603dbc38018
You can now list all docker images using the following command:
docker images
You should get the following output:
REPOSITORY TAG IMAGE ID CREATED SIZEf603dbc38018 32 seconds ago 2.48GB ubuntu latest 7e0aa2d69a15 3 weeks ago 72.7MB
Now, pick the first image id from the above output and start the Apache ModSecurity container with the following command:
docker run --name modsec-apache -ditp 80:80 f603dbc38018
You should get the following output:
40eb0e77e61635c3cee2bfaffbd9489bc7d20aa3e1befb52749de079aaadb528
You can now verify the running container with the following command:
docker ps
You should get the following output:
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 40eb0e77e616 f603dbc38018 "/bin/sh -c 'apachec…" 17 seconds ago Up 15 seconds 0.0.0.0:80->80/tcp, :::80->80/tcp modsec-apache
As you can see, the ModSecurity container is started and listening on port 80.
Verify ModSecurity
At this point, the ModSecurity container is running. Now, it's time to test whether the ModSecurity rules block the malicious requests or not.
To do so, open your terminal and run the following command:
curl localhost?doc=/bin/ls
You should see the following output:
<!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 should see the "403 Forbidden" error. Because ModSecurity has blocked the above request.
You can also check the Apache log for more information.
To do so, first, connect to the container with the following command:
docker exec -it modsec-apache /bin/bash
Once you are connected, you should get the following shell:
root@40eb0e77e616:/#
Now, check the Apache log with the following command:
tail -f /var/log/apache2/error.log
You should see that ModSecurity has blocked the malicious request:
[Sun May 16 07:24:54.456327 2021] [mpm_event:notice] [pid 15:tid 140204464299072] AH00489: Apache/2.4.41 (Ubuntu) configured -- resuming normal operations [Sun May 16 07:24:54.456352 2021] [core:notice] [pid 15:tid 140204464299072] AH00094: Command line: '/usr/sbin/apache2 -D FOREGROUND' [Sun May 16 07:25:36.680515 2021] [:error] [pid 16:tid 140204216108800] [client 172.17.0.1:45298] 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 "162114993662.860969"] [ref "o1,6v10,7t:urlDecodeUni,t:cmdLine,t:normalizePath,t:lowercase"]
Conclusion
Congratulations! you have successfully installed ModSecurity inside Docker container. I hope you have now enough knowledge to implement this solution in the Docker environment. Feel free to ask me if you have any questions.