Seccomp는 Secure Computing mode의 약자로, 시스템 호출을 필터링하기 위해 설계된 Linux 커널의 보안 기능입니다. 이는 프로세스를 제한된 시스템 호출 집합(exit(), sigreturn(), read(), 및 write() 이미 열린 파일 설명자에 대해)으로 제한합니다. 프로세스가 다른 호출을 시도하면 커널에 의해 SIGKILL 또는 SIGSYS로 종료됩니다. 이 메커니즘은 자원을 가상화하지 않고 프로세스를 자원으로부터 격리합니다.
Seccomp를 활성화하는 방법은 두 가지가 있습니다: PR_SET_SECCOMP와 함께 prctl(2) 시스템 호출을 사용하거나, Linux 커널 3.17 이상에서는 seccomp(2) 시스템 호출을 사용하는 것입니다. /proc/self/seccomp에 쓰는 오래된 방법은 prctl()을 선호하여 더 이상 사용되지 않습니다.
향상된 기능인 seccomp-bpf는 Berkeley Packet Filter (BPF) 규칙을 사용하여 사용자 정의 정책으로 시스템 호출을 필터링할 수 있는 기능을 추가합니다. 이 확장은 OpenSSH, vsftpd 및 Chrome OS와 Linux의 Chrome/Chromium 브라우저와 같은 소프트웨어에서 유연하고 효율적인 시스템 호출 필터링을 위해 활용되며, 이제 지원되지 않는 Linux의 systrace에 대한 대안을 제공합니다.
Original/Strict Mode
이 모드에서 Seccomp는 오직 syscallsexit(), sigreturn(), read() 및 write()를 이미 열린 파일 설명자에 대해서만 허용합니다. 다른 시스템 호출이 이루어지면 프로세스는 SIGKILL로 종료됩니다.
seccomp_strict.c
#include<fcntl.h>#include<stdio.h>#include<unistd.h>#include<string.h>#include<linux/seccomp.h>#include<sys/prctl.h>//From https://sysdig.com/blog/selinux-seccomp-falco-technical-discussion///gcc seccomp_strict.c -o seccomp_strictintmain(int argc,char**argv){int output =open("output.txt", O_WRONLY);constchar*val ="test";//enables strict seccomp modeprintf("Calling prctl() to set seccomp strict mode...\n");prctl(PR_SET_SECCOMP, SECCOMP_MODE_STRICT);//This is allowed as the file was already openedprintf("Writing to an already open file...\n");write(output, val, strlen(val)+1);//This isn't allowedprintf("Trying to open file for reading...\n");int input =open("output.txt", O_RDONLY);printf("You will not see this message--the process will be killed first\n");}
Seccomp-bpf
이 모드는 버클리 패킷 필터 규칙을 사용하여 구현된 구성 가능한 정책을 사용하여 시스템 호출을 필터링하는 것을 허용합니다.
seccomp_bpf.c
#include<seccomp.h>#include<unistd.h>#include<stdio.h>#include<errno.h>//https://security.stackexchange.com/questions/168452/how-is-sandboxing-implemented/175373//gcc seccomp_bpf.c -o seccomp_bpf -lseccompvoidmain(void) {/* initialize the libseccomp context */scmp_filter_ctx ctx =seccomp_init(SCMP_ACT_KILL);/* allow exiting */printf("Adding rule : Allow exit_group\n");seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(exit_group),0);/* allow getting the current pid *///printf("Adding rule : Allow getpid\n");//seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(getpid), 0);printf("Adding rule : Deny getpid\n");seccomp_rule_add(ctx, SCMP_ACT_ERRNO(EBADF), SCMP_SYS(getpid),0);/* allow changing data segment size, as required by glibc */printf("Adding rule : Allow brk\n");seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(brk),0);/* allow writing up to 512 bytes to fd 1 */printf("Adding rule : Allow write upto 512 bytes to FD 1\n");seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(write),2,SCMP_A0(SCMP_CMP_EQ,1),SCMP_A2(SCMP_CMP_LE,512));/* if writing to any other fd, return -EBADF */printf("Adding rule : Deny write to any FD except 1 \n");seccomp_rule_add(ctx, SCMP_ACT_ERRNO(EBADF), SCMP_SYS(write),1,SCMP_A0(SCMP_CMP_NE,1));/* load and enforce the filters */printf("Load rules and enforce \n");seccomp_load(ctx);seccomp_release(ctx);//Get the getpid is denied, a weird number will be returned like//this process is -9printf("this process is %d\n", getpid());}
만약 예를 들어 금지하고 싶은 syscall이 uname인 컨테이너가 있다면, https://github.com/moby/moby/blob/master/profiles/seccomp/default.json에서 기본 프로필을 다운로드하고 목록에서 uname 문자열을 제거하면 됩니다.
어떤 바이너리가 도커 컨테이너 내에서 작동하지 않도록 하려면 strace를 사용하여 바이너리가 사용하는 syscalls를 나열한 다음 이를 금지할 수 있습니다.
다음 예제에서는 uname의 syscalls가 발견됩니다:
위 프로필에서는 기본 동작을 "허용"으로 설정하고 "chmod"를 비활성화하는 블랙리스트를 생성했습니다. 더 안전하게 만들기 위해 기본 동작을 드롭으로 설정하고 시스템 호출을 선택적으로 활성화하는 화이트리스트를 생성할 수 있습니다.
다음 출력은 seccomp 프로필에서 비활성화되어 있기 때문에 "chmod" 호출이 오류를 반환하는 것을 보여줍니다.