Varnish WAF


ModSecurity is an open source project started in 2002, currently backed and maintained by SpiderLabs. The main tools in the ModSecurity toolbox are parsing, and the rule engine. At the start of the application, ModSecurity will parse and classify a set of rules. For each request it will buffer the request and response headers/bodies, parse them against the rule set and finally log all relevant information. The version used for Varnish WAF, is 3.0.3. To change the settings of ModSecurity edit /etc/varnish/modsec/modsecurity.conf.



ModSecurity rules are called SecRules. The format for for a SecRule is:


The variables can include specific headers, the request body, etc. The operator will check against the variables. This can be regex or a simple equals. Transformations and actions include id number, phase, hashing, block a request, or deny a request. Transformations are done before the operator comparison, while actions are done if the comparison was successful. Each rule must contain an ID number. An example of a simple rule to check if a request URL is equal to index.php is:

SecRule REQUEST_URI "@streq /index.php" "id:1,phase:1,t:lowercase,deny"

More information about SecRule formats can be found here and more information about what a SecRule is made up of can be found here. Some basic rules on creating a SecRule are:

  1. Every SecRule must have a VARIABLE.
  2. Every SecRule must have an OPERATOR, if none is listed @rx is implied.
  3. Every SecRule must have an ACTION. The only required action is id, however, several actions are implied by SecDefaultAction (default phase:2, log, auditlog, pass).
  4. Every SecRule must have a phase, this determine when the rule is run. If no phase is included the default is 2.
  5. Every SecRule must have a disruptive ACTION. This is an action that describes what should happen when a rule contained a successful comparison. If no disruptive action is included the default is pass.
  6. Transformations are optional but should be used to prevent your rule from being bypassed.

Syntax Check

ModSecurity provides a tool, modsec-rules-check, to determine the validity of rules and configuration files. There are a few different ways to use this program.

# Checking one file
modsec-rules-check /etc/varnish/modsec/owasp-crs-v3.1.1/rules/REQUEST-901-INITIALIZATION.conf 
 : /etc/varnish/modsec/owasp-crs-v3.1.1/rules/REQUEST-901-INITIALIZATION.conf  --  Loaded 40 rules.
Test ok.
# Checking multiple files using globbing
modsec-rules-check /etc/varnish/modsec/owasp-crs-v3.1.1/rules/*.conf
 : /etc/varnish/modsec/owasp-crs-v3.1.1/rules/crs-setup.conf  --  Loaded 1 rules.
 : /etc/varnish/modsec/owasp-crs-v3.1.1/rules/REQUEST-901-INITIALIZATION.conf  --  Loaded 40 rules.

. . . 

Test ok.

Blocking suspicious traffic

The SecRuleEngine in modsecurity.conf dictates how disruptive ModSecurity can be:

  • off: the engine is completely inactive, no detection is even done.
  • DetectionOnly: rules are active, but no disruptive action is undertaken. It’s the default to allow you to analyze what ModSecurity will do once fully activated.
  • on: fully active, traffic is analyzed and possibly blocked if issues are found.

If using the OWASP CRS, you can read how the disruptive actions occur here.

Phases of a Request

ModSecurity goes through a specific order of operations, called phases. The phases determine the risk of a request, ensuring protection of a backend. Each phase will either block and finish the request by logging, or continue to the next phase. The phases are as follows:

  1. Request Headers
    • This phase will examine the connecting and host IP addresses, request URL, request protocol, HTTP version, and request headers. The goal of this phase is to is to determine early if the request body should even be buffered or if the request should be rejected right away.
  2. Request Body
    • This phase can parse the request body. Most of the application-oriented rules should go here. The following encoding types are supported: text/xml, multipart/form-data and application/x-www-form-urlencoded.
    • In order to access the Request Body, you must have SecRequestBodyAccess set to On.
    • The maximum size of the request body and the action of what should happen when that size is hit are set by SecRequestBodyLimit and SecRequestBodyLimitAction.
  3. Response Headers
    • This phase acts similarly to phase 1 but for the response. This phase will read the response headers, determine if there are reasons to block the request, and determine if the response body should be buffered.
  4. Response Body
    • This phase parses the response body and it decides if the request should continue to the user.
    • In order to access the Response Body, you must have SecResponseBodyAccess set to On.
    • The maximum size of the response body and the action of what should happen when that size is hit are set by SecResponsetBodyLimit and SecResponseBodyLimitAction.
    • The response body buffering can also be restricted by content type with SecResponseBodyMimeType.
  5. Logging
    • The final phase finishes up the request. This phase cannot block requests. Rules in this phase will determine how the logging should be done.


ModSecurity’s logging is generally verbose and resource expensive. It is suggested to use logging in the testing phase of an application but disable it once it reaches production. On a disruptive action Varnish will log information from ModSecurity about the request. There are two types of logs: debug and audit.

Debug Log

This is a verbose log on everything ModSecurity does for a request. This tool is best used testing and troubleshooting. The path to the debug log is set with SecDebugLog. There are 7 levels of logging, (1-6, 9) that can be set with SecDebugLogLevel:

  • 1: No logging.
  • 2: Log errors (intercepted requests) only.
  • 3: Log warnings.
  • 4: Log notices.
  • 5: Log details of how transactions are handled.
  • 6: The same as above, but including information about each piece of information handled.
  • 9: Log everything, including very detailed debugging information.

Audit Log

This is a log to track the data of a HTTP request. This is less verbose than the debug log but still resource expensive. SecAuditEngine determines if the audit log should be used. It can be set to On, Off or RelevantOnly. RelevantOnly will log for specific response codes set by SecAuditLogRelevantStatus. This defaults to ^(?:5|4(?!04)). The audit log is broken down into 12 parts, A-K, and Z. All but two are optional. The parts are:

  • A: Audit log header (mandatory).
  • B: Request headers.
  • C: Request body (if SecRequestBodyAccess is set to on).
  • D: Reserved for intermediary response headers; not implemented yet.
  • E: Intermediary response body (if SecResponseBodyAccess is set to On).
  • F: Final response headers (excluding the Date and Server headers).
  • G: Reserved for the actual response body; not implemented yet.
  • H: Audit log trailer.
  • I: This part is a replacement for part C. It will log the same data as C in all cases except when multipart/form-data encoding in used.
  • J: This part contains information about the files uploaded using multipart/form-data encoding.
  • K: This part contains a full list of every rule that matched (one per line) in the order they were matched. The rules are fully qualified and will thus show inherited actions and default operators.
  • Z: Final boundary, signifies the end of the entry (mandatory).