Skip to content

Commit

Permalink
Merge pull request #14 from coreruleset/fix-plugins-include
Browse files Browse the repository at this point in the history
Fix plugins include
  • Loading branch information
fzipi committed Aug 3, 2024
2 parents 064874a + c0b6344 commit 3a10bd3
Show file tree
Hide file tree
Showing 6 changed files with 142 additions and 99 deletions.
95 changes: 81 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,19 +27,55 @@ The following env variables may be set to control Caddy and Coraza.

These values control Coraza.

| Variable | Default | Documentation |
| ----------------------------- | --------------- | ------------------------------------------------------------------------------------------------------------------- |
| `CORAZA_SECRULEENGINE` | `DetectionOnly` | [SecRuleEngine](https://github.com/SpiderLabs/ModSecurity/wiki/Reference-Manual-(v2.x)#SecRuleEngine) |
| `CORAZA_SECREQUESTBODYACCESS` | `On` | [SecRequestBodyAccess](https://github.com/SpiderLabs/ModSecurity/wiki/Reference-Manual-(v2.x)#SecRequestBodyAccess) |
| `CORAZA_REMOVERULEIDS` | `` | [SecRuleRemoveById](https://github.com/SpiderLabs/ModSecurity/wiki/Reference-Manual-(v2.x)#secruleremovebyid) |
| Variable | Default | Documentation |
| - | - | - |
| CORAZA_ARGUMENTS_LIMIT | Default: `1000` | An integer indicating the maximum number of arguments that can be processed before setting the `REQBODY_ERROR` variable |
| CORAZA_AUDIT_ENGINE | Default: `"RelevantOnly"` | |
| CORAZA_AUDIT_LOG | Default: `/dev/stdout` | A string indicating the path to the main audit log file or the concurrent logging index file |
| CORAZA_AUDIT_LOG_FORMAT | Default: `JSON` | A string indicating the output format of the AuditLogs (Default: `JSON`). Accepted values: `JSON`, `Native`. See [SecAuditLogFormat]() |
| CORAZA_AUDIT_LOG_PARTS | Default: `'ABIJDEFHZ'` | A string that defines which parts of each transaction are going to be recorded in the audit log (Default: `'ABIJDEFHZ'`). See [SecAuditLogParts]() for the accepted values. |
| CORAZA_AUDIT_LOG_RELEVANT_STATUS | Default: `"^(?:5\|4[0-9][0-35-9])"` | A regular expression string that defines the http error codes that are relevant for audit logging (Default: `"^(?:5|4(?!04))"`). See [SecAuditLogRelevantStatus]() |
| CORAZA_AUDIT_LOG_TYPE | Default: `Serial` | |
| CORAZA_AUDIT_STORAGE_DIR | Default: `/var/log/coraza/audit/` | |
| CORAZA_DATA_DIR | Default: `/tmp/coraza/data` | |
| CORAZA_DEBUG_LOG | Default: `/dev/null` | |
| CORAZA_DEFAULT_PHASE1_ACTION | Default: `"phase:1,pass,log,tag:'\${CORAZA_TAG}'"` | String with the contents for the default action in phase 1 |
| CORAZA_DEFAULT_PHASE2_ACTION | Default: `"phase:2,pass,log,tag:'\${CORAZA_TAG}'"` | String with the contents for the default action in phase 2 |
| CORAZA_REQ_BODY_ACCESS | Default: `"On"` | A string value allowing ModSecurity to access request bodies. Allowed values: `On`, `Off`. See [SecRequestBodyAccess]() |
| CORAZA_REQ_BODY_JSON_DEPTH_LIMIT | Default: `1024` | |
| CORAZA_REQ_BODY_LIMIT | Default: `13107200` | An integer value indicating the maximum request body size accepted for buffering. See [SecRequestBodyLimit]() |
| CORAZA_REQ_BODY_LIMIT_ACTION | Default: `"Reject"` | A string value for the action when `SecRequestBodyLimit` is reached. Accepted values: `Reject`, `ProcessPartial`. See [SecRequestBodyLimitAction]() |
| CORAZA_REQ_BODY_NOFILES_LIMIT | Default: `524288` | |
| CORAZA_RESP_BODY_ACCESS | Default: `"On"` | A string value allowing ModSecurity to access response bodies. Allowed values: `On`, `Off`. See [SecResponseBodyAccess]() |
| CORAZA_RESP_BODY_LIMIT | Default: `1048576` | An integer value indicating the maximum response body size accepted for buffering. |
| CORAZA_RESP_BODY_LIMIT_ACTION | Default: `"ProcessPartial"` | A string value for the action when `SecResponseBodyLimit` is reached. Accepted values: `Reject`, `ProcessPartial`. See [SecResponseBodyLimitAction]() |
| CORAZA_RESP_BODY_MIMETYPE | Default: `"text/plain text/html text/xml"` | |
| CORAZA_RULE_ENGINE | Default: `On` | A string value enabling Coraza itself. Accepted values: `On`, `Off`, `DetectionOnly`. See [SecRuleEngine]() |
| CORAZA_TAG | Default: `coraza` | A string indicating the default tag action, which will be inherited by the rules in the same configuration context. |
| CORAZA_TMP_DIR | Default: `/tmp/coraza` | A string indicating the path where temporary files will be created |

### CRS Specific

| Variable | Default | Documentation |
| - | - | - |
| PARANOIA | Default: `1` | CRS Paranoia Level setting for logging. It could be different from the BLOCKING level, allowing you to log additional information. |
| ANOMALY_INBOUND | Default: `5` | The score used by CRS to block incoming requests. |
| ANOMALY_OUTBOUND | Default: `4` | The score used by CRS to block outgoing requests. |
| BLOCKING_PARANOIA | Default: `1` | CRS Paranoia Level setting used for blocking |

### Caddy Specific

These values control Caddy.

| Variable | Default | Documentation |
| - | - | - |
| ACCESSLOG | Default: `stderr` | Log to this file access logs. Use `/var/log/caddy/access.log` or similar if you want to store it in the filesystem |
| BACKEND | Default: `localhost:80` | Proxy traffic to this `host:port` |
| PORT | Default: `8080` | Port where the server listens. |

## Important Notes

- The container is configured by default to run as a non-root user. The upstream Caddy containers run using root by default. To allow binding on ports <1024 `cap_net_bind_service` is added on the Caddy binary.
- The container is configured by default to run as a non-root user. The upstream Caddy containers run using root by default. To allow binding on ports <1024 `cap_net_bind_service` is added on the Caddy binary. The default port still is 8080.

## Configuration Files/Directories

Expand All @@ -60,8 +96,8 @@ Various arguments can be provided if building the container yourself. The availa

| Variable | Default | Description |
| ------------------ | ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `CADDY_VERSION` | `2.7.6` | The Caddy Docker container tag to use as a base. |
| `CRS_VERSION` | `v4.3.0` | The OWASP CRS release. |
| `CADDY_VERSION` | `2.8.1` | The Caddy Docker container tag to use as a base. |
| `CRS_VERSION` | `v4.5.0` | The OWASP CRS release. |
| `LIBCAP` | `true` | Install libcap and add the `cap_net_bind_service` capability to the Caddy binary. Required for the container to bind to low ports when not running as root. |
| `CADDY_USER` | `caddy` | The user name that will run Caddy. Can be set to `root` to run Caddy as root rather than a low privleged user. |
| `CADDY_GROUP` | `caddy` | The group name for the Caddy user. Can be set to `root` to run Caddy as root rather than a low privleged user. |
Expand Down Expand Up @@ -97,10 +133,6 @@ To build a specific target for a single platform only (replace target and platfo
```bash
docker buildx bake -f docker-bake.hcl --set "*.platform=linux/amd64" caddy-alpine

## Adding CRS Plugins

To add CRS Plugins, download and decompress the plugin to a directory of your choice. Then start the container bind mounting the directory to `/opt/coraza/owasp-crs/plugins`.

## Advanced Configuration

If you prefer to configure Caddy and/or Coraza yourself there are multiple options.
Expand All @@ -114,6 +146,36 @@ To add Coraza configuration without overwriting any of the container default con

As an example, you may want to create your own rules for Coraza. You would create a volume and mount it in the container at `/opt/coraza/rules.d`; the rules will then be loaded on server start automatically.

## Adding CRS Plugins

To add CRS Plugins, download and decompress the plugin to a directory of your choice. The official plugin list is at https://github.com/coreruleset/plugin-registry.

Create a volume or bind mount a directory of your choice to `/opt/coraza/plugins`; the rules will then be loaded on server start automatically.

Example:
```
curl -sSL https://github.com/coreruleset/wordpress-rule-exclusions-plugin/archive/refs/tags/v1.0.0.tar.gz -o wordpress.tar.gz
tar xvf wordpress.tar.gz --strip-components 1 'wordpress-rule-exclusions-plugin*/plugins'
❯ docker compose run -v $(pwd)/plugins:/opt/coraza/plugins coraza-crs
[+] Creating 1/0
✔ Container coraza-crs-docker-whoami-1 Running 0.0s
Generating configuration files...
- Caddyfile
- Generating Caddyfile from template /templates/Caddyfile
- Done
- Coraza configuration file
- Generating Caddyfile from template /templates/coraza.conf
- Done
- User configuration files loaded from /opt/coraza/config.d
- Done
- Loading user plugins from /opt/coraza/plugins
-> wordpress-rule-exclusions-before.conf
-> wordpress-rule-exclusions-config.conf
- Done
- Loading user defined rule sets from /opt/coraza/rules.d
- Done
```

### Replacement Configuration - Caddy

If you prefer to use your own configuration file for Caddy, simply mount the configuration file as `/config/caddy/Caddyfile` or mount a volume at `/config/Caddy` with a `Caddyfile` inside. You will need to add the relevant Coraza configuration to Caddy yourself if you choose this option. The bare minimum recommended configuration is:
Expand All @@ -133,10 +195,15 @@ If you prefer to use your own configuration file for Caddy, simply mount the con
include /opt/coraza/config/coraza.conf
# User defined configuration files
include /opt/coraza/config.d/*.conf
# OWASP Core Rule Set (CRS) Setup
# OWASP CRS Setup
include /opt/coraza/config/crs-setup.conf
# OWASP Core Rule Set (CRS)
# OWASP CRS Plugins Setup
include /opt/coraza/owasp-crs/plugins/*-config.conf
include /opt/coraza/owasp-crs/plugins/*-before.conf
# OWASP CRS
include /opt/coraza/owasp-crs/*.conf
# OWASP CRS Plugins After
include /opt/coraza/owasp-crs/plugins/*-after.conf
# Other baked in rule sets
include /opt/coraza/rules/*.conf
# User defined rule sets
Expand Down
37 changes: 11 additions & 26 deletions caddy/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -141,50 +141,35 @@ USER "${CADDY_USER}:${CADDY_GROUP}"
ENV \
XDG_CONFIG_HOME="$CADDY_CONFIG_DIR" \
XDG_DATA_HOME="$CADDY_DATA_DIR" \
ACCESSLOG=/var/log/nginx/access.log \
ACCESSLOG=/var/log/caddy/access.log \
BACKEND=localhost:80 \
DNS_SERVER= \
ERRORLOG=/var/log/nginx/error.log \
KEEPALIVE_TIMEOUT=60s \
METRICS_ALLOW_FROM='127.0.0.0/24' \
METRICS_DENY_FROM='all' \
METRICSLOG=/dev/null \
CORAZA_ARGUMENT_SEPARATOR="&" \
CORAZA_ARGUMENTS_LIMIT=1000 \
CORAZA_AUDIT_ENGINE="RelevantOnly" \
CORAZA_AUDIT_LOG=/dev/stdout \
CORAZA_AUDIT_LOG_FORMAT=JSON \
CORAZA_AUDIT_LOG_PARTS='ABIJDEFHZ' \
CORAZA_AUDIT_LOG_RELEVANT_STATUS="^(?:5|4[0-9][0-35-9])" \
CORAZA_AUDIT_LOG_TYPE=Serial \
CORAZA_COOKIE_FORMAT=0 \
CORAZA_AUDIT_STORAGE_DIR=/var/log/modsecurity/audit/ \
CORAZA_DATA_DIR=/tmp/modsecurity/data \
CORAZA_AUDIT_STORAGE_DIR=/var/log/coraza/audit/ \
CORAZA_DATA_DIR=/tmp/coraza/data \
CORAZA_DEBUG_LOG=/dev/null \
CORAZA_DEBUG_LOGLEVEL=0 \
CORAZA_DEFAULT_PHASE1_ACTION="phase:1,pass,log,tag:'\${CORAZA_TAG}'" \
CORAZA_DEFAULT_PHASE2_ACTION="phase:2,pass,log,tag:'\${CORAZA_TAG}'" \
CORAZA_DISABLE_BACKEND_COMPRESSION="Off" \
CORAZA_PCRE_MATCH_LIMIT=100000 \
CORAZA_PCRE_MATCH_LIMIT_RECURSION=100000 \
CORAZA_REQ_BODY_ACCESS=on \
CORAZA_REQ_BODY_JSON_DEPTH_LIMIT=512 \
CORAZA_REQ_BODY_ACCESS="On" \
CORAZA_REQ_BODY_JSON_DEPTH_LIMIT=1024 \
CORAZA_REQ_BODY_LIMIT=13107200 \
CORAZA_REQ_BODY_LIMIT_ACTION="Reject" \
CORAZA_REQ_BODY_NOFILES_LIMIT=131072 \
CORAZA_RESP_BODY_ACCESS=on \
CORAZA_REQ_BODY_NOFILES_LIMIT=524288 \
CORAZA_RESP_BODY_ACCESS="On" \
CORAZA_RESP_BODY_LIMIT=1048576 \
CORAZA_RESP_BODY_LIMIT_ACTION="ProcessPartial" \
CORAZA_RESP_BODY_MIMETYPE="text/plain text/html text/xml" \
CORAZA_RULE_ENGINE=on \
CORAZA_STATUS_ENGINE="Off" \
CORAZA_TAG=modsecurity \
CORAZA_TMP_DIR=/tmp/modsecurity/tmp \
CORAZA_RULE_ENGINE=On \
CORAZA_TAG=coraza \
CORAZA_TMP_DIR=/tmp/coraza \
CORAZA_TMP_SAVE_UPLOADED_FILES="on" \
CORAZA_UNICODE_MAPPING=20127 \
CORAZA_UPLOAD_DIR=/tmp/modsecurity/upload \
CORAZA_UPLOAD_DIR=/tmp/coraza/upload \
CORAZA_UPLOAD_FILE_MODE=0600 \
CORAZA_UPLOAD_KEEP_FILES=Off \
PORT=8080 \
# CRS specific variables
PARANOIA=1 \
Expand Down
34 changes: 34 additions & 0 deletions caddy/entrypoint.sh
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,40 @@ else
echo " - Done"
fi

# Output the extra configuration files that will be read as hints to the user
if [ -z "$(ls -A /opt/coraza/config.d)" ]; then
echo " - No user configuration files found in /opt/coraza/config.d"
else
echo " - User configuration files loaded from /opt/coraza/config.d"
for f in /opt/coraza/config.d/*.conf
do
echo " -> $(basename $f)"
done
echo " - Done"
fi

if [ -z "$(ls -A /opt/coraza/plugins)" ]; then
echo " - No user plugins found in /opt/coraza/plugins"
else
echo " - User plugins loaded from /opt/coraza/plugins"
for f in /opt/coraza/plugins/*.conf
do
echo " -> $(basename $f)"
done
echo " - Done"
fi

if [ -z "$(ls -A /opt/coraza/rules.d)" ]; then
echo " - No user defined rule sets found in /opt/coraza/rules.d"
else
echo " - User defined rule sets loaded from /opt/coraza/rules.d"
for f in /opt/coraza/rules.d/*.conf
do
echo " -> $(basename $f)"
done
echo " - Done"
fi

# Launch Caddy
echo "Launching $*"
exec "$@"
39 changes: 1 addition & 38 deletions renovate.json
Original file line number Diff line number Diff line change
@@ -1,49 +1,12 @@
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": [
"",
"local>coreruleset/renovate-config",
"schedule:weekly"
],
"enabledManagers": [
"custom.regex"
],
"packageRules": [
{
"groupName": "all non-major dependencies",
"groupSlug": "all-minor-patch",
"matchPackagePatterns": [
"*"
],
"matchUpdateTypes": [
"minor",
"patch"
],
"prBodyColumns": [
"Package",
"Type",
"Update",
"Change",
"Pending"
]
},
{
"groupName": "all major dependencies",
"groupSlug": "all-major",
"matchPackagePatterns": [
"*"
],
"matchUpdateTypes": [
"major"
],
"prBodyColumns": [
"Package",
"Type",
"Update",
"Change",
"Pending"
]
}
],
"customManagers": [
{
"description": "Bake file",
Expand Down
14 changes: 13 additions & 1 deletion src/templates/Caddyfile
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
}

# Create the HTTP listener
:80 {
:${PORT} {

# Load Coraza configuration
coraza_waf {
Expand All @@ -24,11 +24,18 @@
include /opt/coraza/owasp-crs/plugins/*-config.conf
include /opt/coraza/owasp-crs/plugins/*-before.conf

# User plugins Setup
include /opt/coraza/plugins/*-config.conf
include /opt/coraza/plugins/*-before.conf

# OWASP Core Rule Set (CRS)
include /opt/coraza/owasp-crs/rules/*.conf

include /opt/coraza/owasp-crs/plugins/*-after.conf

# User plugins after
include /opt/coraza/plugins/*-after.conf

# Other baked in rule sets or configurations
include /opt/coraza/rules/*.conf

Expand All @@ -44,4 +51,9 @@
trusted_proxies private_ranges
}

log {
output file ${ACCESSLOG}
}


}
22 changes: 2 additions & 20 deletions src/templates/coraza.conf
Original file line number Diff line number Diff line change
Expand Up @@ -40,28 +40,10 @@ SecRule REQUEST_HEADERS:Content-Type "^(?:application(?:/soap\+|/)|text/)xml" \
"id:'200000',phase:1,t:none,t:lowercase,pass,nolog,ctl:requestBodyProcessor=XML"
SecRule REQUEST_HEADERS:Content-Type "^application/json" \
"id:'200001',phase:1,t:none,t:lowercase,pass,nolog,ctl:requestBodyProcessor=JSON"
SecRule &ARGS "@ge 1000" \
SecRule &ARGS "@ge ${CORAZA_ARGUMENTS_LIMIT}" \
"id:'200007', phase:2,t:none,log,deny,status:400,msg:'Failed to fully parse request body due to large argument count',severity:2"
SecRule REQBODY_ERROR "!@eq 0" \
"id:'200002', phase:2,t:none,log,deny,status:400,msg:'Failed to parse request body.',logdata:'%{reqbody_error_msg}',severity:2"
SecRule MULTIPART_STRICT_ERROR "!@eq 0" \
"id:'200003',phase:2,t:none,log,deny,status:400, \
msg:'Multipart request body failed strict validation: \
PE %{REQBODY_PROCESSOR_ERROR}, \
BQ %{MULTIPART_BOUNDARY_QUOTED}, \
BW %{MULTIPART_BOUNDARY_WHITESPACE}, \
DB %{MULTIPART_DATA_BEFORE}, \
DA %{MULTIPART_DATA_AFTER}, \
HF %{MULTIPART_HEADER_FOLDING}, \
LF %{MULTIPART_LF_LINE}, \
SM %{MULTIPART_MISSING_SEMICOLON}, \
IQ %{MULTIPART_INVALID_QUOTING}, \
IP %{MULTIPART_INVALID_PART}, \
IH %{MULTIPART_INVALID_HEADER_FOLDING}, \
FL %{MULTIPART_FILE_LIMIT_EXCEEDED}'"
SecRule MULTIPART_UNMATCHED_BOUNDARY "@eq 1" \
"id:'200004',phase:2,t:none,log,deny,msg:'Multipart parser detected a possible unmatched boundary.'"

# Additional rules
SecRule REQUEST_HEADERS:Content-Type "^application/[a-z0-9.-]+[+]json" \
"id:'200006',phase:1,t:none,t:lowercase,pass,nolog,ctl:requestBodyProcessor=JSON"
msg:'Multipart request body failed strict validation."

0 comments on commit 3a10bd3

Please sign in to comment.