NGINX는 처리하는 모든 거래에 대해 매우 자세한 로그를 기록할 수 있습니다. 이러한 로그는 액세스 로그 라고 하며, 사용자 정의 가능한 로그 파일 형식을 사용하여 다양한 서비스나 위치에 대해 기록된 세부 정보를 미세 조정할 수 있습니다.
기본적으로 NGINX는 처리하는 모든 거래를 기록합니다. 이는 규정 준수나 보안 목적으로 필요할 수 있지만, 바쁜 웹사이트의 경우 생성되는 데이터 양이 엄청날 수 있습니다. 이 문서에서는 다양한 기준에 따라 거래를 선택적으로 기록하는 방법과 이러한 지식을 사용하여 빠르고 가벼운 방식으로 요청에 대한 데이터 포인트를 샘플링하는 방법을 보여줍니다.
별도로 명시된 경우를 제외하고 이 게시물은 NGINX 오픈 소스와 NGINX Plus에 모두 적용됩니다. 읽기 편하도록 이 문서 전체에서 NGINX를 참조하겠습니다.
NGINX 액세스 로그는 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가 트랜잭션이 완료되면 이를 기록하도록 지시합니다. 이 지시어는 로그 파일의 위치와 사용할 로그 형식을 지정합니다.
액세스 로그 /var/log/nginx/access.log 메인;
기본적으로 NGINX는 다음 구성을 사용하여 모든 트랜잭션을 기록합니다.
access_log 로그/access.log 결합;
사용자가 직접 access_log를 정의하면 기본 액세스 로그가 재정의(대체)됩니다.
때로는 특정 요청만 기록하고 싶을 수도 있습니다. 이 작업은 다음과 같이 조건부 로깅을 사용하여 수행됩니다.
server {
listen 80;
set $logme 0;
if ( $uri ~ ^/secure ) {
set $logme 1;
}
# 감사자는 /secure에 대한 요청에 대해 추가 로그가 필요합니다.
access_log /var/log/nginx/secure.log notes if=$logme;
# 글로벌 액세스 로그가 있는 경우 여기에 다시 선언해야 합니다.
access_log /var/log/nginx/access.log main;
location / {
# ...
}
}
액세스 로그 설정은 누적되거나 상속되지 않습니다. 즉, 한 컨텍스트의 access_log
지시문은 부모 컨텍스트에서 선언된 액세스 로그를 재정의(대체)합니다.
예를 들어 URI /secure 에 대한 트래픽에 대한 추가 정보를 기록하려면 location
/secure
{...}
블록에서 액세스 로그를 정의할 수 있습니다. 이 액세스 로그는 구성의 다른 곳에서 정의된 일반 액세스 로그를 대체합니다.
이전 섹션 의 예에서는 이 문제를 다룹니다. 동일한 컨텍스트에서 두 개의 액세스 로그를 사용하며, /secure 에 대한 요청을 전용 로그 파일에 기록하는 조건부 로깅을 사용합니다.
귀하의 웹사이트 트래픽에 대한 통계 정보를 확인하고 싶다고 가정해 보겠습니다.
일반 접근 로그는 이 정보를 기록하기에 적합한 곳이 아닌 경우가 많습니다. 연구에 필요한 추가 필드로 액세스 로그를 오염시키고 싶지 않을 수도 있고, 바쁜 사이트에서 모든 거래를 기록하는 오버헤드가 너무 높을 수도 있습니다.
이 경우, 제한된 필드 집합을 특수 로그에 기록할 수 있습니다. 시스템 부하를 줄이려면 요청의 하위 집합을 샘플링할 수도 있습니다.
다음 구성에서는 $request_id
변수를 각 요청에 대한 고유 식별자로 사용합니다. split_clients
블록을 사용하여 요청의 1%만 기록하여 데이터를 샘플링합니다.
split_clients $request_id $logme {
1% 1;
* 0;
}
server {
listen 80;
access_log /var/log/nginx/secure.log 메모 if=$logme;
# ...
}
각 사용자(또는 사용자의 1%)로부터 하나의 데이터 포인트(예: User-Agent
헤더)를 샘플링하고자 한다고 가정해 보겠습니다. 모든 요청에서 샘플링을 할 수는 없습니다. 요청을 대량으로 생성하는 사용자가 데이터에 과도하게 표현되기 때문입니다.
우리는 세션 쿠키의 존재를 감지하기 위해 맵
블록을 사용하는데, 이는 요청이 새로운 사용자로부터 왔는지 아니면 이미 본 사용자로부터 왔는지 알려줍니다. 그런 다음 새로운 사용자에게서만 오는 요청을 샘플링합니다.
map $cookie_SESSION $logme {
"" $perhaps; # 쿠키가 없는 경우, $perhaps를 기록합니다.
default 0;
}
split_clients $request_id $perhaps {
1% 1; # $perhaps는 1%의 시간 동안 참입니다.
* 0;
}
server {
listen 80;
access_log /var/log/nginx/secure.log notes if=$logme;
# 선택 사항: 애플리케이션이 세션 쿠키를 생성하지 않으면, 우리는
# 우리만의
add_header Set-Cookie SESSION=1;
# ...
}를 생성합니다.
하지만 모든 클라이언트가 세션 쿠키를 존중하는 것은 아닙니다. 예를 들어, 웹 스파이더는 쿠키를 무시할 수 있으므로, 해당 웹 스파이더가 발행하는 모든 요청은 새로운 사용자에게서 온 것으로 식별되어 검색 결과가 왜곡될 수 있습니다.
새로운 것을 처음 볼 때 요청을 샘플링할 수 있다면 좋지 않을까요? 이는 새로운 IP 주소, 새로운 세션 쿠키 값, 새로운 사용자 에이전트
헤더, 이전에 보지 못했던 호스트 헤더 또는 이러한 것들의 조합일 수 있습니다. 이런 식으로 우리는 각각의 것에 대한 데이터를 한 번씩만 샘플링합니다.
분명히 우리는 상태(우리가 본 것들 의 목록)를 저장해야 하며, 이를 위해 NGINX Plus의 키-값 저장소를 사용합니다. 키-값 저장소는 변수를 사용하여 NGINX Plus 구성에서 액세스할 수 있는 메모리 내 키-값 데이터베이스를 유지 관리합니다. 데이터베이스는 선택적으로 항목의 자동 만료( 시간 초과
매개변수), 영구 저장소( 상태
) 및 클러스터 동기화( 동기화
)를 지원합니다. 아직 스토어에 없는 각 항목 에 대해 요청을 기록하고 해당 항목을 스토어에 추가하여 다시 기록되지 않도록 합니다.
NGINX Plus R18 이상에서는 트랜잭션을 처리하는 동안 키‑값 쌍을 매우 쉽게 설정할 수 있습니다.
# 적절한 매개변수를 사용하여 keyval zone을 정의합니다.keyval_zone zone=clients:80m timeout=3600s;
# 고유한 $remote_addr마다 $seen 변수를 만듭니다.
keyval $remote_addr $seen zone=clients;
log_format notes '$remote_addr "$request" $status';
server {
listen 80;
# $seen이 비어 있으면 keyval을 업데이트하고(set $seen 1;) 이를 기록합니다.
# request (set $logme 1;)
# 그렇지 않으면 $logme가 설정되지 않고 요청을 기록하지 않습니다.
# 구성된 시간 초과 후 $seen이 ""로 재설정됩니다.
if ($seen = "") {
set $seen 1;
set $logme 1;
}
access_log /var/log/nginx/secure.log notes if=$logme;
location / {
return 200 "모두 OK: -$seen-$logme-\n";
}
location /api {
api;
}
}
이 문서는 실제 문제에서 영감을 받았습니다. 즉, 레거시 장치를 사용하는 사용자를 제외하지 않고 모범 사례에 따라 TLS를 구성하려면 어떻게 해야 할까요?
TLS 모범 사례는 움직이는 표적입니다. TLS 1.3은 1년 전에 비준되었지만 많은 클라이언트가 이전 TLS 버전만 사용합니다. 암호는 '안전하지 않음'으로 선언되어 폐기되었지만 이전 구현은 이에 의존합니다. ECC 인증서는 RSA보다 더 뛰어난 성능을 제공하지만 모든 클라이언트가 ECC를 허용할 수 있는 것은 아닙니다. 많은 TLS 공격은 암호 협상 핸드셰이크를 가로채고 클라이언트와 서버가 덜 안전한 암호를 선택하도록 강제하는 "중간자"에 의존합니다. 따라서 약하거나 레거시 암호를 지원하지 않도록 NGINX Plus를 구성하는 것이 중요하지만, 그렇게 하면 레거시 클라이언트가 제외될 수 있습니다.
다음 구성 예에서는 각 TLS 클라이언트를 샘플링하여 SSL 프로토콜, 암호 및 사용자 에이전트
헤더를 기록합니다. 각 클라이언트가 지원하는 최신 프로토콜과 가장 안전한 암호를 선택한다고 가정하면 샘플링된 데이터를 평가하고 이전 프로토콜과 암호에 대한 지원을 제거하면 얼마나 많은 클라이언트가 제외되는지 확인할 수 있습니다.
우리는 각 클라이언트를 IP 주소와 사용자 에이전트
의 고유한 조합으로 식별하지만, 세션 쿠키나 다른 방법으로 클라이언트를 식별하는 것도 마찬가지로 효과적입니다.
log_format sslparams '$ssl_protocol $ssl_cipher '
'$remote_addr "$http_user_agent"';
# 적절한 매개변수로 keyval zone을 정의합니다.
keyval_zone zone=clients:80m timeout=3600s;
# $remote_addr와
# 'User-Agent' 헤더의 고유한 조합에 대해 $seen 변수를 만듭니다.
keyval $remote_addr:$http_user_agent $seen zone=clients;
server {
listen 443 ssl;
# 기본 NGINX SSL 구성
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;
# ...
}
이렇게 하면 다음과 같은 항목이 포함된 로그 파일이 생성됩니다.
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, Mac OS X와 같은 CPU iPhone OS 9_1) AppleWebKit/601.1.46(Gecko와 같은 KHTML) 버전/9.0 모바일/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(Gecko와 같은 KHTML) 버전/4.0 Chrome/57.0.2987.108 UCBrowser/12.10.2.1164 Mobile Safari/537.36"
그런 다음 다양한 방법을 사용하여 파일을 처리하여 데이터 분포를 확인할 수 있습니다.
$ cat /tmp/sslparams.log | 잘라내기 -d ' ' -f 2,2 | 정렬 | 유니크 -c | 정렬 -rn | 펄 -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 Plus 구성에서 암호를 제거하기로 결정합니다.
NGINX의 조건부 로깅은 NGINX가 관리하는 요청의 하위 집합을 샘플링하고 표준 또는 특수 목적 로그를 작성하는 데 사용할 수 있습니다. 이 기술은 SSL 매개변수의 확산을 확인하는 등 통계 분석을 위해 트래픽 샘플을 빠르게 수집해야 하는 경우에 유용합니다.
바쁜 사용자나 스파이더가 과도하게 표현되지 않도록 데이터를 샘플링하는 방법에 대해 신중히 고려해야 합니다. map
및 split_clients
지시문과 함께 NGINX 구성의 변수를 사용하여 요청을 선택하고 필터링할 수 있습니다.
결정이 더 복잡한 상황이나 정확성에 대한 높은 신뢰도가 요구되는 경우 NGINX 구성에서 정교한 선택기를 빌드할 수 있습니다. NGINX Plus 키-값 저장소를 사용하면 상태를 축적하고 필요한 경우 클러스터의 NGINX Plus 인스턴스에서 이를 공유할 수 있습니다.
NGINX Plus로 요청 샘플링을 직접 시도해 보세요. 오늘 무료 30일 체험판을 시작하거나, 사용 사례에 대해 논의하기 위해 저희에게 연락하세요 .
"이 블로그 게시물에는 더 이상 사용할 수 없거나 더 이상 지원되지 않는 제품이 참조될 수 있습니다. 사용 가능한 F5 NGINX 제품과 솔루션에 대한 최신 정보를 보려면 NGINX 제품군을 살펴보세요. NGINX는 이제 F5의 일부가 되었습니다. 이전의 모든 NGINX.com 링크는 F5.com의 유사한 NGINX 콘텐츠로 리디렉션됩니다."