본문 바로가기
Security Operations

[Security Operations] Docker Compose로 ELK SIEM 실습 환경 만들기: 저장·검색부터 로그 수집까지

by Tru5tC0der 2026. 9. 26.

보안 관제를 처음 배울 때 가장 먼저 부딪히는 벽은 실습 환경입니다. SIEM은 로그를 모으고, 저장하고, 검색하고, 시각화하는 여러 구성 요소가 함께 돌아가야 의미가 있습니다. 그런데 Elasticsearch는 8.0부터 보안 기능이 기본으로 켜져 있어서, 노드 사이 TLS 인증서를 만들고 사용자 비밀번호를 설정해야 클러스터가 제대로 뜹니다. 여기에 커널 파라미터 조정과 메모리 설정까지 더해지면, 수업 첫 시간이 환경 설정으로 끝나기 쉽습니다.

화이트햇 스쿨 수업에서 이 문제를 줄이려고 Docker Compose 한 번으로 올라오는 실습 환경을 만들어 두었습니다. 이번 글에서는 SIEM이 무엇이고 Elastic Stack이 그 안에서 어떤 역할을 하는지 짧게 정리한 뒤, 저장소에 있는 두 가지 구성을 파일 단위로 살펴보겠습니다.

코드는 GitHubgunh0/whs-utils의 simple-elk-for-siem에 있습니다.


목차

  • SIEM이란?
  • Elastic Stack은 어떤 역할을 나눠 맡을까?
  • 저장소의 두 가지 구성
  • basic-elk: 노트북에서 도는 3노드 클러스터
  • 보안 로그를 넣고 찾아보기
  • elk-with-fleet: 수집 경로를 붙인 구성
  • 9.x로 올릴 때 확인할 점
  • 마치며
  • 참고 자료

SIEM이란?

SIEM(Security Information and Event Management)은 서버, 네트워크 장비, 애플리케이션 등 여러 곳에서 나오는 로그와 이벤트를 한곳에 모아 보안 문제를 찾는 시스템입니다. 크게 네 가지 일을 합니다.

기능 내용
수집 여러 소스의 로그를 모아 한 저장소로 보냄
정규화·저장 형식이 다른 로그를 검색할 수 있는 형태로 바꿔 저장
탐지 규칙이나 상관 분석으로 의심스러운 이벤트를 찾음
조사·보고 검색과 대시보드로 사건을 추적하고 보고서를 만듦

상용 SIEM 제품도 많지만, 동작 원리를 익히기에는 직접 구성 요소를 띄워 보는 편이 이해가 빠릅니다. 이번 실습에서는 오픈소스로 시작할 수 있는 Elastic Stack을 씁니다.


Elastic Stack은 어떤 역할을 나눠 맡을까?

흔히 ELK라고 부르는 Elasticsearch, Logstash, Kibana에 수집 에이전트가 더해진 구조입니다.

구성 요소 SIEM에서의 역할
Elasticsearch 로그를 저장하고 검색하는 분산 검색 엔진
Kibana 검색, 대시보드, 보안 기능(Security 앱)을 제공하는 웹 화면
Logstash 로그를 받아 변환한 뒤 Elasticsearch로 보내는 처리 파이프라인
Beats (Filebeat, Metricbeat) 로그 파일이나 시스템 지표를 가볍게 수집해 보내는 에이전트
Elastic Agent + Fleet Server 여러 수집 기능을 하나로 묶은 에이전트와, 이를 Kibana에서 중앙 관리하게 해 주는 서버

실습에서는 저장과 검색(Elasticsearch, Kibana)부터 익히고, 그다음에 수집 경로(Beats, Logstash, Fleet)를 붙이는 순서가 자연스럽습니다. 저장소도 이 순서에 맞춰 두 가지 구성을 나눠 두었습니다.


저장소의 두 가지 구성

구성 버전 서비스 용도
basic-elk Elastic Stack 9.4.2 Elasticsearch 3노드, Kibana 저장과 검색 실습. 적은 자원으로 돌도록 조정
elk-with-fleet Elastic Stack 8.8.2 Elasticsearch, Kibana, Logstash, Filebeat, Metricbeat, Fleet Server, 데모 웹앱 수집 경로와 에이전트 관리 실습

elk-with-fleet은 Elastic 공식 블로그의 튜토리얼 "Getting started with the Elastic Stack and Docker Compose: Part 2"(Eddie Mitchell, 2023-10-13)의 구성을 바탕으로 합니다. 두 구성 모두 개발과 실습용이며, 운영 환경에는 추가 보안 설정과 성능 조정이 필요하다고 README에 적어 두었습니다.


basic-elk: 노트북에서 도는 3노드 클러스터

basic-elk는 명령 하나로 실행됩니다.

cd simple-elk-for-siem/basic-elk
docker compose up

잠시 뒤 http://localhost:5601에서 Kibana에 접속하고, .env에 적힌 elastic 계정으로 로그인하면 됩니다.

인증서와 비밀번호는 setup 컨테이너가 만든다

보안 기능이 켜진 Elasticsearch를 여러 노드로 띄우려면 노드마다 TLS 인증서가 필요하고, Kibana가 Elasticsearch에 접속할 때 쓰는 kibana_system 계정의 비밀번호도 정해야 합니다. 이 작업을 사람이 하지 않도록, 먼저 setup 컨테이너가 CA와 노드별 인증서를 만들고, Elasticsearch가 뜨기를 기다렸다가 비밀번호를 설정합니다.

              echo "Waiting for Elasticsearch availability";
              until curl -s --cacert config/certs/ca/ca.crt https://es01:9200 | grep -q "missing authentication credentials"; do sleep 30; done;
              echo "Setting kibana_system password";
              until curl -s -X POST --cacert config/certs/ca/ca.crt -u "elastic:${ELASTIC_PASSWORD}" -H "Content-Type: application/json" https://es01:9200/_security/user/kibana_system/_password -d "{\"password\":\"${KIBANA_PASSWORD}\"}" | grep -q "^{}"; do sleep 10; done;
              echo "All done!";

GitHubgunh0/whs-utils/simple-elk-for-siem/basic-elk/docker-compose.yml#L51-L55

Elasticsearch가 준비되었는지는 인증 없이 요청을 보냈을 때 "missing authentication credentials" 응답이 오는지로 판단합니다. 보안 기능이 켜진 노드가 요청을 받을 준비가 되었다는 가장 간단한 신호입니다. 시작 순서도 정해 두었습니다. es01은 setup 컨테이너가 인증서를 다 만든 뒤에 시작하고, es02와 es03이 차례로 뒤따릅니다. Kibana는 세 노드가 모두 정상 상태가 된 뒤에 시작합니다.

세 노드가 하나의 클러스터가 되는 설정

            - node.name=es01
            - node.roles=master,data,ingest
            - node.store.allow_mmap=false
            - cluster.name=${CLUSTER_NAME}
            - cluster.initial_master_nodes=es01,es02,es03
            - discovery.seed_hosts=es02,es03

GitHubgunh0/whs-utils/simple-elk-for-siem/basic-elk/docker-compose.yml#L74-L79

세 노드 모두 마스터 후보이면서 데이터를 저장하고 수집 전처리(ingest)도 맡습니다. cluster.initial_master_nodes는 클러스터를 처음 만들 때 투표에 참여할 노드 목록이고, discovery.seed_hosts는 각 노드가 처음에 찾아갈 다른 노드들입니다. 노드 하나가 멈춰도 나머지 두 노드로 클러스터가 유지되는 모습을 실습에서 직접 확인할 수 있습니다.

실습용으로 자원을 줄인 설정

수업에서는 학생들이 각자 노트북에서 이 환경을 띄우기 때문에, 자원을 적게 쓰도록 몇 가지를 조정했습니다.

# Minimal lab resource limits. Increase these if Kibana or Elasticsearch
# becomes unstable under heavier ingestion or dashboard workloads.
ES_MEM_LIMIT=805306368
ES_JAVA_OPTS=-Xms256m -Xmx256m -XX:ActiveProcessorCount=1
KIBANA_MEM_LIMIT=1610612736
KIBANA_NODE_OPTIONS=--max-old-space-size=1024

GitHubgunh0/whs-utils/simple-elk-for-siem/basic-elk/.env#L25-L30

  • 힙 메모리 256MB: Elasticsearch 노드마다 JVM 힙을 256MB로 고정하고, 컨테이너 메모리는 약 768MB로 제한했습니다.
  • 머신러닝과 GeoIP 끄기: xpack.ml.enabled=false, ingest.geoip.downloader.enabled=false로 실습에 필요 없는 기능이 자원을 쓰지 않게 했습니다.
  • 메모리 매핑 끄기: 위 클러스터 설정에 있던 node.store.allow_mmap=false입니다. 아래에서 따로 설명합니다.

vm.max_map_count를 건드리지 않아도 되는 이유

Elasticsearch는 기본적으로 인덱스를 메모리 매핑 파일(mmapfs)로 다루기 때문에 많은 수의 메모리 맵이 필요합니다. 공식 문서는 리눅스 커널 파라미터 vm.max_map_count를 1048576 이상으로 올리라고 안내합니다. 그런데 Docker Desktop이나 WSL에서는 이 값을 바꾸는 방법이 환경마다 달라서, 학생마다 다른 설정 방법을 안내하기 어렵습니다.

node.store.allow_mmap=false는 메모리 매핑을 쓰지 않도록 하는 설정입니다. 공식 문서도 메모리 맵 개수를 조절할 수 없는 환경에서 쓸 수 있는 설정으로 소개합니다. 이 설정 덕분에 학생들은 커널 파라미터를 건드리지 않고 바로 실습을 시작할 수 있습니다. 대신 대용량 데이터를 다루는 운영 환경이라면 vm.max_map_count를 올리고 메모리 매핑을 쓰는 기본 설정을 따르는 편이 맞습니다.


보안 로그를 넣고 찾아보기

클러스터가 떴다면 데이터를 넣어 봅니다. 저장소에는 Kibana의 Dev Tools에 그대로 붙여 넣을 수 있는 예제 로그가 있습니다.

POST _bulk
{"index":{"_index":"security-logs"}}
{"timestamp":"2024-01-27T12:00:00","event_type":"login_attempt","status":"success","user":"user1"}
{"index":{"_index":"security-logs"}}
{"timestamp":"2024-01-27T12:05:00","event_type":"login_attempt","status":"failed","user":"user2"}

GitHubgunh0/whs-utils/simple-elk-for-siem/basic-elk/bulk.template#L1-L5

_bulk API는 여러 문서를 한 번에 넣는 API입니다. 한 줄은 어느 인덱스에 넣을지, 다음 줄은 넣을 문서입니다. 로그인 시도, 파일 접근 같은 보안 이벤트가 security-logs 인덱스에 쌓입니다.

데이터를 넣은 뒤에는 Kibana의 Discover에서 security-logs 인덱스로 데이터 뷰를 만들고, KQL로 로그인 실패만 걸러 볼 수 있습니다.

event_type : "login_attempt" and status : "failed"

여기서부터가 SIEM의 출발점입니다. 실패 이벤트를 사용자별로 세어 보고, 특정 시간 안에 실패가 몰리면 알림을 보내는 탐지 규칙을 만드는 식으로 실습을 넓혀 갈 수 있습니다.


elk-with-fleet: 수집 경로를 붙인 구성

실제 관제 환경에서는 로그를 사람이 붙여 넣지 않습니다. 에이전트가 서버에서 로그와 지표를 모아 자동으로 보냅니다. elk-with-fleet은 이 수집 경로를 네 가지로 붙인 구성입니다.

경로 설정 파일 수집하는 것
Filebeat filebeat.yml ingest_data/의 로그 파일, 같은 호스트에서 도는 컨테이너 로그
Logstash logstash.conf ingest_data/의 파일을 읽어 logstash-날짜 인덱스로 저장
Metricbeat metricbeat.yml Elasticsearch, Logstash, Kibana의 모니터링 지표와 Docker 컨테이너 지표
Fleet Server docker-compose.yml Elastic Agent 등록과 정책 관리, 데모 웹앱의 APM 데이터

Filebeat 설정을 보면, 지정한 경로의 로그 파일을 읽는 입력과 Docker 컨테이너를 자동으로 찾아 로그를 수집하는 설정이 함께 들어 있습니다.

filebeat.inputs:
- type: filestream
  id: default-filestream
  paths:
    - ingest_data/*.log

filebeat.autodiscover:
  providers:
    - type: docker
      hints.enabled: true

GitHubgunh0/whs-utils/simple-elk-for-siem/elk-with-fleet/filebeat.yml#L1-L10

Fleet Server는 Elastic Agent 이미지를 Fleet Server 모드로 띄운 컨테이너입니다. 환경 변수만으로 Kibana에 Fleet을 설정하고(KIBANA_FLEET_SETUP), 자기 자신을 Fleet Server로 등록합니다(FLEET_SERVER_ENABLE).

      - FLEET_ENROLL=1
      - FLEET_INSECURE=true
      - FLEET_SERVER_ELASTICSEARCH_CA=/certs/ca/ca.crt
      - FLEET_SERVER_ELASTICSEARCH_HOST=https://es01:9200
      - FLEET_SERVER_ELASTICSEARCH_INSECURE=true
      - FLEET_SERVER_ENABLE=1
      - FLEET_SERVER_CERT=/certs/fleet-server/fleet-server.crt
      - FLEET_SERVER_CERT_KEY=/certs/fleet-server/fleet-server.key
      - FLEET_SERVER_INSECURE_HTTP=true
      - FLEET_SERVER_POLICY_ID=fleet-server-policy
      - FLEET_URL=https://fleet-server:8220
      - KIBANA_FLEET_CA=/certs/ca/ca.crt
      - KIBANA_FLEET_SETUP=1
      - KIBANA_FLEET_USERNAME=elastic
      - KIBANA_FLEET_PASSWORD=${ELASTIC_PASSWORD}
      - KIBANA_HOST=https://kibana:5601

GitHubgunh0/whs-utils/simple-elk-for-siem/elk-with-fleet/docker-compose.yml#L265-L280

Fleet Server가 올라오면 Kibana의 Fleet 화면에서 에이전트 정책을 만들고, 다른 호스트에 Elastic Agent를 설치해 등록할 수 있습니다. 에이전트가 Elasticsearch의 자체 서명 인증서를 신뢰하도록 CA 인증서와 지문(fingerprint)을 꺼내는 명령은 README에 정리해 두었습니다.


9.x로 올릴 때 확인할 점

basic-elk는 9.4.2로 올렸지만 elk-with-fleet은 아직 8.8.2입니다. 이 구성을 9.x로 올릴 때는 Elastic Agent 쪽 변경 사항을 먼저 확인해야 합니다.

Elastic Agent 9.0 릴리스 노트에 따르면, 기본 설치에는 이제 일부 구성 요소만 포함되고 Fleet Server와 APM Server 같은 구성 요소는 빠졌습니다. 이 구성 요소가 필요하면 설치할 때 --install-servers 옵션을 주거나 ELASTIC_AGENT_FLAVOR=servers 환경 변수를 설정해야 합니다. 또 Elastic Agent의 Docker 이미지 기반이 Ubuntu에서 UBI-minimal로 바뀌었습니다. Fleet Server를 Elastic Agent 컨테이너로 띄우는 이 구성은 두 변경 모두에 영향을 받을 수 있으니, STACK_VERSION만 바꾸지 말고 릴리스 노트를 함께 보고 올리는 편이 안전합니다.


마치며

SIEM을 이해하는 가장 빠른 방법은 로그가 들어와서 저장되고, 검색되고, 탐지로 이어지는 과정을 직접 띄워 보는 것입니다. simple-elk-for-siem은 이 과정을 두 단계로 나눠 실습할 수 있게 만든 구성입니다.

정리하면 다음과 같습니다.

  1. basic-elk는 Elasticsearch 3노드와 Kibana를 9.4.2로 띄웁니다. setup 컨테이너가 인증서와 비밀번호를 자동으로 만들고, 메모리 매핑을 꺼서 vm.max_map_count 조정 없이 노트북에서 실행할 수 있습니다.
  2. elk-with-fleet은 Filebeat, Logstash, Metricbeat, Fleet Server를 붙여 실제 관제처럼 로그가 자동으로 들어오는 경로를 실습합니다. 9.x로 올릴 때는 Elastic Agent의 구성 요소 변경을 먼저 확인해야 합니다.

전체 설정은 GitHubgunh0/whs-utils에서 확인할 수 있습니다.


참고 자료