From 09f09a34328079f04d372ff5fb7faf9e180cc7e4 Mon Sep 17 00:00:00 2001 From: JingLu5 Date: Fri, 24 Aug 2018 10:55:14 +0800 Subject: Modsecurity as a service JIRA: CLOVER-68 1. Add Dockerfile and related files to build clover's modsecurity Docekr container 2. Add mainfest to install the Modsecurity in kubernetes cluster Change-Id: Ia92926e730c04720f931999d7ec30565ce9e54be Signed-off-by: JingLu5 --- samples/services/modsecurity/docker/.htaccess | 3 + samples/services/modsecurity/docker/Dockerfile | 37 ++++ samples/services/modsecurity/docker/apache2.conf | 227 +++++++++++++++++++++ samples/services/modsecurity/docker/build.sh | 16 ++ .../modsecurity/docker/docker-entrypoint.sh | 15 ++ samples/services/modsecurity/docker/proxy.conf | 3 + .../services/modsecurity/yaml/manifest.template | 38 ++++ .../modsecurity/yaml/modsecurity-deployment.yaml | 22 ++ .../modsecurity/yaml/modsecurity-service.yaml | 13 ++ samples/services/modsecurity/yaml/render_yaml.py | 60 ++++++ 10 files changed, 434 insertions(+) create mode 100644 samples/services/modsecurity/docker/.htaccess create mode 100644 samples/services/modsecurity/docker/Dockerfile create mode 100644 samples/services/modsecurity/docker/apache2.conf create mode 100644 samples/services/modsecurity/docker/build.sh create mode 100644 samples/services/modsecurity/docker/docker-entrypoint.sh create mode 100644 samples/services/modsecurity/docker/proxy.conf create mode 100644 samples/services/modsecurity/yaml/manifest.template create mode 100644 samples/services/modsecurity/yaml/modsecurity-deployment.yaml create mode 100644 samples/services/modsecurity/yaml/modsecurity-service.yaml create mode 100644 samples/services/modsecurity/yaml/render_yaml.py diff --git a/samples/services/modsecurity/docker/.htaccess b/samples/services/modsecurity/docker/.htaccess new file mode 100644 index 0000000..a2b059c --- /dev/null +++ b/samples/services/modsecurity/docker/.htaccess @@ -0,0 +1,3 @@ +RewriteEngine on +RewriteCond %{REQUEST_URI} !^/index.html$ +RewriteRule . /index.html [L] \ No newline at end of file diff --git a/samples/services/modsecurity/docker/Dockerfile b/samples/services/modsecurity/docker/Dockerfile new file mode 100644 index 0000000..5a01f21 --- /dev/null +++ b/samples/services/modsecurity/docker/Dockerfile @@ -0,0 +1,37 @@ +FROM owasp/modsecurity:v2-ubuntu-apache +MAINTAINER Jing Lu lvjing5@huawei.com + +ARG COMMIT=v3.1/dev +ARG REPO=SpiderLabs/owasp-modsecurity-crs +ENV PARANOIA=1 + +RUN a2enmod rewrite + +RUN apt-get update && \ + apt-get -y install python git ca-certificates iproute2 vim + +RUN cd /opt && \ + git clone https://github.com/${REPO}.git owasp-modsecurity-crs-3.1 && \ + cd owasp-modsecurity-crs-3.1 && \ + git checkout -qf ${COMMIT} + +RUN cd /opt && \ + cp -R /opt/owasp-modsecurity-crs-3.1/ /etc/apache2/modsecurity.d/owasp-crs/ && \ + mv /etc/apache2/modsecurity.d/owasp-crs/crs-setup.conf.example /etc/apache2/modsecurity.d/owasp-crs/crs-setup.conf && \ + cd /etc/apache2/modsecurity.d && \ + printf "include modsecurity.d/owasp-crs/crs-setup.conf\ninclude modsecurity.d/owasp-crs/rules/*.conf" > include.conf && \ + sed -i -e 's/SecRuleEngine DetectionOnly/SecRuleEngine On/g' /etc/apache2/modsecurity.d/modsecurity.conf && \ + a2enmod proxy proxy_http + +COPY proxy.conf /etc/apache2/modsecurity.d/proxy.conf +COPY docker-entrypoint.sh / + +RUN chmod 777 /docker-entrypoint.sh + +COPY .htaccess /var/www/html/.htaccess +COPY apache2.conf /etc/apache2/apache2.conf + +EXPOSE 80 + +ENTRYPOINT ["/docker-entrypoint.sh"] +CMD ["apachectl", "-D", "FOREGROUND"] diff --git a/samples/services/modsecurity/docker/apache2.conf b/samples/services/modsecurity/docker/apache2.conf new file mode 100644 index 0000000..f7c62d6 --- /dev/null +++ b/samples/services/modsecurity/docker/apache2.conf @@ -0,0 +1,227 @@ +# This is the main Apache server configuration file. It contains the +# configuration directives that give the server its instructions. +# See http://httpd.apache.org/docs/2.4/ for detailed information about +# the directives and /usr/share/doc/apache2/README.Debian about Debian specific +# hints. +# +# +# Summary of how the Apache 2 configuration works in Debian: +# The Apache 2 web server configuration in Debian is quite different to +# upstream's suggested way to configure the web server. This is because Debian's +# default Apache2 installation attempts to make adding and removing modules, +# virtual hosts, and extra configuration directives as flexible as possible, in +# order to make automating the changes and administering the server as easy as +# possible. + +# It is split into several files forming the configuration hierarchy outlined +# below, all located in the /etc/apache2/ directory: +# +# /etc/apache2/ +# |-- apache2.conf +# | `-- ports.conf +# |-- mods-enabled +# | |-- *.load +# | `-- *.conf +# |-- conf-enabled +# | `-- *.conf +# `-- sites-enabled +# `-- *.conf +# +# +# * apache2.conf is the main configuration file (this file). It puts the pieces +# together by including all remaining configuration files when starting up the +# web server. +# +# * ports.conf is always included from the main configuration file. It is +# supposed to determine listening ports for incoming connections which can be +# customized anytime. +# +# * Configuration files in the mods-enabled/, conf-enabled/ and sites-enabled/ +# directories contain particular configuration snippets which manage modules, +# global configuration fragments, or virtual host configurations, +# respectively. +# +# They are activated by symlinking available configuration files from their +# respective *-available/ counterparts. These should be managed by using our +# helpers a2enmod/a2dismod, a2ensite/a2dissite and a2enconf/a2disconf. See +# their respective man pages for detailed information. +# +# * The binary is called apache2. Due to the use of environment variables, in +# the default configuration, apache2 needs to be started/stopped with +# /etc/init.d/apache2 or apache2ctl. Calling /usr/bin/apache2 directly will not +# work with the default configuration. + + +# Global configuration +# + +# +# ServerRoot: The top of the directory tree under which the server's +# configuration, error, and log files are kept. +# +# NOTE! If you intend to place this on an NFS (or otherwise network) +# mounted filesystem then please read the Mutex documentation (available +# at ); +# you will save yourself a lot of trouble. +# +# Do NOT add a slash at the end of the directory path. +# +#ServerRoot "/etc/apache2" + +# +# The accept serialization lock file MUST BE STORED ON A LOCAL DISK. +# +#Mutex file:${APACHE_LOCK_DIR} default + +# +# The directory where shm and other runtime files will be stored. +# + +DefaultRuntimeDir ${APACHE_RUN_DIR} + +# +# PidFile: The file in which the server should record its process +# identification number when it starts. +# This needs to be set in /etc/apache2/envvars +# +PidFile ${APACHE_PID_FILE} + +# +# Timeout: The number of seconds before receives and sends time out. +# +Timeout 300 + +# +# KeepAlive: Whether or not to allow persistent connections (more than +# one request per connection). Set to "Off" to deactivate. +# +KeepAlive On + +# +# MaxKeepAliveRequests: The maximum number of requests to allow +# during a persistent connection. Set to 0 to allow an unlimited amount. +# We recommend you leave this number high, for maximum performance. +# +MaxKeepAliveRequests 100 + +# +# KeepAliveTimeout: Number of seconds to wait for the next request from the +# same client on the same connection. +# +KeepAliveTimeout 5 + + +# These need to be set in /etc/apache2/envvars +User ${APACHE_RUN_USER} +Group ${APACHE_RUN_GROUP} + +# +# HostnameLookups: Log the names of clients or just their IP addresses +# e.g., www.apache.org (on) or 204.62.129.132 (off). +# The default is off because it'd be overall better for the net if people +# had to knowingly turn this feature on, since enabling it means that +# each client request will result in AT LEAST one lookup request to the +# nameserver. +# +HostnameLookups Off + +# ErrorLog: The location of the error log file. +# If you do not specify an ErrorLog directive within a +# container, error messages relating to that virtual host will be +# logged here. If you *do* define an error logfile for a +# container, that host's errors will be logged there and not here. +# +ErrorLog ${APACHE_LOG_DIR}/error.log + +# +# LogLevel: Control the severity of messages logged to the error_log. +# Available values: trace8, ..., trace1, debug, info, notice, warn, +# error, crit, alert, emerg. +# It is also possible to configure the log level for particular modules, e.g. +# "LogLevel info ssl:warn" +# +LogLevel warn + +# Include module configuration: +IncludeOptional mods-enabled/*.load +IncludeOptional mods-enabled/*.conf + +# Include list of ports to listen on +Include ports.conf + + +# Sets the default security model of the Apache2 HTTPD server. It does +# not allow access to the root filesystem outside of /usr/share and /var/www. +# The former is used by web applications packaged in Debian, +# the latter may be used for local directories served by the web server. If +# your system is serving content from a sub-directory in /srv you must allow +# access here, or in any related virtual host. + + Options FollowSymLinks + AllowOverride None + Require all denied + + + + AllowOverride None + Require all granted + + + + Options Indexes FollowSymLinks + AllowOverride All + Require all granted + + +# +# Options Indexes FollowSymLinks +# AllowOverride None +# Require all granted +# + + + + +# AccessFileName: The name of the file to look for in each directory +# for additional configuration directives. See also the AllowOverride +# directive. +# +AccessFileName .htaccess + +# +# The following lines prevent .htaccess and .htpasswd files from being +# viewed by Web clients. +# + + Require all denied + + + +# +# The following directives define some format nicknames for use with +# a CustomLog directive. +# +# These deviate from the Common Log Format definitions in that they use %O +# (the actual bytes sent including headers) instead of %b (the size of the +# requested file), because the latter makes it impossible to detect partial +# requests. +# +# Note that the use of %{X-Forwarded-For}i instead of %h is not recommended. +# Use mod_remoteip instead. +# +LogFormat "%v:%p %h %l %u %t \"%r\" %>s %O \"%{Referer}i\" \"%{User-Agent}i\"" vhost_combined +LogFormat "%h %l %u %t \"%r\" %>s %O \"%{Referer}i\" \"%{User-Agent}i\"" combined +LogFormat "%h %l %u %t \"%r\" %>s %O" common +LogFormat "%{Referer}i -> %U" referer +LogFormat "%{User-agent}i" agent + +# Include of directories ignores editors' and dpkg's backup files, +# see README.Debian for details. + +# Include generic snippets of statements +IncludeOptional conf-enabled/*.conf + +# Include the virtual host configurations: +IncludeOptional sites-enabled/*.conf + +# vim: syntax=apache ts=4 sw=4 sts=4 sr noet diff --git a/samples/services/modsecurity/docker/build.sh b/samples/services/modsecurity/docker/build.sh new file mode 100644 index 0000000..ea0feed --- /dev/null +++ b/samples/services/modsecurity/docker/build.sh @@ -0,0 +1,16 @@ +#!/bin/bash +# +# Copyright (c) Authors of Clover +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +# + +IMAGE_PATH=${IMAGE_PATH:-"localhost:5000"} +IMAGE_NAME=${IMAGE_NAME:-"clover-ns-modsecurity-crs"} + +docker build -t $IMAGE_NAME . +docker tag $IMAGE_NAME $IMAGE_PATH/$IMAGE_NAME +docker push $IMAGE_PATH/$IMAGE_NAME diff --git a/samples/services/modsecurity/docker/docker-entrypoint.sh b/samples/services/modsecurity/docker/docker-entrypoint.sh new file mode 100644 index 0000000..e8e3013 --- /dev/null +++ b/samples/services/modsecurity/docker/docker-entrypoint.sh @@ -0,0 +1,15 @@ +#!/bin/bash +python -c "import re;import os;out=re.sub('(#SecAction[\S\s]*id:900000[\s\S]*paranoia_level=1\")','SecAction \\\\\n \"id:900000, \\\\\n phase:1, \\\\\n nolog, \\\\\n pass, \\\\\n t:none, \\\\\n setvar:tx.paranoia_level='+os.environ['PARANOIA']+'\"',open('/etc/apache2/modsecurity.d/owasp-crs/crs-setup.conf','r').read());open('/etc/apache2/modsecurity.d/owasp-crs/crs-setup.conf','w').write(out)" && \ +python -c "import re;import os;out=re.sub('(#SecAction[\S\s]*id:900330[\s\S]*total_arg_length=64000\")','SecAction \\\\\n \"id:900330, \\\\\n phase:1, \\\\\n nolog, \\\\\n pass, \\\\\n t:none, \\\\\n setvar:tx.total_arg_length=64000\"',open('/etc/apache2/modsecurity.d/owasp-crs/crs-setup.conf','r').read());open('/etc/apache2/modsecurity.d/owasp-crs/crs-setup.conf','w').write(out)" && \ + +if [ ! -z $PROXY ]; then + if [ $PROXY -eq 1 ]; then + APACHE_ARGUMENTS='-D crs_proxy' + if [ -z "$UPSTREAM" ]; then + export UPSTREAM=$(/sbin/ip route | grep ^default | perl -pe 's/^.*?via ([\d.]+).*/$1/g'):81 + fi + fi +fi + + +exec "$@" $APACHE_ARGUMENTS diff --git a/samples/services/modsecurity/docker/proxy.conf b/samples/services/modsecurity/docker/proxy.conf new file mode 100644 index 0000000..4dee0c9 --- /dev/null +++ b/samples/services/modsecurity/docker/proxy.conf @@ -0,0 +1,3 @@ + + ProxyPass "/" "http://${UPSTREAM}/" + diff --git a/samples/services/modsecurity/yaml/manifest.template b/samples/services/modsecurity/yaml/manifest.template new file mode 100644 index 0000000..afeb9dc --- /dev/null +++ b/samples/services/modsecurity/yaml/manifest.template @@ -0,0 +1,38 @@ +--- +apiVersion: extensions/v1beta1 +kind: Deployment +metadata: + name: {{ deploy_name }} + labels: + app: {{ deploy_name }} +spec: + replicas: 1 + template: + metadata: + labels: + app: {{ deploy_name }} + spec: + containers: + - name: {{ deploy_name }} + image: {{ image_path }}/{{ image_name }}:{{ image_tag }} + ports: + - containerPort: {{ http_port }} + env: + - name: PARANOIA + value: {{ paranoia_level }} + +--- +apiVersion: v1 +kind: Service +metadata: + name: {{ deploy_name }} + labels: + app: {{ deploy_name }} +spec: + ports: + - port: {{ http_port }} + name: http-modsecurity-crs + targetPort: {{ http_port }} + selector: + app: {{ deploy_name }} +--- diff --git a/samples/services/modsecurity/yaml/modsecurity-deployment.yaml b/samples/services/modsecurity/yaml/modsecurity-deployment.yaml new file mode 100644 index 0000000..450ede5 --- /dev/null +++ b/samples/services/modsecurity/yaml/modsecurity-deployment.yaml @@ -0,0 +1,22 @@ +apiVersion: extensions/v1beta1 +kind: Deployment +metadata: + name: modsecurity-crs +spec: + replicas: 1 + selector: + matchLabels: + app: modsecurity-crs + template: + metadata: + labels: + app: modsecurity-crs + spec: + containers: + - name: modsecurity-crs + image: clover/clover-ns-modsecurity-crs + ports: + - containerPort: 80 + env: + - name: PARANOIA + value: '1' diff --git a/samples/services/modsecurity/yaml/modsecurity-service.yaml b/samples/services/modsecurity/yaml/modsecurity-service.yaml new file mode 100644 index 0000000..8548dca --- /dev/null +++ b/samples/services/modsecurity/yaml/modsecurity-service.yaml @@ -0,0 +1,13 @@ +apiVersion: v1 +kind: Service +metadata: + name: modsecurity-crs +spec: + type: NodePort + ports: + - port: 80 + name: http-modsecurity-crs + protocol: TCP + targetPort: 80 + selector: + app: modsecurity-crs diff --git a/samples/services/modsecurity/yaml/render_yaml.py b/samples/services/modsecurity/yaml/render_yaml.py new file mode 100644 index 0000000..54f8069 --- /dev/null +++ b/samples/services/modsecurity/yaml/render_yaml.py @@ -0,0 +1,60 @@ +# Copyright (c) Authors of Clover +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 + +import argparse + +from jinja2 import Template + + +def render_yaml(args): + template_file = 'manifest.template' + out_file = 'modsecurity.yaml' + + try: + with open(template_file) as f: + tmpl = Template(f.read()) + output = tmpl.render( + image_path=args['image_path'], + image_name=args['image_name'], + image_tag=args['image_tag'], + deploy_name=args['deploy_name'], + http_port=args['http_port'], + paranoia_level=args['paranoia_level'] + ) + with open(out_file, "wb") as fh: + fh.write(output) + return "Generated manifest for {}".format(args['deploy_name']) + except Exception as e: + print(e) + return "Unable to generate manifest for {}".format( + args['deploy_name']) + + +if __name__ == '__main__': + parser = argparse.ArgumentParser() + parser.add_argument( + '--image_name', default='clover-ns-modsecurity-crs', + help='The image name to use') + parser.add_argument( + '--image_path', default='localhost:5000', + help='The path to the image to use') + parser.add_argument( + '--image_tag', default='latest', + help='The image tag to use') + parser.add_argument( + '--deploy_name', default='modsecurity-crs', + help='The k8s deploy name to use') + parser.add_argument( + '--http_port', default='80', + help='Analyze http traffic on this port') + parser.add_argument( + '--paranoia_level', default='1', + help='The modsecurity paranoia level') + + args = parser.parse_args() + print(render_yaml(vars(args))) + -- cgit 1.2.3-korg