Setup NGINX on Alpine Docker with ModSecurity & Redis Cache

by | Jun 17, 2019

Set up an Alpine Docker Container

Why use Alpine Linux for your Docker container? Alpine makes a great docker container, because it is so small and optimized to be run in RAM. It is also fast. Furthermore, nearly every official Docker image has a tag for Alpine. So basically, it is optimized for size, speed and is easy.

Install Alpine Linux using Dockerfile

In the event that you’re not familiar with the concept, every Docker image is composed of several layers, one for each command in a Dockerfile (and those in its base image).
Include the following in your Dockerfile:

FROM alpine:latest

RUN rm -rf /var/cache/apk/* \
    && rm -rf /tmp/*


The FROM instruction initializes a new build stage and sets the Base Image for subsequent instructions. As such, a valid Dockerfile must start with a FROM instruction. The image can be any valid image – it is especially easy to start by pulling an image from the Public Repositories.


/var/cache is intended for cached data from applications. Such data is locally generated as a result of time-consuming I/O or calculation. The application must be able to regenerate or restore the data. Unlike /var/spool , the cached files can be deleted without data loss. Within the context of our Docker setup, over time, newer packages will replace older ones; the cache directory will contain all older versions of packages. For our purposes, we delete this cache on reboot.


Setup NGINX on Docker with ModSecurity and Redis

ModSecurity is a toolkit for real-time web application monitoring, logging, and access control. If you are concerned about security/firewalls then this is recommended. Read here: and If you want ModSecurity then you will need to compile it into NGINX, meaning a few extra instructions to get this going. This means you’ll have to clone the ModSecurity GitHub repository and build the library from its source code. The following are the required build tools, mandatory dependencies, and most of the optional dependencies on Debian: bison, flex, make, automake, gcc, pkg-config, libtool, doxygen, git, curl, zlib1g-dev, libxml2-dev, libpcre3-dev, build-essential, libyajl-dev, yajl-tools, liblmdb-dev, rdmacm-utils, libgeoip-dev, libcurl4-openssl-dev, liblua5.2-dev, libfuzzy-dev, openssl and libssl-dev. Equivalents need to be identified for Alpine Linux. Redis is a caching system and can be added to NGINX for performance. The details of Redis and ModSecurity are outside the scope of this article.
NGINX is written in C so I include the C libraries and compiler in order to be able to compile it with ModSecurity. The following libraries are required for this setup: gcc # For nginx, modsecurity. GNU Compiler Collection, a free, open-source compiler system. Mandatory in order to compile nginx libc-dev # For nginx .Meta package to pull in correct libc (standard C library). See linux-headers # See zlib-dev # For nginx, modsecurity openrc # For nginx. Alpine Linux uses OpenRC for its init system. autoconf # For nginx automake # Fornginx, modsecurity git # For modsecurity, nginx libressl-dev # Fornginx, modsecurity geoip-dev # For modsecurity. ID user IP geolocation and related characteristics of internet users lmdb-dev # For modsecurity. Lightning Memory-Mapped Database (LMDB) is a software library that provides a high-performance embedded transactional database in the form of a key-value store pcre-dev # For nginx, modsecurity libtool # For modsecurity. Compile, link, install, execute libxml2-dev # For modsecurity. Libxml2 is the XML C parser and toolkit yajl-dev # For modsecurity. YAJL is a small event-driven (SAX-style) JSON parser written in ANSI C pkgconf # For modsecurity. wget # For nginx zlib-dev # For nginx g++ # For modsecurity. g++ and make on Alpine replace “build-essential” on Ubuntu. To compile c++ libcurl # For modsecurity make # For nginx, modsecurity redis # For nginx. Webserver caching


Here is the Dockerfile to build an NGINX dynamic module. Other required files are, my_webapp_nginx.conf, nginx.conf



FROM alpine:latest

RUN rm -rf /var/cache/apk/* \
    && rm -rf /tmp/*

RUN cat /etc/apk/repositories

RUN apk add --update gcc \
                     libc-dev \
                     linux-headers \
                     zlib-dev \
                     openrc \
                     autoconf \
                     automake \
                     git \
                     libressl-dev \
                     geoip-dev \
                     lmdb-dev \
                     pcre-dev \
                     libtool \
                     libxml2-dev \
                     yajl-dev \
                     pkgconf \
                     wget \
                     zlib-dev \
                     g++ \
                     libcurl \
                     make \

# Mod_Security Setup
RUN git clone --depth 1 -b v3/master --single-branch
WORKDIR /ModSecurity
RUN git submodule init \
    && git submodule update \
    && ./ \
    && ./configure \
    && make \
    && make install \
    && make clean

RUN git clone --depth 1 \
    && wget \
    && tar zxvf nginx-1.17.0.tar.gz

WORKDIR /nginx-1.17.0
RUN  ./configure --with-compat --with-http_ssl_module \
    && make \
    && make install \
    && make clean \
    && mkdir /etc/nginx \
    && mkdir /etc/nginx/modules/ \
    && mkdir /usr/local/nginx/modules/ \
    && mkdir /var/log/nginx

RUN ./configure --with-compat --with-http_ssl_module --add-dynamic-module=../ModSecurity-nginx \
    && make \
    && make modules \
    && ls -la /etc/nginx \
    && cp /nginx-1.17.0/objs/ /usr/local/nginx/modules/ \
    && make clean

RUN mkdir /etc/nginx/modsec \
    && echo "load_module modules/;" >> /etc/nginx/nginx.conf \
    && cp /usr/local/nginx/conf/mime.types /etc/nginx/

ADD nginx_config/modsecurity.conf /etc/nginx/modsec/modsecurity.conf
ADD nginx_config/modsec.main.conf  /etc/nginx/modsec/main.conf

# Nginx Config
ADD nginx_config/my_webapp_nginx.conf /etc/nginx/conf.d/
ADD nginx_config/nginx.conf /etc/nginx/nginx.conf

# Install productlisting_framework Libraries
RUN mkdir /run/nginx
RUN mkdir /my_webapp

ADD /my_webapp/

RUN chmod 777 /my_webapp/

RUN adduser -D nginx
ENTRYPOINT /my_webapp/

"echo ""starting""
/usr/bin/redis-server &
/usr/local/nginx/sbin/nginx -c /etc/nginx/nginx.conf &



"# configuration of the server

server {
    modsecurity off;
    modsecurity_rules_file /etc/nginx/modsec/main.conf;

    # the port your site will be served on
    listen      8000;
    port_in_redirect off;

    # the domain name it will serve for
    charset     utf-8;

    # max upload size
    client_max_body_size 75M;





user nginx;

# Set number of worker processes automatically based on number of CPU cores.
worker_processes auto;

# Enables the use of JIT for regular expressions to speed-up their processing.
pcre_jit on;
load_module modules/;

# Configures default error logger.
error_log /var/log/nginx/error.log warn;

# Includes files with directives to load dynamic modules.
include /etc/nginx/modules/*.conf;

events {
	# The maximum number of simultaneous connections that can be opened by
	# a worker process.
	worker_connections 1024;

http {

    perl_set $uri_lowercase 'sub {
      my $r = shift;
      my $uri = $r->uri;
      $uri = lc($uri);
      return $uri;

	# Includes mapping of file name extensions to MIME types of responses
	# and defines the default type.
	include /etc/nginx/mime.types;
	default_type application/octet-stream;

	# Name servers used to resolve names of upstream servers into addresses.
	# It's also needed when using tcpsocket and udpsocket in Lua modules.

	# Don't tell nginx version to clients.
	server_tokens off;

	# Specifies the maximum accepted body size of a client request, as
	# indicated by the request header Content-Length. If the stated content
	# length is greater than this size, then the client receives the HTTP
	# error code 413. Set to 0 to disable.
	client_max_body_size 1m;

	# Timeout for keep-alive connections. Server will close connections after
	# this time.
	keepalive_timeout 65;

	# Sendfile copies data between one FD and other from within the kernel,
	# which is more efficient than read() + write().
	sendfile on;

	# Don't buffer data-sends (disable Nagle algorithm).
	# Good for sending frequent small bursts of data in real time.
	tcp_nodelay on;

	# Causes nginx to attempt to send its HTTP response head in one packet,
	# instead of using partial frames.
	#tcp_nopush on;

	# Path of the file with Diffie-Hellman parameters for EDH ciphers.
	#ssl_dhparam /etc/ssl/nginx/dh2048.pem;

	# Specifies that our cipher suits should be preferred over client ciphers.
	ssl_prefer_server_ciphers on;

	# Enables a shared SSL cache with size that can hold around 8000 sessions.
	ssl_session_cache shared:SSL:2m;

	# Enable gzipping of responses.
	#gzip on;

	# Set the Vary HTTP header as defined in the RFC 2616.
	gzip_vary on;

	# Enable checking the existence of precompressed files.
	#gzip_static on;

	# Specifies the main log format.
	log_format main '$remote_addr - $remote_user [$time_local] "$request" '
			'$status $body_bytes_sent "$http_referer" '
			'"$http_user_agent" "$http_x_forwarded_for"';

	# Sets the path, format, and configuration for a buffered log write.
	access_log /var/log/nginx/access.log main;

	# Includes virtual hosts configs.
	include /etc/nginx/conf.d/*.conf;