diff --git a/.docker/nginx/conf.d/default.conf b/.docker/nginx/conf.d/default.conf index c225ccc..4a89eed 100644 --- a/.docker/nginx/conf.d/default.conf +++ b/.docker/nginx/conf.d/default.conf @@ -54,4 +54,5 @@ server { expires max; log_not_found off; } + include /etc/nginx/server.d/*.conf; } diff --git a/README.md b/README.md index 9cc7eac..a3fcfaa 100644 --- a/README.md +++ b/README.md @@ -110,6 +110,20 @@ On each startup (when WordPress is already installed), the entrypoint can automa Use only one strategy at a time. For local environments importing production data, resetting all users is usually the simplest approach. +### Nginx server snippets + +Every `*.conf` file mounted at `/etc/nginx/server.d/` is included at the end of the `server` block, so an environment can tune nginx without replacing `default.conf`: + +```yaml + nginx: + volumes: + - ./nginx/client-max-body-size.conf:/etc/nginx/server.d/client-max-body-size.conf:ro +``` + +```nginx +client_max_body_size 0; +``` + ### Database dump If you need to bootstrap the environment with existing data, place your SQL dump in the folder below: diff --git a/tests/security/helpers/nginx.bash b/tests/security/helpers/nginx.bash index 845e434..f83d941 100644 --- a/tests/security/helpers/nginx.bash +++ b/tests/security/helpers/nginx.bash @@ -85,6 +85,14 @@ nginx_hardening_integration_setup() { php:8.3-fpm)" } +nginx_hardening_server_snippet() { + local name="$1" + local content="$2" + + mkdir -p "${nginx_hardening_tmp}/server.d" + printf '%s\n' "${content}" > "${nginx_hardening_tmp}/server.d/${name}" +} + nginx_hardening_integration_start() { local value="${1-__UNSET__}" local -a environment_args=() @@ -93,11 +101,17 @@ nginx_hardening_integration_start() { environment_args=(-e "WORDPRESS_XMLRPC_ENABLED=${value}") fi + local -a server_snippets_args=() + if [ -d "${nginx_hardening_tmp}/server.d" ]; then + server_snippets_args=(-v "${nginx_hardening_tmp}/server.d:/etc/nginx/server.d:ro") + fi + nginx_hardening_container="$(docker run -d --rm "${environment_args[@]}" \ --network "${nginx_hardening_network}" \ -v "${nginx_hardening_root}/.docker/nginx/conf.d/default.conf:/etc/nginx/conf.d/default.conf:ro" \ -v "${nginx_hardening_script}:/docker-entrypoint.d/40-xmlrpc-hardening.sh:ro" \ -v "${nginx_hardening_tmp}/document-root:/var/www/html:ro" \ + "${server_snippets_args[@]}" \ -p 127.0.0.1::80 nginx:latest)" nginx_hardening_port="$(docker port "${nginx_hardening_container}" 80/tcp | sed 's/.*://')" @@ -136,9 +150,15 @@ nginx_hardening_config_is_valid() { nginx_hardening_request() { local method="$1" local path="$2" + local request_body_file="${3:-}" local body_file="${nginx_hardening_tmp}/response-body" + local -a data_args=() + + if [ -n "${request_body_file}" ]; then + data_args=(--data-binary "@${request_body_file}") + fi - curl -sS -X "${method}" -o "${body_file}" -w '%{http_code}' \ + curl -sS -X "${method}" "${data_args[@]}" -o "${body_file}" -w '%{http_code}' \ "http://127.0.0.1:${nginx_hardening_port}${path}" } diff --git a/tests/security/nginx-server-snippets.bats b/tests/security/nginx-server-snippets.bats new file mode 100644 index 0000000..a8f2884 --- /dev/null +++ b/tests/security/nginx-server-snippets.bats @@ -0,0 +1,33 @@ +#!/usr/bin/env bats + +load 'helpers/nginx.bash' + +setup() { + nginx_hardening_integration_setup + head -c 2048 /dev/zero > "${nginx_hardening_tmp}/request-body" +} + +teardown() { + nginx_hardening_integration_stop +} + +@test "real nginx config starts without server snippets" { + nginx_hardening_integration_start + nginx_hardening_config_is_valid + + run nginx_hardening_request POST /xmlrpc.php "${nginx_hardening_tmp}/request-body" + [ "${status}" -eq 0 ] + nginx_hardening_assert_status 200 "${output}" + nginx_hardening_assert_upstream_reached +} + +@test "real nginx config applies server snippets" { + nginx_hardening_server_snippet client-max-body-size.conf 'client_max_body_size 1k;' + nginx_hardening_integration_start + nginx_hardening_config_is_valid + + run nginx_hardening_request POST /xmlrpc.php "${nginx_hardening_tmp}/request-body" + [ "${status}" -eq 0 ] + nginx_hardening_assert_status 413 "${output}" + nginx_hardening_assert_upstream_not_reached +}