Home
System Hacking
🐝

eBPF란

Type
Kernel
TIME
2022/05/24 14:12
종류
eBPF
1 more property

BPF(Berkeley Packet Filter)

BPF(Berkeley Packet Filter)Unix-like OSKernel Level에서 Bytecode에 따라 동작하는
경량화된VM(Virtual Machine)입니다. BPF는 의미 그대로 처음에는 Network PacketFiltering하는 Program을 구동하는 용도의 VM이였습니다. 하지만 사용자가 원하는 기능을 수행하는 Program을 언제든지 Kernel Level에서 구동 할 수 있다는 BPF장점 때문에 BPF는 꾸준히 발전하였고, 현재는 다양한 기능을 수행하는 VM이 되었습니다. 현재 Linux에서 BPF를 지원하고 있습니다.
경량화된 VM(Virtual Machine)입니다. BPF는 의미 그대로 처음에는 Network PacketFiltering하는 Program을 구동하는 용도의 VM이였습니다. 하지만 사용자가 원하는 기능을 수행하는 Program을 언제든지 Kernel Level에서 구동 할 수 있다는 BPF장점 때문에 BPF는 꾸준히 발전하였고, 현재는 다양한 기능을 수행하는 VM이 되었습니다. 현재 Linux에서 BPF를 지원하고 있습니다.

eBPF(Extended Berkeley Packet Filter)

eBPFKernelVM(Virtual Machine)의 한 종류입니다. bpf() system call을 통해서 user spaceeBPF Program이 통신할 수 있습니다.
Android에서는 부팅 시 eBPF 프로그램을 로드하여 커널 기능을 확장하는 eBPF 로더 및 라이브러리가 포함되어 있습니다. eBPF로더는 커널, 모니터링 또는 디버깅에서 통계를 수징하는데 사용할 수 있습니다.

eBPF 정보

eBPF는 사용자 제공 eBPF Program을 실행하는 KernelVM(Virtual Machine)입니다. 이러한 ProgramKernelProbeEvent에 연결하여 유용한 통계를 수집하고 결과를 분석, 저장할 수 있습니다.
Linux Kernel v3.18부터 eBPF란 기능이 생겼습니다. 유의미하게 쓸 수 있는 버전은 v.4.9 이후 부터 입니다. 또한 이를 고수준언어에서 활용하게해주는 BCC ToolKit이 있습니다.
eBPFx86-64arm64공통점을 따온 것 처럼 보이는 별도의 어셈블리 언어입니다. 실제로 이런 코드를 사람이 직접 작성 하지는 않습니다. 단지 성능 측정을 위한 C 언어 코드를 작성하면 이를 eBPF 프로그램으로 Transpile합니다. (BCC 툴체인의 기능)
여기서 Transpile이라 함은 한 언어로 작성된 소스코드를 비슷한 수준의 추상화(Abstraction)를 가진 다른 언어로 변환하는 것을 말합니다.
eBPF Programbpf() system call을 통해서 Linux Kernel에 전달하면 해당 내용을 Kernel 안의 Sandbox형태의 Interpreter(JVM 처럼 다른 프로그램을 실행시켜주는 VM)로 동작합니다. 다만 Kernel에서 돌릴 것이라 아래의 몇가지 검증을 합니다.
동작 중에 crash날 만한 코드가 있는지 확인합니다.
허용되지 않은 함수에 접근 하는지 확인합니다.
허용되지 않은 stack이나 memory 접근을 하는지 확인합니다. (직접 선언한 값이나 stack으로 넘어온 에 대하여 참조하는 일부 값만 접근가능합니다.)
데이터는 미리 선언한 일종의 해시맵에만 저장할 수 있습니다
이를 통해 eBPF Program은 아래와 같은 몇가지 데이터수집합니다.
system Call, 혹은 Kernel 내에서의 Function Call
user space(user program) 내의 Function Call
Program에서 정의한 Tracing 지점 (python 이나 JVM, node.js 같은 고수준 언어/프레임워크에서 주로 제공합니다.)
user space에서는 이미 동작 중인 eBPF Program통신해서 동작수정하거나(ex.. 측정 대상을 추가) 현재까지 수집된 데이터가져가거나 할 수 있습니다. 이 역시 bpf() system call을 통해서 이뤄집니다.
결국,  bpf() system call을 통해서 user spcaeeBPF Program이 통신이 가능하게 되는 것 입니다.
BCC(eBPF Compiler Collection)
eBPF성능 측정을 위한 기능을 제공합니다. 하지만 이를 위해서는 C 언어를 이용해서 Program을 짜고 데이터주고 받아야 합니다. 이런 복잡함을 줄이기 위해서 BCC를 이용합니다. BCC를 사용하면 아래와 같은 방법으로 성능 측정을 할 수 있습니다.
1.
C 언어성능 데이터 수집하는 코드 작성
2.
Python이나 go 혹은 lua1에서 작성한 데이터eBPF Program으로 생성합니다. 만약 필터링이 필요한 경우(특정 프로세스스레드로 제한하는 등) 1의 Program전송하기 전수정합니다.
3.
BCC Compile(1의 코드eBPF Program으로 변환) 후 KernelLoad
4.
eBPF Program 추가로 데이터 측정할 곳연결합니다.
이 과정을 마친 후에는 Kernel에 있는 eBPF Programuser spcae에 있는 Program이 통신할 수 있습니다. 주기적으로 현재까지의 데이터를 가져와서 통계를 보여주거나 아니면 추가특정 데이터를 수집하거나 하는 일을 할 수 있습니다.

왜 유용한가?

eBPF일정한 제약이 가해지기 때문에, 성능 측정 대상을 Crash하게 만들기 어렵습니다. 측정하는 데이터에 따라선 측정하는 동안 성능떨어질 순 있습니다. 또한 이 측정측정 대상에 해당하는 Program 수정이나 재시작필요하지 않기 때문에 실 서비스에서도 적용할 수 있고, 측정 부하매우 낮습니다. 모든 측정Kernel 안에서 이뤄집니다. 그리고 이 모든 과정측정 대상 외부에서 이뤄져서, 필요한 부분에 대해 측정 방법을 바꾸고, 수집할 데이터를 달리하면서 성능 문제를 추적할 수 있습니다.
즉, 아래와 같은 과정을 빠르게 반복할 수 있습니다:
1.
성능 문제에 대한 가설을 세웁니다.
2.
가설에 해당하는 측정 데이터를 찾고 eBPF + BCC Program으로 만듭니다.
3.
측정하고, 가설이 맞았는지 확인합니다. 틀렸다면 다시 범위를 좁히거나 다른 영역으로 넘어가서 1로.

cBPF(Classic BPF), eBPF(Extended BPF)

cBPF(Classic BPF), eBPF(Extended BPF)의 차이점

cBPF and eBPF Diff
위 그림은 cBPF(Classic BPF)eBPF(Extended BPF)를 나타내고 있습니다. BPF다양한 기능을 수행하면서 BPF가 갖고있는 매우 제한된 Resource는 큰 걸림돌이 되었습니다. 이러한 BPFResource 문제를 해결하기 위해서 Linux는 더 많은 Resource기능을 이용할 수 있는 eBPF(Extened BPF)를 정의하였습니다. eBPF를 정의하면서 기존의 BPFcBPF(Classic BPF)라고 불립니다.
cBPF에서는 2개32bit Register메모리 역할을 수행하는 16개32bit Scratch Pad만을 이용 할 수 있었습니다. 하지만 eBPF에서는 11개64bit Register, 512개8bit Stack, Key-Value저장할 수 있는 무제한 Map을 이용 할 수 있습니다.
또한 실행할 수 있는 Bytecode도 추가되어 eBPF에서는 KerneleBPF 지원을 위해 제공하는 Kernel Helper Function을 호출하거나 다른 eBPF Program을 호출할 수 있습니다. 이처럼 eBPFcBPF보다 많은 Resource기능을 이용 할 수 있기 때문에 cBPF보다 다양한 기능Program을 구동 할 수 있습니다.

cBPF(Classic BPF), eBPF(Extended BPF)의 지원

현재 Linux에서는 cBPF, eBPF 둘다 이용하고 있으며, BPF실행되는 지점인 HookKernel Version에 따라서 어떤 BPF가 이용될지 결정됩니다. bpf() System Call추가된 Kernel Version3.18인데, 3.18 이전 VersionBPF모두 cBPF입니다. 예를들어 Socket() System CallSO_ATTACH_FILTER Option이나 Seccomp() System CallSECCOMP_SET_MODE_FILTER Option을 통해 이용하던 BPF모두 cBPF였습니다. 하지만 3.18 이후 Version추가된 BPF모두 eBPF입니다. eBPF가 도입되면서 cBPF는 현재 일부에서만 이용되고 있고 추후 eBPFcBPF완전히 대체하게될 예정입니다.
현재 Linux에서 Socket() System Call의 경우 eBPF를 이용하는 SO_ATTACH_BPF Option이 추가되었습니다. 물론 하위 호환성을 위해서 SO_ATTACH_FILTER Option도 여전히 제공합니다. 하지만 SO_ATTACH_FILTER Option을 이용하더라도 내부적으로는 bpf() System Call을 이용하여 cBPF BytecodeeBPF Bytecode를 변경한뒤 eBPF에 적재합니다. Seccomp() System Call의 경우에는 아직도 cBPF만을 지원하지만, 현재 eBPF 지원을 위한 개발이 진행중입니다.

eBPF Program Compile, bpf() System call

eBPF Program Compile, bpf() System Call
위 그림은 eBPF Program의 Compile 과정과 bpf() System Call의 동작을 나타내고 있습니다. LLVM/clangBackendeBPF를 지원합니다. 개발자가 작성한 eBPF Source CodeLLVM/clang을 통해서 eBPF BytecodeCompile됩니다. 그 후 eBPF Bytecodetciproute2같은 eBPF 관리 App(Tool)을 이용해 KerneleBPF에 적재됩니다. eBPF 관리 App은 내부적으로 bpf() System Call을 이용하여 eBPFeBPF Bytecode를 적재합니다.
eBPF BytecodeKernel Level에서 동작하기 때문에 잘못 작성된 eBPF BytecodeSystem 전체큰영향을 줄 수 있습니다. 따라서 KerneleBPF Bytecode를 적재전에 VerifiereBPF Bytecode에 이상이 없는지 검사합니다. VerifiereBPF Bytecode허용되지 않은 Memory 영역을 참조하는지 검사하고, 무한 Loop가 발생하는지도 검사합니다. 또한 허용되지 않은 Kernel Helper Function을 호출했는지도 검사합니다. 검사를 통과못한 eBPF Bytecode는 적재에 실패합니다. 검사를 통과한 eBPF BytecodeeBPF에 적재되어 동작합니다. 필요에 따라 eBPF Bytecode의 일부는 JIT(Just-in-time) Compiler를 통해서 Native Code로 변환되어 Kernel에서 동작합니다.
bpf() System CalleBPF Bytecode 적재 뿐만 아니라 AppeBPF가 이용하는 Map에 접근할 수 있게 만들어줍니다. 따라서 AppeBPFMap을 이용하여 통신을 할 수 있습니다. eBPFApp사이의 통신은 eBPF가 더욱 다양한 기능을 수행 할 수 있도록 만든다.

BPF Program Type

eBPF Program Type
BPF Program TypeBPF Program이 실행될 수 있는 Hook을 결정합니다. 따라서 BPF Program TypeBPF ProgramInput TypeInput Data를 결정합니다. 또한 BPF Program TypeBPF Program이 호출할 수 있는 Kernel Helper Function을 결정합니다. 위 그림은 eBPF Program Type을 나타내고 있습니다. 앞으로 Kernel에 더욱 많은 Hook이 추가되는 만큼 eBPF Program Type도 추가될 예정입니다.

eBPF 사용법

Container에 어떻게 eBPF를 적용할 것 인가?

ContainerKubernetes의 경우 Cilium 또는 Calico와 같은 Network project, BCC, kubectl-trace, Inspektor Gadget과 같은 Debuging SolutionsFalco와 같은 보안 관련 프로젝트를 이용할 수 있습니다.
또한 sysdigeBPF를 활용하여 Host 및 Network Data, Container Process, Resource 활용Cloud 환경 및 Container 환경에 대한 분석 결과를 제공합니다.

Sysdig

상업 제공의 관점에서 Sysdig는 마이크로서비스를 위한 완전한 기능을 갖춘 컨테이너 모니터링 및 컨테이너 보안 솔루션 에서 eBPF trace 기능을 제공하는 최초의 몇 안 되는 회사 중 하나입니다 . 이제 사용자는 eBPF로 완전히 구동되는 Sysdig MonitorSysdig Secure를 채택할 수 있습니다.
sysdig는 고성능 system call trace 프로그램입니다.
stateful filter, container 및 Lua 스크립팅에 매우 광범위한 user space 지원이 특징입니다. 또한 sysdigtroubleshooting ecosystem에서 매우 Container 친화적으로 만들었습니다.
sysdig 인텔리전스user space에서 구현됩니다. 각 system call컨텍스트(ex: process metadata, container 및 orchestrator metadata, file/connection metadata, ...)를 각 개별 이벤트에 연결하는 status system을 거치며, 이 컨테스트필터링에 사용될 수 있습니다. 예를 들어, isolatedwrite() sysdigtrace하는 것만으로 sysdig참조하는 파일, network 연결, process 및 Docker Container를 알 수 있습니다.
sysdig 아키텍처
여기서 다시 한번 중요한 부분은 최신 버전의 ClangLLVM을 사용하여 컴파일되며, C CodeeBPF Byte Code로 변환합니다. sysdig를 이용하여 아래의 6가지 행위가 가능합니다.
1.
system call entry path
2.
system call exit path
3.
Process context switch
4.
Process termination
5.
Minor and major page faults
6.
Process signal delivery
각 프로그램은 실행 지점별 데이터(ex: system call, 프로세스 호출에서 전달한 인수)를 입력으로 받아 처리를 합니다. 여기서 하는 처리는 system call 유형에 따라 다릅니다. system call의 경우 인수전체 이벤트 프레임이 형성될 때까지 임시 저장에 사용되는 eBPF MAP에 그대로 복사됩니다. 다른 더 복잡한 호출의 경우 eBPF 프로그램에서는 인수를 변환하거나 추가하는 논리가 포함되며, 이런 것들을 통해서 user spacesysdig application의 데이터를 완전히 활용할 수 있습니다. 이렇게 추가되는 데이터 중 일부는 아래의 3가지 정도 입니다.
1.
네트워크 연결과 관련된 데이터(ex: TCP/UDP IPv4/IPv6 Tuple, UNIX Socket Name등)
2.
Process에 대한 매우 세분화된 매트릭스(Memory Counters, Page Faults, Socket Queue Length 등)
3.
System call을 실행하는 Process가 속한 cgroup 및 Process가 있는 Namespace와 같은 컨테이터별 데이터.
sysdig eBPF 아키텍처
eBPF 프로그램이 수행할 수 있는 작업의 제한적인 특성으로 인해 이 정보 중 일부는 eBPF Code에서 쉽게 얻을 수 없습니다.
eBPF 프로그램특정 system call에 필요한 모든 데이터를 캡처하면 special native BPF 기능을 사용하여 sysdig user space application이 매우 높은 처리량으로 읽을 수 있는 CPU별 Ling Buffers set데이터를 푸시합니다. 이것이 sysdig에서 eBPF를 사용하는 것이 kernel space에서 생성된 "small data"user space와 공유하기 위해 eBPF MAP을 사용하는 일반적인 패러다임과 다른 점 입니다.
sysdig 아키텍처에서 eBPF MAPuser space와 공유 목적으로 최소한으로 사용됩니다. 모든 데이터는 특정 사용 사례에 맞게 조정된 확장ㅆ 가능한 Ring Buffers를 통해 flows됩니다. 이를 통해 sysdig최소한의 overheadkernel에서 user space로 매우 많은 양의 데이터를 전송할 수 있습니다. 그런 다음 우리는 sysdig Process Memory에서 system status를 데이터를 활용할 수 있습니다.
또한 성능면에서 결과는 좋습니다.
System 별 OverHead
sysdigeBPF가 OverHead 측면에서 Kernel Moudle보다 약간 더 큰 것을 확인할 수 있습니다. 또한 직접 더 Filtering을 수행하여 sysdigOverHead를 적게 발생하도록 조정하는 것 또한 가능합니다.

사용법

eBPF와 함께 sysdig를 사용하는 것은 설계상 문제가 없으며 transparent합니다. 즉, eBPF를 지원하는 최신 Linux Kernel(4.14+)에서 실행 중인 사용자 SYSDIG_BPF_PROBECommand Line에서 환경 변수를 지정하는 표준 sysdig Container를 실행할 수 있으며 아래의 예제와 같이 사용할 수 있습니다.
Command
docker run -i -t --name sysdig --privileged -v /var/run/docker.sock:/host/var/run/docker.sock -v /dev:/host/dev -v /proc:/host/proc:ro -v /boot:/host/boot:ro -v /lib/modules:/host/lib/modules:ro -v /usr:/host/usr:ro --net=host -e SYSDIG_BPF_PROBE="" sysdig/sysdig
Bash
복사
Falcosysdig Monitorsysdig secure aguent에 동일한 control flow을 사용할 수 있습니다. SYSDIG_BPF_PROBE 환경 변수를 전달하는 해당 Container를 시작하기만 하면 됩니다.

Falco

특정 문제 해결 사용 사례 외에 CNCF 샌드박스에 기부된 Falco 프로젝트는 이제 eBPF를 사용할 수 있습니다. Falco는 현재 Linux, Container 및 Kubernetes ecosystem에서 eBPF로 구동되는 Container를 위한 유일한 주요 보안 및 행동 활동 Monitering Tool입니다.
Falco는 2016년 sysdig에 의해 만들어진 runtime 보안 프로젝트입니다. 예기치 않은 어플리케이션 동작을 감지하고 runtime 중으로 위협에 대해 경고합니다.
또한 Falco는 대표적으로 아래의 3가지 장점을 가지고 있습니다.
1.
컨테이너 보안 강화
유연한 규칙 엔진을 사용하면 모든 유형Host 또는 Container 동작을 설명할 수 있습니다.
2.
즉각적인 경고를 통해 위험 감소 정책 위반 경고에 즉시 대응하고 대응 workflows 내에서 Falco를 통합할 수 있습니다.
3.
최신 탐지 규칙 활용 Falco 즉시 사용 가능한 규칙은 악의적인 활동 및 CVE Exploit에 대해 경고합니다.

Reference