ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Nginx] Elastic Beanstalk Nginx RateLimit 설정 (1)
    Nginx 2024. 4. 20. 17:36
    반응형

    서비스를 구축할 때 DDoS와 같은 공격을 방지하기 위한 효과적인 방법 중 하나는 동일한 IP로부터 들어오는 요청에 대한 속도(요청) 제한을 설정하는 것입니다. 이를 위해 Nginx의 Rate Limit 방식을 활용하여 공격으로부터의 보호를 강화할 수 있습니다. 이번 포스팅에서는 Elastic Beanstalk에 배포할 때 Nginx 설정 파일을 Override하여 Rate Limit을 적용하는 방법에 대해 소개 하겠습니다.

     

     

     

    Nginx Rate Limit 방식은 Leaky Bucket 알고리즘과 비슷한 방식으로 동작하여, 일정 속도로 흘러 들어오는 요청을 허용하고 초과하는 요청은 일정 기간 동안 기다리거나 거부하는 메커니즘을 제공합니다.

     

    Leaky Bucket Algorithm:

    Leaky Bucket Algorithm은 특정한 속도로 데이터를 허용하고, 초과하는 데이터는 그릇(버킷)에서 떨어뜨리는 개념입니다.

    이를 Nginx Rate Limit에서는 요청이 버킷(Zone)에 쌓이고, 설정된 속도에 따라 버킷에서 떨어지도록 하는 방식으로 구현됩니다.

     

    https://donghyeon.dev/%EC%9D%B8%ED%94%84%EB%9D%BC/2022/03/18/%EC%B2%98%EB%A6%AC%EC%9C%A8-%EC%A0%9C%ED%95%9C-%EC%9E%A5%EC%B9%98%EC%9D%98-%EC%84%A4%EA%B3%84/

     


     

    이와 같은 Nginx conf 파일을 설정해서 Elastic Beanstalk에 배포를 해야하는데, 기존에는 .ebextendsions를 이용하여 배포를

    진행하면 됐었지만, Amazon Linux 2에서 아래와 같은 설정과 관련한 변경사항이 생겼습니다.

    Proxy configuration files provided in the .ebextensions/nginx directory should move to the 
    .platform/nginx platform hooks directory. For details, expand the Reverse Proxy Configuration section in Extending Elastic Beanstalk Linux platforms.

    https://docs.aws.amazon.com/elasticbeanstalk/latest/dg/using-features.migration-al.html

     

     

    위 내용대로 nginx의 디렉토리 위치가 .platform 아래로 변경되었고, nginx의 설정 변경을 위해서는

    .platform/nginx/conf.d/ 위치에 conf 파일을 생성해야 합니다.


     

    본 포스팅에서는 Spring Boot 애플리케이션을 배포할 때, RateLimit 설정 파일을 같이 배포하는 방법까지
    알아보겠습니다.

     

    Nginx Rate Limit 동작 방식:

    1. limit_req_zone 설정

    • 클라이언트 IP 주소를 기반으로 한 limit_req_zone 지시어를 통해 Rate Limit이 적용될 존(Zone)을 설정합니다.
    • 이 존은 클라이언트 IP 주소 및 요청 횟수를 기록하고 관리하는데 사용됩니다.
    http {
        limit_req_zone $binary_remote_addr zone=mylimit:10m rate=10r/s;
        # ...
    }
    
    # binary_remote_addr : 클라이언트의 IP를 기준으로 제한 한다는 설정
    # zone name : 사용자 임의 zone name 설정
    # share memory assign : 10M
    # rate : 10 request / second

     

    • limit_req_zone: Request를 제한하기 위해서 특정한 영역(zone)을 선언
    • $binary_remote_addr: nginx 에서 기본적으로 제공하는 내장 변수로, 클라이언트의 IP를 기준으로 제한
    • rate 는 요청-request 의 비율로, 여기에서는 초당 10개 이상의 요청-request 이 유입되면 제한을 하겠다는 의미입니다.
      (r/s, r/m 가능)

     

     

    2. limit_req 모듈 설정

    • limit_req모듈을 사용하여 실제로 Rate Limit을 적용할 위치를 설정합니다.
    • 이때, 앞서 설정한 존을 참조하여 설정된 속도에 따라 요청을 제어합니다.
    server {
        location / {
            limit_req zone=mylimit burst=5;        
            # ... 다른 설정들 ...
        }
    }
    • location: '/' 은 모든 요청 경로에 대해서 limit_req를 적용 한다는 의미입니다. 원하는 경로마다 설정 할 수 있습니다.
    • burst: rate(여기서는 10r/s) 이상의 request-요청에 대해서 5개 까지는 queue에 보관하도록 하고, 그 이상은 에러를 반환하게 한다는 의미입니다.

     

     

    3. 동작 과정

    • 클라이언트의 요청이 들어오면, 해당 클라이언트의 IP 주소를 기반으로 한 Zone에 요청이 기록됩니다.
    • 설정된 속도에 따라 허용 가능한 요청이 된다면, 요청은 처리되고 Zone에서 카운터가 감소합니다.
    • 설정된 속도를 초과하는 경우, 클라이언트는 일정 시간 동안 대기하거나, 요청이 거부됩니다.

     

    위와 같이 limit_req 모듈을 활용하여 Nginx RateLimit을 적용할 수 있습니다.

    이제 실제로 .platform 디렉토리 하위에 conf 파일을 생성 해보겠습니다.

     

     

    먼저 프로젝트의 루트 디렉토리 하위에 /platform/nginx/conf.d/elasticbeanstalk 디렉토리를 생성합니다.

     해당 디렉토리 하위에 00_application.conf 파일을 생성합니다.

     

    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;
    }

     

     

    limit_req 지시어 관련 설명은 아래와 같습니다.

    • proxy_rate_limit zone을 기반으로 RateLimit을 설정 합니다.
    • 'burst=20' 은 초과 요청에 대한 허용된 최대 "번버스트"를 나타냅니다. 최대 요청 제한보다 많은 요청이 들어온 경우,
      에러를 반환하지 않고 대기했다가 요청을 보낼 수 있을때 처리하는 개념입니다. 여기서는 20개까지는 초과 요청을 허용합니다.
    • burst 설정을 하면, Delay가 심해져 사용자에게 느리게 응답될 수 있습니다. 이 때 요청들 사이에 허용되는 시간 간격을 제한하지 않고 속도 제한을 적용하는 역할을 하는 것이 'nodelay' 입니다.
    • Rate Limit을 초과한 경우에 반환할 상태 코드를 429 Too Many Requests로 설정 했습니다. 기본은 503 에러를 반환합니다.

    proxy_set_header 관련 설명은 아래와 같습니다.

    1. proxy_set_header   X-Real-IP   $remote_addr

    • 이 설정은 Nginx에서 클라이언트의 실제 IP 주소를 백엔드 서버로 전달하기 위한 것입니다. 
    • $remote_addr는 Nginx가 수신한 클라이언트의 IP 주소를 나타냅니다. 
    • 이렇게 설정함으로써 백엔드 서버는 클라이언트의 원본 IP 주소를 알 수 있게 되어, 로그 및 기타 작업에서 실제 클라이언트의 IP를 사용할 수 있습니다.

     

    2. proxy_set_header   X-Forwarded-For   $proxy_add_x_forwarded_for

    • 이 설정은 웹 서버 사이를 통과하는 각 프록시 서버의 IP 주소를 기록합니다. 
    • $proxy_add_x_forwarded_for는 현재까지의 X-Forwarded-For 헤더에 추가로 현재 요청을 보낸 프록시의 IP 주소를 추가합니다.
    • 따라서 이 헤더는 클라이언트에서부터부터 현재까지의 모든 프록시 서버를 통과한 IP 주소 목록을 나타냅니다.

     

    3. proxy_set_header   X-Real-Forwarded-For   $http_x_forwarded_for;

    • 이 설정은 X-Forwarded-For 헤더를 백엔드 서버로 전달하는 역할을 합니다.
    • $http_x_forwarded_for는 클라이언트에서부터 받은 X-Forwarded-For 헤더 값을 그대로 전달합니다.
    • 이렇게 함으로써 백엔드 서버는 프록시 서버를 통과한 IP 주소 정보를 신뢰할 수 있게 됩니다.

    Rate Limit 예외 경로  설정

    RateLimit을 적용 하지 않을 예외 API 경로를 설정할 수 있습니다. 공통된 패턴을 가지는 경로를 설정하는 방법과 단일 경로를 지정하는 방법 두 가지가 있습니다. 각 설정 방법은 아래와 같습니다.

     

    1. 공통된 패턴을 가지는 경로 설정

    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;
    }

     

     

    2. 단일 경로 지정

    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;
    }

     

    위 내용처럼 limit_req_zone을 설정하지 않은 기본 location 설정을 선언하면 됩니다.


    에러 커스텀

    limit_req 설정에서 limi_req_status 429 부분을 통해서 429 에러를 반환하도록 커스텀 하였습니다.

    하지만 이 에러 또한 HTTP Code를 200으로 반환 하도록 하고 서비스의 사용자 지정 에러 Json을 반환 하도록 설정할 수 있습니다. 뒤 내용에 나오겠지만, 아래 처럼 429 에러의 경우는 location /429.json으로 전송하겠다고 설정해주면 됩니다.

     

    📘 00_application.conf

    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}';
    }

     

     

    📘 rate_limit.conf (다음 포스팅에서 다룹니다)

    error_page 429 = /429.json;

     

    다음 포스팅에서 작성할 conf 파일에 location /429.json 를 지정하게 되면 설정한대로 return 200을 통해 HTTP Code 200을 반환하고 원하는 Json 포멧을 지정하여 응답할 수 있습니다.

     

     

     

    내용이 길어지는 관계로 다음 포스팅에 이어서 작성 하겠습니다.

    https://developer-been.tistory.com/59

     

     

     

     

     

    reference:

    https://www.nginx.com/blog/rate-limiting-nginx/

    https://findstar.pe.kr/2018/06/24/nginx-rate-limiting/

    https://willseungh0.tistory.com/191

    반응형

    'Nginx' 카테고리의 다른 글

    [Nginx] Elastic Beanstalk Nginx RateLimit 설정 (2)  (1) 2024.04.20

    댓글

Designed by Tistory.