---
title: LSM とアクセス制御
url: https://doc.liz6.com/ja/linux-kernel/09-security-mechanisms/01-lsm-and-access-control
locale: ja
area: linux-kernel
tags:
- linux-kernel
- security-mechanisms
date: 2026-06-30
modified: 2026-07-16
description: 'カバー範囲: Linux Security Modules アーキテクチャ → SELinux/AppArmor/SMACK → LSM フック機構 → BPF LSM → キャパビリティ → キーリング → IMA/EVM カーネルバージョン: 2.6 ~ 6.x'
---

# 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 フックアーキテクチャ

```c
// 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)
// → 登録されているすべてのモジュールのフックを走査 → いずれかがエラーを返すと → 操作を拒否
```

### フックの挿入ポイント

<svg viewBox="0 0 720 400" xmlns="http://www.w3.org/2000/svg" font-family="-apple-system,'Source Han Sans CN','Microsoft YaHei',sans-serif" role="img" aria-label="LSM Hook 挿入ポイント: open システムコールが複数モジュールによるチェックフローをトリガー">
  <defs>
    <marker id="arrLSM1" markerWidth="10" markerHeight="8" refX="8" refY="3" orient="auto"><path d="M0,0 L8,3 L0,6 Z" fill="#475569"/></marker>
  </defs>
  <rect width="720" height="400" fill="#ffffff"/>
  <text x="360" y="28" text-anchor="middle" font-size="17" font-weight="700" fill="#1f2933">LSM Hook 挿入ポイント: open() システムコールパス</text>

  <rect x="210" y="46" width="300" height="32" rx="6" fill="#e2e8f0"/>
  <text x="360" y="67" text-anchor="middle" font-size="12.5" font-weight="600" fill="#334155">open("/etc/shadow", O_RDONLY)</text>
  <line x1="360" y1="78" x2="360" y2="92" stroke="#475569" stroke-width="1.6" marker-end="url(#arrLSM1)"/>

  <rect x="210" y="94" width="300" height="32" rx="6" fill="#e2e8f0"/>
  <text x="360" y="115" text-anchor="middle" font-size="12.5" font-weight="600" fill="#334155">vfs_open() → do_dentry_open()</text>
  <line x1="360" y1="126" x2="360" y2="140" stroke="#475569" stroke-width="1.6" marker-end="url(#arrLSM1)"/>

  <rect x="190" y="142" width="340" height="46" rx="8" fill="#4f46e5"/>
  <text x="360" y="163" text-anchor="middle" font-size="13" font-weight="700" fill="#ffffff">security_file_open(file)</text>
  <text x="360" y="180" text-anchor="middle" font-size="10.5" fill="#e0e7ff">LSM hook 挿入ポイント</text>

  <line x1="360" y1="188" x2="210" y2="204" stroke="#475569" stroke-width="1.6" marker-end="url(#arrLSM1)"/>
  <line x1="360" y1="188" x2="530" y2="204" stroke="#475569" stroke-width="1.6" marker-end="url(#arrLSM1)"/>

  <rect x="60" y="206" width="300" height="58" rx="8" fill="#eef2ff" stroke="#c7d2fe"/>
  <text x="210" y="230" text-anchor="middle" font-size="12" font-weight="700" fill="#3730a3">SELinux: file_has_perm()</text>
  <text x="210" y="250" text-anchor="middle" font-size="11" fill="#4f46e5">型強制ルールをチェック</text>

  <rect x="380" y="206" width="300" height="58" rx="8" fill="#f0fdfa" stroke="#99f6e4"/>
  <text x="530" y="230" text-anchor="middle" font-size="12" font-weight="700" fill="#115e59">AppArmor: aa_file_perm()</text>
  <text x="530" y="250" text-anchor="middle" font-size="11" fill="#0f766e">パスプロファイルをチェック</text>

  <line x1="210" y1="264" x2="360" y2="280" stroke="#475569" stroke-width="1.6" marker-end="url(#arrLSM1)"/>
  <line x1="530" y1="264" x2="360" y2="280" stroke="#475569" stroke-width="1.6" marker-end="url(#arrLSM1)"/>

  <rect x="210" y="282" width="300" height="38" rx="8" fill="#dcfce7" stroke="#4ade80"/>
  <text x="360" y="306" text-anchor="middle" font-size="13" font-weight="700" fill="#166534">両方が OK を返す → 操作を許可</text>

  <rect x="60" y="334" width="600" height="50" rx="8" fill="#eef2ff" stroke="#c7d2fe"/>
  <text x="76" y="356" font-size="12.5" fill="#3730a3">同じフックポイントにおいて、SELinux と AppArmor はそれぞれ独立して同じ open 呼び出しをチェックし、</text>
  <text x="76" y="374" font-size="12.5" fill="#3730a3">両方のモジュールが許可して初めて操作が許可されます。</text>
</svg>

---

## 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以降)

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

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

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

```c
// 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 キー使用) またはデジタル署名に基づく
```

## デバッグ

```bash
# 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*
