본문 바로가기

카테고리 없음

[Nginx] 엔진엑스 요청 제한

 

 

 

1. Nginx의 요청 제한

Nginx의 기능 중 하나인 요청 제한은 사용자가 주어진 시간 동안 받을 수 있는 HTTP 요청의 양을 제한할 수 있는 기능

 

 

2. 사용 이유

  • 속도 제한은 무차별 암호 대입 공격을 늦추는 것과 같은 보안 목적으로 사용할 수 있음
  • 들어오는 요청 속도를 실제 사용자에게 일반적인 값으로 제한하고 로깅을 통해 대상 URL을 식별하여 DDos공격으로부터 보호할 수 있음

 

 

3. 기본 요청 제한 구성(configuration)

  • 보통 default.conf 파일 내에 기재
  • 기본 요청 제한을 구성하는 limit_req_zone, limit_req 지시문을 이용하여 구성

 

1) limit_req_zone

  • 요청 제한을 위한 매개변수 정의
  • http 블록에 정의되어 여러 컨텍스트에서 사용(여기서 말하는 컨텍스트는 location에 설정하는 것이 세부 컨텍스트라면 http블록에 정의 되니까 모든 location에 사용하는 것을 정의한다고 볼 수 있음)
  • 매개변수 3개(Key, Zone, Rate)를 사용함
  • 사용 예시
limit_req_zone $binary_remote_addr zone=mylimit:10m rate=10r/s;

① Key

  • 제한이 적용되는 특성 정의
  • 클라이언트의 IP 주소를 작성
  • $Binary_remote_addr은 클라이언트 IP 주소를 나타내는 변수

 

② Zone

  • 각 IP 주소의 상태와 요청 제한 URL에 액세스한 빈도를 저장하는 데 사용되는 공유 메모리 영역 정의
  • 정보를 공유 메모리에 보관한다는 것은 NGINX 작업자 프로세스 간 정보를 공유할 수 있음을 의미
  • Nginx가 새 항목을 추가해야할 때 스토리지가 고갈되면 가장 오래된 항목을 제거함
  • 메모리 고갈 방지를 위해서 새 항목을 생성할 때마다 이전 60초 동안 사용되지 않는 항목을 최대 2개 제거
  • 사용 가능한 공간이 부족한 경우 Nginx 상태 코드 503을 반환
  • 사용법: zone=키워드로 식별되는 영역 이름:크기

 

예시에서는 공유 메모리의 크기를 10m로 설정하였음

Nginx가 1개 IP 주소당 64바이트의 정보를 저장하므로, 공유 메모리를 10MB로 설정했다는 것은 

약 160,000개의 주소를 저장 할 수 있다는 것(1개 IP * 64Byte이므로 10MB/64Byte를 계산하면 163,840이 나옴)

 

③ Rate

  • 최대 요청 비율 설정

예시는 초당 10개 요청을 초과할 수 없음을 나타냄

실제로 Nginx는 밀리초 단위로 추적하므로 이 제한은 100ms마다 요청 1개를 받는 것을 의미함

여러 요청이 100ms에 들어오는데 아래 나올 burst를 허용하지 않은 동시요청의 경우 1개 요청 외에는 거절 당함

 

 

2) limit_req

  • 설정한 컨텍스트 내에서 요청 제한 활성화
  • limet_req_zone은 요청 제한 및 공유 메모리 영역에 대한 매개변수를 설정한다면, limit_req는 요청 속도를 제한함
  • 특정 위치 또는 server 블록에 제한 적용

 

 

4. 버스트 처리(Handling Bursts)

  • 초과 요청을 버퍼링하고 적시에 서비스하게 하는 구성
  • limit_req에 burst 매개변수를 사용 

예시

location /login/ { 
    limit_req zone=mylimit burst=20;
    proxy_pass http://my_upstream; 
}

 

① burst 매개변수

  • 클라이언트가 영역에 지정된 속도를 초과하여 만들 수 있는 요청 수 정의(대기열을 정의한다고 생각하면 됨)
  • 예시와 같이 주어진 경로에 대기열의 크기를 20으로 정의했다면
  • 21개 요청이 동시 도착 시 1개 요청을 업스트림 서버 그룹으로 전달(요청 진행), 
  • 나머지 20개는 대기열에 넣고 limit_req의 zone에 정의한 시간마다 대기열에 있는 요청을 전달함,
  • 대기열에 20개의 요청 수가 있는 상황에서 새로운 요청이 들어오면 503 상태 코드를 반환함

 

② 지연 없는 대기열 nodleay

  • burst가 있는 구성은 트래픽 흐름은 원활하게 이루어지지만 사이트가 느려질 수 있으므로 실용적이지 않음
  • 대기열에 있다가 응답을 받은 경우 무의미한 응답이 될 수 있음
  • 이 상황을 해결하라면 burst 매개변수와 함께 nodelay 매개변수를 사용할 수 있음
  • nodelay 매개변수를 사용하면 burst 매개변수에 따라 대기열에 슬롯을 할당하고 구성된 요청 제한을 하지만대기 중인 요청의 전달에 간격을 두지 않음
  • 예시
location /login/ { 
    limit_req zone=mylimit burst=20 nodelay;
    proxy_pass http://my_upstream; 
}

예를들어 20의 대기열(슬롯)이 있고, IP에 21개의 요청이 동시에 도착하는 경우라면

21개의 요청을 모두 즉시 전달하고, 대기열의 20개는 사용중으로 표시하여 추가 요청은 거절하고 100ms 마다 1개의 대기열(슬롯)을 해제하여 요청을 1개 추가로 받는 형태

 

0초에 21개 동시요청이 들어온 경우

nodelay가 없는 경우 0초 시점에 1개의 요청 전달 / 대기열에 20개의 요청 대기 / 100ms마다 요청 1개씩 전달하고, 대기열이 꽉 차있는 경우 요청 거절, 예를들어 501ms 시점에 20개의 요청이 들어온다면 5개의 요청을 대기열에 대기시킴

nodelay가 있는 경우 0초 시점에 21개의 요청 전달 / 대기열에 20개의 요청 대기있는 것으로 간주 / 특정 시점에 요청이 들어왔을 때, 예를 들어 501ms 시점에 20개의 요청이 들어온다면 5개의 요청 즉시 전달하고 15개는 거절-> 줄을 세워서 시간마다 요청을 1개씩 보내는 것

 

결과적으로 차이는 nodelay를 설정안한다면 nginx자체에서 대기열에 세워두고 설정한 시간마다 요청을 전달하니까 nginx가 제어, nodelay는 요청을 대기열 사이즈만큼은 즉시 전달하므로 실제 처리는 요청을 받는 해당 서버가 처리순서에 대한 처리를해야함 -> 설정한 사이즈만큼 동시 요청 받음

 

③ 2단계 요청 제한

  • Nginx 오픈소스 1.15.7 버전 이상부터 요청 burst를 허용하여 nginx를 구성한 다음 추가 과도한 요청이

거부되는 지점까지 추가 초과 요청을 제한할 수 있음

  • 예시코드
limit_req_zone $binary_remote_addr zone=ip:10m rate=5r/s;

server 
{ 
     listen 80; 
     location / {
        limit_req zone=ip burst=12 delay=8;
        proxy_pass http://website;
    }
}

 

초당 5개의 요청 제한 적용

burst 12는 대기열의 크기, delay 8은 지연 없이 즉시 처리하는 수

8개 이후에 지연 처리한다는 것은 12개 초과 시 요청 거절,  8개 초과 ~ 12개 이하의 4개 요청에 대해서는 초당 5개의 요청에 초과되지 않도록  지연 처리

 

 

5. 요청 제한 고급 구성

기본 요청 제한을 Nginx의 다른 기능을 결합하여 트래픽 제한을 미묘하게 설정할 수 있음

 

1) 허용 목록(Allowlisting)

"허용 목록"에 없는사람을 요청 제한

 

2) 한 location에 여러 limit_req   

limit_req를 여러개 설정하면 지정된 요청과 일치하는 모든 제한이 적용됨

한 개의 limit_req 요청과 일치하면 그것으로 제한되고, 두 개의 limit_req 와 일치하면 두 개 중 더 제한된 설정이 적용됨

 

 

6. 관련 기능 구성

 

1) Logging

error 레벨로 거부된 요청을 기록

warn 레벨로 지연된 요청을 기록

기록 레벨을 변경하려면 limit_req_log_level을 사용하여 변경할 수 있음

 

2) 클라이언트에게 전송된 오류 코드

속도 제한을 초과할 때는 503(서비스를 일시적으로 사용할 수 없음)으로 응답

limit_req_status를 사용하여 다른 상태코드를 설정할 수 있음

 

3) 특정 location의 모든 요청 거부

특정 URL에 대한 모든 요청을 거부하려면 deny를 사용

 

 

 

참고: 요청 제한 Nginx 구현