Seekers 실습 매뉴얼 시작하기 시큐어코딩 CTF LLM CTF DevSecOps ELK 전체 코스
대시보드 · 로그 분석 · 위협 탐지

ELK / SIEM 랩 LIVE

웹/보안 로그를 수집 → 검색 → 시각화 → 위협 탐지하는 SIEM 실습입니다. ELK/OpenSearch는 고사양(수 GB 메모리)이라 본인 PC에 설치하지 않고 아래 공용 분석 서버 한 곳에서 브라우저만으로 실습합니다. 서버가 실시간으로 샘플 로그를 흘려보내고 있어 바로 시작할 수 있습니다. (직접 설치는 맨 아래 부록(apt) 참고)

실습 환경은 이 한 곳뿐입니다 — https://lecture.seekerslab.com/elk/  (아이디 elklab 또는 student / 비밀번호 seekerslab). 설치 없이 브라우저로 접속하세요.

강의 슬라이드

ELK / SIEM 로그 분석 강의 — 세션별 강의 자료(PDF), 클릭하면 새 탭에서 열립니다.

세션 1
SIEM · ELK 개요
구성요소 · SIEM 개념 · 아키텍처와 데이터 흐름
세션 2
로그 수집·인덱싱
인덱스·역색인 · 로그 스키마 · 정규화 · _bulk 적재
세션 3
검색·필터 (KQL)
필드 검색 · 불리언 · 범위 · 와일드카드 · 실전 쿼리
세션 4
시각화 만들기
집계 개념 · 컴포넌트별 설정 · 국가별 접속 지도
세션 5
대시보드 · 관제
패널 조립 · 시간범위 · Auto refresh · 전역 KQL 필터
세션 6
위협 탐지 시나리오
브루트포스 · 공격벡터 · 스캐너 · 데이터유출 · 지리이상
세션 7
탐지 규칙 · 알럿 · 운영
저장 검색 · Alerting 임계치 규칙 · 오탐 튜닝 · 운영 체크리스트

0. 개념 — ELK / OpenSearch / SIEM

세 가지 구성요소가 파이프라인을 이룹니다.

구성요소ELKOpenSearch(이 랩)역할
수집Logstash / BeatsData Prepper / Fluent Bit로그를 모아 파싱·정규화해 전송
저장·검색 엔진ElasticsearchOpenSearch문서를 인덱스에 저장, 역색인으로 초고속 검색·집계
시각화KibanaOpenSearch DashboardsDiscover(검색)·Visualize·Dashboard·Alerting

SIEM(Security Information & Event Management) = 이 스택으로 보안 로그를 한곳에 모아 상관분석·탐지·대응하는 것. 흐름: 로그 발생 → 수집 → 인덱스 적재 → 검색/집계 → 시각화 → 탐지 규칙/알럿.

이 랩은 서버의 loggen 이 2초마다 웹/보안 로그를 logs-demo-YYYY.MM.DD 인덱스로 _bulk 적재합니다. 여러분은 이 데이터를 검색·분석합니다.

1. 로그 스키마 (필드 레퍼런스)

모든 쿼리는 아래 필드명을 사용합니다. 필드명을 정확히 써야 검색이 됩니다.

필드타입예시의미
@timestampdate2026-07-08T04:12:33.482Z이벤트 발생 시각(UTC)
eventkeywordaccess · attack · auth_fail이벤트 분류(핵심 필드)
client_ipip203.0.113.7요청 출발 IP
geo.countrykeywordKR · US · CN · RU접속 국가 코드
http.methodkeywordGET · POST · PUT · DELETEHTTP 메서드
http.statusnumber200 · 401 · 403 · 404 · 500응답 상태코드
http.bytesnumber200 ~ 40000응답 크기(byte)
urltext/login · /.env · /search?id=1'…요청 경로(+공격 payload)
response_msnumber2 ~ 900응답 시간(ms)
user_agenttextChrome · curl/8 · sqlmap/1.8User-Agent 문자열
attack_typekeywordsqli · xss · path_traversal · cmd_injection공격 유형(event:attack 일 때)
severitykeywordmedium · high · critical심각도(공격 이벤트)
userkeywordadmin · root · test로그인 시도 계정(auth_fail 일 때)

실제 문서(예시):

{
  "@timestamp": "2026-07-08T04:12:33.482Z",
  "event": "auth_fail",
  "client_ip": "203.0.113.7",
  "geo": { "country": "RU" },
  "http": { "method": "POST", "status": 401, "bytes": 512 },
  "url": "/login",
  "response_ms": 88,
  "user_agent": "python-requests/2.31",
  "user": "admin"
}

2. 접속 & Discover 로 바로 시작

인덱스 패턴 logs-demo-*이미 생성돼 기본값으로 지정돼 있습니다 — 따로 만들 필요 없이 바로 검색하세요.

  1. 대시보드 열기 → 로그인 elklab 또는 student / seekerslab.
  2. 좌측 메뉴(≡) → Discover.
  3. 좌상단 인덱스 패턴이 logs-demo-* 인지 확인(기본값).
  4. 우상단 시간 범위 Last 1 hour + Refresh(자동 5s) → 수천 건의 로그가 보입니다.
“인덱스 패턴을 만들라(create index pattern)”고 나오면? 드물게 서버 재시작 직후 그럴 수 있고 최대 10분 내 자동 복구됩니다. 급하면 Dashboards Management → Index patterns → Createlogs-demo-* → 시간 필드 @timestamp.

3. Discover — 검색 & 필터 (KQL)

상단 검색창에 KQL(Query Language)을 입력합니다. 좌측 Available fields 에서 필드를 클릭하면 상위 값 분포가 뜨고, 로 표(컬럼)에 추가할 수 있습니다. 문서 왼쪽 로 원본 JSON 을 펼쳐 보세요.

Discover 화면 — logs-demo-* 인덱스, 시간 히스토그램, 필드 목록, 로그 레코드 1 2 3 4 5 6
▲ Discover — 빨간 원 = 클릭 위치: 인덱스 패턴(logs-demo-*) · KQL 검색창 · 시간 범위(Last 1 hour) · Refresh · 필드 클릭(값 분포·+로 컬럼 추가) · 로 문서 원본(JSON) 펼치기
# 이벤트 분류별
event: access
event: auth_fail
event: attack

# 특정 공격 유형
event: attack and attack_type: sqli

# 인증/권한 오류 상태코드
http.status: 401 or http.status: 403

# 서버 오류(5xx)
http.status >= 500

# 스캐너·자동화 도구 UA
user_agent: *sqlmap* or user_agent: *python-requests* or user_agent: *curl*

# 민감 경로 스캔 시도
url: *.env* or url: *wp-login* or url: "/admin"

# 대용량 응답(데이터 유출 의심)
http.status: 200 and http.bytes > 30000

# 특정 국가발 공격
geo.country: RU and event: attack

# 특정 IP 의 모든 활동
client_ip: "203.0.113.7"

# 심각도 높은 공격
severity: (high or critical)

# 로그인 실패 POST (브루트포스 후보)
url: "/login" and http.method: POST and http.status: 401

4. 시각화 만들기 (컴포넌트별 · 스크린샷)

좌측 Visualize → Create visualization → 아래 갤러리에서 컴포넌트를 고르고 인덱스 logs-demo-* 를 선택합니다.

New Visualization 타입 갤러리 1 2
▲ 시각화 컴포넌트 갤러리 — 빨간 원 = 클릭 위치: 이 가이드에서 쓰는 ① Region Map(국가별 접속 지도) · ② Vertical Bar(공격 유형). 그 외 Area·Line·Pie·Data Table·Metric·Heat Map·Tag Cloud·TSVB·Vega 등도 선택 가능.
중요 — 집계 필드는 .keyword: 텍스트 필드(attack_type·geo.country·client_ip·url·user_agent)를 Terms 로 집계할 땐 필드.keyword 를 선택하세요(안 그러면 “invalid for Terms aggregation” 오류). 숫자(http.status·http.bytes)와 날짜(@timestamp)는 그대로.
컴포넌트Metric / Bucket 설정보는 것
Metric 공격 건수필터 event: attack · Metric = Count총 공격 수 한 눈에
Pie / Donut 상태코드Split slices → Termshttp.status2xx/4xx/5xx 비율
Vertical Bar 공격 유형필터 event: attack · X-axis → Termsattack_type.keyword가장 많은 공격 벡터
Line 트래픽 추이X-axis → Date Histogram@timestamp시간대별 스파이크
Data Table 상위 IP필터 event: attack · Bucket → Termsclient_ip.keyword (Size 10)집중 공격 출발지
Heat MapX=geo.country.keyword, Y=http.status, 값=Count국가×상태코드 밀도
Tag CloudTerms → user_agent.keyword많이 쓰인 UA(도구) 워드클라우드

World Map (Region Map) — 국가별 접속 지도

  1. Visualize → Create → Region Map → 인덱스 logs-demo-*.
  2. Buckets → Shape field → Aggregation Terms → Field geo.country.keyword → Size 50.
  3. Options → Layer Settings: Vector map = World Countries, Join field = ISO 3166-1 alpha-2 (iso2) — 우리 geo.country 값이 KR·US·CN 같은 ISO2 국가코드라 iso2 로 조인됩니다.
  4. Color schema Yellow to Red → 상단 UpdateSave.
Region Map — 국가별 접속 세계지도 1 2 3 4
▲ Region Map 편집 — 빨간 원 = 클릭 위치: Buckets → Shape field(Terms → geo.country.keyword) · Layer Options(Vector map=World Countries, Join=iso2) · Update(미리보기 갱신) · Save. geo.country(ISO2)를 국가 경계에 조인해 접속량을 색으로 표현.
이미 만들어 둔 예시가 있습니다 — Visualize 목록의 [랩] … 항목(공격 건수·상태코드 분포·공격 유형·국가별 접속·시간대별 트래픽·상위 공격자 IP)을 열어 설정을 그대로 참고하세요. 저장 시 공유 환경이므로 이름 앞에 본인 이니셜(예 kim-공격유형).

5. 대시보드 조립

  1. 좌측 Dashboard → Create → Add 로 위에서 저장한 시각화들을 추가.
  2. 패널 크기·위치 드래그로 배치 → 우상단 Save (예 kim-보안관제).
  3. 우상단 시간 범위 + Auto refresh 를 켜면 실시간 관제 화면이 됩니다.
  4. 대시보드 상단 검색창에 KQL 을 넣으면 모든 패널이 동시에 필터링됩니다(예 event: attack).
SOC 보안 관제 대시보드 — Metric·Donut·Bar·World Map·Line·Data Table 1 2 3 4
[랩] SOC 보안 관제 대시보드 — 빨간 원 = 클릭 위치: KQL 검색창(넣으면 모든 패널 동시 필터, 예 event: attack) · 시간 범위 · Refresh/Auto refresh · Edit(패널 추가·배치·저장). 공격 건수·상태코드·공격 유형·국가별 접속·트래픽·상위 IP를 한 화면에.

6. 위협 탐지 시나리오 (가설 → 쿼리 → 해석 → 대응)

① 브루트포스 / 크리덴셜 스터핑

② 공격 벡터 집계

③ 취약점 스캐너 탐지

④ 데이터 유출 의심

⑤ 지리·이상 징후

7. 내 데이터 직접 넣기 (Dev Tools)

좌측 Dev Tools → Console 에서 API 로 문서를 넣고 조회할 수 있습니다(스키마 이해에 좋음).

# 문서 여러 개 한 번에 적재 (_bulk)
POST logs-demo-2026.07.08/_bulk
{ "index": {} }
{ "@timestamp":"2026-07-08T04:00:00Z","event":"auth_fail","client_ip":"10.0.0.9","user":"admin","url":"/login","http":{"method":"POST","status":401} }
{ "index": {} }
{ "@timestamp":"2026-07-08T04:00:01Z","event":"attack","attack_type":"sqli","severity":"high","client_ip":"10.0.0.9","url":"/search?id=1' OR '1'='1","http":{"method":"GET","status":500} }

# 집계 조회 — 공격 유형별 개수
GET logs-demo-*/_search
{ "size":0, "query":{"term":{"event":"attack"}},
  "aggs":{"by_type":{"terms":{"field":"attack_type"}}} }

# 상위 공격자 IP
GET logs-demo-*/_search
{ "size":0, "query":{"term":{"event":"attack"}},
  "aggs":{"top_ip":{"terms":{"field":"client_ip","size":10}}} }

8. KQL 치트시트

연산의미
등호event: attack필드 = 값
AND / OR / NOTa and b · a or b · not a불리언 결합
범위http.status >= 400 · http.bytes > 30000숫자 비교
와일드카드user_agent: *curl*부분 일치
존재 여부attack_type: *필드가 존재하는 문서
구문(정확히)url: "/api/users"따옴표로 정확 일치
여러 값http.status: (401 or 403)OR 축약
괄호(a or b) and c우선순위

부록. 직접 설치 (심화 · 실습 필수 아님)

아래는 플레인 Linux 컨테이너에서 apt 로 ELK/OpenSearch 를 직접 설치하는 과정입니다. 실습에 필수가 아니며, 권장 8GB+ RAM · Docker 가 있는 경우에만 시도하세요. (보안 비활성 설정은 학습 전용 — 운영 금지)

A. 준비 — 플레인 Ubuntu 컨테이너 + 커널 설정

# (호스트) 커널 파라미터 — 없으면 엔진이 부팅 실패
sudo sysctl -w vm.max_map_count=262144

# (호스트) 플레인 Ubuntu 컨테이너 — 포트 9200(엔진)·5601(대시보드) 매핑
docker run -it --name elk-lab -p 9200:9200 -p 5601:5601 ubuntu:22.04 bash

# ── 이하 컨테이너 내부 ──
apt-get update
apt-get install -y wget curl gnupg apt-transport-https ca-certificates sudo
플레인 컨테이너엔 systemd 가 없어 systemctl start 가 안 됩니다 → 아래처럼 바이너리를 직접 실행합니다.

B. Elastic Stack (Elasticsearch + Kibana) — apt

# 1) Elastic 공개키 + apt 저장소(8.x)
wget -qO - https://artifacts.elastic.co/GPG-KEY-elasticsearch \
  | gpg --dearmor -o /usr/share/keyrings/elastic-keyring.gpg
echo "deb [signed-by=/usr/share/keyrings/elastic-keyring.gpg] \
https://artifacts.elastic.co/packages/8.x/apt stable main" \
  | tee /etc/apt/sources.list.d/elastic-8.x.list

# 2) 설치
apt-get update && apt-get install -y elasticsearch kibana

# 3) 실습용 설정 (보안·TLS 비활성 — 학습 전용)
cat >> /etc/elasticsearch/elasticsearch.yml <<'YML'
discovery.type: single-node
network.host: 0.0.0.0
xpack.security.enabled: false
YML
printf '%s\n' '-Xms1g' '-Xmx1g' > /etc/elasticsearch/jvm.options.d/heap.options
cat >> /etc/kibana/kibana.yml <<'YML'
server.host: "0.0.0.0"
elasticsearch.hosts: ["http://localhost:9200"]
YML

# 4) 실행 (systemd 없으므로 서비스 유저로 직접)
sudo -u elasticsearch /usr/share/elasticsearch/bin/elasticsearch &
#   엔진이 뜬 뒤(~30초) Kibana
sudo -u kibana /usr/share/kibana/bin/kibana &

# 5) 확인
curl http://localhost:9200          # Elasticsearch JSON
#   브라우저: http://localhost:5601  (Kibana)

C. OpenSearch (이 랩과 동일 엔진) — apt

# 1) 공개키 + apt 저장소(2.x)
curl -o- https://artifacts.opensearch.org/publickeys/opensearch.pgp \
  | gpg --dearmor -o /usr/share/keyrings/opensearch-keyring
echo "deb [signed-by=/usr/share/keyrings/opensearch-keyring] \
https://artifacts.opensearch.org/releases/bundle/opensearch/2.x/apt stable main" \
  | tee /etc/apt/sources.list.d/opensearch-2.x.list
echo "deb [signed-by=/usr/share/keyrings/opensearch-keyring] \
https://artifacts.opensearch.org/releases/bundle/opensearch-dashboards/2.x/apt stable main" \
  | tee /etc/apt/sources.list.d/opensearch-dashboards-2.x.list

# 2) 설치 (2.12+ 는 최초 admin 비밀번호를 환경변수로 요구)
apt-get update
env OPENSEARCH_INITIAL_ADMIN_PASSWORD='Seekers-Lab-2026!' \
  apt-get install -y opensearch opensearch-dashboards

# 3) 실습용 설정 (보안 플러그인 비활성 — 학습 전용)
printf '%s\n' 'plugins.security.disabled: true' 'network.host: 0.0.0.0' 'discovery.type: single-node' \
  >> /etc/opensearch/opensearch.yml

# 4) 실행
sudo -u opensearch /usr/share/opensearch/bin/opensearch &
sudo -u opensearch-dashboards /usr/share/opensearch-dashboards/bin/opensearch-dashboards &

# 5) 확인
curl http://localhost:9200          # OpenSearch JSON
#   브라우저: http://localhost:5601  (Dashboards)

D. 더 간단히 — Docker Compose (권장)

# docker-compose.yml — 단일 노드 OpenSearch + Dashboards (학습용, 보안 비활성)
services:
  opensearch:
    image: opensearchproject/opensearch:2
    environment:
      - discovery.type=single-node
      - plugins.security.disabled=true
      - "OPENSEARCH_JAVA_OPTS=-Xms1g -Xmx1g"
    ports: ["9200:9200"]
  dashboards:
    image: opensearchproject/opensearch-dashboards:2
    environment:
      - OPENSEARCH_HOSTS=http://opensearch:9200
      - DISABLE_SECURITY_DASHBOARDS_PLUGIN=true
    ports: ["5601:5601"]

docker compose up -d      # http://localhost:5601
docker compose down

E. 문제 해결 (Troubleshooting)

증상원인 · 해결
엔진이 바로 죽음 / max virtual memory areas호스트에서 sysctl -w vm.max_map_count=262144
can not run elasticsearch as root서비스 유저로 실행: sudo -u elasticsearch …
느리거나 OOM 종료heap 을 RAM 의 50% 이하로(-Xms1g -Xmx1g), 컨테이너 메모리 4GB+
systemctl 안 됨플레인 컨테이너엔 systemd 없음 → 바이너리 직접 실행, 또는 --privileged+init 이미지
Dashboards 가 엔진에 연결 못 함OPENSEARCH_HOSTS/elasticsearch.hosts 확인, 엔진이 먼저 떠야 함
전체 코스
© Seekers Security Lab · seekerslab.com