NGINX Access Log: 조건부 로깅 샘플링 요청
NGINX는 처리하는 모든 트랜잭션의 매우 상세한 로그를 기록할 수 있습니다.
이러한 로그를 Access Log 라고 하며 사용자 지정 가능한 로그 파일 형식을 사용하여 다양한 서비스 또는 위치에 대해 기록되는 세부 정보를 미세 조정할 수 있습니다.
기본적으로 NGINX는 처리하는 모든 트랜잭션을 기록합니다.
이는 규정 준수 또는 보안 목적에 필요할 수 있지만 사용량이 많은 웹 사이트의 경우 생성되는 데이터의 양이 압도적일 수 있습니다.
해당 포스트에서는 다양한 기준에 따라 트랜잭션을 선택적으로 기록하는 방법과 해당 지식을 사용하여 빠르고 가벼운 방식으로 요청에 대한 데이터 포인트를 샘플링하는 방법을 보여줍니다.
명시된 경우를 제외하고 해당 포스트의 내용은 NGINX 오픈소스와 NGINX Plus 모두에 적용됩니다.
읽기 쉽도록 전체적으로 NGINX를 참조합니다.
목차
1. NGINX Access Log 구성에 대한 빠른 개요
1-1. 조건부 로깅
1-2. Access Log 는 상속되지 않음
2. Access Log 의 문제
3. 샘플링 기법
3-1. 1% 요청에 대한 샘플링
3-2. 1%의 고유 사용자에 대한 샘플링
3-3. Unique 샘플링
4. 실제 사례 – TLS 매개변수 샘플링
5. 결론
1. NGINX Access Log 구성에 대한 빠른 개요
NGINX Access Log 는 log_format
디렉티브를 사용하여 정의됩니다. 요청에 대한 세 가지 데이터 지점을 기록하기 위해 main이라는 이름의 전체 로그 형식과 notes라는 이름의 약어 로그 형식과 같은 여러 가지 명명된 로그 형식을 정의할 수 있습니다.
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
log_format notes '$remote_addr "$request" $status';
로그 형식은 로깅 시 계산된 NGINX 변수 및 기타 값을 참조할 수 있습니다.
그런 다음 access_log
지시문를 사용하여 트랜잭션이 완료되면 로그에 기록하도록 NGINX에 지시합니다. 이 지시문은 로그 파일의 위치와 사용할 로그 형식을 지정합니다
access_log /var/log/nginx/access.log main;
기본적으로 NGINX는 다음 구성을 사용하여 모든 트랜잭션을 기록합니다.
access_log logs/access.log combined;
고유한 access_log 를 정의하면 기본 Access Log를 재정의(대체)합니다.
1-1. 조건부 로깅
때로는 특정 요청만 기록하고 싶을 수도 있습니다. 이는 다음과 같이 조건부 로깅을 사용하여 수행됩니다.
server {
listen 80;
set $logme 0;
if ( $uri ~ ^/secure ) {
set $logme 1;
}
# Auditors require an additional log for requests to /secure
access_log /var/log/nginx/secure.log notes if=$logme;
# If we have a global access log, we need to re-declare it here
access_log /var/log/nginx/access.log main;
location / {
# ...
}
}
1-2. Access Log 는 상속되지 않음
Access Log 설정은 스택 또는 상속되지 않습니다. 한 컨텍스트의 access_log
디렉티브는 상위 컨텍스트에서 선언된 Access Log를 재정의(replaces)합니다.
예를 들어 트래픽에 대한 추가 정보를 URI/secure에 기록하려는 경우 /secure {…} location에 Access Log를 정의할 수 있습니다…} 차단합니다. 이 Access Log는 구성의 다른 위치에 정의된 일반 Access Log를 대체합니다.
이전 섹션의 예제에서는 이 문제를 해결합니다. 동일한 컨텍스트에서 /secure에 대한 요청을 전용 로그 파일에 기록하는 조건부 로깅과 함께 두 개의 Access Log를 사용합니다.
2. Access Log 의 문제
웹사이트 트래픽에 대한 몇 가지 통계 정보를 확인하려고 한다고 가정합니다.
- 사용자의 일반적인 지리적 분할은 어떻게 됩니까?
- 내 사용자는 어떤 SSL/TLS 암호 및 프로토콜을 사용합니까?
- 웹 브라우저의 분할은 무엇입니까?
일반 Access Log 는 종종 이 정보를 기록하기에 적합한 장소가 아닙니다. 연구에 필요한 추가 필드로 Access Log를 오염시키고 싶지 않을 수 있으며 사용량이 많은 사이트에서는 모든 트랜잭션을 기록하는 오버헤드가 너무 높을 수 있습니다.
이 경우 제한된 필드 집합을 특수 로그에 기록할 수 있습니다. 시스템의 부하를 줄이기 위해 요청의 하위 집합을 샘플링할 수도 있습니다.
3. 샘플링 기법
3-1. 1% 요청에 대한 샘플링
다음 구성에서는 $request_id
변수를 각 요청의 고유 식별자로 사용합니다. split_clients
블록을 사용하여 요청의 1%만 기록하여 데이터를 샘플링합니다:
split_clients $request_id $logme {
1% 1;
* 0;
}
server {
listen 80;
access_log /var/log/nginx/secure.log notes if=$logme;
# ...
}
3-2. 1%의 고유 사용자에 대한 샘플링
각 사용자(또는 사용자의 1%)로부터 User-Agent 헤더와 같은 하나의 데이터 포인트를 샘플링한다고 가정합니다. 많은 수의 요청을 생성하는 사용자가 데이터에 너무 많이 표시되므로 모든 요청에서 샘플을 추출할 수는 없습니다.
Map 블록을 사용하여 세션 쿠키의 존재를 탐지합니다. 이 쿠키는 요청이 새 사용자로부터 온 것인지 이전에 본 사용자로부터 온 것인지를 알려줍니다. 그런 다음 신규 사용자의 요청만 샘플링합니다.
map $cookie_SESSION $logme {
"" $perhaps; # If the cookie is missing, we log if $perhaps
default 0;
}
split_clients $request_id $perhaps {
1% 1; # $perhaps is true 1% of the time
* 0;
}
server {
listen 80;
access_log /var/log/nginx/secure.log notes if=$logme;
# Optional: If the application does not generate a session cookie, we
# generate our own
add_header Set-Cookie SESSION=1;
# ...
}
3-3. Unique 샘플링
그러나 모든 클라이언트가 Session Cookie를 준수하는 것은 아닙니다. 예를 들어, 웹 스파이더는 쿠키를 무시할 수 있으므로, 웹 스파이더가 발행하는 모든 요청은 새 사용자가 보낸 것으로 식별되어 결과가 왜곡됩니다.
새로운 것을 처음 볼 때 요청서에서 샘플을 채취할 수 있다면 정말 좋을 것 같지 않나요? 새 IP 주소, 새 Session Cookie 값, 새 User-Agent 헤더, 보이지 않는 호스트 헤더 또는 이들의 조합일 수 있습니다. 이렇게 하면 각 항목에 대한 데이터를 한 번만 샘플링할 수 있습니다.
분명히, 우리는 상태(우리가 본 것들의 목록)를 저장해야 하며, 이를 위해 우리는 NGINX Plus의 Key-Value Store로 눈을 돌립니다. Key-Value Store는 변수를 사용하여 NGINX Plus 구성에서 액세스할 수 있는 In-Memory Key-Value 데이터베이스를 유지합니다.
데이터베이스는 선택적으로 항목의 자동 만료(타임아웃 매개 변수), 영구 저장소(상태) 및 클러스터 동기화(동기화)를 지원합니다. 아직 Store에 없는 각 항목에 대해 요청을 기록하고 다시 로그인하지 않도록 Store에 추가합니다.
NGINX Plus R18 이상에서는 트랜잭션을 처리하는 동안 Key-Value 쌍을 매우 쉽게 설정할 수 있습니다.
# Define a keyval zone with appropriate parameters
keyval_zone zone=clients:80m timeout=3600s;
# Create a variable $seen for each unique $remote_addr
keyval $remote_addr $seen zone=clients;
log_format notes '$remote_addr "$request" $status';
server {
listen 80;
# if $seen is empty, update the keyval (set $seen 1;) and log this
# request (set $logme 1;)
# Otherwise, $logme is not set and we don't log the request
# Note that $seen resets to "" after the configured timeout
if ($seen = "") {
set $seen 1;
set $logme 1;
}
access_log /var/log/nginx/secure.log notes if=$logme;
location / {
return 200 "All OK: -$seen-$logme-\n";
}
location /api {
api;
}
}
4. 실제 사례 – TLS 매개변수 샘플링
이 기사는 실제 문제에서 영감을 받았습니다. 레거시 장치가 있는 사용자를 제외하지 않고 모범 사례에 따라 TLS를 구성하는 방법은 무엇입니까?
TLS 모범 사례는 움직이는 대상입니다. TLS 1.3은 1년 전에 비준되었지만 많은 고객이 이전 TLS 버전만 사용합니다. 암호는 ‘안전하지 않음’으로 선언되고 폐기되지만 이전 구현은 암호에 의존합니다.
ECC 인증서는 RSA보다 뛰어난 성능을 제공하지만 모든 클라이언트가 ECC를 수락할 수 있는 것은 아닙니다.
많은 TLS 공격은 암호 협상 핸드셰이크를 가로채고 클라이언트와 서버가 덜 안전한 암호를 선택하도록 하는 “man in the middle””에 의존합니다. 따라서 취약하거나 레거시 암호를 지원하지 않도록 NGINX Plus를 구성하는 것이 중요하지만 이렇게 하면 레거시 클라이언트가 제외될 수 있습니다.
다음 구성 예에서는 각 TLS 클라이언트를 샘플링하여 SSL 프로토콜, 암호 및 User-Agent 헤더를 로깅합니다.
각 클라이언트가 최신 프로토콜과 지원하는 가장 안전한 암호를 선택한다고 가정하면, 샘플링된 데이터를 평가하고 이전 프로토콜과 암호에 대한 지원을 제거할 경우 제외되는 클라이언트의 비율을 결정할 수 있습니다.
NGINX 는 IP 주소와 User-Agent의 고유한 조합으로 각 클라이언트를 식별하지만 Session Cookie 또는 다른 방법으로도 클라이언트를 식별할 수 있습니다.
log_format sslparams '$ssl_protocol $ssl_cipher '
'$remote_addr "$http_user_agent"';
# Define a keyval zone with appropriate parameters
keyval_zone zone=clients:80m timeout=3600s;
# Create a variable $seen for each unique combination of $remote_addr and
# 'User-Agent' header
keyval $remote_addr:$http_user_agent $seen zone=clients;
server {
listen 443 ssl;
# default NGINX SSL configuration
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
if ($seen = "") {
set $seen 1;
set $logme 1;
}
access_log /tmp/sslparams.log sslparams if=$logme;
# ...
}
그러면 다음과 같은 항목이 포함된 Access Log 파일이 생성됩니다.
TLSv1.2 AES128-SHA 1.1.1.1 "Mozilla/5.0 (X11; Linux x86_64; rv:45.0) Gecko/20100101 Firefox/45.0"
TLSv1.2 ECDHE-RSA-AES128-GCM-SHA256 2.2.2.2 "Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1"
TLSv1.2 ECDHE-RSA-AES128-GCM-SHA256 3.3.3.3 "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:58.0) Gecko/20100101 Firefox/58.0"
TLSv1.2 ECDHE-RSA-AES128-GCM-SHA256 4.4.4.4 "Mozilla/5.0 (Android 4.4.2; Tablet; rv:65.0) Gecko/65.0 Firefox/65.0"
TLSv1 AES128-SHA 5.5.5.5 "Mozilla/5.0 (Android 4.4.2; Tablet; rv:65.0) Gecko/65.0 Firefox/65.0"
TLSv1.2 ECDHE-RSA-CHACHA20-POLY1305 6.6.6.6 "Mozilla/5.0 (Linux; U; Android 5.0.2; en-US; XT1068 Build/LXB22.46-28) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/57.0.2987.108 UCBrowser/12.10.2.1164 Mobile Safari/537.36"
그런 다음 다양한 방법을 사용하여 파일을 처리하여 데이터 확산을 결정할 수 있습니다.
$ cat /tmp/sslparams.log | cut -d ' ' -f 2,2 | sort | uniq -c | sort -rn | perl -ane 'printf "%30s %s\n", $F[1], "="x$F[0];'
ECDHE-RSA-AES128-GCM-SHA256 =========================
ECDHE-RSA-AES256-GCM-SHA384 ========
AES128-SHA ====
ECDHE-RSA-CHACHA20-POLY1305 ==
ECDHE-RSA-AES256-SHA384 ==
NGINX는 용량이 적고 덜 안전한 암호를 식별하고 로그를 확인하여 어떤 클라이언트가 암호를 사용하고 있는지 확인한 다음 NGINX Plus 구성에서 암호를 제거하는 것에 대해 정보에 입각한 결정을 내립니다.
5. 결론
NGINX의 조건부 Logging NGINX가 관리하는 요청의 하위 집합을 샘플링하고 표준 또는 특수 목적 로그를 작성하는 데 사용할 수 있습니다. 이 기술은 SSL 매개 변수의 범위를 결정하는 것과 같이 통계 분석을 위해 트래픽의 빠른 샘플을 추출해야 하는 경우 유용합니다.
바쁜 사용자나 스파이더가 과도하게 표현되지 않도록 데이터를 샘플링하는 방법을 고려해야 합니다. NGINX 구성의 변수를 map
및 split_clients
디렉티브와 함께 사용하여 요청을 선택하고 필터링할 수 있습니다.
의사 결정이 더 복잡하거나 높은 정확도가 필요한 상황의 경우 NGINX 구성에서 정교한 Selector를 구축할 수 있습니다. NGINX Plus Key-Value Store를 사용하면 상태를 축적하고 필요한 경우 클러스터의 NGINX Plus 인스턴스 간에 공유할 수 있습니다.
NGINX Plus에서 직접 요청 샘플링을 시도해 보십시오. 지금 30일 무료 평가판을 시작하거나 당사에 문의하여 사용 사례에 대해 논의하십시오.