쿼리 연습

로그프레소의 꽃은 쿼리입니다. 로그프레소 쿼리는 빅데이터 검색을 전문으로 하는 도메인 특화 언어(DSL, Domain Specific Language)로, 쿼리문은 파이프라인(|)으로 데이터를 전달해 처리하는 유닉스 계열 명령어와 유사한 방식으로 작성됩니다.

로그프레소 플랫폼은 수집된 데이터를 가공하지 않은 원본 그대로 로그프레소 테이블에 저장합니다. 로그프레소 쿼리는 테이블에 저장된 데이터에서 필요한 정보를 추출하여 가공할 수 있도록 쿼리 명령과 함수를 제공합니다. 쿼리 명령과 함수의 사용법은 다음 문서와 자료를 참조하십시오.

예제용 데이터셋

GitHub 로그프레소 저장소에서 예제용 데이터셋을 제공합니다.

Note
데이터셋은 예고없이 변경될 수 있습니다. 샘플 데이터에 대한 문의/제안을 깃허브에 이슈로 등록해주십시오.

이 문서에서 예시로 제공하는 쿼리문은 모든 데이터가 /opt/logpresso/samples 디렉터리에 있는 것으로 가정하고 작성되었습니다. 쿼리 예시문 단락에서 클립보드로 복사 아이콘을 누른 후, 복사된 쿼리문을 쿼리 > 쿼리에 붙여넣고 실행을 누르면 실행 결과를 볼 수 있습니다.

연습 1: 로컬 파일에서 데이터 불러오기

로그프레소 서버는 외부 시스템이나 로컬 파일시스템에 있는 파일, 로그프레소 서버나 센트리의 로거에서 수집한 데이터 스트림, 로그프레소 테이블에 저장된 데이터 등을 가져와 로딩할 수 있습니다. 여기서는 데이터 원천이라 할 수 있는 외부 혹은 로컬에 저장된 파일에서 데이터를 가져오는 방법을 배웁니다.

먼저 POSIX 호환 셸에서 다음 명령을 실행해 예제를 실행할 수 있도록 환경을 준비합니다. 사용자는 /opt/logpresso/samples 디렉터리에 대해 읽기 및 쓰기 권한이 있어야 합니다.

cd /opt/logpresso/samples && \
curl -L -o wp-nginx.csv https://raw.githubusercontent.com/logpresso/dataset/main/wp-nginx.csv && \
curl -L -o wp-nginx.json https://raw.githubusercontent.com/logpresso/dataset/main/wp-nginx.json && \
curl -L -o wp-nginx.tsv https://raw.githubusercontent.com/logpresso/dataset/main/wp-nginx.tsv

CSV/TSV 파일

CSV 파일의 1행은 보통 열 이름을 정의하고 있습니다. 로그프레소는 열 이름을 필드의 이름으로 사용합니다. TSV 파일은 구분자로 탭 문자를 사용한다는 점 외에 CSV 파일과 구조가 동일합니다. CSV, TSV 형식 파일의 정보를 읽어올 때에는 csvfile 구문을 이용합니다.

다음은 로컬 경로에 있는 CSV 파일을 읽어오는 예시와 실행결과입니다.

csvfile /opt/logpresso/samples/wp-nginx.csv

CSV 파일 읽어오기 쿼리문을 실행한 결과

다음은 로컬 경로에 있는 TSV 파일을 읽어오는 예시와 실행결과입니다.

csvfile tab=t /opt/logpresso/samples/wp-nginx.tsv

제시된 쿼리문은 csvfile 구문의 옵션인 tab=t을 이용해 구분자가 탭 문자임을 지정해서 TSV 파일을 읽어옵니다. 이와 같이 쿼리 옵션을 이용해 실행할 쿼리 구문의 동작을 지정하거나 변경할 수 있습니다.

  • t는 참(true), f는 거짓(false)을 의미하는 불리언 값입니다. 불리언 값으로 쿼리 명령의 특정한 동작을 변경할 수 있으며, 불리언 옵션을 지정하지 않으면 기본값을 적용합니다.
  • 문자열이나 정수, 실수같은 숫자값을 요구하는 옵션도 있습니다.

TSV 파일 읽어오기 쿼리문을 실행한 결과

JSON 파일

JSON 형식 파일을 로그프레소 서버에 가져오려면 jsonfile 구문을 이용합니다.

다음은 로컬 경로에 있는 JSON 파일을 읽어오는 예시와 실행결과입니다.

jsonfile /opt/logpresso/samples/wp-nginx.json

jsonfile 명령으로 데이터를 가져온 결과

Note
살구색 셀은 데이터가 할당되지 않았음(null)을 의미합니다.

데이터를 가공하는 쿼리는 원본에서 필요한 데이터를 읽어온 후 메모리 상에서 가공하여 보여주므로 항상 원본을 훼손하지 않고 유지합니다. 원본 데이터를 함께 보고 싶다면, 해당 명령어가 overlay=t 옵션을 지원하는지 확인해보십시오.

jsonfile overlay=t /opt/logpresso/samples/wp-nginx.json

실행 결과에서 line 필드가 새로 추가된 것을 확인할 수 있습니다. line 필드는 보통 가공되지 않은 데이터 입력을 출력하는 용도로 사용됩니다.

overlay=t 옵션을 적용해 wp-nginx.json에서 데이터를 가져온 결과

연습 2: 웹에서 데이터 불러오기

wget 구문으로 웹에서 URL로 지정된 리소스에서 데이터를 추출하거나, REST API를 호출해 결과를 활용할 수 있습니다.

GitHub에서 예제 파일 가져오기

다음은 GitHub에 저장된 wp-nginx.log 파일을 가져와 로딩하는 예시와 실행결과입니다.

wget url="https://raw.githubusercontent.com/logpresso/dataset/main/wp-nginx.log"
| eval line = subarray(split(line, "\n"), 0)
| explode line
  • 파이프(|)로 구분되는 2번 행 이하 각 쿼리 구문을 모두 #으로 주석 처리하고, 한 행씩 주석을 해제하고 실행하면서 어떤 형태로 동작하는지 확인해보십시오. # 뒤에는 반드시 공백문자가 있어야 합니다.
  • 이렇게 가져온 로그는 line 필드에 웹 로그 한 줄이 모두 들어가 있습니다. 이렇게 가져온 로그를 구조화된 형태로 활용하려면 다른 방법이 필요합니다.

wp-nginx.log 파일을 가져오는 쿼리문 실행 결과

파서를 적용해 데이터 추출하기

이제 데이터 추출할 때 파서를 적용해 데이터를 정규화고자 합니다.

파서 설정하기

먼저 가져오는 데이터 스트림에 적용할 파서를 설정합니다.

  1. 설정 > 파서에서 도구 모음에 있는 + 새 파서 만들기를 누릅니다.
  2. 파일 아래에 있는 아파치 웹 로그를 선택하고, 다음을 누릅니다.
  3. 다음 항목을 입력하고 완료를 누릅니다.
    • 이름: apache_log
    • Log Format 옵션 설정: %h %l %u %t "%r" %>s %O "%{Referer}i" "%{User-Agent}i"
Note
아파치 웹 서버와 Nginx 웹 서버는 모두 표준 형식인 Common Logfile Format에 따라 로그를 기록합니다: https://www.w3.org/Daemon/User/Config/Logging.html#common-logfile-format

쿼리문에 파서 적용하기

다음과 같은 쿼리문을 실행합니다.

wget url="https://raw.githubusercontent.com/logpresso/dataset/main/wp-nginx.log"
| eval line = subarray(split(line, "\n"), 0) | explode line
| parse apache_log  | # apache_log 파서로 line 필드를 정규화
| eval _time = date | # _time 필드에 date 필드의 값을 할당
| order _time, remote_host, request, status, referer, user_agent, sent, user | # 필드 순서 변경
| sort -_time       | # _time 필드 기준 내림차순 레코드 정렬

wp-nginx.log에 파서를 적용한 결과

Tip
데이터를 수집하는 쿼리문에 파서를 적용하면 파서로 정규화되는 필드 외에 다른 데이터는 버려집니다. 테이블에 저장된 원본, 또는 다른 데이터 스트림에서 필요한 데이터를 추출해서 가져오는 쿼리문에 적용하는 것이 바람직합니다.

log4j2-scan 다운로드 횟수 집계

API를 호출하면 JSON 형식으로 통신하는 경우가 많습니다. 다음 쿼리문은 GitHub API를 호출하고 응답을 처리해 원하는 결과를 얻는 예시와 실행결과입니다. 예시에 사용하는 데이터는 GitHub에서 배포하는 Log4j2 스캐너의 다운로드 이력입니다.

wget url="https://api.github.com/repos/logpresso/CVE-2021-44228-Scanner/releases?per_page=100"
| parsejson
| explode line | parsemap field=line
| explode assets | parsemap field=assets
| stats sum(download_count) as download_count by name
| stats sum(download_count)

log4j2-scan 다운로드 횟수를 확인하는 쿼리문 실행 결과

연습 3. 테이블에 데이터 저장하기

연습 1, 2를 통해 데이터를 가져와 확인했지만, 지금까지 가져온 데이터는 아직 로그프레소 테이블에 저장되어 있지 않습니다. 가져온 데이터를 활용하려면 로그프레소 테이블에 저장해야 합니다.

테이블에 원본 그대로 기록하기

다음 쿼리문을 실행하면 wp-nginx.log 파일에 기록된 로그를 아파치 웹 로그 형식으로 정규화하여 raw_web_log 테이블에 기록합니다. import 구문을 실행하려면 로그프레소 관리자 권한이 필요합니다.

wget url="https://raw.githubusercontent.com/logpresso/dataset/main/wp-nginx.log"
| eval line = subarray(split(line, "\n"), 0)
| explode line
| fields - _wget_code | # _wget_code 필드 제거
| import create=t raw_web_log

원본 형태로 테이블에 저장한 웹 서버 로그

위와 같은 쿼리를 통해 데이터를 수집해서 로그를 원본 형태 그대로 로그프레소 테이블에 저장할 수 있습니다. 이렇게 데이터를 저장한 테이블에 풀텍스트 검색을 위한 인덱스를 생성하거나, 정규화하여 통계 및 분석에 활용하거나, 애드혹 쿼리로 필요한 데이터를 추출하는 방식으로 활용할 수 있습니다.

정규화하여 다른 테이블에 기록하기(1)

이제 raw_web_log 테이블에 저장된 원본을 apache_log 파서로 정규화하여 다른 테이블(web_log)에 저장합니다.

table raw_web_log
| parse apache_log | # apache_log 파서로 정규화
| eval _time=date  | # date 필드의 값을 _time에 할당
| import create=t web_log

정규화해 web_log 테이블에 웹 서버 로그를 저장

퀴리문 실행 후 화면에 출력되는 데이터는 table raw_web_log | parse apache_log | eval _time=date을 실행한 결과입니다. import create=t web_log는 입력으로 받은 데이터를 web_log 테이블에 저장하면서 동시에 출력합니다. 다음 쿼리문을 실행하여 web_log 테이블에 저장된 결과를 별도로 확인할 수 있습니다.

table web_log

정규화하여 다른 테이블에 기록하기(2)

또 다른 방법으로 테이블에 파서를 등록하고 쿼리 실행 시점에 파서를 적용할 수 있습니다. 먼저 테이블을 생성하고 파서를 설정합니다.

사용할 테이블 생성

  1. 테이블에서 새 테이블 만들기를 누릅니다.

  2. 새 테이블 만들기에서 필요한 항목을 설정하고 생성을 누릅니다.

    속성
    이름web_log_with_parser
    타입v3p
    압축 방식snappy
    암호화 프로파일선택 안 함
    데이터 배열row

새 테이블 만들기

테이블에 파서 설정

  1. 테이블에서 파서를 추가할 테이블을 선택한 다음, 파서 설정을 누릅니다.
  2. 파서 설정 창에서 아파치 웹 로그를 선택하고 다음을 누릅니다.
  3. 완료를 누릅니다.

테이블에 파서를 설정하는 화면

테이블에 데이터 가져오기

이제 데이터를 가져올 쿼리를 단순하게 구성해도 레코드를 정규화해서 기록합니다.

table raw_web_log | import web_log_with_parser

쿼리문을 실행하고 쿼리 화면에 출력되는 데이터는 table raw_web_log의 출력입니다. web_log_with_parser 테이블에 저장된 데이터를 조회하려면 다음 쿼리문을 실행합니다.

table web_log_with_parser | eval _time=date | sort -_time

web_log_with_parser 테이블의 레코드 조회 화면

연습 4. 데이터 검색하기

이제 테이블에 저장된 데이터에 대해 검색을 해봅니다.

특정한 시간 동안 발생한 레코드 조회

wp-nginx.log에 기록된 정보 중에서 특정한 기간 동안 발생한 로그를 조회할 수 있습니다. web_log 테이블에 저장된 데이터에 대해 특정 기간 동안 기록된 레코드를 조회해보겠습니다.

table from=2022040511 to=202204051201 web_log | sort -_time

from, to를 이용한 특정한 시간 범위 내의 로그 검색 결과

검색 결과 개수를 제한하여 레코드 조회

검색 결과 개수를 제한하여 레코드를 조회할 수 있습니다.

table limit=10 web_log

웹 서버 공격 시도 이력 조회

.., \x16\x03\x01과 같은 문자열은 흔히 웹 서버의 취약점을 이용한 공격에서 발견되는 문자열입니다. HTTP 요청에서 관련 문자열을 검색해서 외부의 공격 시도가 있었는지 확인합니다.

table web_log
| search request=="*..*" or request=="*\\*"
| fields date, remote_host, request

웹 서버 공격 시도 이력 조회 화면

워드프레스 서버의 admin-ajax 취약점 공격 시도 이력 조회

/wp-admin/admin-ajax.php는 워드프레스 사이트의 AJAX-API이며, 워드프레스의 플러그인 중에 취약점이 있으면 쉽게 악용당할 수 있습니다. 인가되지 않은 IP 주소에서 POST /wp-admin/admin-ajax.php HTTP/1.1와 같이 POST 메소드를 이용한 접근이 있다면 공격인지 확인해볼 필요가 있습니다.

table web_log
| search request=="*POST /wp-admin/admin-ajax.php HTTP/1.1*"
| fields date, remote_host, request

워드프레스 서버의 취약점 공격 시도 조회