안녕하세요! 😊
지난번에는 Wazuh SIEM 시스템을 실제로 구축해보았고,
이번에는 실제 애플리케이션 환경을 이해하기 위해 직접 KMS(Key Management System) 로그를 Wazuh에 연동해보기로 했습니다. 단순히 파일만 읽으면 되겠지 싶었는데... 6시간의 삽질 끝에 겨우 성공했습니다. 😅
긴 시간 투자한 만큼, 이번 글은 단순한 설정 가이드가 아니라 SIEM의 핵심 개념을 이해하고 실제로 부딪힌 문제들을 해결한 과정을 담아보겠습니다.
🔹 SIEM의 로그 처리 파이프라인
본격적인 실습 전에, Wazuh가 로그를 어떻게 처리하는지 정리하고 가보겠습니다.
전체 파이프라인
[1] 로그 파일 생성
KMS → /var/log/remote/HA1.log
[2] Agent 수집 (Syslog 서버)
log_format: syslog로 설정
↓
[3] Manager 전송
TCP 1514 포트
↓
[4] archives.log 저장 (Wazuh Manager 서버)
⚠️ 모든 로그가 여기 저장됨 (Rule 무관)
↓
[5] Decoder 처리
로그 파싱 및 필드 추출
↓
[6] Rule 매칭
조건 확인 및 Alert 생성
↓
[7] alerts.log 저장
⚠️ Rule에 매칭된 것만 저장됨
↓
[8] Filebeat 전송
↓
[9] OpenSearch 인덱싱
↓
[10] Dashboard 표시
여기서 핵심은 4단계와 7단계의 차이라고 생각합니다.
archives.log vs alerts.log
처음 겪은 혼란
"archives.log에 로그가 쌓이는데 왜 대시보드에 안 보이지?"
→ archives.log는 Filebeat가 읽지 않음
[참고] Filebeat란 - 로그 데이터를 전달하고 중앙 집중화하기 위한 경량 전달자 역할을 함
"Rule을 만들었는데 왜 alerts.log에 안 보이지?"
→ <decoded_as> 방식으로는 작동 안 함 (나중에 발견)
"wazuh-logtest에서는 성공하는데 왜 실제로는 안 되지?"
정리
| archives.log | 모든 원본 로그 | 로그 수집 여부 확인, 디버깅 |
| alerts.log | Rule 매칭된 Alert만 | 실제 분석, 대시보드 표시 |
디버깅 순서:
1. archives.log 확인 → 로그 수집 OK?
2. wazuh-logtest → Decoder/Rule 작동 OK?
3. alerts.log 확인 → Alert 생성 OK?
4. 대시보드 확인 → 인덱싱 OK?
archives.log - 모든 로그의 원본 저장소
tail -f /var/ossec/logs/archives/archives.log | grep HA1.log
출력:
2025 Nov 27 03:09:22 (cent9) any->/var/log/remote/HA1.log 2025-11-27T12:09:21+09:00 HA1 {"category":"access","level":"info",...}
특징:
- Agent에서 수집된 모든 로그가 원본 그대로 저장
- Rule 매칭 여부와 무관하게 무조건 기록
- 로그 수집이 정상적으로 되고 있는지 확인하는 용도
- 형식: 날짜 시간 (호스트) 위치-> 파일경로 원본로그
언제 사용하나?
- "Agent가 파일을 제대로 읽고 있나?" 확인 가능
- "Manager가 로그를 받고 있나?" 확인 가능
- Rule 작성 전 로그 형식 파악 가능
alerts.log - Rule에 매칭된 로그만
grep HA1.log /var/ossec/logs/alerts/alerts.log
# 처음엔 로그 내용이 증적되지 않음
특징:
- ✅ Rule에 매칭된 이벤트만 기록
- ❌ Rule이 없으면 아무것도 기록 안 됨
- ✅ 실제 보안 분석에 사용되는 Alert
언제 사용하나?
- "Rule이 제대로 작동하나?" 확인
- "Alert가 생성되고 있나?" 확인
- 대시보드에 표시될 데이터 확인
🔹 1단계: 로그 수집 설정 - log_format 구성의 중요성
KMS 로그 형식 분석
먼저 연동할 로그 파일의 형식을 확인했습니다:
tail /var/log/remote/HA1.log
# 출력:
2025-11-27T12:09:21+09:00 HA1 {"category":"access","level":"info","event":"reqSymmkeySuccess","timestamp":"2025-11-27 12:09:21","message":"It requested the symmetric key.","meta":{"requestAgent":"SYLEE","remoteIp":"192.168.0.172"}}
형식 분석:
- 타임스탬프: 2025-11-27T12:09:21+09:00 (ISO 8601 + KST)
- 호스트명: HA1
- JSON 데이터: {"category":...}
로그 정보에 JSON 데이터가 있어 log_format을 json으로 구성하였습니다.
첫 번째 시도: json 포맷
<!-- /var/ossec/etc/ossec.conf (Wazuh Manager 서버) -->
<localfile>
<log_format>json</log_format>
<location>/var/log/remote/HA1.log</location>
</localfile>
파일 수정 후 Agent 재시작:
systemctl restart wazuh-agent
결과 확인:
# Agent가 파일을 읽고 있는지 확인
lsof | grep HA1.log
# 결과: 아무것도 안 나옴! ❌
실패한 이유:
- log_format: json은 순수 JSON 형식만 지원
- 우리 로그는 타임스탬프 + 호스트명 + JSON 혼합 형식
- Agent가 파싱 실패 → 파일을 읽지 않음
두 번째 시도: multi-line 포맷
<localfile>
<log_format>multi-line:1</log_format>
<location>/var/log/remote/HA1.log</location>
</localfile>
결과: 여전히 실패 ❌
실패한 이유:
- multi-line은 여러 줄에 걸친 로그를 하나로 합치는 용도
- 우리 로그는 한 줄이지만 형식이 복잡한 경우 -> 이것도 아님!
해결: syslog 포맷
공식 문서를 통해 아래 내용을 확인하였습니다 :
syslog format: 타임스탬프 + 호스트명 + 메시지 형식의 로그
<localfile>
<log_format>syslog</log_format>
<location>/var/log/remote/HA1.log</location>
</localfile>
결과 확인:
systemctl restart wazuh-agent
lsof | grep HA1.log
# 출력:
wazuh-age 1234 wazuh 10r REG 8,2 2621440 /var/log/remote/HA1.log
✅ 드디어 파일을 읽기 시작!
archives.log도 확인:
# Wazuh Manager 서버에서
tail -f /var/ossec/logs/archives/archives.log | grep HA1.log
# 로그가 쌓이기 시작함!
2025 Nov 27 03:09:22 (cent9) any->/var/log/remote/HA1.log 2025-11-27T12:09:21+09:00 HA1 {"category":"access"...}
#cent9 -> syslog 서버 호스트명
배운 점: log_format 선택 기준
[참고] 형식log_format예시
| 순수 JSON | json | {"timestamp":"2025-11-27","level":"info"} |
| Syslog 형식 | syslog | Nov 27 12:00:00 host app: message |
| 타임스탬프+JSON | syslog | 2025-11-27T12:00:00 host {"data":"value"} ⭐ 이번 syslog 연동 테스트 케이스 |
| 여러 줄 로그 | multi-line:N | Exception:\n at line 1\n at line 2 |
핵심: 타임스탬프나 호스트명이 앞에 있을 때 syslog 포맷을 사용할 것!
🔹 2단계: Decoder와 Rule 이해하기
로그가 수집되기 시작했으니, 이제 로그에 대한 Alert를 구성하고 싶었습니다.
하지만 Alert를 생성하기 전에, Decoder와 Rule이라는 생소한 개념을 먼저 이해해야 했습니다.
Decoder와 Rule, 차이점이 무엇일까?
처음에는 이 둘의 차이를 이해하는 게 헷갈렸습니다.
[원본 로그]
↓
[Decoder] ← "로그를 이해하는 규칙" (파싱/구조화)
↓
[Rule] ← "위협을 판단하는 규칙" (조건 검사/Alert 생성)
Decoder
역할: 원시 데이터 로그를 "구조화"하는 것
원본 로그: "Failed password for admin from 192.168.1.100"
Decoder가 하는 일:
→ 이 로그는 SSH 로그인 실패야
→ 사용자는 "admin"이네
→ IP는 "192.168.1.100"이군
→ 액션은 "Failed password"구나
결과 (구조화된 데이터):
- user: admin
- srcip: 192.168.1.100
- action: Failed password
원본 로그: "2025-11-27 12:00:00 user admin logged in from 192.168.1.100"
Decoder 처리 후:
- timestamp: 2025-11-27 12:00:00
- user: admin
- action: logged in
- srcip: 192.168.1.100
핵심: Decoder는 로그를 읽고, 분해만 합니다. 위협 판단은 하지 않습니다.
Rule
역할: Decoder가 추출한 정보로 "위협 판단"
Decoder가 준 정보:
- user: admin
- srcip: 192.168.1.100
- action: Failed password
Rule이 하는 일:
→ 만약 "Failed password"가 5분 내 5회 이상 발생하면
→ 이건 Brute Force 공격 시도야!
→ Alert 생성! (level 10, 긴급)
핵심: Rule은 조건을 검사하고 Alert를 생성합니다.
Decoder와 Rule을 분리하는 이유
분리의 장점:
→ 하나의 Decoder로 여러 Rule 작성이 가능하다고 합니다.
Decoder (재사용 가능)
- SSH 로그는 항상 같은 방식으로 파싱
Rule (다양한 시나리오)
- Rule A: 로그인 실패 5회 → Brute Force
- Rule B: root 계정 로그인 성공 → 의심스러운 활동
- Rule C: 알려진 악성 IP → 즉시 차단
🔹 [중간 정리] Wazuh의 로그 처리 프로세스
[원본 로그]
↓
[Phase 1: Pre-decoding]
→ 기본 정보 추출 (타임스탬프, 호스트명)
↓
[Phase 2: Decoding]
→ Decoder 매칭 및 필드 추출
↓
[Phase 3: Rule Matching]
→ Rule 조건 확인 및 Alert 생성
wazuh-logtest - 실시간 테스트 도구
실습하면서 wazuh-logtest 를 많이 사용하였습니다.
이 도구를 통해, 실제 로그를 Manager에 보내기 전에, 로컬에서 Decoder와 Rule을 테스트할 수 있습니다.
사용법:
# Wazuh Manager 서버에서
echo '2025-11-27T12:09:21+09:00 HA1 {"level":"error"}' | /var/ossec/bin/wazuh-logtest
# 출력:
**Phase 1: Completed pre-decoding.
full event: '2025-11-27T12:09:21+09:00 HA1 {"level":"error"}'
timestamp: '2025 Nov 27'
hostname: 'HA1'
**Phase 2: Completed decoding.
No decoder matched.
(Phase 3 없음 - Decoder가 없어서 Rule도 없음) **
이걸 보고 어떤 부분에서 처리가 잘못 되었을지 트러블슈팅이 바로 가능했습니다.
ex) "아, Decoder가 없구나!"
wazuh-logtest의 장점:
- ✅ 실제 로그 보내기 전에 미리 테스트
- ✅ Phase별로 어디서 문제인지 바로 확인
- ✅ Decoder 작성 → Rule 작성 → 즉시 검증 가능
- ✅ 실수로 Manager 재시작 반복 안 해도 됨
실제 사용 예:
# Decoder 작성
vi /var/ossec/etc/decoders/local_decoder.xml
# 바로 테스트 (Manager 재시작 불필요!)
echo '테스트 로그' | /var/ossec/bin/wazuh-logtest
# Rule 작성
vi /var/ossec/etc/rules/local_rules.xml
# 바로 테스트
echo '테스트 로그' | /var/ossec/bin/wazuh-logtest
즉시 피드백을 받을 수 있다는 게 정말 편했습니다.
"No decoder matched"라는 메시지가 나왔습니다. 커스텀 Decoder가 필요하다는 뜻입니다.
Decoder 작성 - 정규표현식의 함정
처음에는 타임스탬프, 호스트명, JSON을 전부 파싱하려고 복잡한 정규표현식(Regex)을 작성했습니다.
정규표현식 자체도 생소한데, 여기서 또 문제가 발생했습니다.
<!-- 실패한 시도 -->
<decoder name="ha_kms">
<prematch>^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\+\d{2}:\d{2} HA\d+</prematch>
</decoder>
결과:
ERROR: Syntax error on regex
POSIX Regex vs Perl Regex (생소한 부분)
알고 보니 Wazuh는 POSIX Regex를 사용합니다!
일반적으로 많이 쓰는 Perl Regex와는 문법이 다릅니다:
패턴Perl RegexPOSIX Regex (Wazuh)
| 숫자 | \d | [0-9] ⭐ |
| 공백 | \s | (공백 문자 직접) ⭐ |
| + 기호 | + | \+ (이스케이프 필요) ⭐ |
저에게는 정규표현식 구성하는것 자체도 어려운데, POSIX 문법까지 신경 써야 하는 부분이 정말 어려웠습니다. 😅
AI툴과 구글링을 하면서 패턴을 구성해보았지만 잘 되지 않았습니다.
다음번에 이 정규표현식에 대해서 별도로 공부하고, 블로그 글로 정리해볼 생각입니다.
POSIX로 수정:
<decoder name="ha_kms">
<prematch>[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}\+[0-9]{2}:[0-9]{2} HA[0-9]\+</prematch>
</decoder>
결과: 문법 오류는 해결되었지만 Rule에서 제대로 작동하지 않음
결론: Decoder는 단순하게!
"정규표현식을 완벽하게 만들려고 하지 말자..."
복잡한 Regex는:
- ❌ 작성하기 어려움
- ❌ POSIX 문법 실수 가능성 높음
- ❌ 유지보수 어려움
- ❌ 오히려 매칭 실패 확률 높음
최종 Decoder (극도로 단순화):
<!-- /var/ossec/etc/decoders/local_decoder.xml (170 서버) -->
<decoder name="ha_kms">
<prematch>HA1.log|HA2.log</prematch>
</decoder>
핵심:
- Decoder는 로그를 식별할 수 있으면 충분
- 파일명(HA1.log, HA2.log)만으로도 충분히 식별 가능
- 복잡한 파싱은 오히려 오류 가능성만 높임
검증:
echo '2025-11-27T12:09:21+09:00 HA1 {"level":"error"}' | /var/ossec/bin/wazuh-logtest
# 출력:
**Phase 2: Completed decoding.
decoder: 'ha_kms'
✅ Decoder 매칭 성공!
🔹 최종 배운 점
1. archives.log vs alerts.log
- archives.log = 모든 수집 로그 (Rule 무관)
- alerts.log = Rule 매칭된 로그만
로그 수집 성공 ≠ Alert 생성
대시보드에 로그가 안 보일때 확인했어야 하는 부분들:
- archives.log 먼저 확인 (수집은 되나?)
- alerts.log 확인 (Alert는 생성되나?)
- Filebeat 확인 (전송은 되나?)
2. Decoder vs Rule
| 역할 | 로그 파싱 (이해/구조화) | 위협 탐지 (조건/Alert) |
| 입력 | 원본 로그 | Decoder 결과 |
| 출력 | 구조화된 데이터 | Alert |
| 예시 | user: admin 추출 | admin 5회 실패 → Alert |
3. log_format의 중요성
타임스탬프나 호스트명이 앞에 있으면 syslog!
시도포맷결과
| 1차 | json | ❌ 순수 JSON이 아니라서 실패 |
| 2차 | multi-line:1 | ❌ 한 줄 로그라서 실패 |
| 3차 | syslog | ✅ 성공! |
4. Decoder는 단순하게
복잡한 정규표현식보다 최소한의 식별 패턴이 더 안정적입니다.
<!-- ❌ 복잡함 + POSIX 문법 어려움 -->
<prematch>[0-9]{4}-[0-9]{2}-[0-9]{2}T...</prematch>
<!-- ✅ 단순함 + 확실함 -->
<prematch>HA1.log|HA2.log</prematch>
5. wazuh-logtest를 통한 사전 검증
- Phase별 즉시 확인
- Manager 재시작 불필요
- 실제 운영 환경 영향 없음
- 개발 → 테스트 → 배포 사이클 단축
🔹 유용한 명령어 정리
로그 수집 확인
# archives.log에서 원본 로그 확인
tail -f /var/ossec/logs/archives/archives.log | grep "HA1.log\|HA2.log"
# Agent가 파일을 읽고 있는지 확인
lsof | grep HA1.log
Decoder 테스트
# Decoder 매칭 확인
echo '2025-11-27T12:10:00+09:00 HA1 {"level":"error"}' | /var/ossec/bin/wazuh-logtest
# 파일에서 읽어서 테스트
cat sample.log | /var/ossec/bin/wazuh-logtest
Wazuh (Manager/Agent) 서비스 재시작 & 상태 조회
# Wazuh Agent
systemctl restart wazuh-agent
systemctl status wazuh-agent
# Wazuh Manager
systemctl restart wazuh-manager
systemctl status wazuh-manager
🔹 마치며
이번 글에서는 SIEM의 로그 수집부터 Decoder까지를 다뤘습니다.
핵심 내용:
- archives.log vs alerts.log - 수집과 Alert는 다르다
- Decoder vs Rule - 파싱과 탐지는 분리되어 있다
- log_format - 형식에 맞는 설정이 중요하다
- wazuh-logtest - 즉시 테스트로 개발 속도 향상
- POSIX Regex - 생소하지만 단순하게 OK
Wazuh 시스템 구축 테스트를 진행하면서,
archives.log와 alerts.log의 차이를 이해한 것이 가장 중요한 것 같습니다. 실제로 테스트를 진행하며 "로그는 수집되는데 왜 대시보드에 안 보이지?"하면서 계속 헤맸습니다.
통합 SIEM 관점에서 분산된 로그들을 한곳에 모아 분석하는 시각이 열릴 수 있도록 계속 테스트를 진행해볼 예정입니다.
다음 편에서는 Rule 작성부터 대시보드까지 - 완전한 파이프라인을 다뤄보겠습니다.
Decoder로 파싱한 로그를 실제 Alert로 만들고, Filebeat와 OpenSearch를 거쳐 대시보드에 표시하는 과정을 공유해보겠습니다.
'기술 공부 > DevOps' 카테고리의 다른 글
| VirtualBox 새 파티션 LVM 디스크 확장 (0) | 2025.12.14 |
|---|---|
| Wazuh SIEM에서 Syslog 연동 실습 (2편) - Rule 작성부터 대시보드까지 (1) | 2025.12.14 |
| SIEM이란? Wazuh 오픈소스 SIEM 구축하기 (VirtualBox 환경) (0) | 2025.11.25 |
| Self-hosted GitHub Runner를 통한 CI/CD 자동 배포 파이프라인 구축 (0) | 2025.11.02 |
| Linux Git 완전 설치 및 다중 사용자 설정 가이드 (0) | 2025.09.28 |