2. 샌드박스 정책 만들기
실행기가 있으면 Sandboxee의 샌드박스 정책을 정의하는 것이 좋습니다. 그렇지 않으면 Sandboxee는 기본 시스템 호출 정책으로만 보호됩니다.
샌드박스 정책의 목표는 Sandboxee가 실행할 수 있는 시스템 호출 및 인수와 액세스할 수 있는 파일을 제한하는 것입니다. 샌드박스 처리하려는 코드에 필요한 시스템 호출을 자세히 이해해야 합니다. 시스템 호출을 관찰하는 한 가지 방법은 Linux의 명령줄 도구인 strace로 코드를 실행하는 것입니다.
시스템 호출 목록이 있으면 PolicyBuilder 를 사용하여 정책을 정의할 수 있습니다.
PolicyBuilder에는 많은 일반적인 작업을 허용하는 여러 편의 함수와 도우미 함수가 함께 제공됩니다 (대부분은 이미 DefaultPolicyBuilder()에 포함되어 있음). 다음 목록은 사용 가능한 함수의 일부 발췌문일 뿐입니다.
- 프로세스 시작을 위한 시스템 호출 허용 목록:
AllowStaticStartup();AllowDynamicStartup();
- 열기/읽기/쓰기* 시스템 호출 허용 목록:
AllowOpen();AllowRead();AllowWrite();
- 종료/액세스/상태 관련 시스템 호출 허용 목록:
AllowExit();AllowStat();AllowAccess();
- 절전/시간 관련 시스템 호출 허용 목록:
AllowTime();AllowSleep();
이러한 편의 함수는 관련 시스템 호출을 허용 목록에 추가합니다. 이렇게 하면 특정 시스템 호출을 사용할 수 없는 여러 아키텍처에서 동일한 정책을 사용할 수 있지만 (예: ARM64에는 OPEN 시스템 호출이 없음) 필요 이상으로 많은 시스템 호출을 사용 설정하는 약간의 보안 위험이 있습니다. 예를 들어 AllowOpen()을 사용하면 Sandboxee가 열기 관련 시스템 호출을 호출할 수 있습니다. 특정 시스템 호출 하나만 허용 목록에 추가하려면 AllowSyscall();을 사용하고 여러 시스템 호출을 한 번에 허용하려면 AllowSyscalls()를 사용하면 됩니다.
지금까지 정책은 시스템 호출 식별자만 확인합니다. 정책을 더 강화해야 하고 특정 인수가 있는 시스템 호출만 허용하는 정책을 정의하려면 AddPolicyOnSyscall() 또는 AddPolicyOnSyscalls()를 사용해야 합니다. 이러한 함수는 시스템 호출 ID를 인수로 사용할 뿐만 아니라 Linux 커널의 bpf 도우미 매크로를 사용하는 원시 seccomp-bpf 필터도 사용합니다. BPF에 관한 자세한 내용은 커널
문서
를 참고하세요. 사용성 래퍼가 있어야 한다고 생각되는 반복적인 BPF 코드를 작성하는 경우 언제든지 기능 요청을 제출하세요.
시스템 호출 관련 함수 외에도 PolicyBuilder는 AddFile() 또는 AddDirectory()와 같은 여러 파일 시스템 관련 함수를 제공하여 파일/디렉터리를 샌드박스에 바인드 마운트합니다. AddTmpfs() 도우미를 사용하여 샌드박스 내에 임시 파일 저장소를 추가할 수 있습니다.
특히 유용한 함수는 바이너리에 필요한 라이브러리와 링커를 추가하는 AddLibrariesForBinary()입니다.
허용 목록에 추가할 시스템 호출을 찾는 것은 여전히 약간의 수동 작업입니다. 바이너리에 필요한 시스템 호출로 정책을 만들고 일반적인 워크로드로 실행합니다. 위반이 트리거되면 시스템 호출을 허용 목록에 추가하고 프로세스를 반복합니다. 허용 목록에 추가하는 것이 위험할 수 있다고 생각되고 프로그램이 오류를 정상적으로 처리하는 위반이 발생하면 BlockSyscallWithErrno()를 사용하여 오류를 반환하도록 시도할 수 있습니다.
다음 예에서는 sandbox2::PolicyBuilder를 직접 구성하여 write 및 mprotect 인수를 제한하고 openat(그렇지 않으면 DefaultPolicyBuilder()가 허용함)을 차단하는 방법을 보여줍니다.
#include "sandboxed_api/sandbox2/policy.h"
#include "sandboxed_api/sandbox2/policybuilder.h"
#include "sandboxed_api/sandbox2/util/bpf_helper.h"
std::unique_ptr<sandbox2::Policy> CreatePolicy() {
return sandbox2::PolicyBuilder()
.AllowSyscall(__NR_read) // See also AllowRead()
.AllowTime() // Allow time, gettimeofday and clock_gettime
.AddPolicyOnSyscall(__NR_write, {
ARG(0), // fd is the first argument of write (argument #0)
JEQ(1, ALLOW), // allow write only on fd 1
KILL, // kill if not fd 1
})
.AddPolicyOnSyscall(__NR_mprotect, {
ARG_32(2), // prot is a 32-bit wide argument, so it's OK to use *_32
// macro here
JNE32(PROT_READ | PROT_WRITE, KILL), // prot must be the RW, otherwise
// kill the process
ARG(1), // len is a 64-bit argument
JNE(0x1000, KILL), // Allow single page syscalls only, otherwise kill
// the process
ALLOW, // Allow for the syscall to proceed, if prot and
// size match
})
// Allow the openat() syscall but always return "not found".
.BlockSyscallWithErrno(__NR_openat, ENOENT)
.BuildOrDie();
}