MENU

Linuxのseccomp入門|システムコールフィルタリングでプロセスの攻撃面を最小化する実践ガイド

「seccompって聞いたことはあるけど、実際にどう設定するのか分からない」「Dockerでseccompプロファイルを使っているが、仕組みをちゃんと理解できていない」——そんな疑問を持つエンジニアは多いのではないでしょうか。

seccomp(Secure Computing Mode)は、Linuxカーネルが提供するシステムコールフィルタリング機能です。プロセスが呼び出せるシステムコールを事前に制限することで、脆弱性を突かれた際の被害拡大を大幅に抑えられます。DockerやKubernetes、Firefox、Chromeといった主要ソフトウェアがすでに採用している、実戦済みの防御技術です。

この記事では、seccompの仕組みから実際の設定方法・監査手順まで、現場で使えるレベルで解説します。Linux上で動くサービスやコンテナの攻撃面を最小化したいインフラエンジニア・社内SEの方に向けた内容です。

TOC

seccompとは?なぜ今これが重要なのか

Linuxカーネルには300種類以上のシステムコール(プロセスがカーネルに処理を依頼するための仕組み)が存在します。WebサーバーとしてのみHTTPリクエストをさばくnginxが、ptrace(別プロセスの監視・操作)やmount(ファイルシステムのマウント)を呼び出す必要は通常ありません。にもかかわらず、何も制限しなければプロセスはこれらを自由に使えてしまいます。

攻撃者がnginxのバッファオーバーフロー脆弱性を突いてコード実行に成功した場合、次のステップは「権限昇格」です。このときよく悪用されるのがptraceexecveなどのシステムコールです。seccompでこれらを事前に禁止しておくと、攻撃者はコード実行には成功しても次の手が打てなくなります。

seccompが提供する価値を一言でまとめると「最小権限の原則をシステムコールレベルで実装できる」ということです。SELinuxやAppArmorと組み合わせることで、多層防御(ディフェンス・イン・デプス)がより強固になります。

Linuxのアクセス制御の基礎については、姉妹サイトLinuxMaster.JPでも詳しく解説しています。SELinuxやAppArmorの設定と組み合わせて取り組むことをお勧めします。

seccompの仕組みと2つのモード

seccompには動作モードが2つあります。

STRICTモード(mode 1): readwriteexitsigreturnの4つのシステムコールしか許可しません。実用的なアプリケーションには厳しすぎて、一般的なサービスへの適用は現実的ではありません
FILTERモード(mode 2): BPF(Berkeley Packet Filter)プログラムで許可・拒否するシステムコールを柔軟に定義できます。DockerやChrome、OpenSSHが採用しているのはこちらです

FILTERモードの仕組みを少し詳しく説明します。プロセスがprctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog)を呼び出すことでフィルターが有効になります。フィルターはBPFプログラムとして記述され、カーネルのシステムコールディスパッチ経路に挿入されます。プロセスがシステムコールを発行するたびにBPFプログラムが評価され、以下のいずれかのアクションが実行されます。

SECCOMP_RET_ALLOW: そのままシステムコールを実行する
SECCOMP_RET_ERRNO: システムコールをブロックし、指定したエラーコード(例: EPERM)をプロセスに返す
SECCOMP_RET_KILL_PROCESS: プロセスを即座に強制終了する(Linux 4.14以降)
SECCOMP_RET_LOG: 許可するが監査ログに記録する(デバッグ時に有用、Linux 4.14以降)

フィルターはfork()/clone()で生成した子プロセスにも引き継がれます。一度設定したフィルターは削除できず、より制限の強いフィルターを重ねることしかできない(追加のみ)という設計が、誤設定による解除を防ぎます。

seccompのサポート状況を確認する

まず、自分のLinux環境がseccompに対応しているか確認しましょう。

# カーネルのseccompサポート確認 grep -i seccomp /boot/config-$(uname -r) # CONFIG_SECCOMP=y かつ CONFIG_SECCOMP_FILTER=y が表示されれば対応済み # 実行中シェルのseccomp状態確認 cat /proc/$$/status | grep Seccomp # Seccomp: 0 → 無効、1 → STRICTモード、2 → FILTERモード # libseccompのインストール(フィルター記述ライブラリ) # RHEL/AlmaLinux系 dnf install libseccomp-devel # Debian/Ubuntu系 apt install libseccomp-dev

Linuxカーネル3.5(2012年リリース)以降はseccompが標準搭載されています。現在主流のAlmaLinux 8/9、Ubuntu 22.04/24.04ではデフォルトで有効です。

Dockerでseccompプロファイルを活用する

最も手軽にseccompを体験できるのがDockerです。Dockerはデフォルトでseccompプロファイルを適用しており、約300のシステムコールのうち約44個をブロックしています(add_keykeyctlptracereboot等)。

1. デフォルトプロファイルの確認と設定確認

# Dockerのseccomp有効状態確認 docker info | grep -i seccomp # "Security Options: seccomp" が表示されれば有効 # seccompなしでコンテナを起動(テスト・デバッグ目的のみ) docker run --security-opt seccomp=unconfined --rm alpine sh # カスタムプロファイルを指定して起動 docker run --security-opt seccomp=/etc/docker/seccomp/nginx-strict.json nginx # コンテナ内でseccompの状態確認 docker run --rm alpine cat /proc/1/status | grep Seccomp # Seccomp: 2 → FILTERモードが適用されている

2. カスタムseccompプロファイルの基本構造

DockerのseccompプロファイルはJSONフォーマットです。以下は「デフォルトで全拒否し、必要なシステムコールのみ許可する」ホワイトリスト方式の基本構造です。

{ "defaultAction": "SCMP_ACT_ERRNO", "architectures": ["SCMP_ARCH_X86_64", "SCMP_ARCH_AARCH64"], "syscalls": [ { "names": [ "read", "write", "open", "close", "stat", "fstat", "lstat", "poll", "mmap", "mprotect", "munmap", "brk", "rt_sigaction", "rt_sigprocmask", "ioctl", "pread64", "pwrite64", "readv", "writev", "access", "mmap2", "getdents64", "getpid", "getppid", "exit", "exit_group", "epoll_wait", "epoll_ctl", "epoll_create1", "socket", "connect", "accept4", "sendto", "recvfrom", "bind", "getsockname", "setsockopt", "getsockopt", "fcntl", "clock_gettime", "nanosleep", "gettimeofday", "getuid", "getgid", "geteuid", "getegid", "prctl", "arch_prctl", "openat", "newfstatat", "readlinkat", "sendmsg", "recvmsg" ], "action": "SCMP_ACT_ALLOW" } ] }

実際の運用では、まずDockerのデフォルトプロファイル(GitHub公式リポジトリで公開されています)をベースに、不要なシステムコールを削っていくアプローチが安全です。

systemdサービスにseccompを適用する実践手順

コンテナを使わない場合でも、systemdのサービスユニットに直接seccompを設定できます。設定ファイルに数行追加するだけで有効化できるため、既存のサービスに後付けで適用しやすいのが利点です。

1. 利用可能なシステムコールグループの確認

systemdは主要なシステムコールをあらかじめグループ化しています。個別のシステムコールを全部列挙する必要がなく、グループ名で指定できるため設定が格段に楽になります。

# systemdが定義済みのシステムコールグループを一覧表示 systemd-analyze syscall-filter # 主なグループ(抜粋) # @basic-io : read/write等の基本I/O # @network-io : ソケット・ネットワーク関連 # @file-system : ファイルシステム操作 # @process : プロセス生成・管理 # @system-service : 一般的なサービスに必要な一式(上記を包含) # @privileged : 特権が必要な操作(要注意・極力避ける) # @module-load : カーネルモジュールのロード(通常不要) # @mount : マウント操作(通常不要) # @raw-io : 生I/O・デバイス直接操作(通常不要)

2. Nginxへのseccomp設定例(ドロップイン方式)

既存のサービスユニットを直接編集せず、ドロップインファイルとして追記する方法を使います。これにより、パッケージ更新でユニットファイルが上書きされても設定が消えません。

# ドロップインディレクトリを作成 mkdir -p /etc/systemd/system/nginx.service.d/ # seccomp設定ファイルを作成 cat > /etc/systemd/system/nginx.service.d/seccomp.conf << 'EOF' [Service] # @system-service グループのシステムコールを許可(デフォルト拒否) SystemCallFilter=@system-service # Nginx固有の追加許可(バインド・ソケット操作) SystemCallFilter=setsockopt getsockopt bind listen accept accept4 # 不許可時のエラーコード(プロセスはEPERMを受け取り終了) SystemCallErrorNumber=EPERM EOF # 設定を反映 systemctl daemon-reload systemctl restart nginx # 起動確認 systemctl status nginx

3. 設定後の監査と調整

seccompフィルターを本番適用する前に、必ずステージング環境でのテストを行います。拒否されたシステムコールはカーネルのauditログに記録されるため、これを参照しながら許可リストを調整します。

# 拒否されたシステムコールをauditdで確認 ausearch -m SECCOMP -ts recent # journaldでカーネルメッセージから確認 journalctl -k | grep "seccomp" # 出力例(このシステムコールが拒否されている) # kernel: audit: type=1326 audit(1717600000.123:456): auid=33 uid=33 # syscall=socket success=no exit=-1 ... # strace で事前にシステムコールを棚卸しする(ステージング環境のみ) strace -c -f nginx -g nginx & # 10秒ほど負荷をかけた後で確認(使われたシステムコールと回数が一覧表示される)

中小企業でも今日からできること

「seccompを理解しても、設定コストが高すぎる」と感じた方もいるかもしれません。ただ、段階的に取り組めるアプローチがあります。

Dockerを使っているなら何もしなくてもOK: DockerはデフォルトでseccompのFILTERモードが有効です。まず確認すべきは--security-opt seccomp=unconfinedオプションが使われていないかどうかです。このオプションはすべての制限を解除するため、使われている場合は即座に見直してください
systemdサービスへの段階的適用: 外部公開しているWebサーバー(Apache/Nginx)にSystemCallFilter=@system-serviceを追加するだけで攻撃面を大幅に削減できます。ステージング環境で動作確認してから本番適用しましょう
seccomp-toolsの活用: gemパッケージで提供されているseccomp-toolsを使うと、プロセスが実際に使うシステムコールを自動収集してくれます。手動でリストを作る手間が大幅に減ります
クラウドマネージドサービスの活用: AWS FargateやGoogle Cloud Runといったマネージドコンテナサービスは、プラットフォーム側でseccompプロファイルが適用されています。自前管理コストを下げたい場合の有力な選択肢です

よくある誤解と注意点

seccompに関してよく見られる誤解を整理します。

「seccompだけで十分」は危険: seccompはシステムコールを制限するものであり、ファイルパーミッションやネットワークアクセス制御を担うものではありません。SELinux・AppArmor・firewalldとの組み合わせが前提です
設定を誤るとサービスが停止する: ホワイトリスト方式でシステムコールを絞りすぎると、アプリケーションが必要なシステムコールを呼べずにクラッシュします。SECCOMP_RET_LOGでまずログモードで様子を見てから、実際のブロックに移行する段階的アプローチが安全です
子プロセスにも引き継がれる: seccompフィルターはfork()/clone()で生成した子プロセスにも継承されます。Apacheのように子プロセスを多数生成するサービスでは、子プロセスが使うシステムコールも考慮が必要です
一度設定したフィルターは解除不可: 誤設定でサービスが落ちた場合は、systemdユニットの設定を削除してサービスを再起動するしかありません。本番適用前のテストが必須です
カーネルバージョンによる機能差: SECCOMP_RET_KILL_PROCESSSECCOMP_RET_LOGはLinux 4.14以降で追加された機能です。古いカーネルでは一部の動作が異なります

本記事のまとめ

項目 内容
seccompとは システムコールフィルタリングでプロセスの攻撃面を最小化するLinuxカーネル機能
動作モード STRICTモード(4コール限定・実用困難)とFILTERモード(BPFで柔軟定義)の2種類
Dockerでの活用 デフォルトで有効。--security-opt seccomp=unconfinedを使わないことが最初の一歩
systemdでの設定 SystemCallFilter=@system-serviceをドロップインファイルに追記するだけ
監査方法 ausearch -m SECCOMPまたはjournalctl -k | grep seccompで拒否ログを確認
注意点 単体では不十分。SELinux・AppArmor・firewalldとの多層防御が前提

seccompは「カーネルが用意してくれたもう一枚の盾」です。設定すれば必ずサービスが壊れるわけではなく、systemdのグループ定義を使えば比較的少ない作業量で導入できます。まずはステージング環境のDockerサービス、次に外部公開Webサーバーのsystemdユニットから試してみてください。

Linuxのセキュリティ設定全般については、姉妹サイトLinuxMaster.JPでも実践的なコマンドと設定例を公開しています。あわせてご活用ください。

サーバーの攻撃面をもっと体系的に削りたいですか?

seccompはLinuxセキュリティ強化の一要素です。SELinux・AppArmor・firewalld・権限管理との組み合わせで、多層防御の設計が完成します。
正しいセキュリティ知識を体系的に身につけたい方へ、メルマガで実践的なセキュリティ対策ノウハウをお届けしています。

Let's share this post !

Author of this article

TOC