보는 BPF 와 막는 BPF
한 줄 요약
seccomp 는 프로세스가 부를 수 있는 시스템 콜을 번호와 인자로 거르는 필터이고, BPF LSM 은 커널의 보안 훅(파일 열기, 실행, 소켓 연결 등) 자리에서 BPF 프로그램이 허용과 거부를 정하는 장치입니다. 둘 다 BPF 를 쓰지만 보는 것과 막을 수 있는 것이 다릅니다.
왜 둘이 따로 있나
앞 단원들의 BPF 는 전부 관측이었습니다. 사건을 세고 기록할 뿐 결과를 바꾸지 않았습니다. 보안에는 거부가 필요합니다. 가장 오래된 방법이 seccomp 입니다. 컨테이너 런타임의 기본 프로필은 수백 개 시스템 콜 가운데 컨테이너에 필요 없는 수십 개(커널 모듈 올리기, 시계 바꾸기 등)를 막습니다. 그런데 seccomp 가 볼 수 있는 것은 시스템 콜 번호와 레지스터에 들어 있는 인자 값뿐입니다. openat 의 파일 이름은 사용자 메모리에 있는 문자열이라 볼 수 없고, 보더라도 검사한 순간과 커널이 실제로 읽는 순간 사이에 값을 바꾸는 공격(TOCTOU)에 열려 있습니다.
LSM(Linux Security Modules)은 그 반대편에서 막습니다. 커널이 이미 경로를 풀고 inode 를 찾은 뒤, 실제로 열기 직전의 훅에서 묻습니다. AppArmor 와 SELinux 가 이 자리를 씁니다. BPF LSM 은 그 훅에 고정된 정책 대신 BPF 프로그램을 붙일 수 있게 한 것입니다.
어떻게 동작하나
seccomp 필터는 고전 BPF(cBPF) 프로그램이고, 시스템 콜마다 seccomp_data(번호, 아키텍처, 인자 여섯 개)를 보고 허용·오류 반환·종료·사용자 공간 알림 중 하나를 고릅니다. 필터는 프로세스에 붙으면 떼어 낼 수 없고, 자식에게 물려집니다. 맵도 헬퍼 함수도 쓸 수 없어서 상태를 기억하는 정책은 만들 수 없습니다.
BPF LSM 프로그램은 eBPF 이고 SEC("lsm/file_open") 처럼 훅 이름으로 붙습니다. 훅의 인자(예: struct file *)를 BTF 로 읽고, 맵과 헬퍼를 쓸 수 있고, 0 이 아닌 값을 돌려주면 그 동작이 거부됩니다. 다만 이 장치는 커널이 CONFIG_BPF_LSM 으로 빌드되어 있어도 활성 LSM 목록에 bpf 가 있어야 동작합니다. 목록은 cat /sys/kernel/security/lsm 으로 보고, 바꾸려면 부팅 인자 lsm= 에 bpf 를 넣고 다시 부팅해야 합니다. 이 코스의 실습 VM 은 기본값 그대로라 목록이 lockdown,capability,landlock,yama,apparmor 이고 bpf 가 없습니다. 그래서 이 단원은 실습 없이 읽기와 퀴즈로 둡니다.
BPF LSM 프로그램은 대략 이렇게 생겼습니다. 활성 목록에 bpf 가 있는 커널이라면 이 프로그램은 이름이 secret 인 파일 열기를 거부합니다.
SEC("lsm/file_open")
int BPF_PROG(deny_secret, struct file *file)
{
char name[16];
bpf_probe_read_kernel_str(name, sizeof(name), file->f_path.dentry->d_name.name);
if (__builtin_memcmp(name, "secret", 7) == 0)
return -EPERM; /* 0 이 아니면 거부 */
return 0;
}
seccomp 필터로는 같은 일을 할 수 없습니다. 필터가 받는 것은 openat 의 번호와 인자 레지스터 값뿐이고, 그 값은 파일 이름 문자열이 아니라 사용자 메모리의 주소이기 때문입니다.
현장에서 만나는 모습
쿠버네티스에서는 파드의 securityContext.seccompProfile 을 RuntimeDefault 로 두는 것이 기본 권장입니다. 이 저장소의 실습 파드도 그렇게 떠 있고, 그 때문에 파드 안에서 rootless podman 이 사용자 네임스페이스를 만들지 못합니다. 막아야 할 것을 막고 있는 것입니다.
KubeArmor 와 Tetragon 은 BPF LSM 이나 kprobe 기반 강제 종료로 "이 컨테이너에서는 이 경로의 실행을 막는다" 같은 정책을 겁니다. 이런 도구를 올리기 전에 노드의 활성 LSM 목록부터 확인합니다. bpf 가 없으면 도구가 관측 모드로만 동작하거나 다른 방식으로 대체하는데, 그 사실이 화면에 크게 드러나지 않는 경우가 있습니다.
English corner
- enforce / audit mode: 실제로 막는 모드와 기록만 하는 모드입니다. "Start in audit mode, then switch to enforce."
- deny by default: 명시적으로 허용한 것만 통과시키는 정책입니다. "The profile denies by default."
- RFC: Request For Comments. 방향을 묻고 싶을 때
[RFC PATCH]로 보냅니다. 머지를 기대하지 않는다는 표시이기도 합니다. - NAK / Nacked-by: 반대한다는 뜻입니다. 이유를 함께 적는 것이 예의이고, 관리자의 NAK 은 그 방식으로는 받지 않겠다는 뜻입니다.
다음 퀴즈에서 확인할 것
seccomp 가 볼 수 있는 것과 없는 것, LSM 훅이 경로 검사에서 유리한 이유, BPF LSM 이 동작하려면 무엇이 켜져 있어야 하는지, 그리고 관측용 BPF 와 강제용 BPF 의 차이를 확인합니다.