본문 바로가기
서버구축 (WEB,DB)

Wolfi + Elasticsearch: 보안 중심 self-managed 클러스터 구축 방법론

by 날으는물고기 2025. 12. 6.

Wolfi + Elasticsearch: 보안 중심 self-managed 클러스터 구축 방법론

728x90

Elasticsearch를 Docker Compose + Wolfi 이미지로 구성한다는 건, 결국 이 조합입니다.

  1. 기반
    • Docker / Docker Compose 환경
    • Elastic에서 제공하는 공식 Docker Compose 템플릿 (3노드 ES + Kibana)
  2. 이미지
    • 기본: docker.elastic.co/elasticsearch/elasticsearch:${STACK_VERSION}
    • Wolfi Hardened: docker.elastic.co/elasticsearch/elasticsearch-wolfi:${STACK_VERSION}
  3. 구성 파일
    • .env : 비밀번호, 버전, 포트, 메모리 등
    • docker-compose.yml : ES 3노드 + Kibana 서비스 정의
  4. 보안/운영 포인트
    • 포트 노출 최소화(ES_PORT, KIBANA_PORT)
    • X-Pack Security / TLS / 권한 분리
    • Rootless/최소 권한, 이미지 서명/스캔, 모니터링/로그 수집

Wolfi 이미지란? 왜 쓰는가?

1. Wolfi 개념

  • Chainguard에서 만든 컨테이너 전용 미니멀 리눅스 배포판
  • 특징
    • 매우 작은 패키지셋, 불필요 도구 제거
    • 지속적으로 업데이트되는 패키지로 CVE를 최소화
    • “컨테이너+보안”에 초점이 맞춰져 있음

2. Elasticsearch Wolfi 이미지

Elastic 공식 문서에서 이렇게 설명합니다.

  • Hardened Docker images: Wolfi 기반으로 빌드된 ES 이미지
  • 사용하려면 Docker 20.10.10 이상 필요
  • 사용법: 이미지 태그에 -wolfi를 추가
    • # 최신(문서 기준 예시)
      docker pull docker.elastic.co/elasticsearch/elasticsearch-wolfi:9.2.2
  • 나머지 설정(환경변수, compose 구조)은 일반 ES Docker와 거의 동일

보안 입장에서는 “기본 이미지보다 CVE가 적은 하드닝 이미지” 정도의 컨셉으로 이해하시면 됩니다.

사전 준비: Docker / Compose / 시스템 설정

1. Docker & Compose 설치

  • OS: Linux / macOS / Windows 어디든 가능.
  • Docker Desktop 환경이면 Compose 포함.
  • 공식 문서에서는 최소 4GB 메모리를 Docker에 할당하라고 안내합니다.

2. 시스템 설정 (리눅스 호스트 기준 자주 쓰는 것)

# mmap 영역 증가 (ES 필수 튜닝 중 하나)
sudo sysctl -w vm.max_map_count=262144

# 영구 적용 (예)
echo "vm.max_map_count=262144" | sudo tee /etc/sysctl.d/99-elasticsearch.conf
sudo sysctl --system

Compose 템플릿만으로도 로컬 개발은 돌아가지만, 운영 전에는 이런 튜닝을 권장합니다.

(Read Medium articles with AI)

디렉터리 및 파일 구조

1. 프로젝트 디렉터리 생성

mkdir es-wolfi-cluster
cd es-wolfi-cluster

2. 공식 템플릿 다운로드

Elastic 문서에서는 해당 디렉터리에 .envdocker-compose.yml을 저장하라고 되어 있습니다.

  • 직접 복사하거나,
  • 사내 Git 리포지토리로 관리하는 것을 추천드립니다.
300x250

.env 파일 구성 (보안/운영 핵심)

공식 문서 예제를 바탕으로, Wolfi와 상관 없이 공통으로 사용하는 .env 구조입니다.

# Password for the 'elastic' user (at least 6 characters)
ELASTIC_PASSWORD=Elastic123   # 예시 (알파벳+숫자만)

# Password for the 'kibana_system' user (at least 6 characters)
KIBANA_PASSWORD=Kibana123

# Version of Elastic products
STACK_VERSION=9.2.2

# Cluster name
CLUSTER_NAME=wolfi-docker-cluster

# License type: basic or trial
LICENSE=basic
#LICENSE=trial

# Port to expose Elasticsearch HTTP API to the host
#ES_PORT=9200           # 0.0.0.0:9200 (위험)
ES_PORT=127.0.0.1:9200  # 보안상 권장

# Port to expose Kibana to the host
KIBANA_PORT=5601
#KIBANA_PORT=80

# Memory limit for Elasticsearch containers (in bytes)
MEM_LIMIT=2147483648    # 2GB

# Project namespace (optional)
#COMPOSE_PROJECT_NAME=my-es-wolfi

1. 비밀번호 규칙 주의

  • ELASTIC_PASSWORD, KIBANA_PASSWORD알파벳+숫자만 사용 (특수문자 사용 시 bash 스크립트 동작 문제 발생)
  • 운영에서는
    • 이 값들을 Git에 커밋하지 않고,
    • CI/CD / Vault / Secret Manager와 연동하는 방식을 권장.

2. ES 포트 노출 제한 (매우 중요)

  • 문서에서도 “포트 9200이 외부에 노출되지 않도록 ES_PORT=127.0.0.1:9200로 설정하라”고 명시합니다.
  • 서비스 프로바이더 관점
    • ES를 직접 인터넷에 노출하는 건 웬만하면 피하고,
    • 내부망 + WAF/Reverse Proxy(Kibana, NGINX) 구조로 두는 것이 안전합니다.

docker-compose.yml 구성 & Wolfi 적용

공식 Compose는 크게 4 서비스로 구성됩니다.

  1. setup (초기 TLS/사용자 설정 스크립트 수행)
  2. es01, es02, es03 (3 노드 ES)
  3. kibana

여기서 Wolfi 적용 포인트는 단 하나

Elasticsearch 서비스의 image를 elasticsearch-wolfi:${STACK_VERSION} 로 바꾸는 것

1. 예시 docker-compose.yml (핵심 부분만)

version: "2.2"

services:
  setup:
    image: docker.elastic.co/elasticsearch/elasticsearch-wolfi:${STACK_VERSION}
    container_name: es-setup
    environment:
      - ELASTIC_PASSWORD=${ELASTIC_PASSWORD}
      - KIBANA_PASSWORD=${KIBANA_PASSWORD}
      - STACK_VERSION=${STACK_VERSION}
    volumes:
      - certs:/usr/share/elasticsearch/config/certs
    user: "0"
    command: >
      bash -c '
        if [ x${ELASTIC_PASSWORD} == x ]; then
          echo "Set the ELASTIC_PASSWORD environment variable in the .env file";
          exit 1;
        elif [ x${KIBANA_PASSWORD} == x ]; then
          echo "Set the KIBANA_PASSWORD environment variable in the .env file";
          exit 1;
        fi;
        # CA, 노드 인증서 생성 스크립트 (공식 예제 그대로)
        if [ ! -f config/certs/ca.zip ]; then
          echo "Creating CA";
          bin/elasticsearch-certutil ca --silent --pem -out config/certs/ca.zip;
          unzip config/certs/ca.zip -d config/certs;
        fi;
        if [ ! -f config/certs/certs.zip ]; then
          echo "Creating certs";
          # es01, es02, es03, kibana 용 인증서 생성...
          # (공식 예제의 instances.yml 생성 부분)
        fi;
        echo "Setting file permissions"
        chown -R root:root config/certs;
        find . -type d -exec chmod 750 {} \;;
        find . -type f -exec chmod 640 {} \;;
        echo "Setup done."
      '

  es01:
    image: docker.elastic.co/elasticsearch/elasticsearch-wolfi:${STACK_VERSION}
    container_name: es01
    depends_on:
      - setup
    environment:
      - node.name=es01
      - cluster.name=${CLUSTER_NAME}
      - discovery.seed_hosts=es01,es02,es03
      - cluster.initial_master_nodes=es01,es02,es03
      - ELASTIC_PASSWORD=${ELASTIC_PASSWORD}
      - xpack.security.enabled=true
      - xpack.security.http.ssl.enabled=true
      - xpack.security.http.ssl.key=certs/es01/es01.key
      - xpack.security.http.ssl.certificate=certs/es01/es01.crt
      - xpack.security.http.ssl.certificate_authorities=certs/ca/ca.crt
      - xpack.security.transport.ssl.enabled=true
      - xpack.security.transport.ssl.key=certs/es01/es01.key
      - xpack.security.transport.ssl.certificate=certs/es01/es01.crt
      - xpack.security.transport.ssl.certificate_authorities=certs/ca/ca.crt
      - xpack.security.transport.ssl.verification_mode=certificate
      - ES_JAVA_OPTS=-Xms1g -Xmx1g
    volumes:
      - certs:/usr/share/elasticsearch/config/certs
      - esdata01:/usr/share/elasticsearch/data
    ports:
      - "${ES_PORT}:9200"
    ulimits:
      memlock:
        soft: -1
        hard: -1
    mem_limit: ${MEM_LIMIT}

  es02:
    image: docker.elastic.co/elasticsearch/elasticsearch-wolfi:${STACK_VERSION}
    container_name: es02
    depends_on:
      - setup
    environment:
      - node.name=es02
      - cluster.name=${CLUSTER_NAME}
      - discovery.seed_hosts=es01,es02,es03
      - cluster.initial_master_nodes=es01,es02,es03
      - ELASTIC_PASSWORD=${ELASTIC_PASSWORD}
      # 나머지 xpack/security, TLS 설정은 es01과 동일 패턴
    volumes:
      - certs:/usr/share/elasticsearch/config/certs
      - esdata02:/usr/share/elasticsearch/data

  es03:
    image: docker.elastic.co/elasticsearch/elasticsearch-wolfi:${STACK_VERSION}
    container_name: es03
    depends_on:
      - setup
    environment:
      - node.name=es03
      - cluster.name=${CLUSTER_NAME}
      - discovery.seed_hosts=es01,es02,es03
      - cluster.initial_master_nodes=es01,es02,es03
      - ELASTIC_PASSWORD=${ELASTIC_PASSWORD}
    volumes:
      - certs:/usr/share/elasticsearch/config/certs
      - esdata03:/usr/share/elasticsearch/data

  kibana:
    image: docker.elastic.co/kibana/kibana:${STACK_VERSION}
    container_name: kibana
    depends_on:
      - es01
    environment:
      - ELASTICSEARCH_HOSTS=https://es01:9200
      - ELASTICSEARCH_USERNAME=kibana_system
      - ELASTICSEARCH_PASSWORD=${KIBANA_PASSWORD}
      - SERVER_PUBLICBASEURL=http://localhost:${KIBANA_PORT}
      - SERVER_NAME=kibana
      - SERVER_PORT=5601
      - SERVER_HOST=0.0.0.0
      - ELASTICSEARCH_SSL_CERTIFICATEAUTHORITIES=/usr/share/kibana/config/certs/ca/ca.crt
    ports:
      - "${KIBANA_PORT}:5601"
    volumes:
      - certs:/usr/share/kibana/config/certs

volumes:
  certs:
  esdata01:
  esdata02:
  esdata03:

위 예시는 공식 템플릿 구조를 Wolfi 이미지에 맞게 바꾼 예시입니다.
실제 공식 docker-compose.yml의 스크립트/instances.yml 생성 부분을 그대로 사용하시면 됩니다.

클러스터 기동 & 확인

1. 클러스터 기동

docker-compose up -d
  • setup → 인증서 생성 후 종료
  • es01, es02, es03 → 클러스터 형성
  • kibana → ES에 연결 후 UI 제공

2. Kibana 접속

http://localhost:5601
  • ID: elastic
  • PW: .envELASTIC_PASSWORD
    (문서에서도 elastic/ELASTIC_PASSWORD로 로그인하라고 안내합니다.

3. curl로 헬스 체크 예시

TLS On일 때

curl -k -u elastic:${ELASTIC_PASSWORD} \
  https://localhost:9200/_cluster/health?pretty

TLS Off(테스트용)일 때

curl -u elastic:${ELASTIC_PASSWORD} \
  http://localhost:9200/_cluster/health?pretty

종료

# 컨테이너/네트워크만 정지 및 삭제 (데이터는 유지)
docker-compose down

# 컨테이너/네트워크/볼륨(데이터까지) 모두 삭제
docker-compose down -v

공식 문서에서도 같은 방식으로 안내합니다.

보안 점검 포인트 정리

Wolfi 이미지 + ES Docker Compose 구성을 내부에 소개하려면, 아래 항목들을 “보안 가이드 또는 체크리스트”로 만들면 좋습니다.

1. 이미지 및 취약점 관리

  1. 이미지 출처
    • docker.elastic.co/elasticsearch/elasticsearch-wolfi:${STACK_VERSION} 만 사용
  2. 사내 레지스트리/프록시
    • Pull 후 사내 레지스트리에 재저장
    • Trivy, Grype 등으로 정기 CVE 스캔 수행
  3. 버전 정책
    • STACK_VERSION 고정 (예: 9.2.2)
    • ES 전체 스택(ES, Kibana, Beats 등) 버전 일치 필요

2. 네트워크 & 접근 통제

  1. .env에서 ES_PORT=127.0.0.1:9200 설정
  2. 운영망 구성
    • ES는 내부망 전용 / VPN 또는 Bastion 호스트에서만 접근
    • Kibana, API Gateway, NGINX를 통해 외부 사용자 접근 통제
  3. Docker 네트워크
    • ES/Kibana 전용 docker network 사용 (외부 컨테이너와 분리)

3. 인증/권한 (X-Pack Security)

  1. xpack.security.enabled=true 필수
  2. 역할/계정 분리
    • Superadmin: elastic (최소 사용)
    • Kibana: kibana_system (자동 설정)
    • 애플리케이션 계정: 특정 인덱스/alias만 read/write
    • 모니터링 계정: read/monitor 전용 role
  3. API 키
    • 장기 토큰 대신 API Key 기반 연동을 기본으로 가이드

4. TLS/암호화

  1. 공식 Compose는 CA + 노드 인증서 자동 생성 스크립트를 포함
    setup 서비스가 이를 수행
  2. 운영 시에는
    • 사내 PKI/CA와 연동하거나,
    • ACME/Let’s Encrypt + Reverse Proxy 사용 고려
  3. 내부 전송(Transport)도 TLS 적용되어 있음 (기본 스크립트가 처리)

5. 권한/Rootless

  1. Wolfi 이미지와 잘 맞는 패턴
    • Podman Rootless 혹은 Docker --user 옵션 활용
  2. 데이터 디렉터리
    • ES UID/GID에만 RW 권한 부여
  3. 컨테이너 Capabilities 최소화
    • 필요 시 cap_drop: [ALL] + 필요한 Cap만 cap_add 하는 패턴 고려

6. 로그 & 모니터링 연동

  1. ES 로그
    • stdout/파일로 수집 → Fluentd/Logstash/Beats/Wazuh 등으로 중앙수집
  2. 중요 이벤트 모니터링
    • 인증 실패, cluster health RED/ORANGE, 노드 join/leave, shard relocation
  3. Prometheus
    • Wolfi 기반 exporter와 연계해 메트릭 수집 가능 (Chainguard의 Exporter 또는 Elastic의 Exporter 활용) 

다음 단계 제안

정리하면

  • 구성 방식은 공식 Docker Compose 예제를 그대로 이용하되,
    Elasticsearch 이미지 이름만 Wolfi 하드닝 이미지로 교체하는 것이 핵심입니다.
  • .env에서
    • 비밀번호(알파벳+숫자),
    • STACK_VERSION,
    • ES_PORT=127.0.0.1:9200
      만 잘 잡아주면 로컬/테스트 환경은 바로 기동 가능합니다.
  • 보안 입장에서는
    • 이미지 소스 및 CVE 관리,
    • 포트 노출/네트워크 구조,
    • 계정/권한 분리,
    • TLS/로그/모니터링
      정책 문서+체크리스트로 정리해두면, 이후 프로젝트/서비스에서 재사용하기 좋습니다.
728x90
그리드형(광고전용)

댓글