From 025816887107349391dcbe7d5fee50982ae189e3 Mon Sep 17 00:00:00 2001 From: Moctar Date: Thu, 28 Sep 2023 18:23:34 +0200 Subject: [PATCH 1/3] introduce nginx request limit variables --- .env | 16 ++++++++++++++++ docker/nginx/root/entrypoint.sh | 31 +++++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+) diff --git a/.env b/.env index 243d5d9137..5c5ddb8291 100644 --- a/.env +++ b/.env @@ -189,6 +189,22 @@ GATEWAY_ALLOWED_IPS= GATEWAY_DENIED_IPS= GATEWAY_USERS= +# activate and manage http incoming request limits +# this feature is based on ip adresses and need PHRASEANET_TRUSTED_PROXIES +# defined to get real_ip +# @run +GLOBAL_REQUEST_LIMIT_ACTIVATE=0 # switch to 1 to enable limiting http requests using parameters below +GLOBAL_REQUEST_LIMIT_MEMORY=10m # For Exemple 16,000 IP addresses takes 1 megabyte, so our zone can store about 160,000 addresses. +GLOBAL_REQUEST_LIMIT_RATE=10r/s # Sets the maximum request rate. By default here the rate cannot exceed 10 requests per second +GLOBAL_REQUEST_LIMIT_BURST=20 # The burst parameter defines how many requests a client can make in excess of the rate specified by the zone +GLOBAL_REQUEST_LIMIT_NODELAY=1 # With the nodelay parameter, NGINX still allocates slots in the queue according to the burst parameter and imposes the configured rate limit. if not extra request queues waits 2 seconds to be forwarded + +API_REQUEST_LIMIT_ACTIVATE=0 # switch to 1 to enable limiting api requests using parameters below +API_REQUEST_LIMIT_MEMORY=10m # For Exemple 16,000 IP addresses takes 1 megabyte, so our zone can store about 160,000 addresses. +API_REQUEST_LIMIT_RATE=10r/s # Sets the maximum request rate. By default here the rate cannot exceed 10 requests per second +API_REQUEST_LIMIT_BURST=20 # The burst parameter defines how many requests a client can make in excess of the rate specified by the zone +API_REQUEST_LIMIT_NODELAY=1 # With the nodelay parameter, NGINX still allocates slots in the queue according to the burst parameter and imposes the configured rate limit. if not extra request queues waits 2 seconds to be forwarded + # https and reverse proxy (on/off) # set to on in the case : https behind a proxy # @run diff --git a/docker/nginx/root/entrypoint.sh b/docker/nginx/root/entrypoint.sh index a002bb9112..adaa29572a 100755 --- a/docker/nginx/root/entrypoint.sh +++ b/docker/nginx/root/entrypoint.sh @@ -81,6 +81,37 @@ if [[ ! -z $GATEWAY_ALLOWED_IPS ]] || [[ ! -z $GATEWAY_DENIED_IPS ]] || [[ ! -z echo "deny all;" >> /etc/nginx/restrictions fi fi + +if [[ ! -z $GLOBAL_REQUEST_LIMIT_ACTIVATE ]]; then + echo "limit_req_zone \$binary_remote_addr zone=globaliplimits:$GLOBAL_REQUEST_LIMIT_MEMORY rate=$GLOBAL_REQUEST_LIMIT_RATE;" >> /etc/nginx/restrictions + if [[ ! -z $GLOBAL_REQUEST_LIMIT_BURST ]]; then + if [ "$GLOBAL_REQUEST_LIMIT_NODELAY" = "on" ] || [ "$GLOBAL_REQUEST_LIMIT_NODELAY" = "1" ];then + export GLOBAL_REQUEST_LIMITS = "limit_req zone=globaliplimits burst=$GLOBAL_REQUEST_LIMIT_BURST nodelay;" + else + export GLOBAL_REQUEST_LIMITS = "limit_req zone=globaliplimits burst=$GLOBAL_REQUEST_LIMIT_BURST;" + fi + else + export GLOBAL_REQUEST_LIMITS = "limit_req zone=globaliplimits;" + fi +fi + + + +if [[ ! -z $API_REQUEST_LIMIT_ACTIVATE ]]; then + echo "limit_req_zone \$binary_remote_addr zone=apiiplimits:$API_REQUEST_LIMIT_MEMORY rate=$API_REQUEST_LIMIT_RATE;" >> /etc/nginx/restrictions + if [[ ! -z $API_REQUEST_LIMIT_BURST ]]; then + if [ "$API_REQUEST_LIMIT_NODELAY" = "on" ] || [ "$API_REQUEST_LIMIT_NODELAY" = "1" ];then + export API_REQUEST_LIMITS = "limit_req zone=apiiplimits burst=$API_REQUEST_LIMIT_BURST nodelay;" + else + export API_REQUEST_LIMITS = "limit_req zone=apiiplimits burst=$API_REQUEST_LIMIT_BURST;" + fi + else + export API_REQUEST_LIMITS = "limit_req zone=apiiplimits;" + fi +fi + + + unset GATEWAY_USERS unset GATEWAY_DENIED_IPS unset GATEWAY_ALLOWED_IPS From 324a38cbfd9f1160da1aa37e77b203ec7fc345ed Mon Sep 17 00:00:00 2001 From: Moctar Date: Fri, 29 Sep 2023 12:04:51 +0200 Subject: [PATCH 2/3] PHRAS-3588 update entrypoint --- docker-compose.yml | 20 ++++++++++++++++++++ docker/nginx/root/entrypoint.sh | 27 +++++++++++++-------------- docker/nginx/root/nginx.conf.sample | 1 + 3 files changed, 34 insertions(+), 14 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 991fb0c4bb..3347a18f42 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -36,6 +36,16 @@ services: - GATEWAY_DENIED_IPS - GATEWAY_USERS - GATEWAY_CSP + - GLOBAL_REQUEST_LIMIT_ACTIVATE + - GLOBAL_REQUEST_LIMIT_MEMORY + - GLOBAL_REQUEST_LIMIT_RATE + - GLOBAL_REQUEST_LIMIT_BURST + - GLOBAL_REQUEST_LIMIT_NODELAY + - API_REQUEST_LIMIT_ACTIVATE + - API_REQUEST_LIMIT_MEMORY + - API_REQUEST_LIMIT_RATE + - API_REQUEST_LIMIT_BURST + - API_REQUEST_LIMIT_NODELAY ports: - ${PHRASEANET_APP_PORT}:80 networks: @@ -70,6 +80,16 @@ services: - GATEWAY_USERS - GATEWAY_FASTCGI_HTTPS - GATEWAY_CSP + - GLOBAL_REQUEST_LIMIT_ACTIVATE + - GLOBAL_REQUEST_LIMIT_MEMORY + - GLOBAL_REQUEST_LIMIT_RATE + - GLOBAL_REQUEST_LIMIT_BURST + - GLOBAL_REQUEST_LIMIT_NODELAY + - API_REQUEST_LIMIT_ACTIVATE + - API_REQUEST_LIMIT_MEMORY + - API_REQUEST_LIMIT_RATE + - API_REQUEST_LIMIT_BURST + - API_REQUEST_LIMIT_NODELAY networks: - internal labels: diff --git a/docker/nginx/root/entrypoint.sh b/docker/nginx/root/entrypoint.sh index adaa29572a..6d78fc814f 100755 --- a/docker/nginx/root/entrypoint.sh +++ b/docker/nginx/root/entrypoint.sh @@ -57,6 +57,7 @@ fi #GATEWAY_DENIED_IPS="172.1.0.1,172.1.0.2" #GATEWAY_USERS="user1(password1),user2(password2) touch /etc/nginx/restrictions +touch /etc/nginx/apirestrictions touch /etc/nginx/.htpasswd if [[ ! -z $GATEWAY_ALLOWED_IPS ]] || [[ ! -z $GATEWAY_DENIED_IPS ]] || [[ ! -z $GATEWAY_USERS ]]; then @@ -82,36 +83,34 @@ if [[ ! -z $GATEWAY_ALLOWED_IPS ]] || [[ ! -z $GATEWAY_DENIED_IPS ]] || [[ ! -z fi fi -if [[ ! -z $GLOBAL_REQUEST_LIMIT_ACTIVATE ]]; then - echo "limit_req_zone \$binary_remote_addr zone=globaliplimits:$GLOBAL_REQUEST_LIMIT_MEMORY rate=$GLOBAL_REQUEST_LIMIT_RATE;" >> /etc/nginx/restrictions +if [[ ! -z $GLOBAL_REQUEST_LIMIT_ACTIVATE ]] && [[ "$GLOBAL_REQUEST_LIMIT_ACTIVATE" = "1" ]]; then + echo "limit_req_zone \$binary_remote_addr zone=globaliplimits:$GLOBAL_REQUEST_LIMIT_MEMORY rate=$GLOBAL_REQUEST_LIMIT_RATE;" >> /etc/nginx/conf.d/limits.conf if [[ ! -z $GLOBAL_REQUEST_LIMIT_BURST ]]; then if [ "$GLOBAL_REQUEST_LIMIT_NODELAY" = "on" ] || [ "$GLOBAL_REQUEST_LIMIT_NODELAY" = "1" ];then - export GLOBAL_REQUEST_LIMITS = "limit_req zone=globaliplimits burst=$GLOBAL_REQUEST_LIMIT_BURST nodelay;" + export GLOBAL_REQUEST_LIMITS="limit_req zone=globaliplimits burst=$GLOBAL_REQUEST_LIMIT_BURST nodelay;" else - export GLOBAL_REQUEST_LIMITS = "limit_req zone=globaliplimits burst=$GLOBAL_REQUEST_LIMIT_BURST;" + export GLOBAL_REQUEST_LIMITS="limit_req zone=globaliplimits burst=$GLOBAL_REQUEST_LIMIT_BURST;" fi else - export GLOBAL_REQUEST_LIMITS = "limit_req zone=globaliplimits;" + export GLOBAL_REQUEST_LIMITS="limit_req zone=globaliplimits;" fi + echo $GLOBAL_REQUEST_LIMITS >> /etc/nginx/restrictions fi - - -if [[ ! -z $API_REQUEST_LIMIT_ACTIVATE ]]; then - echo "limit_req_zone \$binary_remote_addr zone=apiiplimits:$API_REQUEST_LIMIT_MEMORY rate=$API_REQUEST_LIMIT_RATE;" >> /etc/nginx/restrictions +if [[ ! -z $API_REQUEST_LIMIT_ACTIVATE ]] && [[ "$API_REQUEST_LIMIT_ACTIVATE" = "1" ]]; then + echo "limit_req_zone \$binary_remote_addr zone=apiiplimits:$API_REQUEST_LIMIT_MEMORY rate=$API_REQUEST_LIMIT_RATE;" >> /etc/nginx/conf.d/limits.conf if [[ ! -z $API_REQUEST_LIMIT_BURST ]]; then if [ "$API_REQUEST_LIMIT_NODELAY" = "on" ] || [ "$API_REQUEST_LIMIT_NODELAY" = "1" ];then - export API_REQUEST_LIMITS = "limit_req zone=apiiplimits burst=$API_REQUEST_LIMIT_BURST nodelay;" + export API_REQUEST_LIMITS="limit_req zone=apiiplimits burst=$API_REQUEST_LIMIT_BURST nodelay;" else - export API_REQUEST_LIMITS = "limit_req zone=apiiplimits burst=$API_REQUEST_LIMIT_BURST;" + export API_REQUEST_LIMITS="limit_req zone=apiiplimits burst=$API_REQUEST_LIMIT_BURST;" fi else - export API_REQUEST_LIMITS = "limit_req zone=apiiplimits;" + export API_REQUEST_LIMITS="limit_req zone=apiiplimits;" fi + echo $API_REQUEST_LIMITS >> /etc/nginx/apirestrictions fi - - unset GATEWAY_USERS unset GATEWAY_DENIED_IPS unset GATEWAY_ALLOWED_IPS diff --git a/docker/nginx/root/nginx.conf.sample b/docker/nginx/root/nginx.conf.sample index 7b37ac4b80..0958434447 100644 --- a/docker/nginx/root/nginx.conf.sample +++ b/docker/nginx/root/nginx.conf.sample @@ -26,6 +26,7 @@ server { if (-f /var/alchemy/Phraseanet/datas/nginx/maintenance.html) { return 401; } + include apirestrictions; rewrite ^(.*)$ /api.php/$1 last; } From 6050156ae1481a59a1e51c63ad84106dcb46df66 Mon Sep 17 00:00:00 2001 From: Moctar Date: Fri, 29 Sep 2023 12:16:08 +0200 Subject: [PATCH 3/3] PHRAS-3588 set response status code to 429 --- docker/nginx/root/entrypoint.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docker/nginx/root/entrypoint.sh b/docker/nginx/root/entrypoint.sh index 6d78fc814f..3ffa2f6880 100755 --- a/docker/nginx/root/entrypoint.sh +++ b/docker/nginx/root/entrypoint.sh @@ -83,6 +83,8 @@ if [[ ! -z $GATEWAY_ALLOWED_IPS ]] || [[ ! -z $GATEWAY_DENIED_IPS ]] || [[ ! -z fi fi +echo "limit_req_status 429;" > /etc/nginx/conf.d/limits.conf + if [[ ! -z $GLOBAL_REQUEST_LIMIT_ACTIVATE ]] && [[ "$GLOBAL_REQUEST_LIMIT_ACTIVATE" = "1" ]]; then echo "limit_req_zone \$binary_remote_addr zone=globaliplimits:$GLOBAL_REQUEST_LIMIT_MEMORY rate=$GLOBAL_REQUEST_LIMIT_RATE;" >> /etc/nginx/conf.d/limits.conf if [[ ! -z $GLOBAL_REQUEST_LIMIT_BURST ]]; then