NGINX JavaScript 모듈을 사용한 가상 패치

이번 포스트는 NGINX JavaScript 모듈을 사용하여 가상 패치를 진행하는 방법에 대해 설명합니다.

가상 패치는 코드 자체가 아닌 관련 인프라를 변경하여 애플리케이션 코드의 문제를 해결하는 것을 말합니다. 보안 영역에서는 ModSecurity를 사용하여 취약성을 가상으로 패치하는 것이 일반적입니다. 그러나 가상 패치는 운영 환경에서 흔히 볼 수 있는 Backend 애플리케이션의 버그와 같은 다른 유형의 버그에도 적용될 수 있습니다. 다양한 이유로 이러한 버그를 직접 수정하는 것은 어려울 수 있으며(예: 원래 개발자가 회사를 떠난 경우) 가상 패치가 실질적인 대안입니다.

NGINX Plus 고객이 최근에 비정상적인 문제를 경험했습니다. 클라이언트 측 애플리케이션이 소문자로 get 및 post 요청을 하고 있었습니다. Backend 애플리케이션은 RFC 7231 섹션 4.1에 명시된 대로 대문자를 사용해야 하므로 요청을 처리할 수 없습니다. 고객은 Backend 애플리케이션 코드를 수정할 방법이 없었습니다.

지원팀과 협력하여 고객은 NGINX JavaScript 모듈을 사용하여 소문자 get 및 post를 대문자 GET 및 POST로 변환한 후 NGINX Plus가 Backend 애플리케이션으로 요청을 Proxy 하는 가상 패치를 개발했습니다. NGINX JavaScript 모듈을 사용하면 JavaScript를 사용하여 Open Source 및 상용 제품 모두에서 고급 구성을 수행할 수 있습니다. 이 포스트는 NGINX Plus와 NGINX Open Source 모두에 적용됩니다. 읽기 쉽도록 NGINX라는 용어는 둘 다를 나타냅니다.

이 포스트는 JS_IMPORT 지시문을 사용하도록 업데이트되었으며 NGINX Plus R23 이상의 JS_INCLUDE 지침을 대체합니다. 자세한 내용은 NGINX JavaScript 모듈에 대한 참조 문서를 참조하십시오. 예제 구성 섹션은 NGINX 구성 및 JavaScript 파일에 대한 올바른 구문을 보여줍니다.

목차

1. NGINX Javascript 가상 패치 생성
1-1. 가상 패치에 대한 TCP/UDP 구성
1-2. 가상 패치에 대한 HTTP 구성

1-3. 가상 패치의 JavaScript 코드
2. NGINX Javascript 가상 패치 결론

1. NGINX Javascript 가상 패치 생성

가상 패치는 트래픽을 수정할 수 없는 Backend 애플리케이션에 Proxy 하는 기존 HTTP 가상 서버 앞에 새로운 TCP/UDP 가상 서버를 추가하여 작동합니다. stream {} 컨텍스트의 TCP/UDP 가상 서버는 NGINX JavaScript 모듈을 사용하여 콘텐츠를 필터링할 수 있습니다.

이 블로그의 경우 기본 구성 파일, /etc/nginx/nginx.conf에 포함된 지시문을 포함하여 기능별 구성 및 JavaScript 코드를 HTTP {} 컨텍스트와 stream {} 컨텍스트로 읽기 위한 기존 체계를 사용하고 있습니다.

http {
    include conf.d/*.conf;
    include conf.d/*.js;
}

stream {
    include stream.d/*.conf;
    include stream.d/*.js;
}

그런 다음 /etc/nginx에 conf.d 및 stream.d 하위 디렉터리를 생성하고 HTTP 및 TCP/UDP에 대한 기능 별 .conf 및 .js 파일을 각각 배치합니다.

또는 지시문을 nginx.conf에 직접 배치할 수 있습니다. 이 경우 Snippets을 http {} 또는 stream {} 블록에 적절하게 배치해야 합니다.

1-1. 가상 패치에 대한 TCP/UDP 구성

JavaScript 코드를 NGINX Plus 구성으로 읽으려면 아래 2행에서 stream {} 블록의 JS_Import 지시문과 이름을 지정합니다. (JavaScript 코드 분석은 아래 가상 패치에 대한 JavaScript 코드를 참조하십시오.)

새 가상 서버 (3-11행)를 정의하고 JS_Filter 지시문 (8 행)으로 JavaScript 함수를 호출합니다. NGINX Plus는 포트 81 (9 행)에서 HTTP 가상 서버로 변환 된 요청을 Proxy합니다. NGINX Plus을 HTTP 가상 서버로 전달할 수 있도록 Proxy 프로토콜 (10행)을 활성화합니다.

# Place or include in the stream{} context
js_import stream.d/methods.js;
server {
    listen 80;
    listen 443 ssl;
    ssl_certificate     /etc/nginx/certs/bundle.crt;
    ssl_certificate_key /etc/nginx/certs/key.pem;
    js_filter methods.method_up;
    proxy_pass 127.0.0.1:81;
    proxy_protocol on;
}

1-2. 가상 패치에 대한 HTTP 구성

또한 Backend 애플리케이션에 대한 요청을 처리하는 기존 HTTP 가상 서버를 수정하여 포트 81에서 수신하고 PROXY 프로토콜(3행)을 사용하여 원래 클라이언트 IP 주소가 전달되도록 합니다.

# Place or include in the http{} context
server {
    listen 81 proxy_protocol;

    set_real_ip_from 127.0.0.1;
    real_ip_header proxy_protocol;

    # existing configuration (location blocks, etc.) for virtual server
}

1-3. 가상 패치의 JavaScript 코드

가상 패치에 대한 다음 JavaScript 코드를 /etc/nginx/stream.d 디렉토리의 Methods.js라는 파일에 넣습니다. 소문자 get 및 post 요청을 사용하여 대문자 GET 및 POST를 사용하는 요청을 변환합니다.

function method_up(s) {
    var proxy_proto_header = '';
    var req = '';

    s.on('upload', function(data, flags) {
        var n;

	  req += data;
	  n = req.search('\n');

	  // Forward past PROXY Protocol header if present
	  if (n != -1 && req.startsWith('PROXY ')) {
		  proxy_proto_header = req.substr(0, n+1);
		  req = req.substr(n+1); 
		  n = req.search('\n');
	  }
	  if (n != -1) {
		  req = req.replace(/^(get|post)(\s\S+\sHTTP\/\d\.\d)/, function(m,method,uri_version) {
			  return method.toUpperCase() + uri_version;
		  });
		  s.send(proxy_proto_header + req, flags);
		  s.off('upload');
	   }
     });
}

export default { method_up }

변수 (5 행)는 클라이언트 요청의 모든 관련 정보를 캡처합니다. 코드는 요청에서 PROXY 프로토콜 헤더의 끝을 건너뛰고 다음 Chunk된 사용자 데이터 (요청 줄 끝의 Newline까지)를 캡처하여 req 변수에 씁니다.

가상 패치는 PCRE -스타일 정규 표현식 (18 행)을 사용하여 메서드 이름을 대문자로 변환하고 나머지 요청을 그대로 캡처합니다. 대부분의 정규 표현식과 마찬가지로 이 표현식 또 해석하기 쉽지 않지만 다음 예제는 요청 문자열을 처리하는 방법을 보여줍니다.

Request Lineget http://www.example.com/ HTTP/1.1
Regular expression^(get|post)(\s\S+\sHTTP\/1\.\d)

첫 번째 캡처 그룹 (첫 번째 괄호에서)은 get 또는 post에서 정확히 일치합니다.

두 번째 캡처 그룹에서 :

  • \ s는 GET 또는 POST 후 공간과 일치합니다.
  • \S+는 공백이 아닌 하나 이상의 문자(이 경우 URL(주황색 텍스트)와 일치합니다.
  • 두 번째 \s는 URL 뒤의 공백과 일치합니다.
  • HTTP\/1.d는 문자열 HTTP/1.1(녹색 텍스트)과 일치합니다. 보다 정확하게는 \d는 임의의 숫자와 일치하므로 정규 표현식은 HTTP/1.0 요청에도 사용할 수 있습니다.

Note: 정규식은 이론적으로 HTTP/2.0과 일치하지만 프로토콜은 이진 형식의 헤더를 사용합니다. 정규식이 이진 문자열과 일치하지 않으므로 이 가상 패치는 HTTP/2.0에서 작동하지 않습니다.

두 캡처 그룹의 일치는 메서드 및 URI_Version 변수에 각각 저장됩니다. 메서드는 HTTP 메서드를 보유하고 URI_Version은 URL 및 HTTP 버전을 보유합니다 (18 행). 그런 다음 표준 javaScript 함수 replace () (18 ) 및 toupperCase () (19 행) HTTP 메서드를 대문자로 변환합니다.

업데이트된 RFC -Compliant 요청 헤더는 21 행에서 s.send ()를 사용하여 전송됩니다.

2. NGINX Javascript 가상 패치 결론

지금까지 NGINX JavaScript 모듈을 사용하여 가상 패치를 생성하는 방법에 대해 설명했습니다. JavaScript의 기능을 통해 NGINX Plus의 요청 및 응답 데이터를 적합한 방법으로 수정할 수 있습니다. 이를 통해 생산 문제에 신속하게 대응하고 해결할 수 있는 강력한 솔루션을 제공합니다.

NGINX Plus와 JavaScript 모듈을 직접 사용해 보거나 테스트해 보려면 지금 30일 무료 평가판을 신청하거나 사용 사례에 대해 최신 소식을 빠르게 전달받고 싶으시면 아래 뉴스레터를 구독하세요.