-
[Nginx] Elastic Beanstalk Nginx RateLimit 설정 (2)Nginx 2024. 4. 20. 17:49반응형
✍️ 저번 포스팅에서는 limit_req module, limit_req_zone, 사용자 지정 에러 등을 설정 했습니다.
이번 포스팅에서는 RateLimit 설정 마무리와 각 서비스에서 필요한 예외 설정까지 진행하고
최종적으로 AWS ElasticBeanstalk를 배포할 때 적용하는 방법까지 알아보겠습니다.
1. Client Max Body Size 제한
📘 /project/platform/nginx/conf.d/rate_limit.conf
# limit body_size client_max_body_size 5M;
클라이언트가 전송할 수 있는 요청 본문의 크기를 제한 할 수 있는 설정입니다. 위 예시는 클라이언트가 보낼 수 있는 요청의 본문 크기를 5MB로 제한합니다.
2. Nginx 설정에서 클라이언트의 실제 IP 주소 가져오기
📘 /project/platform/nginx/conf.d/rate_limit.conf
# real-ip set_real_ip_from {Your load balancer IP}; real_ip_header X-Forwarded-For;
로드 밸런서를 사용하여 서버에 요청을 전달할 때, 클라이언트의 실제 IP 주소를 가져올 수 있는 설정입니다. 기본적으로 로드 밸런서를 통과하는 요청은 로드 밸런서의 IP 주소가 서버에 전달됩니다. 따라서 서버에서 http request 객체에서 사용자의 IP를 조회하면 로드 밸런서의 IP주소가 출력 됩니다. 이때, 로드 밸런서의 주소가 아닌 실제 요청한 클라이언트의 IP 주소를 가져오기 위해 해당 Nginx의 설정을 사용할 수 있습니다.
set_real_ip_from 지시문에는 실제 클라이언트의 IP 주소를 알고 있는 로드 밸런서의 IP 주소를 입력합니다.real_ip_header 지시문은 실제 클라이언트 IP 주소가 요청 헤더의 어떤 필드에 들어있는지를 지정합니다. 대부분의 경우, 클라이언트의 실제 IP 주소는 X-Forwarded-For 헤더에 들어있습니다.
3. RateLimit 예외 IP 변수 설정
📘 /project/platform/nginx/conf.d/rate_limit.conf
geo $limited { default 1; # exclude IP 123.123.123.123/25 0; 111.11.11.11 0; 222.22.22.22 0; }
- NGINX 변수인 $limited를 설정합니다.
- default 1: 기본값으로 $limited 변수를 1로 설정합니다.
- 예시로 입력한 특정 IP 주소나 범위의 경우 $limited 변수 값을 0으로 설정합니다.
이 설정은 주어진 IP 주소나 범위를 제외한 모든 경우에는 $limited 변수가 1로 설정되고, 특정 IP 주소나 범위에 해당하는 경우에는 $limited 변수가 0으로 설정됩니다. 이를 통해 특정 IP 주소나 범위에서 오는 요청을 제어하거나 허용할 수 있습니다. 해당 포스팅에서는 Rate Limit을 적용하지 않을 예외 IP를 지정하기 위해서 사용합니다.
4. RateLimit 예외 Web Method 설정
📘 /project/platform/nginx/conf.d/rate_limit.conf
map $request_method $limited_method { GET 0; OPTIONS 0; default 1; }
- $limited_method 변수는 NGINX의 $request_method 모듈을 이용합니다.
- GET 및 OPTIONS 메서드에 대해 $limited_method 변수를 0으로 설정합니다.
- 기본적으로 모든 요청 메서드에 대해 default 1 설정을 통해 $limited_method 변수를 1로 설정합니다.
보통 웹사이트의 메인 페이지 같은 경우에 클라이언트 사이드에서 많은 GET, OPTIONS 요청을 하거나 이 외에도 조회성 API를 한번에 여러번 호출 하는 경우가 많습니다. 하지만 이 요청까지 RateLimit을 적용하게 되면 필요한 API들이 호출 되지 못하는 문제가 발생합니다. 이 설정은 이와 같은 경우를 위해서 GET 및 OPTIONS 메서드를 0으로 설정하여 해당 요청 메서드로 오는 요청을 제어하고, 나머지 요청 메서드에 대해서는 기본적으로 허용하기 위해서 1로 설정합니다.
5. RateLimit 설정
📘 /project/platform/nginx/conf.d/rate_limit.conf
map $limited$limited_method $limit { 00 ""; 01 ""; 10 ""; 11 $binary_remote_addr; }
- $limited와 $limited_method 변수는 이전 설정에서 정의되었습니다.
- 00, 01, 10 및 11은 $limited와 $limited_method 변수의 가능한 조합을 나타냅니다.
- $binary_remote_addr는 클라이언트의 이진 형태 IP 주소를 나타냅니다.
위 설정은 $limited 변수 값이 1이고 $limited_method 변수 값이 1이면 Nginx RateLimit을 적용하겠다는 의미입니다.
즉, 위에서 설정한 각 예외 케이스들을 제외한 경우에만 Nginx RateLimit을 적용합니다.
📘 /project/platform/nginx/conf.d/rate_limit.conf
limit_req_zone $limit zone=proxy_rate_limit:100m rate=60r/m;
위 설정은 $limit 변수에 따라 요청을 그룹화하고, 이를 특정 제한 속도(여기서는 100m의 메모리 공간에 저장)로 제어합니다. rate=60r/m은 초당 최대 60개의 요청을 허용한다는 것을 의미합니다. 따라서 이 설정은 초당 최대 60개의 요청을 초과하는 경우에 대한 제한을 설정합니다. 여기서 $limit 변수는 위에서 설정한대로, 예외 케이스의 경우 "" 빈 문자열로 적용이 되므로 Rate Limit이 적용 되지 않습니다. 그리고 그렇지 않은 경우에는 클라이언트의 IP 주소를 대상으로 RateLimit이 적용 됩니다.
6. 에러 페이지 경로 설정
📘 /project/platform/nginx/conf.d/rate_limit.conf
error_page 413 = /413.json; error_page 429 = /429.json;
error_page 지시어는 nginx에서 특정 HTTP 오류 코드에 대한 처리 방법을 설정하는데 사용됩니다. 여기서는 413과 429 오류 코드에 대한 처리 방법을 설정하고 있습니다.
- 클라이언트가 요청 본문의 크기가 서버가 허용하는 최대 크기를 초과할 경우 발생하는 413 Request Entity Too Large 오류를 처리합니다. 이 오류가 발생하면 클라이언트에게 /413.json 경로에 정의된 JSON 파일을 반환합니다.
- 서버에서 요청 속도 제한을 초과한 경우 발생하는 429 Too Many Requests 오류를 처리합니다. 이 오류가 발생하면 클라이언트에게 /429.json 경로에 정의된 JSON 파일을 반환합니다. 413, 429 json 응답값은 이전 포스팅에서 정의 했었습니다.
이로써 각 서비스에 필요할만한 예외 설정들과 RateLimit 설정을 마무리 했습니다. 최종 설정 파일은 아래와 같습니다.
📘 /project/platform/nginx/conf.d/elasticbeanstalk/00_application.conf
location / { limit_req zone=proxy_rate_limit burst=20 nodelay; limit_req_status 429; proxy_pass http://127.0.0.1:5000; proxy_http_version 1.1; proxy_set_header Connection $connection_upgrade; proxy_set_header Upgrade $http_upgrade; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Real-Forwarded-For $http_x_forwarded_for; } location ~ ^/api/result/(A|B|C|D)/callback { proxy_pass http://127.0.0.1:5000; proxy_http_version 1.1; proxy_set_header Connection $connection_upgrade; proxy_set_header Upgrade $http_upgrade; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Real-Forwarded-For $http_x_forwarded_for; } location /api/result/callback { proxy_pass http://127.0.0.1:5000; proxy_http_version 1.1; proxy_set_header Connection $connection_upgrade; proxy_set_header Upgrade $http_upgrade; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Real-Forwarded-For $http_x_forwarded_for; } location /413.json { add_header 'Content-Type' 'application/json charset=UTF-8'; add_header 'Access-Control-Allow-Headers' 'Content-Type, baggage, sentry-trace'; add_header 'Access-Control-Allow-Credentials' true; add_header 'Access-Control-Allow-Origin' $allow_origin; return 200 '{"code": 413, "message": "Client intended to send too large body", "body": null, "errors": null}'; } location /429.json { add_header 'Content-Type' 'application/json charset=UTF-8'; add_header 'Access-Control-Allow-Headers' 'Content-Type, baggage, sentry-trace'; add_header 'Access-Control-Allow-Credentials' true; add_header 'Access-Control-Allow-Origin' $allow_origin; return 200 '{"code": 429, "message": "Too many requests. Please try again later.", "body": null, "errors": null}'; }
📘 /project/platform/nginx/conf.d/rate_limit.conf
# limit body_size client_max_body_size 5M; # real-ip set_real_ip_from {Your load balancer IP}; real_ip_header X-Forwarded-For; geo $limited { default 1; # exclude IP 123.123.123.123/25 0; 111.11.11.11 0; 222.22.22.22 0; } map $request_method $limited_method { GET 0; OPTIONS 0; default 1; } map $limited$limited_method $limit { 00 ""; 01 ""; 10 ""; 11 $binary_remote_addr; } map $http_origin $allow_origin { ~^(https?://.*)$ $1; } limit_req_zone $limit zone=proxy_rate_limit:100m rate=60r/m; error_page 413 = /413.json; error_page 429 = /429.json;
최종적으로 AWS Elasticbeanstalk에 Spring Boot Project jar 파일을 배포할 때,
RateLimit Nginx conf 파일들을 포함하여 zip 파일로 압축한 뒤 배포를 하게 되면 Nginx 설정이 자동으로
적용 됩니다.📘 ci script example
- ./mvnw clean package - mkdir temp - mkdir .platform && cp -r ./platform/nginx .platform - mv .platform temp && cp $JAR_PATH temp - cd temp && rm -rf .platform/nginx/conf.d/rate_limit.conf - zip -r $ZIP_PATH .platform web-$MAVEN_VERSION.jar
위 스크립트와 같이 nginx conf 파일들과 jar를 zip으로 압축한 뒤, AWS Elasticbeanstalk에 배포를 하면 됩니다.
정리
Ddos 공격 방어, 분당 요청 수 제한 등을 위해서 Nginx의 Rate Limit을 적용할 수 있으며 Rate Limit을 적용하지 않을 예외 IP나 API 경로도 지정하여 커스텀 할 수 있습니다. 또한 AWS Elasticbeanstalk에 서버를 배포하는 환경일 때, Nginx Rate Limit 설정 파일을 같이 포함하게 된다면, 자동으로 Beanstalk이 Nginx 설정을 override 하여 적용 해줍니다.
감사합니다.
반응형'Nginx' 카테고리의 다른 글
[Nginx] Elastic Beanstalk Nginx RateLimit 설정 (1) (3) 2024.04.20