Skip to content

Commit

Permalink
wip: adds example commands, ftw
Browse files Browse the repository at this point in the history
  • Loading branch information
M4tteoP committed Apr 24, 2023
1 parent 889ae47 commit 332f6fc
Show file tree
Hide file tree
Showing 16 changed files with 398 additions and 25 deletions.
23 changes: 23 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,29 @@

OWASP Coraza WAF is 100% compatible with OWASP Coreruleset and Modsecurity syntax.

## Getting started

`go run mage.go -l` lists all the available commands:

```bash
▶ go run mage.go -l
Targets:
build builds the plugin.
buildLinux builds the plugin with GOOS=linux.
check runs lint and tests.
coverage runs tests with coverage and race detector enabled.
doc runs godoc, access at http://localhost:6060
e2e runs e2e tests with a built plugin against the example deployment.
format formats code in this repository.
ftw runs CRS regressions tests.
lint verifies code quality.
precommit installs a git hook to run check when committing
reloadExample reload the test environment.
runExample spins up the test environment, access at http://localhost:8080.
teardownExample tears down the test environment.
test runs all tests.
```

## Plugin syntax

```caddy
Expand Down
Empty file added caddy.log
Empty file.
4 changes: 2 additions & 2 deletions e2e/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
FROM debian

COPY e2e/Caddyfile /etc/caddy/Caddyfile
COPY example/Caddyfile /etc/caddy/Caddyfile

COPY build/caddy-linux /usr/bin/caddy

CMD ["/usr/bin/caddy", "run", "--config", "/etc/caddy/Caddyfile", "--adapter", "caddyfile"]
CMD ["/usr/bin/caddy", "run", "--config", "/etc/caddy/Caddyfile", "--adapter", "caddyfile"]
2 changes: 1 addition & 1 deletion e2e/Dockerfile.curl
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ RUN apk add --no-cache bash
COPY ./e2e/e2e-example.sh /workspace/e2e-example.sh
RUN cat /workspace/e2e-example.sh

ENV ENVOY_HOST=caddy:8080
ENV CADDY_HOST=caddy:8080
ENV HTTPBIN_HOST=httpbin:8081

CMD ["bash","-c", "/workspace/e2e-example.sh"]
1 change: 0 additions & 1 deletion e2e/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ services:
build:
context: ..
dockerfile: ./e2e/Dockerfile

tests:
depends_on:
- caddy
Expand Down
36 changes: 18 additions & 18 deletions e2e/e2e-example.sh
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
#!/bin/bash
# Copyright 2022 The OWASP Coraza contributors
# SPDX-License-Identifier: Apache-2.0
# Copied from https://github.com/corazawaf/coraza-proxy-wasm/tree/main/e2e
ENVOY_HOST=${ENVOY_HOST:-"localhost:8080"}
# Revisited from https://github.com/corazawaf/coraza-proxy-wasm/tree/main/e2e
CADDY_HOST=${CADDY_HOST:-"localhost:8080"}
HTTPBIN_HOST=${HTTPBIN_HOST:-"localhost:8081"}
TIMEOUT_SECS=${TIMEOUT_SECS:-5}

[[ "${DEBUG}" == "true" ]] && set -x

# if env variables are in place, default values are overridden
health_url="http://${HTTPBIN_HOST}"
envoy_url_unfiltered="http://${ENVOY_HOST}"
envoy_url_filtered="${envoy_url_unfiltered}/admin"
envoy_url_filtered_resp_header="${envoy_url_unfiltered}/status/406"
envoy_url_echo="${envoy_url_unfiltered}/anything"
url_unfiltered="http://${CADDY_HOST}"
url_filtered="${url_unfiltered}/admin"
url_filtered_resp_header="${url_unfiltered}/status/406"
url_echo="${url_unfiltered}/anything"

tueNegativeBodyPayload="This is a payload"
truePositiveBodyPayload="maliciouspayload"
Expand Down Expand Up @@ -90,63 +90,63 @@ wait_for_service "${health_url}" 15
# Testing envoy container reachability with an unfiltered GET request
((step+=1))
echo "[${step}/${total_steps}] (onRequestheaders) Testing true negative request"
wait_for_service "${envoy_url_echo}?arg=arg_1" 20
wait_for_service "${url_echo}?arg=arg_1" 20

# Testing filtered request
((step+=1))
echo "[${step}/${total_steps}] (onRequestheaders) Testing true positive custom rule"
check_status "${envoy_url_filtered}" 403
check_status "${url_filtered}" 403
# This test ensures the response body is empty on interruption. Specifically this makes
# sure no body is returned although actionContinue is passed in phase 3 & 4.
# See https://github.com/corazawaf/coraza-proxy-wasm/pull/126
check_body "${envoy_url_filtered}" true
check_body "${url_filtered}" true

# Testing body true negative
((step+=1))
echo "[${step}/${total_steps}] (onRequestBody) Testing true negative request (body)"
check_status "${envoy_url_echo}" 200 -X POST -H 'Content-Type: application/x-www-form-urlencoded' --data "${tueNegativeBodyPayload}"
check_status "${url_echo}" 200 -X POST -H 'Content-Type: application/x-www-form-urlencoded' --data "${tueNegativeBodyPayload}"

# Testing body detection
((step+=1))
echo "[${step}/${total_steps}] (onRequestBody) Testing true positive request (body)"
check_status "${envoy_url_unfiltered}" 403 -X POST -H 'Content-Type: application/x-www-form-urlencoded' --data "${truePositiveBodyPayload}"
check_status "${url_unfiltered}" 403 -X POST -H 'Content-Type: application/x-www-form-urlencoded' --data "${truePositiveBodyPayload}"

# Testing response headers detection
((step+=1))
#echo "[${step}/${total_steps}] (onResponseHeaders) Testing true positive"
#check_status "${envoy_url_filtered_resp_header}" 403
#check_status "${url_filtered_resp_header}" 403

# TODO(M4tteoP): Update response body e2e after https://github.com/corazawaf/coraza-proxy-wasm/issues/26
# Testing response body true negative
((step+=1))
echo "[${step}/${total_steps}] (onResponseBody) Testing true negative"
check_body "${envoy_url_unfiltered}" false -X POST -H 'Content-Type: application/x-www-form-urlencoded' --data "${trueNegativeBodyPayloadForResponseBody}"
check_body "${url_unfiltered}" false -X POST -H 'Content-Type: application/x-www-form-urlencoded' --data "${trueNegativeBodyPayloadForResponseBody}"

# Testing response body detection
((step+=1))
echo "[${step}/${total_steps}] (onResponseBody) Testing true positive"
check_body "${envoy_url_echo}" true -X POST -H 'Content-Type: application/x-www-form-urlencoded' --data "${truePositiveBodyPayloadForResponseBody}"
check_body "${url_echo}" true -X POST -H 'Content-Type: application/x-www-form-urlencoded' --data "${truePositiveBodyPayloadForResponseBody}"

## Testing extra requests examples from the readme and some CRS rules in anomaly score mode.

# Testing XSS detection during phase 1
((step+=1))
echo "[${step}/${total_steps}] Testing XSS detection at request headers"
check_status "${envoy_url_echo}?arg=<script>alert(0)</script>" 403
check_status "${url_echo}?arg=<script>alert(0)</script>" 403

# Testing SQLI detection during phase 2
((step+=1))
echo "[${step}/${total_steps}] Testing SQLi detection at request body"
check_status "${envoy_url_echo}" 403 -X POST --data "1%27%20ORDER%20BY%203--%2B"
check_status "${url_echo}" 403 -X POST --data "1%27%20ORDER%20BY%203--%2B"

# Triggers a CRS scanner detection rule (913100)
((step+=1))
echo "[${step}/${total_steps}] (onRequestBody) Testing CRS rule 913100"
check_status "${envoy_url_echo}" 403 --user-agent "Grabber/0.1 (X11; U; Linux i686; en-US; rv:1.7)" -H "Host: localhost" -H "Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5"
check_status "${url_echo}" 403 --user-agent "Grabber/0.1 (X11; U; Linux i686; en-US; rv:1.7)" -H "Host: localhost" -H "Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5"

# True negative GET request with an usual user-agent
((step+=1))
echo "[${step}/${total_steps}] True negative GET request with user-agent"
check_status "${envoy_url_echo}" 200 --user-agent "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36"
check_status "${url_echo}" 200 --user-agent "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36"

echo "[Done] All tests passed"
5 changes: 2 additions & 3 deletions e2e/Caddyfile → example/Caddyfile
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
Include @owasp_crs/*.conf
SecRuleEngine On
SecDebugLog /dev/stdout
SecDebugLogLevel 3
SecDebugLogLevel 9
SecRule REQUEST_URI "@streq /admin" "id:101,phase:1,t:lowercase,deny,status:403"
SecRule REQUEST_BODY "@rx maliciouspayload" "id:102,phase:2,t:lowercase,deny,status:403"
SecRule RESPONSE_HEADERS::status "@rx 406" "id:103,phase:3,t:lowercase,deny,status:403"
Expand All @@ -21,6 +21,5 @@
SecRule RESPONSE_BODY "@contains responsebodycode" "id:104,phase:4,t:lowercase,deny,status:403"
`
}

reverse_proxy httpbin:8081
}
}
7 changes: 7 additions & 0 deletions example/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
FROM debian

COPY ../example/Caddyfile /etc/caddy/Caddyfile

COPY build/caddy-linux /usr/bin/caddy

CMD /usr/bin/caddy run --config /etc/caddy/Caddyfile --adapter caddyfile 2>&1 | tee /home/caddy/logs/caddy.log
54 changes: 54 additions & 0 deletions example/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
services:
httpbin:
image: mccutchen/go-httpbin:v2.5.0
environment:
- MAX_BODY_SIZE=15728640 # 15 MiB
command: [ "/bin/go-httpbin", "-port", "8081" ]
ports:
- 8081:8081

chown:
image: alpine:3.16
command:
- /bin/sh
- -c
- chown -R 101:101 /home/caddy/logs
volumes:
- logs:/home/caddy/logs:rw

caddy:
depends_on:
- httpbin
build:
context: ..
dockerfile: ./example/Dockerfile
volumes:
- logs:/home/caddy/logs:rw
ports:
- 8080:8080

caddy-logs:
depends_on:
- caddy
- coraza-logs
image: debian:11-slim
entrypoint: bash
command:
- -c
- tail -c +0 -f /home/caddy/logs/caddy.log
volumes:
- logs:/home/caddy/logs:ro

coraza-logs:
depends_on:
- caddy
image: debian:11-slim
entrypoint: bash
command:
- -c
- tail -c +0 -f /home/caddy/logs/caddy.log | grep --line-buffered "http.handlers.waf"
volumes:
- logs:/home/caddy/logs:ro

volumes:
logs:
51 changes: 51 additions & 0 deletions ftw/Caddyfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
{
debug
auto_https off
order coraza_waf first
}

:8080 {
coraza_waf {
directives `
Include @coraza.conf-recommended
# FTW config
SecDefaultAction "phase:3,log,auditlog,pass"
SecDefaultAction "phase:4,log,auditlog,pass"
SecDefaultAction "phase:5,log,auditlog,pass"
SecDebugLogLevel 3
SecAction "id:900005,\
phase:1,\
nolog,\
pass,\
ctl:ruleEngine=DetectionOnly,\
ctl:ruleRemoveById=910000,\
setvar:tx.blocking_paranoia_level=4,\
setvar:tx.crs_validate_utf8_encoding=1,\
setvar:tx.arg_name_length=100,\
setvar:tx.arg_length=400,\
setvar:tx.total_arg_length=64000,\
setvar:tx.max_num_args=255,\
setvar:tx.max_file_size=64100,\
setvar:tx.combined_file_sizes=65535"
SecRule REQUEST_HEADERS:X-CRS-Test "@rx ^.*$" \
"id:999999,\
phase:1,\
pass,\
t:none,\
log,\
msg:'X-CRS-Test %{MATCHED_VAR}',\
ctl:ruleRemoveById=1-999999"
Include @crs-setup.conf.example
Include @owasp_crs/*.conf
`
}
# Only access logs are logged via log directive, ftw is looking for error logs
# log {
# output file /home/caddy/logs/caddy.log
# level error
# }
reverse_proxy httpbin:8081
}
7 changes: 7 additions & 0 deletions ftw/Dockerfile.caddy
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
FROM debian

COPY ftw/Caddyfile /etc/caddy/Caddyfile

COPY build/caddy-linux /usr/bin/caddy

CMD /usr/bin/caddy run --config /etc/caddy/Caddyfile --adapter caddyfile 2>&1 | tee /home/caddy/logs/caddy.log
27 changes: 27 additions & 0 deletions ftw/Dockerfile.ftw
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Copyright 2022 The OWASP Coraza contributors
# SPDX-License-Identifier: Apache-2.0

FROM ghcr.io/coreruleset/go-ftw:0.4.9

RUN apk update && apk add curl

WORKDIR /workspace

# TODOs:
# - update when new CRS version is tagged: https://github.com/coreruleset/coreruleset/archive/refs/tags/v4.0.0-rc1.tar.gz
# - keep it aligned with the https://github.com/corazawaf/coraza-coreruleset commit used
ADD https://github.com/coreruleset/coreruleset/tarball/477d8c3431d042294af2651f08d63d10b6f3fd60 /workspace/coreruleset/
RUN cd coreruleset && tar -xf 477d8c3431d042294af2651f08d63d10b6f3fd60 --strip-components 1

# Logs are written in json format, therefore we need to tweak the tests to match this output format
# Replaces `id rule-id` with `id \"rule-id\"` across all tests
RUN cd coreruleset/tests/regression/tests/ && find . -type f -name "*.yaml" -exec sed -r -i 's/id "([0-9]*)"/id \\\\"\1\\\\"/g' {} +
# Replaces `"id \"rule-id\""` with `id \"rule-id\"` across all tests
RUN cd coreruleset/tests/regression/tests/ && find . -type f -name "*.yaml" -exec sed -r -i 's/"id \\"([0-9]*)\\""/id \\\\\"\1\\\\\"/g' {} +

COPY ftw.yml /workspace/ftw.yml
COPY tests.sh /workspace/tests.sh

ENTRYPOINT ["sh"]
CMD ["-c", "/workspace/tests.sh"]

63 changes: 63 additions & 0 deletions ftw/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
services:
httpbin:
image: mccutchen/go-httpbin:v2.5.0
command: [ "/bin/go-httpbin", "-port", "8081" ]
ports:
- 8081:8081

chown:
image: alpine:3.16
command:
- /bin/sh
- -c
- chown -R 101:101 /home/caddy/logs
volumes:
- logs:/home/caddy/logs:rw
caddy:
depends_on:
- chown
- httpbin
build:
context: ..
dockerfile: ./ftw/Dockerfile.caddy
volumes:
- ../build:/build
- .:/conf
- logs:/home/caddy/logs:rw
ports:
- 8080:8080
coraza-logs:
depends_on:
- caddy
image: debian:11-slim
entrypoint: bash
command:
- -c
- tail -c +0 -f /home/caddy/logs/caddy.log | grep --line-buffered "http.handlers.waf" > /home/caddy/logs/ftw.log
volumes:
- logs:/home/caddy/logs:rw
caddy-logs:
depends_on:
- caddy
image: debian:11-slim
entrypoint: bash
command:
- -c
- tail -c +0 -f /home/caddy/logs/caddy.log > /build/ftw-caddy.log
volumes:
- logs:/home/caddy/logs:ro
- ../build:/build
ftw:
depends_on:
- caddy-logs
- coraza-logs
build:
dockerfile: Dockerfile.ftw
environment:
- FTW_CLOUDMODE
- FTW_INCLUDE
volumes:
- logs:/home/caddy/logs:ro
- ../build:/build
volumes:
logs:
Loading

0 comments on commit 332f6fc

Please sign in to comment.