seccomp

seccompとは

Seccompとは、Linuxカーネルが持つセキュリティ機構の一つで、Secure Computing Modeの略です。 簡単に言うと、Seccompはシステムコールの許可・不許可を設定できるようにし、危険なシステムコールを実行できなくするためのものです。
突然の「Operation not permitted」-Dockerが採用するセキュリティ機構「Seccomp」とは何か? #docker #seccomp #mirantis - クリエーションライン株式会社

2つのモード

linuxのseccompにはSECCOMP_MODE_STRICTとSECCOMP_MODE_FILTERという2つのモードがある。
前者はread, write, exit, sigreturn以外のシステムコールを禁止するシンプルなものになっており、後者はBPFを利用して開発者が任意のシステムコールをフィルタできる柔軟なものになっている。

SECCOMP_MODE_STRICT

SECCOMP_MODE_STRICTで許可されるシステムコールはread, write, exit, sigreturnのみ。
違反するとプロセスにSIGKILLが送られる。

検証

#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <linux/seccomp.h>
#include <sys/prctl.h>

int main(int argc, char *argv[]) {

int before_seccomp = open("before_seccomp.txt", O_RDWR);
close(before_seccomp);

prctl(PR_SET_SECCOMP, SECCOMP_MODE_STRICT);

int after_seccomp = open("after_seccomp.txt", O_RDWR);
close(after_seccomp);
}

実行結果

$ clang seccomp_strict_test.c -o seccomp_strict_test
$ ./seccomp_strict_test
1. invoked open system call
1. invoked close system call
Killed

prctlでseccompが呼び出した後、openシステムコールが呼び出せずに、SIGKILLが送られている。

SECCOMP_MODE_FILTER

SECCOMP_MODE_STRICTでは許可されるシステムコールが予め決まっていたが、SECCOMP_MODE_FILTERでは任意のシステムコールを指定できる。
また、違反した際のアクションについてもいろいろと設定可能になっている。例えば、プロセスSIGKILLを送るのではなく、ログに記載するだけみたいなこともできる。

検証

#include <seccomp.h>
#include <stdio.h>
#include <sys/utsname.h>
#include <unistd.h>

int main(void) {
  scmp_filter_ctx ctx = seccomp_init(SCMP_ACT_ALLOW);
  if (ctx == NULL) {
    goto ERROR;
  }
  int rule_add_ret;
  rule_add_ret = seccomp_rule_add(ctx, SCMP_ACT_KILL, SCMP_SYS(uname), 0);
  if (rule_add_ret != 0) {
    goto ERROR;
  }
  seccomp_load(ctx);

  printf("before uname invocation\n");
  struct utsname buf;
  if (uname(&buf) != 0) {
    goto ERROR;
  };
  printf("after uname invocation\n");
  printf("sysname: %s, nodename: %s, version:%s\n", buf.sysname, buf.nodename,
         buf.version);
  seccomp_release(ctx);
  return 0;

ERROR:
  perror("failed");
  return -1;
}

※prctlやseccompシステムコールを利用するのではなく、便利なlibseccompを利用している。

実行結果

$ clang seccomp_mode_filter.c -lseccomp -o filter
$ ./filter
before uname invocation
Bad system call (core dumped)

unameの呼び出しがフィルタされているのがわかる。