このページの目次

LSM とアクセス制御

カバー範囲: Linux Security Modules アーキテクチャ → SELinux/AppArmor/SMACK → LSM フック機構 → BPF LSM → キャパビリティ → キーリング → IMA/EVM カーネルバージョン: 2.6 ~ 6.x

概要

従来の Unix DAC(Discretionary Access Control)では、所有者/グループ/その他という3段階の権限しか表現できず、「この Web サーバーは /var/www のみを読み取り可能で、/home/user/.ssh にはアクセス不可」といったきめ細かなポリシーを表現できません。LSM は、重要なシステムコールのパスにフックを挿入することで、セキュリティモジュールが追加のアクセス制御判断を行えるようにします。

現在、主要な2つの LSM は、SELinux(型強制に基づく強制アクセス制御)と AppArmor(パスマッチングに基づくプロファイルポリシー)です。

LSM フックアーキテクチャ

// include/linux/lsm_hooks.h
// 200以上のフックがすべての機微な操作をカバーします:
struct security_hook_heads {
    struct hlist_head file_open;          // open() の最終ステップ → security_file_open()
    struct hlist_head inode_create;       // inode の作成
    struct hlist_head inode_unlink;       // unlink()
    struct hlist_head socket_create;      // socket()
    struct hlist_head socket_bind;        // bind()
    struct hlist_head task_kill;          // kill()
    struct hlist_head sb_mount;           // mount()
    // ... その他200以上
};

// 呼び出しチェーン:
// security_file_open(file) → call_void_hook(file_open, file)
// → 登録されているすべてのモジュールのフックを走査 → いずれかがエラーを返すと → 操作を拒否

フックの挿入ポイント

LSM Hook 挿入ポイント: open() システムコールパス open("/etc/shadow", O_RDONLY) vfs_open() → do_dentry_open() security_file_open(file) LSM hook 挿入ポイント SELinux: file_has_perm() 型強制ルールをチェック AppArmor: aa_file_perm() パスプロファイルをチェック 両方が OK を返す → 操作を許可 同じフックポイントにおいて、SELinux と AppArmor はそれぞれ独立して同じ open 呼び出しをチェックし、 両方のモジュールが許可して初めて操作が許可されます。

SELinux: 型強制

コア概念:
  - すべてにラベル (label) が付与される: ファイル、プロセス、ソケットなど
    → ls -Z: system_u:object_r:httpd_sys_content_t:s0
  - ポリシーは「どのソース型が、どのターゲット型の、どのクラスにアクセスできるか」を定義する
    → ホワイトリストモデル: デフォルトは拒否、明示的に許可されたもののみが許可
  - AVC (Access Vector Cache): ポリシー照会結果のキャッシュ (パフォーマンスの要)

SELinux モード:
  /sys/fs/selinux/enforce
    Enforcing (1): 拒否 + ログ記録
    Permissive (0): 許可 + ログ記録 (デバッグ用)
    Disabled: 完全にロードしない

ラベルの保存:
  ext4/xfs: xattr security.selinux
  tmpfs/procfs: 実行時に生成 (genfscon)

AppArmor: パスマッチング

パスプロファイルモデル:
  セキュリティポリシーは型に基づかず、パスマッチングに基づきます:
    /usr/bin/nginx {
      /var/www/** r,
      /var/log/nginx/* w,
      /run/nginx.pid w,
      /tmp/** rw,
      capability net_bind_service,
    }

  利点:
    - SELinux より直感的 (パスを見ればポリシーがわかる)
    - ポリシーファイルが独立している (プログラムごとに1つ)
  欠点:
    - パスで回避可能 (ハードリンク、bind mount)
    - 複雑なグローバル制約の表現が困難

SMACK: 簡略化された型強制

SELinux に似ているが 90% 簡略化:
  - ラベルのみ、ロール/ユーザー/MLS はなし
  - ルール: 「subject label が object label にアクセス」
  - 用途: Tizen (サムスン)、Automotive Grade Linux

カーネルコンパイル: CONFIG_SECURITY_SMACK

BPF LSM (5.7以降)

// BPF プログラムで LSM フックを実装:
// bpf(BPF_PROG_LOAD, BPF_PROG_TYPE_LSM, ...)
// attach: bpf(BPF_RAW_TRACEPOINT_OPEN, lsm/file_mprotect)

// カーネルモジュール相比の利点:
//   - カーネルモジュールをコンパイルする必要がない
//   - verifier により安全性が保証される (パニックしない)
//   - 配布と更新が容易

キャパビリティ: ルート権限の分割

// include/uapi/linux/capability.h
// ルートの全能力をきめ細かなビットに分割:
CAP_SYS_ADMIN      // システム管理
CAP_NET_RAW        // raw socket
CAP_NET_BIND_SERVICE  // 1024未満のポートへのバインド
CAP_SYS_PTRACE     // 他のプロセスの ptrace
CAP_KILL           // 他のユーザーのプロセスへのシグナル送信
// ... 40以上のキャパビリティ

// コンテナセキュリティの重要ポイント:
//   docker run --cap-drop=ALL --cap-add=NET_BIND_SERVICE
//   → コンテナ内の root はポートバインドのみ可能、モジュールの読み込みは不可

IMA/EVM: ファイル整合性

IMA (Integrity Measurement Architecture):
  測定: ファイルが読み込まれるたびにハッシュを計算 → TPM に記録
  評価: ファイルハッシュを検証 → 改ざんされたファイルを拒否

EVM (Extended Verification Module):
  ファイルのメタデータ (xattr, モード, オーナー) が改ざんされないよう保護
  HMAC (TPM キー使用) またはデジタル署名に基づく

デバッグ

# SELinux
getenforce
ausearch -m avc --start recent  # 最近の拒否イベント
sealert -a /var/log/audit/audit.log  # 拒否理由の分析

# AppArmor
aa-status
cat /proc/<pid>/attr/apparmor/current

# キャパビリティ
capsh --print  # 現在のシェルが持つキャパビリティ
getpcaps <pid>

参考

  • ソースコード⁠: security/ (lsm, selinux, apparmor, smack, integrity)、include/linux/lsm_hooks.h
  • カーネルドキュメント⁠: Documentation/admin-guide/LSM/
  • LWN: 「The LSM hook interface」、「BPF LSM」

キーワード: LSM, SELinux, AppArmor, BPF LSM, キャパビリティ, IMA, EVM, 型強制, AVC