Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion .github/workflows/verifyimage.yml
Original file line number Diff line number Diff line change
Expand Up @@ -83,8 +83,11 @@ jobs:

- name: Run ${{ matrix.target }}
run: |
# get the major version from the matrix.target
# The targets end with `<major>-<minor>-<patch>`.
CRS_VERSION="v$(awk -F'-' '{print $(NF-2)}' <<< ${{ matrix.target }})"
. .github/workflows/configure-rules-for-test.sh \
src/opt/modsecurity/configure-rules.conf \
"src/opt/modsecurity/configure-rules.${CRS_VERSION}.conf" \
README.md \
"${{ matrix.target }}.env"
echo "Starting container ${{ matrix.target }}-verification"
Expand Down
11 changes: 8 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,14 @@ Stable Tags are composed of:
* date, in the format `YYYYMMDDHHMM`

The stable tag format is `<CRS version>-<web server>[-<os>][-<writable>]-<date>`.
Examples:
* `4-nginx-202401121309`
* `4.0-apache-alpine-202401121309`
Examples (do not blindly copy these labels):
* `4-nginx-202509051009`
* `4.18-nginx-202509051009`
* `4.18.0-nginx-202509051009`
Or for the previous major release:
* `3-nginx-alpine-202509051009`
* `3.3-nginx-alpine-202509051009`
* `3.3.7-nginx-alpine-202509051009`

### Rolling Tags

Expand Down
27 changes: 21 additions & 6 deletions docker-bake.hcl
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,22 @@ variable "modsec2-flags" {
default = "--with-yajl --with-ssdeep --with-pcre2"
}

variable "crs-version" {
variable "previous-major-crs-version" {
default = "3.3.7"
}

variable "major-crs-version" {
# renovate: depName=coreruleset/coreruleset datasource=github-releases
default = "4.18.0"
}

variable "crs-versions" {
default = {
"previous" = previous-major-crs-version,
"latest" = major-crs-version
}
}

variable "nginx-version" {
# renovate: depName=nginxinc/nginx-unprivileged datasource=docker
default = "1.28.0"
Expand Down Expand Up @@ -115,7 +126,6 @@ target "platforms-base" {
"org.opencontainers.image.source" = "https://github.com/coreruleset/modsecurity-crs-docker"
}
args = {
CRS_RELEASE = "${crs-version}"
MODSEC2_VERSION = "${modsec2-version}"
MODSEC2_FLAGS = modsec2-flags
MODSEC3_VERSION = "${modsec3-version}"
Expand All @@ -127,6 +137,7 @@ target "platforms-base" {

target "apache" {
matrix = {
crs_release = crs-versions
base = [
{
name = "debian"
Expand All @@ -146,21 +157,23 @@ target "apache" {
}

inherits = ["platforms-base"]
name = "apache-${base.name}"
name = "apache-${base.name}-${replace(crs_release, ".", "-")}"
contexts = {
image = base.image
}
dockerfile = base.dockerfile
args = {
CRS_RELEASE = "${crs_release}"
LUA_MODULES = base.lua_modules
}
tags = concat(tag(base.tag_base),
vtag("${crs-version}", base.tag_base)
vtag("${crs_release}", base.tag_base)
)
}

target "nginx" {
matrix = {
crs_release = crs-versions
base = [
{
name = "debian"
Expand All @@ -175,6 +188,7 @@ target "nginx" {
image = "docker-image://nginxinc/nginx-unprivileged:${nginx-version}-alpine"
lua_modules = join(" ", lua-modules-alpine)
tag_base = "nginx-alpine"
crs_release = crs-versions
}
],
read-only-fs = [
Expand All @@ -189,19 +203,20 @@ target "nginx" {
]
}
inherits = ["platforms-base"]
name = "nginx-${base.name}-${read-only-fs.name}"
name = "nginx-${base.name}-${read-only-fs.name}-${replace(crs_release, ".", "-")}"
contexts = {
image = base.image
}
dockerfile = base.dockerfile
args = {
CRS_RELEASE = crs_release
NGINX_VERSION = nginx-version
LUA_MODULES = base.lua_modules
NGINX_DYNAMIC_MODULES = join(" ", [for mod in nginx-dynamic-modules : join(" ", [mod.owner, mod.name, mod.version])])
NGINX_HOME = "/etc/nginx"
READ_ONLY_FS = read-only-fs.read-only
}
tags = concat(tag("${base.tag_base}${equal(read-only-fs.read-only, "true") ? "-read-only" : ""}"),
vtag("${crs-version}", "${base.tag_base}${equal(read-only-fs.read-only, "true") ? "-read-only" : ""}")
vtag("${crs_release}", "${base.tag_base}${equal(read-only-fs.read-only, "true") ? "-read-only" : ""}")
)
}
2 changes: 1 addition & 1 deletion nginx/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@

ENV NGINX_HOME="${NGINX_HOME}"

ENV \

Check warning on line 129 in nginx/Dockerfile

View workflow job for this annotation

GitHub Actions / build (nginx-debian-writable-3-3-7)

Sensitive data should not be used in the ARG or ENV commands

SecretsUsedInArgOrEnv: Do not use ARG or ENV instructions for sensitive data (ENV "SSL_CERT_KEY") More info: https://docs.docker.com/go/dockerfile/rule/secrets-used-in-arg-or-env/

Check warning on line 129 in nginx/Dockerfile

View workflow job for this annotation

GitHub Actions / build (nginx-debian-writable-3-3-7)

Sensitive data should not be used in the ARG or ENV commands

SecretsUsedInArgOrEnv: Do not use ARG or ENV instructions for sensitive data (ENV "PROXY_SSL_CERT_KEY") More info: https://docs.docker.com/go/dockerfile/rule/secrets-used-in-arg-or-env/

Check warning on line 129 in nginx/Dockerfile

View workflow job for this annotation

GitHub Actions / build (nginx-debian-writable-4-18-0)

Sensitive data should not be used in the ARG or ENV commands

SecretsUsedInArgOrEnv: Do not use ARG or ENV instructions for sensitive data (ENV "PROXY_SSL_CERT_KEY") More info: https://docs.docker.com/go/dockerfile/rule/secrets-used-in-arg-or-env/

Check warning on line 129 in nginx/Dockerfile

View workflow job for this annotation

GitHub Actions / build (nginx-debian-writable-4-18-0)

Sensitive data should not be used in the ARG or ENV commands

SecretsUsedInArgOrEnv: Do not use ARG or ENV instructions for sensitive data (ENV "SSL_CERT_KEY") More info: https://docs.docker.com/go/dockerfile/rule/secrets-used-in-arg-or-env/
ACCESSLOG=/var/log/nginx/access.log \
BACKEND=http://localhost:80 \
CORS_HEADER_403_ALLOW_ORIGIN="*" \
Expand Down Expand Up @@ -225,7 +225,7 @@
COPY nginx/docker-entrypoint.d/*.sh /docker-entrypoint.d/
COPY src/opt/modsecurity/activate-plugins.sh /docker-entrypoint.d/94-activate-plugins.sh
COPY src/opt/modsecurity/configure-rules.sh /docker-entrypoint.d/95-configure-rules.sh
COPY src/opt/modsecurity/configure-rules.conf /docker-entrypoint.d/
COPY src/opt/modsecurity/configure-rules.*.conf /docker-entrypoint.d/
# We use the templating mechanism from the nginx image here.
COPY nginx/templates /etc/nginx/templates/
COPY src/bin/* /usr/local/bin/
Expand Down
2 changes: 1 addition & 1 deletion nginx/Dockerfile-alpine
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@

ENV NGINX_HOME="${NGINX_HOME}"

ENV \

Check warning on line 130 in nginx/Dockerfile-alpine

View workflow job for this annotation

GitHub Actions / build (nginx-alpine-writable-4-18-0)

Sensitive data should not be used in the ARG or ENV commands

SecretsUsedInArgOrEnv: Do not use ARG or ENV instructions for sensitive data (ENV "SSL_CERT_KEY") More info: https://docs.docker.com/go/dockerfile/rule/secrets-used-in-arg-or-env/

Check warning on line 130 in nginx/Dockerfile-alpine

View workflow job for this annotation

GitHub Actions / build (nginx-alpine-writable-4-18-0)

Sensitive data should not be used in the ARG or ENV commands

SecretsUsedInArgOrEnv: Do not use ARG or ENV instructions for sensitive data (ENV "PROXY_SSL_CERT_KEY") More info: https://docs.docker.com/go/dockerfile/rule/secrets-used-in-arg-or-env/

Check warning on line 130 in nginx/Dockerfile-alpine

View workflow job for this annotation

GitHub Actions / build (nginx-alpine-writable-3-3-7)

Sensitive data should not be used in the ARG or ENV commands

SecretsUsedInArgOrEnv: Do not use ARG or ENV instructions for sensitive data (ENV "SSL_CERT_KEY") More info: https://docs.docker.com/go/dockerfile/rule/secrets-used-in-arg-or-env/

Check warning on line 130 in nginx/Dockerfile-alpine

View workflow job for this annotation

GitHub Actions / build (nginx-alpine-writable-3-3-7)

Sensitive data should not be used in the ARG or ENV commands

SecretsUsedInArgOrEnv: Do not use ARG or ENV instructions for sensitive data (ENV "PROXY_SSL_CERT_KEY") More info: https://docs.docker.com/go/dockerfile/rule/secrets-used-in-arg-or-env/
ACCESSLOG=/var/log/nginx/access.log \
BACKEND=http://localhost:80 \
CORS_HEADER_403_ALLOW_ORIGIN="*" \
Expand Down Expand Up @@ -225,7 +225,7 @@
COPY nginx/docker-entrypoint.d/*.sh /docker-entrypoint.d/
COPY src/opt/modsecurity/activate-plugins.sh /docker-entrypoint.d/94-activate-plugins.sh
COPY src/opt/modsecurity/configure-rules.sh /docker-entrypoint.d/95-configure-rules.sh
COPY src/opt/modsecurity/configure-rules.conf /docker-entrypoint.d/
COPY src/opt/modsecurity/configure-rules.*.conf /docker-entrypoint.d/
# We use the templating mechanism from the nginx image here.
COPY nginx/templates /etc/nginx/templates/
COPY src/bin/* /usr/local/bin/
Expand Down
49 changes: 35 additions & 14 deletions src/opt/modsecurity/configure-rules.sh
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,36 @@ if [ -n "${MANUAL_MODE}" ]; then
return
fi


# Default config file path
setup_conf_path="/etc/modsecurity.d/owasp-crs/crs-setup.conf"

# Accept optional config file
if [ -n "${CONFIG_FILE}" ]; then
setup_conf_path="${CONFIG_FILE}"
fi

# Detect CRS version based on the config file content
detect_crs_version() {
if grep -q "tx\.blocking_paranoia_level" "${setup_conf_path}"; then
echo "v4"
elif grep -q "tx\.paranoia_level" "${setup_conf_path}"; then
echo "v3"
else
echo "Unknown CRS version"
exit 10
fi
}

# Get the CRS version
CRS_VERSION="$(detect_crs_version)"
echo "Detected CRS config file version: ${CRS_VERSION}"

set_value() {
local rule="${1}"
local var_name="${2}"
local tx_var_name="${3}"
local var_value="${4}"
rule="${1}"
var_name="${2}"
tx_var_name="${3}"
var_value="${4}"

echo "Configuring ${rule} for ${var_name} with ${tx_var_name}=${var_value}"

# For each rule, we do one pass to uncomment the rule (up to first blank line after the rule),
Expand All @@ -45,6 +67,7 @@ EOF
# by either `,`, `'`, or `"`, depending on whether it's the last line of the rule
# and whether the expression is enclosed in single quotes.
# Use `#` as pattern delimiter, as `/` is part of some variable values.
# Try to find and update the variable (with or without quotes)
ed -s "${setup_conf_path}" <<EOF 2 > /dev/null
/id:${rule}/
/setvar:'\?tx\.${tx_var_name}=/
Expand All @@ -58,16 +81,15 @@ should_set() {
}

can_set() {
local rule="${1}"
local tx_var_name="${2}"
rule="${1}"
tx_var_name="${2}"

if ! grep -q "id:${rule}" "${setup_conf_path}"; then
return 1
elif ! grep -Eq "setvar:'?tx\.${tx_var_name}" "${setup_conf_path}"; then
return 1
else
return 0
fi
return 0
}

get_legacy() {
Expand All @@ -80,7 +102,7 @@ get_var_name() {

get_var_value() {
# Get the variable name, produce "${<var name>}" and use eval to expand
eval "echo $(echo "${1}" | awk -F'\|' '{print "${"$2"}"}')"
eval "echo $(echo "${1}" | awk -F'\|' '{print "${"$2"}"}' || true)"
}

get_rule() {
Expand All @@ -101,7 +123,6 @@ while read -r line; do
var_value="$(get_var_value "${line}")"
rule="$(get_rule "${line}")"
tx_var_name="$(get_tx_var_name "${line}")"

if should_set "${var_value}" "${tx_var_name}"; then
if ! can_set "${rule}" "${tx_var_name}"; then
if [ "${legacy}" = "true" ]; then
Expand All @@ -114,14 +135,14 @@ while read -r line; do

set_value "${rule}" "${var_name}" "${tx_var_name}" "${var_value}"
fi
done < "${DIRECTORY}/configure-rules.conf"
done < "${DIRECTORY}/configure-rules.${CRS_VERSION}.conf"

# Add SecDefaultActions
var="${MODSEC_DEFAULT_PHASE1_ACTION}"
if should_set "${var}"; then
if ! grep -Eq "^SecDefaultAction.*phase:1" "${setup_conf_path}"; then
echo "Failed to find definition of SecDefaultAction for phase 1 in ${setup_conf_path}. Aborting"
exit 1
exit 2
fi
ed -s "${setup_conf_path}" <<EOF 2 > /dev/null
/^SecDefaultAction.*phase:1/
Expand All @@ -133,7 +154,7 @@ var="${MODSEC_DEFAULT_PHASE2_ACTION}"
if should_set "${var}"; then
if ! grep -Eq "^SecDefaultAction.*phase:2" "${setup_conf_path}"; then
echo "Failed to find definition of SecDefaultAction for phase 2 in ${setup_conf_path}. Aborting"
exit 1
exit 3
fi
ed -s "${setup_conf_path}" <<EOF 2 > /dev/null
/^SecDefaultAction.*phase:2/
Expand Down
26 changes: 26 additions & 0 deletions src/opt/modsecurity/configure-rules.v3.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Format: <legacy (0|1)>|<env var>|<rule ID>|<tx var name>|<test value>
# The octothorpe (#) designates a comment, comments are ignored
# See `.github/workflows/configure-rules-for-test.sh` for how the test value is used.

# Superceded by BLOCKING_PARANOIA
true|PARANOIA|900000|paranoia_level|4
false|BLOCKING_PARANOIA|900000|paranoia_level|4
# Superceded by DETECTION_PARANOIA
true|EXECUTING_PARANOIA|900001|executing_paranoia_level|4
false|DETECTION_PARANOIA|900001|executing_paranoia_level|4
false|ENFORCE_BODYPROC_URLENCODED|900010|enforce_bodyproc_urlencoded|0
false|ANOMALY_INBOUND|900110|inbound_anomaly_score_threshold|6
false|ANOMALY_OUTBOUND|900110|outbound_anomaly_score_threshold|6
false|ALLOWED_METHODS|900200|allowed_methods|GET OPTIONS
false|ALLOWED_REQUEST_CONTENT_TYPE|900220|allowed_request_content_type|application/json
false|ALLOWED_REQUEST_CONTENT_TYPE_CHARSET|900280|allowed_request_content_type_charset|utf-8
false|ALLOWED_HTTP_VERSIONS|900230|allowed_http_versions|1.1
false|RESTRICTED_EXTENSIONS|900240|restricted_extensions|.exe/
false|RESTRICTED_HEADERS_BASIC|900250|restricted_headers|/if/
false|MAX_NUM_ARGS|900300|max_num_args|100
false|ARG_NAME_LENGTH|900310|arg_name_length|200
false|ARG_LENGTH|900320|arg_length|300
false|TOTAL_ARG_LENGTH|900330|total_arg_length|400
false|MAX_FILE_SIZE|900340|max_file_size|500
false|COMBINED_FILE_SIZES|900350|combined_file_sizes|600
false|VALIDATE_UTF8_ENCODING|900950|crs_validate_utf8_encoding|0
Loading