2. サンドボックス ポリシーを作成する
エグゼキュータを設定したら、Sandboxee 用にサンドボックス ポリシーを定義することをおすすめします。それ以外の場合、Sandboxee はデフォルトの Syscall ポリシーでのみ保護されます。
サンドボックス ポリシーの目標は、Sandboxee が作成できるシステムコールと引数、およびアクセスできるファイルを制限することです。サンドボックス化するコードに必要なシステムコールを詳細に理解する必要があります。システムコールを監視する 1 つの方法は、Linux のコマンドライン ツールの strace を使用してコードを実行することです。
システムコールのリストを取得したら、PolicyBuilder を使用してポリシーを定義できます。PolicyBuilder には、多くの一般的な操作を実行できる便利なヘルパー関数が多数用意されています。使用可能な関数のごく一部のみを次のリストに示します。
- プロセス起動のシステムコールを許可リストに登録します。
AllowStaticStartup();
AllowDynamicStartup();
- オープン状態の /read/write* のシステムコールを許可リストに登録します。
AllowOpen();
AllowRead();
AllowWrite();
- 終了/アクセス/状態に関連するシステムコールを許可リストに登録します。
AllowExit();
AllowStat();
AllowAccess();
- 睡眠/時間に関連するシステムコールを許可リストに登録します。
AllowTime();
AllowSleep();
これらの便利な関数は、関連するシステムコールを許可リストに登録します。この利点は、特定のシステムコールが使用できないさまざまなアーキテクチャ(ARM64 には OPEN システムコールがないなど)でも同じポリシーを使用できるという利点がありますが、必要以上に多くの sycsall を有効にするというわずかなセキュリティ上のリスクがあります。たとえば、AllowOpen() は Sandboxee がオープンに関連するシステムコールを呼び出せるようにします。特定のシステムコールを 1 つだけ許可リストに登録する場合は、AllowSyscall();
を使用して一度に複数のシステムコールを許可し、AllowSyscalls()
を使用します。
今のところ、ポリシーはシステムコール ID のみをチェックします。ポリシーをさらに強化する必要があり、特定の引数を持つシステムコールのみを許可するポリシーを定義する場合は、AddPolicyOnSyscall()
または AddPolicyOnSyscalls()
を使用する必要があります。これらの関数は、システムコール ID を引数として受け取るだけでなく、Linux カーネルの bpf ヘルパーマクロを使用する生の seccomp-bpf フィルタも受け取ります。BPF の詳細については、カーネル ドキュメントをご覧ください。ユーザビリティ ラッパーが必要なと思われる BPF コードを繰り返し記述していると思われる場合は、お気軽に機能リクエストをお送りください。
PolicyBuilder には、syscall 関連の関数とは別に、ファイル/ディレクトリをサンドボックスにバインド マウントするための AddFile()
や AddDirectory()
などのファイルシステム関連の関数も数多く用意されています。AddTmpfs()
ヘルパーを使用すると、サンドボックス内に一時ファイル ストレージを追加できます。
特に便利な関数は、バイナリに必要なライブラリとリンカーを追加する AddLibrariesForBinary()
関数です。
残念ながら、許可リストへのシステムコールはまだ手作業が多くかかります。バイナリのニーズがわかっているシステムコールでポリシーを作成し、一般的なワークロードでポリシーを実行します。違反が発生した場合は、システムコールを許可リストに登録して、このプロセスを繰り返します。許可リストに登録するとリスクが高いと思われる違反に遭遇し、プログラムでエラーが適切に処理されている場合は、BlockSyscallWithErrno()
を使用して、エラーを返すようにすることができます。
#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();
}