분석 · 관제심화 · 16분 읽기

ALPACA: 센서와 중앙 분석의 설계 사례

수업의 ALPACA 설계를 통해 패킷·경보 수집과 중앙 상관분석을 분리해 이해한다. 설계 문서는 완성된 운영 성능의 증거와 구분해서 읽는다.

개념부터 찾아보기

정의·역할·동작 원리를 독립 페이지에서 확인하세요. 본문의 밑줄 용어에도 설명이 있습니다.

이 노트에서 설명할 수 있게 되는 것

  1. PKLP 헤더 필드와 TCP 바이트 스트림의 관계를 설명한다.
  2. 부분 수신·길이 상한·연결 종료에 대한 수신 상태를 추적한다.
  3. 큐와 재전송 정책의 지연·유실·중복 영향을 구분한다.

01개념과 구성 요소

  • Packet과 Alert를 독립 수집·전송하고 중앙 분석에서 시간·통신·출처로 연결하는 프로젝트 구조다.
  • PacketCapture·AlertRead·각 SendToSiem의 네 스레드와 두 가 수집과 송신을 분리한다.
  • 10바이트 공통 헤더와 타입별 payload를 사용하는 위의 애플리케이션 프레임이다.

02PKLP 패킷 배치가 중앙 수신기에 도달하는 순서

  1. libpcap에서 번호·timestamp·raw bytes를 얻어 Packet 에 넣는다. 항목 수 한도는 바이트 한도와 다르다.
  2. 송신기가 개수·바이트·최대 대기시간 조건으로 배치를 모은다.
  3. !4sBBI 공통 헤더에 magic·version·type·payload 바이트 길이를 쓰고 type2의 count와 레코드들을 연결한다.
  4. 수신기는 10바이트 헤더를 모아 검사한 뒤 지정된 payload를 정확히 읽는다. 한 recv가 한 프레임이라는 전제는 사용하지 않는다.
  5. Count·각 raw_length·남은 바이트·전체 상한을 검사한 뒤 저장 큐에 전달한다.
  6. Send 성공과 DB commit을 구분하고 센서·세션·레코드 키로 중복을 다룬다. 재전송만으로 exactly-once가 보장되지는 않는다.

03센서와 중앙 분석을 나눈 구조

  • ALPACA는 Packet·Alert를 독립 수집하여 중앙 에서 연결하는 수업 프로젝트다. 센서는 원시 데이터 확보·버퍼링·전송을 맡고 자세한 파싱과 상관분석은 중앙으로 옮긴다.
  • 패킷과 fast alert는 생성 시점과 표현이 달라 같은 사건을 직접 같은 번호로 나타내지 않는다. 시각·통신 정보·출처를 연결하는 구조가 필요하다.
  • 센서의 클래스·큐·프레임 구조와 중앙의 분석 모델은 서로 다른 설계 층이다. 센서 전송 구조가 정해졌다는 사실만으로 중앙의 모든 분석 모델이 운영 검증된 것은 아니다.
  • 확정된 전달 방향, 비교용 스키마 초안, 그 구조에서 추가로 필요한 신뢰성 조건을 구분한다.

044개 스레드와 두 Queue

  • PacketCapture는 libpcap 핸들과 BPF 필터를 준비하고 번호·timestamp·raw bytes를 Packet 에 넣는다. AlertRead는 파일의 읽은 위치 이후 새 줄을 Alert 에 넣는다.
  • 각 SendToSiem은 해당 에서 배치를 모아 전송한다. 패킷 수집·패킷 송신·경보 읽기·경보 송신 네 흐름을 나누어 한쪽의 짧은 지연이 즉시 모든 수집을 멈추지 않도록 한 것이다.
  • DataBuffer의 max_size=10000은 항목 수이며 바이트 수가 아니다. 패킷 크기에 따라 같은 항목 수도 메모리량이 달라진다.
  • 는 영구 저장소가 아니므로 종료 때의 미전송 자료는 별도 대책이 필요하다. 가득 찼을 때 producer 대기·drop·디스크 저장 중 무엇을 선택하는지에 따라 손실 위치와 지연이 달라진다.

05개수·용량·대기시간으로 배치 만들기

  • collect는 개수·용량·대기시간 한도까지 레코드를 모은다. 개수만 제한하면 큰 패킷이 많은 배치가 과도하게 커지고, 바이트만 제한하면 트래픽이 적을 때 오래 기다릴 수 있다.
  • 최대 대기시간이 작은 배치의 전달 지연을 제한한다.
  • 배치는 socket 호출과 헤더 반복 비용을 줄이지만 개별 기록의 도착이 늦어질 수 있다. 큐 대기·배치 대기·전송·저장 시간을 따로 관찰해야 느린 화면의 원인을 구분할 수 있다.
  • 총 throughput만 높고 가장 오래 대기한 자료가 계속 남아 있는 상황도 있으므로 배치 단위와 레코드 단위 지연은 별개다.

06PKLP 공통 헤더의 10바이트

  • (Packet Log Protocol)는 이 프로젝트가 정의한 애플리케이션 프로토콜이다. 모든 타입은 Magic 4B·version 1B·type 1B·payload length 4B의 공통 헤더를 쓴다.
  • struct의 !4sBBI에서 !는 network byte order인 big-endian과 표준 크기·정렬을 뜻한다.
  • payload length는 뒤따르는 전체 바이트 수이고 packet_count와 같은 레코드 수가 아니다. 아래는 type2에 count 필드만 있는 빈 배치 예다.
  • 길이를 잘못 계산하면 수신자가 다음 메시지 헤더까지 현재 payload로 읽어 이후 모든 경계가 틀어진다.
text
!4sBBI → 10 bytes
50 4b 4c 50 | 01 | 02 | 00 00 00 04
 P  K  L  P     v1 type2  payload 4B

payload: 00 00 00 00  # packet_count=0

원리를 설명하는 가상 학습 예시입니다.

07타입 1·3: UTF-8 문자열 길이

  • 타입1은 sensor_id·capture_id·interface, 타입3은 sensor_id·alert_session_id·ids_type·interface 문자열을 순서대로 담는다. 각 문자열 앞의 !H 2바이트는 로 인코딩한 바이트 길이다.
  • 별도 고정 payload 헤더가 있는 구조가 아니다.
  • “가”는 문자 하나지만 에서 3바이트이므로 문자 수를 넣고 바이트를 보내면 다음 필드의 시작이 어긋난다. 길이 범위 초과·잘못된 ·남은 payload 부족을 구분해야 한다.
  • 재연결 후 세션 정보를 다시 보내는 이유는 수신자가 후속 순번을 어느 센서·실행 문맥에 연결할지 알아야 하기 때문이다.

08타입 2: Packet 레코드의 20바이트 고정부

  • payload는 packet_count 4B로 시작한다. 각 레코드는 packet_no 8B(!Q), timestamp 8B(!d), raw_length 4B(!I), raw_packet 가변 바이트다.
  • 전체 길이는 4 + 각 레코드의 (20+raw_length) 합이다. 디코더는 count만큼 읽되 각 raw_length가 남은 바이트 안에 있는지 검사해야 한다.
  • packet_no는 캡처 세션 안의 순번이므로 sensor·capture_id와 묶어야 다른 실행의 1번과 구분된다. timestamp는 libpcap의 Unix 시각을 double로 표현한 값이다.
  • 부동소수점과 캡처 해상도 때문에 표시 자릿수가 실제 측정 정밀도를 보증하지 않는다.
필드크기역할
packet_count4B배치 개수
packet_no8B세션 안 순번
timestamp8Bdouble Unix 시각
raw_length4B다음 원시 바이트 수
raw_packet가변캡처 바이트
원리 심화

09PKLP 길이 필드와 중첩 레코드의 검증 경계

  • 공통 헤더 !4sBBI는 표준 크기·네트워크 바이트 순서에서 10바이트다. 헤더를 완성한 뒤 선언된 payload 길이를 검증한다.
  • 타입 2 payload의 패킷 개수는 뒤 레코드 수와 일치해야 한다. 외부 길이가 정상이어도 내부 count·각 raw 길이의 합이 맞지 않을 수 있다.
  • !QdI의 8+8+4=20바이트는 각 레코드의 고정부다. 그 뒤 raw 길이만큼 패킷 바이트가 따라오므로 다음 레코드 위치를 계산할 수 있다.
  • count·개별 길이·합계 길이·완료 대기시간을 각각 제한한다. 길이 필드를 읽었다는 이유만으로 무제한 메모리를 할당하지 않는다.
  • recv_exact가 프레임을 완성하는 것과 DB 적재 완료는 다르다. ACK·재전송·중복 제거는 애플리케이션 프로토콜에서 별도로 설계해야 한다.
예제로 추적

10가상 60·100바이트 패킷 배치의 길이 계산

  1. 타입 2 배치에 raw 길이가 60바이트와 100바이트인 패킷 두 개가 있다고 가정한다. 실제 통신을 생성하지 않는 포맷 계산이다.
  2. 첫 레코드는 20+60=80바이트, 두 번째는 20+100=120바이트다. 고정부가 각 패킷마다 반복된다.
  3. 개수 필드 4바이트를 더하면 payload는 4+80+120=204바이트다. 헤더의 payload 길이 필드는 이 값을 나타낸다.
  4. 10바이트 공통 헤더를 더하면 총 214바이트다. 분할 때문에 실제 recv 횟수나 세그먼트 수는 달라질 수 있다.
  5. count=3인데 payload가 두 레코드에서 끝나면 정상 완료로 넘기지 않는다. 미완성·잘못된 선언을 구분하여 연결·레코드 처리 정책을 적용한다.
구성바이트 수누적 payload
패킷 개수44
첫 레코드 고정부+raw20+60=8084
두 번째 레코드 고정부+raw20+100=120204
공통 헤더10payload 밖: 전체 214
검증·예외

11중앙에 일부 레코드만 나타날 때의 진단

  • 센서 생성·큐 입력·프레임 송신·중앙 파싱·적재 개수를 같은 ID 범위로 비교한다. 서로 다른 시점의 총계는 직접 같지 않을 수 있다.
  • 타입 1·3의 길이가 바이트 기준인지 확인한다. 한글 한 글자가 1바이트라는 가정은 이후 필드 경계를 깨뜨린다.
  • 연결 종료 때 헤더·payload 중 어디까지 받았는지 기록한다. 정상 연결 수와 정상 프레임 수를 동일시하지 않는다.
  • ACK 전에 재연결되어 같은 배치가 재전송되었는지 확인한다. 도착 순서·개수만으로 exactly-once를 입증하지 않는다.

12타입 4: 줄바꿈으로 끝나는 Alert

  • 타입4는 alert_count 4B 뒤에 alert_no 4B와 raw_alert를 반복한다. Alert마다 별도의 문자열 길이 필드가 없고 fast alert 한 건이 한 줄이라는 전제에서 줄바꿈으로 경계를 찾는다.
  • Packet 레코드의 길이 기반 구조와 다르다.
  • 다른 logger의 여러 줄 출력이나 아직 쓰기 중인 부분 줄을 그대로 넣으면 경계가 깨진다. reader는 완성되지 않은 줄을 다음 읽기까지 보관할 수 있어야 한다.
  • count개를 읽은 뒤 payload가 정확히 끝나는지, 지나치게 긴 줄이나 누락된 newline이 있는지도 상태로 다뤄야 한다.

13TCP 스트림에서 프레임을 완성하기

  • 는 send의 메시지 경계를 보존하지 않는다. 한 번의 recv가 헤더 일부만 주거나 여러 프레임을 합쳐 줄 수 있다.
  • 먼저 10바이트를 모으고 Magic·version·type·길이 상한을 검사한 뒤 지정한 payload를 정확히 모아 타입별로 해석한다. 중간 EOF는 미완성 프레임이며 정상 메시지로 저장하지 않는다.
  • 길이 필드를 그대로 믿고 메모리를 할당하면 비정상 입력이 자원을 소진할 수 있다. 전체 길이 상한, 레코드 수 상한, 내부 길이의 합을 함께 검증한다.
  • 프로토콜 framing은 원시 바이트를 구조로 바꾸는 규칙이며 암호화·발신자 인증을 대신하지 않는다.
text
NEED_HEADER(10)
 → magic/version/type/길이 상한 검사
 → NEED_PAYLOAD(length)
 → count·레코드 경계 검사
 → 저장 큐에 전달
 → 다음 HEADER

중간 EOF → 미완성 프레임으로 종료

원리를 설명하는 가상 학습 예시입니다.

14재전송에서 exactly-once가 자동으로 나오지 않는다

  • 연결을 재시도할 때 세션 정보를 다시 보내고 실패 배치를 재전송할 수 있다. send 성공은 중앙 DB의 영구 저장 완료와 다르다.
  • 서버가 저장한 직후 연결이 끊기면 송신자는 성공 여부를 몰라 같은 배치를 다시 보낼 수 있다. 재전송은 손실 위험을 줄이지만 중복 가능성을 만든다.
  • 센서·세션·레코드 순번 같은 안정된 키와 저장 중복 제어가 필요하다. 애플리케이션 ACK가 수신·큐 적재·DB commit 중 어디까지를 확인하는지도 정해야 한다.
  • 디스크 spool과 확정 은 이런 문제를 해결하는 설계 요소이며 자료에서 모두 구현·검증되었다는 뜻은 아니다.

15copytruncate 대응과 복구의 한계

  • AlertRead는 파일 크기가 보다 작아지면 회전을 추정하고 최근 비압축 회전 파일에서 남은 구간을 읽은 뒤 현재 파일 처음으로 돌아간다. 그러나 크기 비교만으로 모든 파일 교체나 빠른 재성장을 구분할 수 없다.
  • 회전 파일 선택도 수정 시각뿐 아니라 파일 정체성과 처리 기록이 필요하다.
  • copy와 truncate 사이에서 사라진 기록은 사후 조정으로 복원할 수 없는 경우가 있다. Alert 파일의 레코드 누락과 NIC의 패킷 drop은 다른 수집 경로의 문제다.
  • 원시 파일 범위·순번 공백· 상태를 함께 읽어야 어느 단계에서 자료가 없어졌는지 설명할 수 있다.

16중앙 필드와 원본으로 돌아가는 참조

  • 통합 문서는 소스별 파싱·1차 분석 뒤 공통 Event를 중앙으로 보내는 방향을 명시한다. event_time·received_time·주소·포트·protocol은 의미를 맞추고 Packet·Alert·· 고유 필드는 상세 구조에 보존한다.
  • 중앙 flow_id와 external_flow_id를 같은 값으로 취급하지 않는다.
  • 최종 사건은 원본 테이블·행 또는 raw file의 ·길이로 돌아갈 수 있어야 한다. 대량 payload를 모두 검색 DB에 복제하기보다 파일의 checksum·record_count와 색인을 연결하면 조사 가능성을 유지하면서 저장 목적을 나눌 수 있다.
  • 비교용 테이블 목록은 설계 선택지이며 실제 배포된 구조와 구분한다.

176개 패킷의 timestamp 실험이 보여 준 범위

  • 별도 시험은 같은 NIC의 HTTP 요청3개·응답3개에서 두 수집 경로의 시각 차이가 0 또는 1마이크로초였다고 기록한다. 소수 표본의 연결 가능성을 보여 주지만 고부하·시계 변경·장기 운영의 무손실을 입증하지 않는다.
  • 캡처 시각과 프로그램이 파일에 쓴 시각은 구분해야 한다.
  • 초당 입력량, 깊이, 가장 오래 기다린 레코드, 재시도·중복·순번 공백, 중앙 저장률을 연결하면 부하 증가 시 어느 단계가 막히는지 알 수 있다. 프로젝트의 학습 가치는 센서에서 저장까지 바이트·상태·확인 지점을 직접 설계한 데 있다.

설명과 함께 보는 보조 도식

구조 살펴보기

PKLP 공통 헤더와 본문

수업 프로젝트 자료에 기록된 형식을 단순화한 모식도다. 공개 표준 프로토콜이 아니며 본문 형식은 Type에 따라 달라진다.

Magic 4바이트

프로토콜 식별 문자열을 확인한다.

PKLP

Version 1바이트

지원하는 해석 규칙의 버전인지 확인한다.

1

Type 1바이트

본문의 메시지 종류를 구분한다.

Payload length 4바이트

네트워크 바이트 순서로 본문 바이트 수를 읽고 상한을 검사한다.

Payload Length만큼

헤더 뒤에서 선언한 바이트 수만큼 누적한 뒤 해당 유형으로 해석한다.

각 필드를 누르면 역할과 예시를 확인할 수 있습니다. 칸의 너비는 실제 바이트 비율과 다릅니다.

공통 헤더는 4 + 1 + 1 + 4 = 10바이트다. 도식의 표시 폭은 실제 바이트 비율이 아니며 TCP 세그먼트 경계와 메시지 경계는 일치하지 않을 수 있다.
학습 확인

이해했는지 확인하기

  • 수집·큐·전송·저장 단계의 책임과 큐 포화 정책을 설명한다.
  • PKLP 길이·버전과 TCP 부분 수신 처리를 확인한다.
  • 재연결·순환 시험에서 누락·중복·지연을 별도로 측정한다.

함께 확인할 공식 자료