Linux Kernel Security Modules

Linux Security Modules (LSM) 総合技術ガイド - SELinux, AppArmor, その他のセキュリティフレームワーク

最終更新日: 2026-04-10 対象カーネルバージョン: Linux 5.x - 6.x 対象読者: システム管理者、セキュリティエンジニア、DevOps/SREエンジニア


目次

  1. はじめに
  2. LSMフレームワークのアーキテクチャ
  3. SELinux 詳細解説
  4. SELinux 実践的設定
  5. AppArmor 詳細解説
  6. AppArmor 実践的設定
  7. その他のLSM: Smack, TOMOYO, Yama
  8. Landlock: 非特権サンドボックス
  9. LSMの比較
  10. LSMスタッキング
  11. 実践例: Webサーバー、コンテナ、サービスのセキュリティ強化
  12. トラブルシューティング
  13. ツールリファレンス
  14. まとめ

1. はじめに

1.1 なぜLSMが必要なのか

従来のUnix/LinuxのDAC(Discretionary Access Control: 任意アクセス制御)モデルでは、ファイルの所有者がアクセス権を自由に設定できる。しかし、このモデルにはいくつかの根本的な問題がある。

  • root権限の万能性: root ユーザーはすべてのDACチェックをバイパスできる
  • 権限昇格の危険性: setuid プログラムの脆弱性がシステム全体の侵害につながる
  • 最小権限の原則の不徹底: プロセスが必要以上のリソースにアクセスできる
  • 内部脅威への対応困難: 正当なユーザーによる不正アクセスを防げない

MAC(Mandatory Access Control: 強制アクセス制御)は、システム管理者が定義したポリシーに基づいてアクセスを制御する。個々のユーザーやプロセスがポリシーを変更することはできない。

DAC (任意アクセス制御):
  ユーザー → 自分のファイルの権限を自由に設定
  問題: root が全権限を持つ、権限昇格に脆弱

MAC (強制アクセス制御):
  管理者 → セキュリティポリシーを定義
  カーネル → すべてのアクセスをポリシーに基づいて検証
  利点: root でもポリシーに従う、最小権限の原則を強制

1.2 LSMの歴史

LSMフレームワークは、Linux カーネルにおけるセキュリティモジュールの標準化されたインターフェースとして開発された。

イベント
2001Linux Security Modules プロジェクト開始
2003Linux 2.6.0 に LSM フレームワーク統合、SELinux マージ
2006AppArmor が開発開始(Immunix/Novell)
2009TOMOYO Linux がメインラインカーネルに統合
2009Smack が Linux 2.6.25 に統合
2010AppArmor が Linux 2.6.36 にマージ
2012Yama LSM が Linux 3.4 に統合
2017LSM スタッキングの改善が始まる
2021Landlock が Linux 5.13 に統合
2023LSM BPF が進展

2. LSMフレームワークのアーキテクチャ

2.1 LSMフレームワークの概要

LSMフレームワークは、カーネルのセキュリティ判断を外部モジュールに委譲するための仕組みである。DACのチェックが成功した後、LSMフックが呼び出され、追加のセキュリティチェックが行われる。

ユーザープロセス
    |
    v
システムコール (例: open, execve, connect)
    |
    v
DAC チェック (従来のUNIXパーミッション)
    |  成功
    v
LSM フック呼び出し
    |
    +---> SELinux チェック
    +---> AppArmor チェック
    +---> Smack チェック
    +---> (その他のLSM)
    |
    v  すべて許可
リソースへのアクセス

2.2 LSMフックの仕組み

LSMフックは、カーネルの各種操作ポイントに配置されたコールバック関数のセットである。主要なフックカテゴリは以下の通り。

/* カーネルソースからの概念的な抜粋 (include/linux/lsm_hooks.h) */

struct security_hook_heads {
    /* タスク関連フック */
    struct hlist_head task_alloc;
    struct hlist_head task_free;
    struct hlist_head task_setpgid;
    struct hlist_head task_getpgid;
    struct hlist_head task_kill;

    /* ファイル関連フック */
    struct hlist_head file_permission;
    struct hlist_head file_alloc_security;
    struct hlist_head file_free_security;
    struct hlist_head file_ioctl;
    struct hlist_head file_mmap;
    struct hlist_head file_mprotect;
    struct hlist_head file_lock;
    struct hlist_head file_open;

    /* inode 関連フック */
    struct hlist_head inode_alloc_security;
    struct hlist_head inode_free_security;
    struct hlist_head inode_init_security;
    struct hlist_head inode_create;
    struct hlist_head inode_link;
    struct hlist_head inode_unlink;
    struct hlist_head inode_symlink;
    struct hlist_head inode_mkdir;
    struct hlist_head inode_rmdir;
    struct hlist_head inode_rename;
    struct hlist_head inode_permission;
    struct hlist_head inode_setattr;
    struct hlist_head inode_getattr;
    struct hlist_head inode_setxattr;
    struct hlist_head inode_getxattr;

    /* ソケット関連フック */
    struct hlist_head socket_create;
    struct hlist_head socket_bind;
    struct hlist_head socket_connect;
    struct hlist_head socket_listen;
    struct hlist_head socket_accept;
    struct hlist_head socket_sendmsg;
    struct hlist_head socket_recvmsg;

    /* プログラム実行フック */
    struct hlist_head bprm_creds_for_exec;
    struct hlist_head bprm_creds_from_file;
    struct hlist_head bprm_check_security;
    struct hlist_head bprm_committing_creds;
    struct hlist_head bprm_committed_creds;

    /* ネットワーク関連フック */
    struct hlist_head unix_stream_connect;
    struct hlist_head unix_may_send;
    struct hlist_head sk_alloc_security;
    struct hlist_head sk_free_security;

    /* IPC 関連フック */
    struct hlist_head msg_msg_alloc_security;
    struct hlist_head msg_queue_alloc_security;
    struct hlist_head shm_alloc_security;
    struct hlist_head sem_alloc_security;

    /* ... 数百のフックが存在 */
};

2.3 LSMフックの呼び出しフロー

以下は open() システムコール時のLSMフック呼び出しの詳細フローである。

ユーザー空間: open("/etc/shadow", O_RDONLY)
    |
    v
カーネル空間: sys_open() / do_sys_open()
    |
    v
do_filp_open()
    |
    v
path_openat()
    |
    v
do_last() → vfs_open()
    |
    v
DAC チェック: inode_permission()
    |  (ファイルオーナー、グループ、other のrwxチェック)
    |  成功
    v
LSM フック: security_inode_permission(inode, MAY_READ)
    |
    +---> SELinux: selinux_inode_permission()
    |     - プロセスのセキュリティコンテキスト取得
    |     - ファイルのセキュリティコンテキスト取得
    |     - Type Enforcement ルールの検証
    |     - AVC (Access Vector Cache) の参照/更新
    |
    +---> AppArmor: apparmor_inode_permission()
    |     - プロセスのプロファイル取得
    |     - パスベースのルール照合
    |
    v
すべてのLSMが許可 → ファイルオープン成功
いずれかが拒否 → EACCES 返却

2.4 セキュリティBlobとデータ構造

LSMフレームワークでは、各カーネルオブジェクトにセキュリティ情報を付加するための「セキュリティBlob」メカニズムが使われる。

/* タスク構造体のセキュリティBlob */
struct task_struct {
    /* ... 他のフィールド ... */
    void *security;  /* LSMセキュリティBlob */
};

/* inode構造体のセキュリティBlob */
struct inode {
    /* ... 他のフィールド ... */
    void *i_security;  /* LSMセキュリティBlob */
};

/* SELinuxの場合のinode セキュリティ構造体 */
struct inode_security_struct {
    struct inode *inode;           /* 親inode */
    struct list_head list;
    u32 task_sid;                  /* タスクのSID */
    u32 sid;                       /* inodeのSID */
    u16 sclass;                    /* セキュリティクラス */
    unsigned char initialized;     /* 初期化フラグ */
    spinlock_t lock;
};

2.5 LSMの初期化と登録

カーネルブート時のLSM初期化プロセスは以下の通りである。

/* カーネル起動時のLSM初期化 (概念的なコード) */

/* security/security.c */
int __init security_init(void)
{
    int i;
    struct hlist_head *list = (struct hlist_head *) &security_hook_heads;
    
    /* すべてのフックリストを初期化 */
    for (i = 0; i < sizeof(security_hook_heads) / sizeof(struct hlist_head); i++)
        INIT_HLIST_HEAD(&list[i]);
    
    /* 各LSMの初期化関数を呼び出し */
    /* ブートパラメータ lsm= で指定された順序で初期化 */
    
    pr_info("LSM: Security Framework initialized\n");
    return 0;
}

カーネルのブートパラメータでLSMの有効化と順序を制御できる。

# /etc/default/grub の GRUB_CMDLINE_LINUX に設定

# SELinuxを使用する場合
GRUB_CMDLINE_LINUX="lsm=lockdown,capability,yama,selinux"

# AppArmorを使用する場合
GRUB_CMDLINE_LINUX="lsm=lockdown,capability,yama,apparmor"

# 複数のLSMをスタックする場合 (Linux 5.1+)
GRUB_CMDLINE_LINUX="lsm=lockdown,capability,yama,apparmor,selinux"

# 設定後にGRUBを更新
sudo update-grub
# または
sudo grub2-mkconfig -o /boot/grub2/grub.cfg

2.6 LSMの主要カテゴリ

LSM フレームワーク
├── メジャーLSM (排他的 → スタッキングで変化中)
│   ├── SELinux      - ラベルベース、Type Enforcement
│   ├── AppArmor     - パスベース、プロファイル制御
│   ├── Smack        - シンプルなラベルベース
│   └── TOMOYO       - パスベース、学習モード
│
├── マイナーLSM (スタック可能)
│   ├── Yama         - ptrace制限
│   ├── LoadPin      - カーネルモジュールの信頼性
│   └── Lockdown     - カーネルの自己保護
│
└── 新世代LSM
    ├── Landlock     - 非特権サンドボックス
    └── BPF LSM      - eBPFベースの動的ポリシー

3. SELinux 詳細解説

3.1 SELinuxの概要と歴史

SELinux(Security-Enhanced Linux)は、NSA(米国国家安全保障局)が開発した強制アクセス制御システムである。Fedora、RHEL、CentOS、Rocky Linux、AlmaLinux などのRed Hat系ディストリビューションで標準的に有効化されている。

# SELinuxの状態確認
$ getenforce
Enforcing

$ sestatus
SELinux status:                 enabled
SELinuxfs mount:                /sys/fs/selinux
SELinux root directory:         /etc/selinux
Loaded policy name:             targeted
Current mode:                   enforcing
Mode from config file:          enforcing
Policy MLS status:              enabled
Policy deny_unknown status:     allowed
Memory protection checking:     actual (secure)
Max kernel policy version:      33

3.2 SELinuxのアーキテクチャ

SELinuxのアーキテクチャは、以下の主要コンポーネントで構成される。

┌─────────────────────────────────────────────────────┐
│                   ユーザー空間                        │
│                                                       │
│  ┌──────────┐  ┌──────────┐  ┌──────────────────┐   │
│  │ アプリ    │  │ アプリ    │  │ ポリシー管理     │   │
│  │ (httpd)  │  │ (sshd)   │  │ ツール           │   │
│  └────┬─────┘  └────┬─────┘  │ - semanage       │   │
│       │              │        │ - setsebool      │   │
│       │              │        │ - restorecon     │   │
│       │              │        │ - audit2allow    │   │
│       │              │        └──────────────────┘   │
├───────┼──────────────┼───────────────────────────────┤
│       │  カーネル空間 │                               │
│       v              v                               │
│  ┌──────────────────────────────────┐                │
│  │        LSM フック層              │                │
│  └────────────┬─────────────────────┘                │
│               │                                       │
│  ┌────────────v─────────────────────┐                │
│  │     SELinux セキュリティサーバー   │                │
│  │                                   │                │
│  │  ┌─────────────────────────────┐ │                │
│  │  │  AVC (Access Vector Cache)  │ │                │
│  │  │  - キャッシュされた判定結果  │ │                │
│  │  └─────────────────────────────┘ │                │
│  │                                   │                │
│  │  ┌─────────────────────────────┐ │                │
│  │  │  ポリシーデータベース        │ │                │
│  │  │  - Type Enforcement ルール   │ │                │
│  │  │  - RBAC ルール               │ │                │
│  │  │  - MLS/MCS ルール            │ │                │
│  │  └─────────────────────────────┘ │                │
│  │                                   │                │
│  │  ┌─────────────────────────────┐ │                │
│  │  │  SID テーブル                │ │                │
│  │  │  (Security ID マッピング)    │ │                │
│  │  └─────────────────────────────┘ │                │
│  └──────────────────────────────────┘                │
│                                                       │
│  ┌──────────────────────────────────┐                │
│  │    selinuxfs (/sys/fs/selinux)   │                │
│  │    - ユーザー空間とのインターフェース │            │
│  └──────────────────────────────────┘                │
└─────────────────────────────────────────────────────┘

3.3 セキュリティコンテキスト

SELinuxでは、すべてのプロセス、ファイル、ソケット、ポートなどのオブジェクトに「セキュリティコンテキスト」(ラベル)が付与される。

セキュリティコンテキストの形式:
  ユーザー:ロール:タイプ:レベル

例:
  system_u:system_r:httpd_t:s0
  │         │        │      │
  │         │        │      └── MLS/MCSレベル (s0 = 最低レベル)
  │         │        └── タイプ (Type Enforcement の核心)
  │         └── ロール (RBAC用)
  └── SELinuxユーザー (Linuxユーザーとのマッピング)
# プロセスのセキュリティコンテキスト確認
$ ps -eZ | head -20
LABEL                             PID TTY          TIME CMD
system_u:system_r:init_t:s0         1 ?        00:00:03 systemd
system_u:system_r:kernel_t:s0       2 ?        00:00:00 kthreadd
system_u:system_r:sshd_t:s0-s0:c0.c1023 1234 ? 00:00:00 sshd
system_u:system_r:httpd_t:s0      5678 ?        00:00:01 httpd
unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 9012 pts/0 00:00:00 bash

# ファイルのセキュリティコンテキスト確認
$ ls -lZ /var/www/html/index.html
-rw-r--r--. root root system_u:object_r:httpd_sys_content_t:s0 /var/www/html/index.html

$ ls -lZ /etc/shadow
----------. root root system_u:object_r:shadow_t:s0 /etc/shadow

$ ls -lZ /usr/sbin/httpd
-rwxr-xr-x. root root system_u:object_r:httpd_exec_t:s0 /usr/sbin/httpd

# ポートのセキュリティコンテキスト確認
$ semanage port -l | grep http
http_cache_port_t              tcp      8080, 8118, 8123, 10001-10010
http_cache_port_t              udp      3130
http_port_t                    tcp      80, 81, 443, 488, 8008, 8009, 8443, 9000

3.4 Type Enforcement (TE)

Type Enforcement は SELinux の中核となるセキュリティメカニズムである。プロセス(サブジェクト)のタイプ(ドメイン)とリソース(オブジェクト)のタイプに基づいてアクセスを制御する。

Type Enforcement の基本概念:

  プロセス (httpd_t) ---[read]---> ファイル (httpd_sys_content_t)  ✓ 許可
  プロセス (httpd_t) ---[read]---> ファイル (shadow_t)            ✗ 拒否
  プロセス (httpd_t) ---[write]--> ファイル (httpd_log_t)          ✓ 許可
  プロセス (httpd_t) ---[write]--> ファイル (etc_t)                ✗ 拒否

TE ポリシールールの構文

# 基本構文
allow source_type target_type : object_class { permissions };

# 具体例

# httpd_tドメインがhttpd_sys_content_tタイプのファイルを読み取れる
allow httpd_t httpd_sys_content_t : file { read open getattr ioctl lock };

# httpd_tドメインがhttpd_sys_content_tタイプのディレクトリにアクセスできる
allow httpd_t httpd_sys_content_t : dir { search open read getattr };

# httpd_tドメインがhttpd_log_tタイプのファイルに書き込める
allow httpd_t httpd_log_t : file { open append write create getattr setattr };

# httpd_tドメインがhttp_port_tのTCPソケットにバインドできる
allow httpd_t http_port_t : tcp_socket { name_bind };

# httpd_tドメインがネットワーク接続を行える
allow httpd_t self : tcp_socket { create connect accept listen };

ドメイン遷移

プロセスが新しいプログラムを実行する際、ドメインが遷移する仕組みがある。

ドメイン遷移の例: init_t → sshd_t

  init プロセス (init_t) が /usr/sbin/sshd (sshd_exec_t) を実行
    → sshd プロセスは sshd_t ドメインに遷移

必要なルール:
  1. type_transition init_t sshd_exec_t : process sshd_t;
  2. allow init_t sshd_exec_t : file { read execute open getattr };
  3. allow init_t sshd_t : process { transition };
  4. allow sshd_t sshd_exec_t : file { entrypoint read execute };
# ポリシーモジュールでの記述例
policy_module(my_sshd_transition, 1.0.0)

# ドメイン遷移の定義
type_transition init_t sshd_exec_t : process sshd_t;

# 実行権限
allow init_t sshd_exec_t : file { read execute open getattr };

# 遷移権限
allow init_t sshd_t : process { transition };

# エントリポイント
allow sshd_t sshd_exec_t : file { entrypoint read execute open getattr };

3.5 RBAC (Role-Based Access Control)

RBACは、ユーザーに対してロールを割り当て、各ロールに許可されたドメインを制限する機構である。

SELinux ユーザー/ロール/ドメインの関係:

  SELinuxユーザー    ロール           ドメイン
  ─────────────    ──────          ─────────
  system_u    →    system_r    →    httpd_t, sshd_t, ...
  staff_u     →    staff_r     →    staff_t, ...
                   sysadm_r    →    sysadm_t, ...  (su/sudoで遷移)
  user_u      →    user_r      →    user_t
  unconfined_u →   unconfined_r →   unconfined_t  (制約なし)
  sysadm_u    →    sysadm_r    →    sysadm_t
# SELinuxユーザーとLinuxユーザーのマッピング確認
$ semanage login -l
Login Name           SELinux User         MLS/MCS Range        Service
__default__          unconfined_u         s0-s0:c0.c1023       *
root                 unconfined_u         s0-s0:c0.c1023       *
system_u             system_u             s0-s0:c0.c1023       *

# ユーザーマッピングの追加
$ sudo semanage login -a -s staff_u -r s0-s0:c0.c1023 webadmin

# SELinuxユーザーが利用可能なロール確認
$ semanage user -l
                Labeling   MLS/       MLS/
SELinux User    Prefix     MCS Level  MCS Range            SELinux Roles
guest_u         user       s0         s0                   guest_r
root            user       s0         s0-s0:c0.c1023       staff_r sysadm_r system_r unconfined_r
staff_u         user       s0         s0-s0:c0.c1023       staff_r sysadm_r unconfined_r
sysadm_u        user       s0         s0-s0:c0.c1023       sysadm_r
system_u        user       s0         s0-s0:c0.c1023       system_r unconfined_r
unconfined_u    user       s0         s0-s0:c0.c1023       system_r unconfined_r
user_u          user       s0         s0                   user_r

3.6 MLS/MCS (Multi-Level Security / Multi-Category Security)

MLS は軍事・政府機関レベルのセキュリティ分類を実現し、MCS は コンテナ(特にDocker/Podman)の分離に活用される。

MLS レベル:
  s0 (unclassified)  - 非機密
  s1 (confidential)  - 部外秘
  s2 (secret)        - 極秘
  s3 (top_secret)    - 最高機密

MCS カテゴリ:
  c0-c1023 (最大1024カテゴリ)
  
  例: s0:c0,c1         - レベルs0、カテゴリ0と1
      s0:c100.c200     - レベルs0、カテゴリ100-200
      s0-s0:c0.c1023   - レベルs0のすべてのカテゴリ
# MCS を使ったコンテナの分離 (svirt_lxc_net_t)
# 各コンテナに異なるカテゴリが割り当てられる

# コンテナ1: svirt_lxc_net_t:s0:c100,c200
# コンテナ2: svirt_lxc_net_t:s0:c300,c400

# コンテナ1はコンテナ2のリソースにアクセスできない
# (カテゴリが異なるため)

# Podmanでの確認
$ podman run -d --name test1 nginx
$ podman inspect test1 | grep -i label
"MountLabel": "system_u:object_r:container_file_t:s0:c100,c200",
"ProcessLabel": "system_u:system_r:container_t:s0:c100,c200",

$ podman run -d --name test2 nginx
$ podman inspect test2 | grep -i label
"MountLabel": "system_u:object_r:container_file_t:s0:c300,c400",
"ProcessLabel": "system_u:system_r:container_t:s0:c300,c400",

3.7 SELinux ブーリアン

ブーリアン(Boolean)は、ポリシーを再コンパイルすることなく、特定の機能のオン/オフを切り替えられる仕組みである。

# すべてのブーリアンの一覧表示
$ getsebool -a
abrt_anon_write --> off
abrt_handle_event --> off
allow_console_login --> on
allow_cvs_read_shadow --> off
allow_daemons_dump_core --> on
allow_daemons_use_tcp_wrapper --> off
allow_domain_fd_use --> on
allow_execheap --> off
allow_execmem --> on
allow_execmod --> on
allow_execstack --> on
allow_ftpd_anon_write --> off
allow_ftpd_full_access --> off
allow_ftpd_use_cifs --> off
allow_ftpd_use_nfs --> off
allow_gssd_read_tmp --> on
allow_guest_exec_content --> off
allow_httpd_anon_write --> off
httpd_can_connect_ftp --> off
httpd_can_connect_ldap --> off
httpd_can_network_connect --> off
httpd_can_network_connect_db --> off
httpd_can_network_relay --> off
httpd_can_sendmail --> off
httpd_enable_cgi --> on
httpd_enable_homedirs --> off
httpd_use_cifs --> off
httpd_use_nfs --> off
...

# 特定のブーリアンの状態確認
$ getsebool httpd_can_network_connect
httpd_can_network_connect --> off

# ブーリアンの一時的な変更 (再起動で元に戻る)
$ sudo setsebool httpd_can_network_connect on

# ブーリアンの永続的な変更
$ sudo setsebool -P httpd_can_network_connect on

# ブーリアンの説明を表示
$ sudo semanage boolean -l | grep httpd_can_network
httpd_can_network_connect      (off  ,  off)  Allow httpd to can network connect
httpd_can_network_connect_cobbler (off  ,  off)  Allow httpd to can network connect cobbler
httpd_can_network_connect_db   (off  ,  off)  Allow httpd to can network connect db
httpd_can_network_relay        (off  ,  off)  Allow httpd to can network relay

よく使われるブーリアンの一覧:

# Web サーバー関連
httpd_can_network_connect        # httpdがネットワーク接続を許可
httpd_can_network_connect_db     # httpdがデータベース接続を許可
httpd_can_sendmail               # httpdがメール送信を許可
httpd_enable_cgi                 # httpdがCGI実行を許可
httpd_enable_homedirs            # httpdがホームディレクトリアクセスを許可
httpd_use_nfs                    # httpdがNFSを使用可能に
httpd_use_cifs                   # httpdがCIFSを使用可能に
httpd_unified                    # httpdのコンテンツタイプ統合

# FTP 関連
ftpd_anon_write                  # FTPの匿名書き込み
ftpd_full_access                 # FTPの完全アクセス
ftpd_use_passive_mode            # FTPパッシブモード

# NFS/Samba 関連
use_nfs_home_dirs                # NFSホームディレクトリ使用
use_samba_home_dirs              # Sambaホームディレクトリ使用
samba_enable_home_dirs           # Sambaホームディレクトリ有効化

# その他
allow_user_exec_content          # ユーザーがコンテンツを実行可能
domain_can_mmap_files            # ドメインがファイルをmmapできる
virt_use_nfs                     # 仮想化がNFS使用可能

3.8 SELinux ポリシー言語の詳細

ポリシーモジュールの構造

# SELinux ポリシーモジュールの基本構造
# myapp.te (Type Enforcement ファイル)

policy_module(myapp, 1.0.0)

########################################
# 型宣言
########################################

# 新しいドメインタイプの宣言
type myapp_t;

# ドメインのエントリポイントとなる実行ファイルタイプ
type myapp_exec_t;

# ログファイルタイプ
type myapp_log_t;

# 設定ファイルタイプ
type myapp_conf_t;

# データファイルタイプ
type myapp_var_t;

# PIDファイルタイプ
type myapp_var_run_t;

# tmpファイルタイプ
type myapp_tmp_t;

########################################
# マクロ呼び出し
########################################

# デーモンとして初期化 (init_tからのドメイン遷移)
init_daemon_domain(myapp_t, myapp_exec_t)

# ログファイルの管理
logging_log_file(myapp_log_t)

# 設定ファイル
files_config_file(myapp_conf_t)

# /var/run下のファイル
files_pid_file(myapp_var_run_t)

# 一時ファイル
files_tmp_file(myapp_tmp_t)

########################################
# ローカルポリシー
########################################

# 自身のドメインに対する基本操作
allow myapp_t self:process { signal_perms fork };
allow myapp_t self:fifo_file rw_fifo_file_perms;
allow myapp_t self:unix_stream_socket create_stream_socket_perms;
allow myapp_t self:tcp_socket create_stream_socket_perms;

# 設定ファイルの読み取り
allow myapp_t myapp_conf_t:file read_file_perms;
allow myapp_t myapp_conf_t:dir list_dir_perms;

# ログファイルへの書き込み
allow myapp_t myapp_log_t:file { create_file_perms append_file_perms };
allow myapp_t myapp_log_t:dir { create_dir_perms };

# データファイルの管理
allow myapp_t myapp_var_t:file manage_file_perms;
allow myapp_t myapp_var_t:dir manage_dir_perms;

# PIDファイルの管理
allow myapp_t myapp_var_run_t:file manage_file_perms;
allow myapp_t myapp_var_run_t:dir manage_dir_perms;

# 一時ファイルの管理
allow myapp_t myapp_tmp_t:file manage_file_perms;
allow myapp_t myapp_tmp_t:dir manage_dir_perms;
files_tmp_filetrans(myapp_t, myapp_tmp_t, { file dir })

# ネットワークアクセス
corenet_tcp_bind_generic_node(myapp_t)
corenet_tcp_bind_http_port(myapp_t)
corenet_tcp_connect_http_port(myapp_t)

# DNSの解決
sysnet_dns_name_resolve(myapp_t)

# ログ機能
logging_send_syslog_msg(myapp_t)

# /etc/resolv.conf の読み取り
sysnet_read_config(myapp_t)

# /proc の読み取り
kernel_read_system_state(myapp_t)

# 共有ライブラリの使用
libs_use_ld_so(myapp_t)
libs_use_shared_libs(myapp_t)

ファイルコンテキスト定義

# myapp.fc (File Context ファイル)

# 実行ファイル
/usr/sbin/myapp             --  gen_context(system_u:object_r:myapp_exec_t,s0)
/usr/bin/myapp-cli           --  gen_context(system_u:object_r:myapp_exec_t,s0)

# 設定ファイル
/etc/myapp(/.*)?                gen_context(system_u:object_r:myapp_conf_t,s0)
/etc/myapp/myapp\.conf       -- gen_context(system_u:object_r:myapp_conf_t,s0)

# ログファイル
/var/log/myapp(/.*)?            gen_context(system_u:object_r:myapp_log_t,s0)

# データファイル
/var/lib/myapp(/.*)?            gen_context(system_u:object_r:myapp_var_t,s0)

# PIDファイル
/var/run/myapp\.pid          -- gen_context(system_u:object_r:myapp_var_run_t,s0)
/run/myapp\.pid              -- gen_context(system_u:object_r:myapp_var_run_t,s0)

# systemd ユニットファイル
/usr/lib/systemd/system/myapp\.service -- gen_context(system_u:object_r:myapp_unit_file_t,s0)

インターフェース定義

# myapp.if (Interface ファイル)

########################################
## <summary>
##   myapp の管理インターフェース
## </summary>

########################################
## <summary>
##   myapp のドメインに遷移する
## </summary>
## <param name="domain">
##   <summary>遷移元のドメイン</summary>
## </param>
interface(`myapp_domtrans',`
    gen_require(`
        type myapp_t, myapp_exec_t;
    ')
    corecmd_search_bin($1)
    domtrans_pattern($1, myapp_exec_t, myapp_t)
')

########################################
## <summary>
##   myapp のログファイルを読み取る
## </summary>
## <param name="domain">
##   <summary>読み取りを許可するドメイン</summary>
## </param>
interface(`myapp_read_log',`
    gen_require(`
        type myapp_log_t;
    ')
    logging_search_logs($1)
    allow $1 myapp_log_t:file read_file_perms;
    allow $1 myapp_log_t:dir list_dir_perms;
')

########################################
## <summary>
##   myapp のすべてのログファイルを管理する
## </summary>
## <param name="domain">
##   <summary>管理を許可するドメイン</summary>
## </param>
interface(`myapp_manage_log',`
    gen_require(`
        type myapp_log_t;
    ')
    logging_search_logs($1)
    allow $1 myapp_log_t:file manage_file_perms;
    allow $1 myapp_log_t:dir manage_dir_perms;
')

4. SELinux 実践的設定

4.1 SELinux の基本操作

# SELinux のモード切り替え

# 現在のモード確認
$ getenforce
Enforcing

# 一時的にPermissiveモードに変更 (再起動で元に戻る)
$ sudo setenforce 0
$ getenforce
Permissive

# Enforcingモードに戻す
$ sudo setenforce 1
$ getenforce
Enforcing

# 永続的な設定変更は /etc/selinux/config を編集
$ cat /etc/selinux/config
# This file controls the state of SELinux on the system.
# SELINUX= can take one of these three values:
#     enforcing - SELinux security policy is enforced.
#     permissive - SELinux prints warnings instead of enforcing.
#     disabled - No SELinux policy is loaded.
SELINUX=enforcing
# SELINUXTYPE= can take one of three values:
#     targeted - Targeted processes are protected,
#     minimum - Modification of targeted policy. Only selected processes are protected.
#     mls - Multi Level Security protection.
SELINUXTYPE=targeted

4.2 Targeted ポリシー

RHEL系で標準的に使用される targeted ポリシーでは、特定のデーモンのみがSELinuxの制限下に置かれ、その他は unconfined(制限なし)で動作する。

# 制限されたドメイン(confined)の例
$ ps -eZ | grep -E "(httpd|sshd|named|mysqld|postfix)"
system_u:system_r:httpd_t:s0     5678 ?  00:00:01 httpd
system_u:system_r:sshd_t:s0-s0:c0.c1023  1234 ?  00:00:00 sshd
system_u:system_r:named_t:s0     3456 ?  00:00:00 named
system_u:system_r:mysqld_t:s0    4567 ?  00:00:02 mysqld

# 制限されていないドメイン(unconfined)の例
$ ps -eZ | grep unconfined
unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023  9012 pts/0 00:00:00 bash

4.3 ファイルコンテキストの管理

# ファイルコンテキストの確認
$ ls -lZ /var/www/html/
drwxr-xr-x. root root system_u:object_r:httpd_sys_content_t:s0 .
-rw-r--r--. root root system_u:object_r:httpd_sys_content_t:s0 index.html

# ファイルコンテキストの復元(restorecon)
# ファイルを移動した場合、元のコンテキストが保持される
$ sudo cp /home/user/mypage.html /var/www/html/
$ ls -lZ /var/www/html/mypage.html
-rw-r--r--. root root unconfined_u:object_r:user_home_t:s0 mypage.html
# ↑ コンテキストが正しくない

# restorecon で正しいコンテキストに復元
$ sudo restorecon -v /var/www/html/mypage.html
Relabeled /var/www/html/mypage.html from unconfined_u:object_r:user_home_t:s0 to system_u:object_r:httpd_sys_content_t:s0

# ディレクトリ以下を再帰的に復元
$ sudo restorecon -Rv /var/www/html/
Relabeled /var/www/html/mypage.html from unconfined_u:object_r:user_home_t:s0 to system_u:object_r:httpd_sys_content_t:s0

# カスタムファイルコンテキストの追加
# /opt/mywebapp 以下をhttpd_sys_content_tとして定義
$ sudo semanage fcontext -a -t httpd_sys_content_t "/opt/mywebapp(/.*)?"

# 定義した後、実際にラベルを適用
$ sudo restorecon -Rv /opt/mywebapp/

# カスタムファイルコンテキストの確認
$ sudo semanage fcontext -l | grep mywebapp
/opt/mywebapp(/.*)?    all files    system_u:object_r:httpd_sys_content_t:s0

# 書き込み可能なWebコンテンツディレクトリ
$ sudo semanage fcontext -a -t httpd_sys_rw_content_t "/opt/mywebapp/uploads(/.*)?"
$ sudo restorecon -Rv /opt/mywebapp/uploads/

# CGIスクリプトディレクトリ
$ sudo semanage fcontext -a -t httpd_sys_script_exec_t "/opt/mywebapp/cgi-bin(/.*)?"
$ sudo restorecon -Rv /opt/mywebapp/cgi-bin/

4.4 ポートコンテキストの管理

# 現在のポートコンテキスト一覧
$ sudo semanage port -l | head -30
SELinux Port Type              Proto    Port Number
afs3_callback_port_t           tcp      7001
afs3_callback_port_t           udp      7001
afs_bos_port_t                 udp      7007
afs_fs_port_t                  tcp      2040
afs_fs_port_t                  udp      7000, 7005
...
http_port_t                    tcp      80, 81, 443, 488, 8008, 8009, 8443, 9000
...
ssh_port_t                     tcp      22

# カスタムポートの追加
# Apacheが8888ポートを使用する場合
$ sudo semanage port -a -t http_port_t -p tcp 8888

# SSHが2222ポートを使用する場合
$ sudo semanage port -a -t ssh_port_t -p tcp 2222

# ポートタイプの変更
$ sudo semanage port -m -t http_port_t -p tcp 3000

# ポートの削除
$ sudo semanage port -d -t http_port_t -p tcp 8888

# 確認
$ sudo semanage port -l | grep -E "(http_port_t|ssh_port_t)"
http_port_t                    tcp      3000, 80, 81, 443, 488, 8008, 8009, 8443, 9000
ssh_port_t                     tcp      2222, 22

4.5 audit2allow によるポリシー生成

# audit2allow は拒否ログからポリシーモジュールを生成するツール

# まず、拒否ログを確認
$ sudo ausearch -m avc --start recent
----
time->Thu Apr 10 14:30:00 2026
type=AVC msg=audit(1712750200.000:1234): avc:  denied  { name_connect } for  pid=5678 comm="httpd" dest=3306 scontext=system_u:system_r:httpd_t:s0 tcontext=system_u:object_r:mysqld_port_t:s0 tclass=tcp_socket permissive=0

# audit2allow で必要なルールを表示
$ sudo ausearch -m avc --start recent | audit2allow
#============= httpd_t ==============
allow httpd_t mysqld_port_t:tcp_socket name_connect;

# ブーリアンで解決できるか確認 (-R オプション)
$ sudo ausearch -m avc --start recent | audit2allow -R
#============= httpd_t ==============
setsebool -P httpd_can_network_connect_db 1

# ポリシーモジュールとして生成
$ sudo ausearch -m avc --start recent | audit2allow -M my_httpd_mysql
******************** IMPORTANT ***********************
To make this policy package active, execute:

semodule -i my_httpd_mysql.pp

# 生成されたモジュールをインストール
$ sudo semodule -i my_httpd_mysql.pp

# インストールされたモジュールの確認
$ sudo semodule -l | grep my_httpd
my_httpd_mysql 1.0

4.6 SELinux ポリシーモジュールの作成と管理

# カスタムポリシーモジュールの作成手順

# 1. ポリシーファイルの作成
$ mkdir ~/selinux-modules && cd ~/selinux-modules

# myapp.te (Type Enforcement)
$ cat > myapp.te << 'EOF'
policy_module(myapp, 1.0.0)

require {
    type httpd_t;
    type var_t;
    class file { read write open getattr };
    class dir { search read open getattr };
}

# /var/myapp 以下の独自タイプ
type myapp_data_t;
files_type(myapp_data_t)

# httpd がmyapp_data_tにアクセスできる
allow httpd_t myapp_data_t:file { read write open getattr };
allow httpd_t myapp_data_t:dir { search read open getattr };
EOF

# myapp.fc (File Context)
$ cat > myapp.fc << 'EOF'
/var/myapp(/.*)?    gen_context(system_u:object_r:myapp_data_t,s0)
EOF

# 2. ポリシーモジュールのコンパイル
$ make -f /usr/share/selinux/devel/Makefile myapp.pp
Compiling targeted myapp module
Creating targeted myapp.pp policy package
rm tmp/myapp.mod tmp/myapp.mod.fc

# 3. ポリシーモジュールのインストール
$ sudo semodule -i myapp.pp

# 4. ファイルコンテキストの適用
$ sudo mkdir -p /var/myapp
$ sudo restorecon -Rv /var/myapp/

# 5. 確認
$ ls -lZd /var/myapp/
drwxr-xr-x. root root system_u:object_r:myapp_data_t:s0 /var/myapp/

# ポリシーモジュールの管理コマンド
# モジュール一覧
$ sudo semodule -l

# モジュールの無効化
$ sudo semodule -d myapp

# モジュールの有効化
$ sudo semodule -e myapp

# モジュールの削除
$ sudo semodule -r myapp

# モジュールの優先度設定 (400が最高)
$ sudo semodule -X 400 -i myapp.pp

4.7 semanage による総合管理

# semanage の主要サブコマンド

# ユーザーマッピング管理
$ sudo semanage login -l                    # マッピング一覧
$ sudo semanage login -a -s staff_u user1   # マッピング追加
$ sudo semanage login -m -s user_u user1    # マッピング変更
$ sudo semanage login -d user1              # マッピング削除

# SELinuxユーザー管理
$ sudo semanage user -l                     # ユーザー一覧
$ sudo semanage user -a -R "staff_r sysadm_r" -r s0-s0:c0.c1023 myuser_u

# ファイルコンテキスト管理
$ sudo semanage fcontext -l                 # コンテキスト一覧
$ sudo semanage fcontext -a -t httpd_sys_content_t '/opt/www(/.*)?'
$ sudo semanage fcontext -d '/opt/www(/.*)?'

# ポート管理
$ sudo semanage port -l                     # ポート一覧
$ sudo semanage port -a -t http_port_t -p tcp 8080
$ sudo semanage port -d -t http_port_t -p tcp 8080

# ブーリアン管理
$ sudo semanage boolean -l                  # ブーリアン一覧(説明付き)
$ sudo semanage boolean -m --on httpd_can_network_connect
$ sudo semanage boolean -m --off httpd_can_network_connect

# ネットワークインターフェース管理
$ sudo semanage interface -l
$ sudo semanage interface -a -t netif_t eth0

# ノード管理(IPアドレスベース)
$ sudo semanage node -l
$ sudo semanage node -a -t node_t -p ipv4 -M 255.255.255.0 192.168.1.0

# エクスポート/インポート
$ sudo semanage export > selinux_customizations.txt
$ sudo semanage import < selinux_customizations.txt

5. AppArmor 詳細解説

5.1 AppArmorの概要

AppArmor(Application Armor)は、Ubuntu、Debian、SUSE で標準的に使用されるMAC(強制アクセス制御)システムである。SELinuxと異なり、パスベースのアクセス制御を採用しているため、ファイルシステムのラベリングが不要で設定が比較的容易である。

# AppArmor の状態確認
$ sudo aa-status
apparmor module is loaded.
56 profiles are loaded.
  38 - enforce mode.
  18 - complain mode.
2 processes have profiles defined.
  2 - enforce mode.
  0 - complain mode.
  0 - unconfined (with profile).

# カーネルにロードされたプロファイル
$ cat /sys/kernel/security/apparmor/profiles | head
/snap/snapd/21759/usr/lib/snapd/snap-confine (enforce)
/snap/snapd/21759/usr/lib/snapd/snap-confine//mount-namespace-capture-helper (enforce)
/usr/bin/man (enforce)
/usr/sbin/cups-browsed (enforce)
/usr/sbin/cupsd (enforce)
/usr/sbin/ntpd (enforce)
/usr/sbin/tcpdump (enforce)
/usr/lib/NetworkManager/nm-dhcp-client.action (enforce)

5.2 AppArmorのアーキテクチャ

┌─────────────────────────────────────────────────────┐
│                   ユーザー空間                        │
│                                                       │
│  ┌──────────┐  ┌──────────┐  ┌──────────────────┐   │
│  │ アプリ    │  │ アプリ    │  │ プロファイル管理   │   │
│  │ (nginx)  │  │ (cups)   │  │ ツール           │   │
│  └────┬─────┘  └────┬─────┘  │ - aa-genprof     │   │
│       │              │        │ - aa-logprof     │   │
│       │              │        │ - aa-enforce     │   │
│       │              │        │ - aa-complain    │   │
│       │              │        │ - aa-disable     │   │
│       │              │        └──────────────────┘   │
├───────┼──────────────┼───────────────────────────────┤
│       │  カーネル空間 │                               │
│       v              v                               │
│  ┌──────────────────────────────────┐                │
│  │        LSM フック層              │                │
│  └────────────┬─────────────────────┘                │
│               │                                       │
│  ┌────────────v─────────────────────┐                │
│  │     AppArmor セキュリティモジュール│                │
│  │                                   │                │
│  │  ┌─────────────────────────────┐ │                │
│  │  │  プロファイルデータベース    │ │                │
│  │  │  (パスベースのルール)       │ │                │
│  │  └─────────────────────────────┘ │                │
│  │                                   │                │
│  │  ┌─────────────────────────────┐ │                │
│  │  │  ポリシーキャッシュ          │ │                │
│  │  │  (判定結果のキャッシュ)      │ │                │
│  │  └─────────────────────────────┘ │                │
│  │                                   │                │
│  │  ┌─────────────────────────────┐ │                │
│  │  │  apparmorfs                  │ │                │
│  │  │  (/sys/kernel/security/      │ │                │
│  │  │   apparmor/)                 │ │                │
│  │  └─────────────────────────────┘ │                │
│  └──────────────────────────────────┘                │
└─────────────────────────────────────────────────────┘

5.3 プロファイルモード

AppArmorのプロファイルには3つのモードがある。

1. Enforce(強制)モード:
   - ポリシーに違反するアクセスを拒否し、ログに記録
   - 本番環境で使用

2. Complain(苦情)モード:
   - ポリシーに違反するアクセスを許可するが、ログに記録
   - プロファイル開発・テスト時に使用

3. Unconfined(非制限):
   - プロファイルがロードされていない状態
   - AppArmorによる制限なし
# プロファイルモードの切り替え

# Enforceモードに設定
$ sudo aa-enforce /usr/sbin/nginx
Setting /usr/sbin/nginx to enforce mode.

# Complainモードに設定
$ sudo aa-complain /usr/sbin/nginx
Setting /usr/sbin/nginx to complain mode.

# プロファイルを無効化
$ sudo aa-disable /usr/sbin/nginx

# すべてのプロファイルをenforcekモードに
$ sudo aa-enforce /etc/apparmor.d/*

# すべてのプロファイルをcomplainモードに
$ sudo aa-complain /etc/apparmor.d/*

5.4 プロファイルの基本構造

# /etc/apparmor.d/usr.sbin.nginx
# AppArmor プロファイルの基本構造

#include <tunables/global>

# プロファイル名 (通常は実行ファイルのパス)
/usr/sbin/nginx {
    # abstractions (共通ルールセットの読み込み)
    #include <abstractions/base>
    #include <abstractions/nameservice>
    #include <abstractions/openssl>
    #include <abstractions/ssl_keys>

    # capability (Linux capabilities)
    capability net_bind_service,
    capability setuid,
    capability setgid,
    capability dac_override,

    # ネットワークアクセス
    network inet stream,
    network inet6 stream,
    network inet dgram,
    network inet6 dgram,

    # 実行ファイル
    /usr/sbin/nginx mr,

    # 設定ファイル (読み取り)
    /etc/nginx/** r,
    /etc/nginx/nginx.conf r,
    /etc/nginx/mime.types r,
    /etc/nginx/conf.d/** r,
    /etc/nginx/sites-available/** r,
    /etc/nginx/sites-enabled/** r,

    # Webコンテンツ (読み取り)
    /var/www/** r,
    /var/www/html/** r,

    # ログファイル (書き込み)
    /var/log/nginx/** w,
    /var/log/nginx/access.log w,
    /var/log/nginx/error.log w,

    # PIDファイル
    /run/nginx.pid rw,
    /var/run/nginx.pid rw,

    # 一時ファイル
    /var/lib/nginx/** rw,
    /var/lib/nginx/body/** rw,
    /var/lib/nginx/proxy/** rw,
    /var/lib/nginx/fastcgi/** rw,

    # /proc の読み取り
    /proc/sys/kernel/random/boot_id r,
    owner /proc/*/status r,
    /proc/sys/net/core/somaxconn r,

    # SSL証明書
    /etc/ssl/certs/** r,
    /etc/ssl/private/** r,

    # 共有ライブラリ
    /usr/lib/** mr,
    /usr/lib/x86_64-linux-gnu/** mr,
    /lib/** mr,
    /lib/x86_64-linux-gnu/** mr,

    # ソケット
    /var/run/nginx/*.sock rw,

    # シグナル
    signal (receive) peer=unconfined,

    # 子プロセスのプロファイル (hat)
    ^worker {
        #include <abstractions/base>
        #include <abstractions/nameservice>
        
        /var/www/** r,
        /var/log/nginx/** w,
        /var/lib/nginx/** rw,
        network inet stream,
        network inet6 stream,
    }
}

5.5 アクセス権限修飾子

AppArmor パーミッション修飾子:

ファイルアクセス:
  r  - 読み取り (read)
  w  - 書き込み (write)
  a  - 追記 (append)
  k  - ファイルロック (lock)
  l  - ハードリンク (link)
  m  - メモリマップ実行 (memory map for execution)

実行:
  ix - inherit (親プロファイルを継承して実行)
  cx - child (子プロファイルで実行)
  px - profile (指定されたプロファイルで実行)
  ux - unconfined (制限なしで実行)
  Cx - cx + 子プロファイルが無い場合はアクセス拒否
  Px - px + プロファイルが無い場合はアクセス拒否
  Ux - ux + (安全でないため非推奨)

owner 条件:
  owner /path/to/file rw,   # ファイルの所有者のみrw可能

deny:
  deny /path/to/secret r,   # 明示的に拒否(ログにも記録しない)
  audit deny /path r,       # 明示的に拒否してログに記録

5.6 Tunables と Abstractions

# Tunables: 変数定義ファイル
# /etc/apparmor.d/tunables/global

@{HOME}=/home/*/ /root/
@{HOMEDIRS}=/home/ /root/
@{multiarch}=*-linux-gnu*
@{PROC}=/proc/
@{run}=/run/ /var/run/
@{sys}=/sys/
@{etc_ro}=/etc/

# プロファイル内での使用例
/usr/sbin/myapp {
    @{HOME}/.config/myapp/** r,
    @{PROC}/sys/kernel/** r,
}

# Abstractions: 共通ルールセット
# /etc/apparmor.d/abstractions/base
#  - 基本的なシステムファイルアクセス
#  - /etc/ld.so.cache, /usr/lib/**, libc 等

# /etc/apparmor.d/abstractions/nameservice
#  - DNS, NIS, LDAP の名前解決
#  - /etc/resolv.conf, /etc/hosts, /etc/nsswitch.conf

# /etc/apparmor.d/abstractions/openssl
#  - OpenSSL ライブラリとCA証明書
#  - /etc/ssl/**, /usr/share/ca-certificates/**

# /etc/apparmor.d/abstractions/apache2-common
#  - Apache2 共通設定
#  - モジュール, ログ, コンテンツ

# /etc/apparmor.d/abstractions/php
#  - PHP 関連ファイル

# /etc/apparmor.d/abstractions/mysql
#  - MySQL/MariaDB クライアントアクセス

# /etc/apparmor.d/abstractions/python
#  - Python インタプリタとライブラリ

6. AppArmor 実践的設定

6.1 aa-genprof によるプロファイル生成

# aa-genprof はアプリケーションの動作を監視してプロファイルを自動生成する

# 1. aa-genprof を起動
$ sudo aa-genprof /usr/sbin/nginx

# 2. 別のターミナルでアプリケーションを操作
# (Webブラウザでアクセス、設定のリロードなど)
$ sudo systemctl start nginx
$ curl http://localhost/
$ curl http://localhost/test.html
$ sudo nginx -s reload

# 3. aa-genprof に戻り、'S'でスキャン
#    ログから検出されたアクセスを一つずつ確認
#    Allow / Deny / Inherit / (other options) を選択

Reading log entries from /var/log/syslog.
Updating AppArmor profiles in /etc/apparmor.d.

Profile:  /usr/sbin/nginx
Execute:  /usr/sbin/nginx
Severity: unknown

(I)nherit / (C)hild / (P)rofile / (N)amed / (U)nconfined / (X) ix On / (D)eny / Abo(r)t / (F)inish

# 4. 'F'でプロファイルを保存

# 生成されたプロファイルは /etc/apparmor.d/ に保存される

6.2 aa-logprof によるプロファイル更新

# aa-logprof は既存プロファイルの拒否ログを解析して更新を提案する

# 1. Complainモードでプロファイルを運用
$ sudo aa-complain /usr/sbin/nginx

# 2. アプリケーションを通常通り使用
# 3. ログを解析してプロファイルを更新
$ sudo aa-logprof

Reading log entries from /var/log/syslog.
Updating AppArmor profiles in /etc/apparmor.d.

Profile:  /usr/sbin/nginx
Path:     /etc/nginx/modules-enabled/
New Mode: r
Severity: 4

 [1 - #include <abstractions/base>]
  2 - /etc/nginx/modules-enabled/ r,

(A)llow / [(D)eny] / (I)gnore / (G)lob / Glob with (E)xtension / (N)ew / Audi(t) / (O)wner / Abo(r)t / (F)inish

# 'A'で許可ルールを追加
# 繰り返し選択後、'S'で保存

# 4. Enforceモードに変更
$ sudo aa-enforce /usr/sbin/nginx

6.3 プロファイルの手動作成例

Nginx 用の完全なプロファイル

# /etc/apparmor.d/usr.sbin.nginx

#include <tunables/global>

/usr/sbin/nginx {
    #include <abstractions/base>
    #include <abstractions/nameservice>
    #include <abstractions/openssl>
    #include <abstractions/ssl_certs>

    # Capabilities
    capability net_bind_service,
    capability setuid,
    capability setgid,
    capability dac_override,
    capability dac_read_search,
    capability chown,

    # ネットワーク
    network inet stream,
    network inet6 stream,
    network inet dgram,
    network inet6 dgram,

    # 実行ファイル自身
    /usr/sbin/nginx rmix,

    # 設定ファイル
    /etc/nginx/ r,
    /etc/nginx/** r,
    /etc/nginx/conf.d/ r,
    /etc/nginx/conf.d/** r,
    /etc/nginx/sites-available/ r,
    /etc/nginx/sites-available/** r,
    /etc/nginx/sites-enabled/ r,
    /etc/nginx/sites-enabled/** r,
    /etc/nginx/modules-available/ r,
    /etc/nginx/modules-available/** r,
    /etc/nginx/modules-enabled/ r,
    /etc/nginx/modules-enabled/** r,
    /etc/nginx/snippets/ r,
    /etc/nginx/snippets/** r,

    # MIME types
    /etc/nginx/mime.types r,

    # Webコンテンツ
    /var/www/ r,
    /var/www/** r,

    # PHP-FPM ソケット
    /run/php/php*-fpm.sock rw,

    # ログ
    /var/log/nginx/ rw,
    /var/log/nginx/** rw,
    /var/log/nginx/access.log w,
    /var/log/nginx/error.log w,

    # PID & ソケット
    /run/nginx.pid rw,
    /run/nginx/ rw,
    /run/nginx/** rw,

    # 一時ファイル / キャッシュ
    /var/lib/nginx/ r,
    /var/lib/nginx/** rw,
    /var/cache/nginx/ rw,
    /var/cache/nginx/** rw,

    # 共有メモリ
    /dev/shm/ r,
    /dev/shm/nginx_* rw,

    # /proc
    owner @{PROC}/@{pid}/fd/ r,
    owner @{PROC}/@{pid}/status r,
    @{PROC}/sys/net/core/somaxconn r,
    @{PROC}/sys/kernel/random/boot_id r,
    @{PROC}/cpuinfo r,

    # SSL/TLS
    /etc/letsencrypt/ r,
    /etc/letsencrypt/** r,
    /etc/ssl/ r,
    /etc/ssl/** r,

    # モジュール
    /usr/lib/nginx/modules/ r,
    /usr/lib/nginx/modules/** mr,
    /usr/share/nginx/ r,
    /usr/share/nginx/** r,

    # シグナル処理
    signal (receive) peer=unconfined,
    signal (send,receive) peer=/usr/sbin/nginx,

    # deny ルール(セキュリティ強化)
    deny /etc/shadow r,
    deny /etc/passwd w,
    deny /root/** rw,
    deny @{HOME}/.ssh/** rw,
}

Node.js アプリケーション用プロファイル

# /etc/apparmor.d/opt.myapp.server.js

#include <tunables/global>

profile myapp /usr/bin/node /opt/myapp/server.js {
    #include <abstractions/base>
    #include <abstractions/nameservice>

    # ネットワーク
    network inet stream,
    network inet6 stream,
    network inet dgram,

    # Node.js 実行ファイル
    /usr/bin/node rix,

    # アプリケーションファイル
    /opt/myapp/ r,
    /opt/myapp/** r,
    /opt/myapp/node_modules/** mr,

    # データディレクトリ
    /opt/myapp/data/ rw,
    /opt/myapp/data/** rw,

    # ログ
    /var/log/myapp/ rw,
    /var/log/myapp/** w,

    # 一時ファイル
    /tmp/myapp-* rw,
    /tmp/v8-compile-cache-*/ rw,
    /tmp/v8-compile-cache-*/** rw,

    # PID
    /run/myapp.pid rw,

    # /proc
    owner @{PROC}/@{pid}/fd/ r,
    owner @{PROC}/@{pid}/stat r,
    owner @{PROC}/@{pid}/status r,
    @{PROC}/sys/kernel/random/uuid r,
    @{PROC}/version r,
    @{PROC}/cpuinfo r,
    @{PROC}/meminfo r,

    # DNS
    /etc/resolv.conf r,
    /etc/hosts r,
    /etc/nsswitch.conf r,

    # SSL
    /etc/ssl/certs/ r,
    /etc/ssl/certs/** r,
    /usr/share/ca-certificates/ r,
    /usr/share/ca-certificates/** r,

    # deny
    deny /etc/shadow r,
    deny /etc/sudoers r,
    deny /root/** rw,
    deny @{HOME}/.ssh/** rw,
    deny /proc/kcore r,
}

6.4 プロファイルの管理コマンド

# プロファイルの読み込み
$ sudo apparmor_parser -r /etc/apparmor.d/usr.sbin.nginx

# プロファイルの追加読み込み(置き換えなし)
$ sudo apparmor_parser -a /etc/apparmor.d/usr.sbin.nginx

# プロファイルの削除(アンロード)
$ sudo apparmor_parser -R /etc/apparmor.d/usr.sbin.nginx

# すべてのプロファイルの再読み込み
$ sudo systemctl reload apparmor

# AppArmor サービスの管理
$ sudo systemctl status apparmor
$ sudo systemctl start apparmor
$ sudo systemctl stop apparmor
$ sudo systemctl restart apparmor

# プロファイルのデバッグ(構文チェック)
$ sudo apparmor_parser -p /etc/apparmor.d/usr.sbin.nginx

# キャッシュの管理
$ sudo apparmor_parser --cache-loc /var/cache/apparmor/ -r /etc/apparmor.d/usr.sbin.nginx

# プロファイルのステータス詳細
$ sudo aa-status --verbose

7. その他のLSM: Smack, TOMOYO, Yama

7.1 Smack (Simplified Mandatory Access Control Kernel)

Smackは、SELinuxのような複雑なポリシーを必要としない、シンプルなラベルベースのMACシステムである。組み込みシステムやIoTデバイスで特に有用である。

Smack の基本概念:
  - すべてのオブジェクトとプロセスにラベル(テキスト文字列)を付与
  - シンプルなルールでアクセスを制御
  - SELinuxより設定が容易

ラベルの例:
  floor  - 基本的な読み取りアクセス
  hat    - 基本的な読み書きアクセス  
  star   - すべてへの読み取りアクセス
  _      - Webサーバー用ラベル
# Smack の有効化(カーネルブートパラメータ)
# GRUB_CMDLINE_LINUX="security=smack"

# ラベルの確認
$ cat /proc/self/attr/current
floor

# ファイルのラベル設定
$ sudo attr -S -s SMACK64 -V "webdata" /var/www/html/index.html

# ラベルの確認
$ getfattr -n security.SMACK64 /var/www/html/index.html

# Smack アクセスルールの設定
# /sys/fs/smackfs/load2
# 形式: subject_label object_label access_type

# "webserver" ラベルが "webdata" ラベルのファイルを読めるようにする
$ echo "webserver webdata r" | sudo tee /sys/fs/smackfs/load2

# "admin" ラベルが "webdata" ラベルのファイルを読み書きできるようにする
$ echo "admin webdata rw" | sudo tee /sys/fs/smackfs/load2

# ルールの確認
$ cat /sys/fs/smackfs/load2
webserver webdata r
admin webdata rw

# プロセスのラベル変更
$ echo "newlabel" | sudo tee /proc/self/attr/current

# Smack のビルトインルール
# _ (floor): すべてのプロセスが読み取り可能
# ^ (hat): すべてのプロセスが読み書き可能
# * (star): すべてに対して読み取り可能

7.2 TOMOYO Linux

TOMOYO Linuxは、日本(NTTデータ)で開発されたパスベースのMACシステムである。「学習モード」が特徴的で、アプリケーションの動作を自動的に学習してポリシーを生成できる。

TOMOYO Linux の特徴:
  - パスベースのアクセス制御(AppArmorに類似)
  - 学習モード(自動ポリシー生成)
  - ドメイン遷移による制御
  - 設定が比較的容易

ドメインの概念:
  <kernel>
  ├── /usr/sbin/sshd
  │   └── /bin/bash
  │       └── /usr/bin/vi
  └── /usr/sbin/httpd
      └── /usr/bin/php
# TOMOYO Linux の有効化
# GRUB_CMDLINE_LINUX="security=tomoyo"

# ポリシーエディタ(対話型ツール)
$ sudo /usr/sbin/tomoyo-editpolicy

# 学習モードの有効化
$ echo "use_profile 1" | sudo tee /sys/kernel/security/tomoyo/domain_policy

# プロファイルの種類:
#   0 - disabled (無効)
#   1 - learning (学習)
#   2 - permissive (許可 + ログ)
#   3 - enforcing (強制)

# ドメインポリシーの確認
$ sudo cat /sys/kernel/security/tomoyo/domain_policy

# ポリシーの例
# /sys/kernel/security/tomoyo/domain_policy
<kernel> /usr/sbin/httpd
use_profile 3
file read /var/www/html/\*
file read /etc/httpd/\*
file write /var/log/httpd/\*
network inet stream listen 80
network inet stream listen 443

# 例外ポリシー
$ sudo cat /sys/kernel/security/tomoyo/exception_policy

7.3 Yama

Yamaは、ptrace(プロセストレース)を制限するための軽量なLSMである。他のメジャーLSMとスタック可能で、追加のプロセス保護を提供する。

# Yama の ptrace スコープ確認
$ cat /proc/sys/kernel/yama/ptrace_scope
1

# ptrace_scope の値:
# 0 - classic (制限なし、従来の動作)
# 1 - restricted (親プロセスのみ ptrace 可能) ← Ubuntu デフォルト
# 2 - admin-only (CAP_SYS_PTRACE を持つプロセスのみ)
# 3 - no-attach (完全に ptrace を無効化)

# 一時的な変更
$ sudo sysctl kernel.yama.ptrace_scope=2

# 永続的な変更
$ echo "kernel.yama.ptrace_scope = 2" | sudo tee /etc/sysctl.d/10-ptrace.conf
$ sudo sysctl -p /etc/sysctl.d/10-ptrace.conf

# Yama が有効か確認
$ cat /sys/kernel/security/lsm
lockdown,capability,yama,apparmor

# ptrace の実験
# scope=1 の場合、gdb は子プロセスのみデバッグ可能
$ gdb -p $(pgrep -f "some_process")  # 親子関係がないと失敗

# 特定のプロセスに ptrace 許可を与える
# PR_SET_PTRACER を使用(プログラムから)
# prctl(PR_SET_PTRACER, debugger_pid);
/* Yama のptrace制限を活用したコード例 */
#include <sys/prctl.h>

int main() {
    /* 特定のプロセスからのptrace のみ許可 */
    prctl(PR_SET_PTRACER, getppid(), 0, 0, 0);
    
    /* ptrace を完全に拒否 */
    prctl(PR_SET_PTRACER, PR_SET_PTRACER_ANY, 0, 0, 0);
    /* または */
    prctl(PR_SET_DUMPABLE, 0);  /* コアダンプも無効化 */
    
    /* ... アプリケーションの処理 ... */
    return 0;
}

8. Landlock: 非特権サンドボックス

8.1 Landlockの概要

Landlock は Linux 5.13 で導入された新しいLSMで、非特権プロセスがサンドボックスを作成できる。これまでのLSMはすべて管理者権限が必要だったが、Landlockは一般ユーザーでも利用可能である。

Landlock の特徴:
  - 非特権プロセスがサンドボックスを作成可能
  - プロセスとその子孫に対してファイルシステムアクセスを制限
  - ネストされたサンドボックスをサポート
  - Linux 5.13+ で利用可能
  - Linux 5.19+ でネットワーク制限も可能

対応バージョン:
  - ABI v1 (Linux 5.13): 基本的なファイルシステム制限
  - ABI v2 (Linux 5.19): ファイル参照制限の改善
  - ABI v3 (Linux 6.2):  ファイルの切り詰め(truncate)制限
  - ABI v4 (Linux 6.7):  ネットワーク制限 (TCP bind/connect)

8.2 Landlock プログラミング

/* landlock_sandbox.c - Landlock サンドボックスの例 */

#define _GNU_SOURCE
#include <fcntl.h>
#include <linux/landlock.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/prctl.h>
#include <sys/syscall.h>
#include <unistd.h>
#include <errno.h>

/* Landlock のシステムコールラッパー */
#ifndef landlock_create_ruleset
static inline int
landlock_create_ruleset(const struct landlock_ruleset_attr *attr,
                        size_t size, __u32 flags)
{
    return syscall(__NR_landlock_create_ruleset, attr, size, flags);
}
#endif

#ifndef landlock_add_rule
static inline int
landlock_add_rule(int ruleset_fd,
                  enum landlock_rule_type rule_type,
                  const void *rule_attr, __u32 flags)
{
    return syscall(__NR_landlock_add_rule, ruleset_fd,
                   rule_type, rule_attr, flags);
}
#endif

#ifndef landlock_restrict_self
static inline int
landlock_restrict_self(int ruleset_fd, __u32 flags)
{
    return syscall(__NR_landlock_restrict_self, ruleset_fd, flags);
}
#endif

/* ファイルシステムの読み取りアクセスを許可するヘルパー */
int allow_read_path(int ruleset_fd, const char *path)
{
    int fd = open(path, O_PATH | O_CLOEXEC);
    if (fd < 0) {
        perror("open");
        return -1;
    }

    struct landlock_path_beneath_attr path_beneath = {
        .allowed_access =
            LANDLOCK_ACCESS_FS_READ_FILE |
            LANDLOCK_ACCESS_FS_READ_DIR |
            LANDLOCK_ACCESS_FS_EXECUTE,
        .parent_fd = fd,
    };

    int ret = landlock_add_rule(ruleset_fd,
                                 LANDLOCK_RULE_PATH_BENEATH,
                                 &path_beneath, 0);
    close(fd);
    return ret;
}

/* ファイルシステムの読み書きアクセスを許可するヘルパー */
int allow_readwrite_path(int ruleset_fd, const char *path)
{
    int fd = open(path, O_PATH | O_CLOEXEC);
    if (fd < 0) {
        perror("open");
        return -1;
    }

    struct landlock_path_beneath_attr path_beneath = {
        .allowed_access =
            LANDLOCK_ACCESS_FS_READ_FILE |
            LANDLOCK_ACCESS_FS_READ_DIR |
            LANDLOCK_ACCESS_FS_WRITE_FILE |
            LANDLOCK_ACCESS_FS_MAKE_REG |
            LANDLOCK_ACCESS_FS_MAKE_DIR |
            LANDLOCK_ACCESS_FS_REMOVE_FILE |
            LANDLOCK_ACCESS_FS_REMOVE_DIR,
        .parent_fd = fd,
    };

    int ret = landlock_add_rule(ruleset_fd,
                                 LANDLOCK_RULE_PATH_BENEATH,
                                 &path_beneath, 0);
    close(fd);
    return ret;
}

int main(int argc, char *argv[])
{
    /* 1. Landlock ABI バージョンの確認 */
    int abi = landlock_create_ruleset(NULL, 0,
                                       LANDLOCK_CREATE_RULESET_VERSION);
    if (abi < 0) {
        perror("Landlock is not supported");
        return 1;
    }
    printf("Landlock ABI version: %d\n", abi);

    /* 2. ルールセットの作成 */
    struct landlock_ruleset_attr ruleset_attr = {
        .handled_access_fs =
            LANDLOCK_ACCESS_FS_READ_FILE |
            LANDLOCK_ACCESS_FS_READ_DIR |
            LANDLOCK_ACCESS_FS_WRITE_FILE |
            LANDLOCK_ACCESS_FS_EXECUTE |
            LANDLOCK_ACCESS_FS_MAKE_REG |
            LANDLOCK_ACCESS_FS_MAKE_DIR |
            LANDLOCK_ACCESS_FS_REMOVE_FILE |
            LANDLOCK_ACCESS_FS_REMOVE_DIR |
            LANDLOCK_ACCESS_FS_MAKE_SYM |
            LANDLOCK_ACCESS_FS_REFER,
    };

    int ruleset_fd = landlock_create_ruleset(&ruleset_attr,
                                              sizeof(ruleset_attr), 0);
    if (ruleset_fd < 0) {
        perror("landlock_create_ruleset");
        return 1;
    }

    /* 3. ルールの追加 */
    /* /usr と /lib は読み取りのみ許可 */
    if (allow_read_path(ruleset_fd, "/usr") < 0)
        return 1;
    if (allow_read_path(ruleset_fd, "/lib") < 0)
        return 1;
    if (allow_read_path(ruleset_fd, "/lib64") < 0)
        return 1;
    if (allow_read_path(ruleset_fd, "/etc") < 0)
        return 1;

    /* /tmp は読み書き許可 */
    if (allow_readwrite_path(ruleset_fd, "/tmp") < 0)
        return 1;

    /* 4. No New Privileges フラグの設定 */
    if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) {
        perror("prctl(NO_NEW_PRIVS)");
        return 1;
    }

    /* 5. サンドボックスの適用 */
    if (landlock_restrict_self(ruleset_fd, 0)) {
        perror("landlock_restrict_self");
        return 1;
    }
    close(ruleset_fd);

    printf("Landlock sandbox activated!\n");

    /* 6. サンドボックス内でコマンド実行 */
    if (argc > 1) {
        execvp(argv[1], &argv[1]);
        perror("execvp");
        return 1;
    }

    /* テスト: /etc/passwd は読める */
    FILE *f = fopen("/etc/passwd", "r");
    if (f) {
        printf("[OK] /etc/passwd is readable\n");
        fclose(f);
    } else {
        printf("[BLOCKED] /etc/passwd: %s\n", strerror(errno));
    }

    /* テスト: /home にはアクセスできない */
    f = fopen("/home/user/secret.txt", "r");
    if (f) {
        printf("[OK] /home/user/secret.txt is readable\n");
        fclose(f);
    } else {
        printf("[BLOCKED] /home/user/secret.txt: %s\n",
               strerror(errno));
    }

    /* テスト: /tmp には書き込める */
    f = fopen("/tmp/landlock_test.txt", "w");
    if (f) {
        fprintf(f, "Landlock sandbox test\n");
        fclose(f);
        printf("[OK] /tmp/landlock_test.txt is writable\n");
    } else {
        printf("[BLOCKED] /tmp/landlock_test.txt: %s\n",
               strerror(errno));
    }

    return 0;
}
# コンパイルと実行
$ gcc -o landlock_sandbox landlock_sandbox.c
$ ./landlock_sandbox
Landlock ABI version: 4
Landlock sandbox activated!
[OK] /etc/passwd is readable
[BLOCKED] /home/user/secret.txt: Permission denied
[OK] /tmp/landlock_test.txt is writable

# サンドボックス内でシェルを起動
$ ./landlock_sandbox /bin/bash
Landlock sandbox activated!
$ ls /etc/passwd     # 成功
$ ls /home/          # Permission denied
$ cat /root/.bashrc  # Permission denied

8.3 Landlock のネットワーク制限 (ABI v4, Linux 6.7+)

/* Landlock ネットワーク制限の例 */

/* ネットワークルールセットの作成 */
struct landlock_ruleset_attr ruleset_attr = {
    .handled_access_fs = /* ... */,
    .handled_access_net =
        LANDLOCK_ACCESS_NET_BIND_TCP |
        LANDLOCK_ACCESS_NET_CONNECT_TCP,
};

int ruleset_fd = landlock_create_ruleset(&ruleset_attr,
                                          sizeof(ruleset_attr), 0);

/* TCP ポート 80 への接続を許可 */
struct landlock_net_port_attr net_port = {
    .allowed_access = LANDLOCK_ACCESS_NET_CONNECT_TCP,
    .port = 80,
};
landlock_add_rule(ruleset_fd, LANDLOCK_RULE_NET_PORT,
                  &net_port, 0);

/* TCP ポート 443 への接続を許可 */
net_port.port = 443;
landlock_add_rule(ruleset_fd, LANDLOCK_RULE_NET_PORT,
                  &net_port, 0);

/* TCP ポート 8080 でのバインドを許可 */
struct landlock_net_port_attr bind_port = {
    .allowed_access = LANDLOCK_ACCESS_NET_BIND_TCP,
    .port = 8080,
};
landlock_add_rule(ruleset_fd, LANDLOCK_RULE_NET_PORT,
                  &bind_port, 0);

/* サンドボックス適用 */
prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
landlock_restrict_self(ruleset_fd, 0);

/* この時点で:
 * - TCP 80, 443 への接続のみ許可
 * - TCP 8080 でのバインドのみ許可
 * - その他のネットワーク操作は拒否
 */

9. LSMの比較

9.1 機能比較マトリックス

┌──────────────────┬──────────┬──────────┬──────────┬──────────┬──────────┬──────────┐
│ 機能              │ SELinux  │ AppArmor │ Smack    │ TOMOYO   │ Yama     │ Landlock │
├──────────────────┼──────────┼──────────┼──────────┼──────────┼──────────┼──────────┤
│ アクセス制御方式   │ ラベル   │ パス     │ ラベル   │ パス     │ ptrace   │ パス     │
│ MACレベル         │ 高       │ 中       │ 低-中    │ 中       │ 限定的   │ 限定的   │
│ 設定の複雑さ      │ 高       │ 低-中    │ 低       │ 低-中    │ 最低     │ 中       │
│ TE (型強制)       │ ○       │ ×       │ △       │ ×       │ ×       │ ×       │
│ RBAC             │ ○       │ ×       │ ×       │ ×       │ ×       │ ×       │
│ MLS/MCS          │ ○       │ ×       │ △       │ ×       │ ×       │ ×       │
│ パスベース        │ ×       │ ○       │ ×       │ ○       │ ×       │ ○       │
│ ネットワーク制御   │ ○       │ ○       │ ○       │ △       │ ×       │ ○(v4)  │
│ 学習モード        │ ×       │ ○       │ ×       │ ○       │ ×       │ ×       │
│ 非特権利用        │ ×       │ ×       │ ×       │ ×       │ ×       │ ○       │
│ スタック可能      │ △       │ △       │ △       │ △       │ ○       │ ○       │
│ コンテナ対応      │ ○       │ ○       │ △       │ △       │ ○       │ ○       │
│ 主要ディストリ    │ RHEL系   │ Ubuntu系 │ Tizen    │ -        │ 多数     │ 多数     │
│ メンテナンス      │ 活発     │ 活発     │ 中程度   │ 限定的   │ 安定     │ 活発     │
└──────────────────┴──────────┴──────────┴──────────┴──────────┴──────────┴──────────┘

○: 完全対応  △: 部分的対応  ×: 非対応

9.2 用途別の推奨LSM

用途                              推奨LSM           理由
──────────────────────────────    ─────────         ─────
エンタープライズサーバー           SELinux            最も包括的なMAC
デスクトップ / 開発マシン          AppArmor           設定が容易
IoT / 組み込みデバイス            Smack              軽量でシンプル
セキュリティ学習 / 研究            TOMOYO             学習モードが優秀
コンテナ環境                      SELinux + Landlock  多層防御
プロセス分離の強化                Yama               ptraceの制限
非特権サンドボックス              Landlock            root不要
政府 / 軍事 (MLS要件)            SELinux (MLS)       唯一のMLS対応
Web アプリケーション              AppArmor / SELinux  パス/ラベルベース

10. LSMスタッキング

10.1 LSMスタッキングの概要

Linux カーネルは、複数のLSMを同時に利用する「LSMスタッキング」をサポートしている。

従来: メジャーLSMは1つしか使えない
  SELinux OR AppArmor OR Smack

現在 (Linux 5.1+): 制限付きスタッキング
  マイナーLSM (Yama, LoadPin, Lockdown) + メジャーLSM 1つ

将来 (開発中): 完全スタッキング
  複数のメジャーLSMを同時利用可能
# 現在アクティブなLSMの確認
$ cat /sys/kernel/security/lsm
lockdown,capability,yama,apparmor

# または
$ cat /sys/kernel/security/lsm
lockdown,capability,yama,selinux

# ブートパラメータでLSMスタックを設定
# /etc/default/grub
GRUB_CMDLINE_LINUX="lsm=lockdown,capability,yama,apparmor,landlock"

# Landlock はマイナーLSMとしてスタック可能
# 最終的な構成例:
#   lockdown + capability + yama + apparmor + landlock
#   lockdown + capability + yama + selinux + landlock

10.2 スタッキングの判定フロー

アクセス要求
    │
    v
Capability LSM ──── チェック ──── 拒否 → アクセス拒否
    │ 許可
    v
Lockdown LSM ───── チェック ──── 拒否 → アクセス拒否
    │ 許可
    v
Yama LSM ────────── チェック ──── 拒否 → アクセス拒否
    │ 許可
    v
メジャーLSM ─────── チェック ──── 拒否 → アクセス拒否
(SELinux/AppArmor)
    │ 許可
    v
Landlock LSM ───── チェック ──── 拒否 → アクセス拒否
    │ 許可
    v
アクセス許可

※ すべてのLSMが許可した場合のみアクセスが許可される

11. 実践例: Webサーバー、コンテナ、サービスのセキュリティ強化

11.1 Apache HTTP Server の SELinux による保護

# === Apache HTTP Server + SELinux ===

# 1. Apache のSELinuxコンテキスト確認
$ ps -eZ | grep httpd
system_u:system_r:httpd_t:s0     5678 ?  00:00:01 httpd

# 2. ドキュメントルートの設定
# 標準パスの場合はラベルが自動設定される
$ ls -lZd /var/www/html
drwxr-xr-x. root root system_u:object_r:httpd_sys_content_t:s0 /var/www/html

# カスタムパスの場合
$ sudo mkdir -p /opt/webapp/public
$ sudo semanage fcontext -a -t httpd_sys_content_t "/opt/webapp/public(/.*)?"
$ sudo restorecon -Rv /opt/webapp/public/

# 3. CGI/動的コンテンツの設定
$ sudo semanage fcontext -a -t httpd_sys_script_exec_t "/opt/webapp/cgi-bin(/.*)?"
$ sudo restorecon -Rv /opt/webapp/cgi-bin/

# 4. 書き込み可能なディレクトリ(アップロード用)
$ sudo semanage fcontext -a -t httpd_sys_rw_content_t "/opt/webapp/uploads(/.*)?"
$ sudo restorecon -Rv /opt/webapp/uploads/

# 5. SSL証明書
$ ls -lZ /etc/pki/tls/certs/
-rw-r--r--. root root system_u:object_r:cert_t:s0 ca-bundle.crt
-rw-------. root root system_u:object_r:cert_t:s0 localhost.crt

# 6. ネットワーク接続の許可
# データベースへの接続
$ sudo setsebool -P httpd_can_network_connect_db on

# 外部APIへの接続
$ sudo setsebool -P httpd_can_network_connect on

# メール送信
$ sudo setsebool -P httpd_can_sendmail on

# 7. カスタムポートの使用
$ sudo semanage port -a -t http_port_t -p tcp 8443

# 8. ホームディレクトリのWebアクセス(UserDir)
$ sudo setsebool -P httpd_enable_homedirs on
$ sudo setsebool -P httpd_read_user_content on

# 9. PHP-FPMとの連携
# Unix ソケット経由の場合、追加設定は通常不要
# TCP ポート経由の場合
$ sudo setsebool -P httpd_can_network_connect on

11.2 Docker/Podman コンテナの SELinux による保護

# === コンテナセキュリティ (SELinux) ===

# 1. コンテナランタイムのSELinuxサポート確認
$ docker info | grep -i security
Security Options: seccomp selinux

$ podman info | grep -i security
security: 
  apparmorEnabled: false
  rootless: true
  seccompEnabled: true
  selinuxEnabled: true

# 2. コンテナのSELinuxラベル確認
$ podman run -d --name web nginx
$ podman inspect web | jq '.[0].ProcessLabel'
"system_u:system_r:container_t:s0:c123,c456"

$ podman inspect web | jq '.[0].MountLabel'
"system_u:object_r:container_file_t:s0:c123,c456"

# 3. ホストボリュームのマウント
# :z オプション - 共有ラベル (複数コンテナから共有)
$ podman run -v /opt/data:/data:z nginx

# :Z オプション - プライベートラベル (単一コンテナ専用)
$ podman run -v /opt/data:/data:Z nginx

# 4. SELinux ラベルの手動確認
$ ls -lZ /opt/data/
# :z の場合: container_file_t:s0
# :Z の場合: container_file_t:s0:c123,c456 (特定のMCSカテゴリ)

# 5. コンテナのSELinux無効化 (推奨されない)
$ podman run --security-opt label=disable nginx

# 6. カスタムSELinuxラベル
$ podman run --security-opt label=type:svirt_apache_t nginx

# 7. Read-only ファイルシステム + SELinux
$ podman run --read-only \
    --security-opt label=type:container_t \
    -v /opt/data:/data:Z,ro \
    nginx

# 8. Podman の rootless コンテナ + SELinux
$ podman run --userns=keep-id \
    -v ~/webapp:/app:Z \
    node:18 node /app/server.js

11.3 systemd サービスの AppArmor による保護

# === systemd サービス + AppArmor ===

# 1. カスタムサービスの作成
$ cat /etc/systemd/system/myapi.service
[Unit]
Description=My API Server
After=network.target

[Service]
Type=simple
User=myapi
Group=myapi
WorkingDirectory=/opt/myapi
ExecStart=/usr/bin/node /opt/myapi/server.js
Restart=always
RestartSec=5

# AppArmor プロファイルの適用
AppArmorProfile=/opt/myapi/server.js

# 追加のセキュリティ設定
NoNewPrivileges=true
ProtectSystem=strict
ProtectHome=true
PrivateTmp=true
ReadWritePaths=/var/log/myapi /opt/myapi/data

[Install]
WantedBy=multi-user.target

# 2. AppArmor プロファイルの作成
$ cat /etc/apparmor.d/opt.myapi.server.js
#include <tunables/global>

/opt/myapi/server.js {
    #include <abstractions/base>
    #include <abstractions/nameservice>

    /usr/bin/node rix,
    
    /opt/myapi/ r,
    /opt/myapi/** r,
    /opt/myapi/node_modules/** mr,
    /opt/myapi/data/ rw,
    /opt/myapi/data/** rw,
    
    /var/log/myapi/ rw,
    /var/log/myapi/** w,
    
    /tmp/ r,
    owner /tmp/v8-* rw,
    
    network inet stream,
    network inet6 stream,
    
    deny /etc/shadow r,
    deny /root/** rw,
}

# 3. プロファイルのロードと有効化
$ sudo apparmor_parser -r /etc/apparmor.d/opt.myapi.server.js
$ sudo aa-enforce /etc/apparmor.d/opt.myapi.server.js

# 4. サービスの起動
$ sudo systemctl daemon-reload
$ sudo systemctl start myapi
$ sudo systemctl status myapi

11.4 PostgreSQL の SELinux による保護

# === PostgreSQL + SELinux ===

# 1. PostgreSQL のSELinuxドメイン確認
$ ps -eZ | grep postgres
system_u:system_r:postgresql_t:s0  5432 ?  00:00:01 postgres

# 2. データディレクトリのコンテキスト
$ ls -lZd /var/lib/pgsql/data/
drwx------. postgres postgres system_u:object_r:postgresql_db_t:s0 /var/lib/pgsql/data/

# 3. カスタムデータディレクトリ
$ sudo semanage fcontext -a -t postgresql_db_t "/opt/pgdata(/.*)?"
$ sudo restorecon -Rv /opt/pgdata/

# 4. PostgreSQL のログ
$ ls -lZ /var/log/postgresql/
-rw-------. postgres postgres system_u:object_r:postgresql_log_t:s0 postgresql.log

# 5. カスタムポート
$ sudo semanage port -a -t postgresql_port_t -p tcp 5433

# 6. sepgsql (SELinux + PostgreSQL 行レベルセキュリティ)
# postgresql.conf で有効化
# shared_preload_libraries = 'sepgsql'

# sepgsql のラベル設定例
# CREATE TABLE sensitive_data (
#     id SERIAL,
#     data TEXT,
#     security_label TEXT
# );
# SECURITY LABEL ON TABLE sensitive_data IS 'system_u:object_r:sepgsql_table_t:s0';

12. トラブルシューティング

12.1 SELinux のトラブルシューティング

# === SELinux 拒否の診断 ===

# 1. 拒否ログの確認 (audit.log)
$ sudo ausearch -m avc --start recent
----
time->Thu Apr 10 10:30:00 2026
type=AVC msg=audit(1712742600.000:5678): avc:  denied  { read } for  pid=1234 comm="httpd" name="config.yml" dev="sda1" ino=1234567 scontext=system_u:system_r:httpd_t:s0 tcontext=system_u:object_r:default_t:s0 tclass=file permissive=0

# 2. AVC メッセージの解読
# avc: denied { read }           → 読み取りが拒否された
# comm="httpd"                   → httpd プロセスが
# name="config.yml"              → config.yml ファイルに対して
# scontext=...httpd_t...         → ソースコンテキスト (プロセスのタイプ)
# tcontext=...default_t...       → ターゲットコンテキスト (ファイルのタイプ)
# tclass=file                    → オブジェクトクラス

# 3. sealert による詳細分析 (setroubleshoot-server パッケージ)
$ sudo sealert -a /var/log/audit/audit.log
found 1 alerts in /var/log/audit/audit.log
--------------------------------------------------------------------------------
SELinux is preventing httpd from read access on the file config.yml.

*****  Plugin catchall_labels (83.8 confidence) suggests  *******************
If you want to allow httpd to have read access on the config.yml file
Then you need to change the label on config.yml
Do
# semanage fcontext -a -t httpd_sys_content_t '/opt/myapp/config.yml'
# restorecon -v '/opt/myapp/config.yml'

# 4. Permissive モードでの問題切り分け
# 特定のドメインのみPermissiveに設定
$ sudo semanage permissive -a httpd_t
# → httpd_t ドメインのみ許可(ログは記録される)

# Permissiveドメインの確認
$ sudo semanage permissive -l
Builtin Permissive Types:
Customized Permissive Types:
httpd_t

# Permissiveの解除
$ sudo semanage permissive -d httpd_t

# 5. 一般的な問題と解決策

# 問題: ファイルのコンテキストが間違っている
$ ls -lZ /opt/myapp/config.yml
-rw-r--r--. root root unconfined_u:object_r:default_t:s0 config.yml
# 解決: restorecon でコンテキストを修正
$ sudo restorecon -v /opt/myapp/config.yml

# 問題: semanage fcontext にルールがない
# 解決: ルールを追加
$ sudo semanage fcontext -a -t httpd_sys_content_t "/opt/myapp(/.*)?"
$ sudo restorecon -Rv /opt/myapp/

# 問題: ブーリアンが無効
# 解決: 必要なブーリアンを有効化
$ sudo setsebool -P httpd_can_network_connect on

# 問題: カスタムポートが許可されていない
# 解決: ポートラベルを追加
$ sudo semanage port -a -t http_port_t -p tcp 8080

12.2 AppArmor のトラブルシューティング

# === AppArmor 拒否の診断 ===

# 1. 拒否ログの確認
# journalctl で確認
$ sudo journalctl -k | grep DENIED
Apr 10 10:30:00 server kernel: audit: type=1400 audit(1712742600.000:100): apparmor="DENIED" operation="open" profile="/usr/sbin/nginx" name="/opt/myapp/secret.key" pid=1234 comm="nginx" requested_mask="r" denied_mask="r" fsuid=0 ouid=0

# /var/log/kern.log で確認
$ grep "apparmor.*DENIED" /var/log/kern.log

# /var/log/syslog で確認
$ grep "apparmor.*DENIED" /var/log/syslog

# 2. ログメッセージの解読
# apparmor="DENIED"          → アクセスが拒否された
# operation="open"           → ファイルオープン操作
# profile="/usr/sbin/nginx"  → nginx のプロファイルによる拒否
# name="/opt/myapp/secret.key" → アクセス先のパス
# requested_mask="r"         → 要求されたアクセス (読み取り)
# denied_mask="r"            → 拒否されたアクセス

# 3. Complainモードでの問題切り分け
$ sudo aa-complain /usr/sbin/nginx
# → 拒否はされないが、ログに記録される
# → ログを分析してプロファイルを修正

# 4. aa-logprof でプロファイルを更新
$ sudo aa-logprof
# → ログの拒否エントリを解析
# → 必要なルールの追加を提案

# 5. 一般的な問題と解決策

# 問題: 新しいパスへのアクセスが拒否される
# 解決: プロファイルにパスを追加
# /etc/apparmor.d/usr.sbin.nginx に以下を追加:
# /opt/myapp/secret.key r,
$ sudo apparmor_parser -r /etc/apparmor.d/usr.sbin.nginx

# 問題: プロファイルの構文エラー
$ sudo apparmor_parser -p /etc/apparmor.d/usr.sbin.nginx
# エラーメッセージが表示される

# 問題: abstractionの不足
# 解決: 必要なabstractionを追加
# #include <abstractions/ssl_keys>

# 6. AppArmor のデバッグモード
$ sudo aa-notify -p -f /var/log/kern.log
# リアルタイムで拒否通知を表示

12.3 監査ログの分析

# === 監査ログの詳細分析 ===

# ausearch の使い方
# 最近のAVCメッセージ
$ sudo ausearch -m avc -ts recent

# 特定のプロセスの拒否
$ sudo ausearch -m avc -c httpd

# 特定の日時以降
$ sudo ausearch -m avc -ts 04/10/2026 10:00:00

# 特定のファイルに関する拒否
$ sudo ausearch -m avc -f /var/www/html/

# aureport でサマリー
$ sudo aureport --avc
AVC Report
==========
# date time perm source target class

1. 04/10/2026 10:30:00 read httpd_t default_t file
2. 04/10/2026 10:31:00 write httpd_t var_t file
3. 04/10/2026 10:32:00 connect httpd_t mysqld_port_t tcp_socket

# 拒否の統計
$ sudo aureport --avc --summary
AVC Summary Report
===================
Number of AVC's: 3
Number of AVC denials: 3
Number of AVC granted: 0

13. ツールリファレンス

13.1 SELinux ツール

# ─── 状態確認ツール ───

# getenforce - SELinux の現在のモードを表示
$ getenforce
Enforcing

# sestatus - SELinux の詳細な状態を表示
$ sestatus
SELinux status:                 enabled
SELinuxfs mount:                /sys/fs/selinux
SELinux root directory:         /etc/selinux
Loaded policy name:             targeted
Current mode:                   enforcing

# seinfo - ポリシーの統計情報
$ seinfo
Statistics for policy file: /etc/selinux/targeted/policy/policy.33
Policy Version:              33
Classes:            130    Permissions:        458
Sensitivities:        1    Categories:        1024
Types:             4932    Attributes:         253
Users:                8    Roles:               14
Booleans:           338    Cond. Expr.:        381
Allow:           109547    Neverallow:           0
...

# sesearch - ポリシールールの検索
$ sesearch --allow -s httpd_t -t httpd_sys_content_t
allow httpd_t httpd_sys_content_t : file { ioctl read getattr lock map open } ;
allow httpd_t httpd_sys_content_t : dir { ioctl read getattr lock search open } ;
allow httpd_t httpd_sys_content_t : lnk_file { read getattr } ;

# ─── 管理ツール ───

# setenforce - SELinuxのモード変更(一時的)
$ sudo setenforce 0    # Permissive
$ sudo setenforce 1    # Enforcing

# semanage - SELinuxポリシー管理
# (前述のセクションで詳細解説済み)

# setsebool - ブーリアンの設定
$ sudo setsebool -P httpd_can_network_connect on

# getsebool - ブーリアンの確認
$ getsebool httpd_can_network_connect

# restorecon - ファイルコンテキストの復元
$ sudo restorecon -Rv /var/www/html/

# chcon - ファイルコンテキストの一時的な変更
$ sudo chcon -t httpd_sys_content_t /opt/myfile.html
# 注意: restoreconで上書きされる。永続的にはsemanage fcontextを使う

# ─── 監査ツール ───

# audit2allow - 拒否ログからポリシールールを生成
$ sudo ausearch -m avc | audit2allow -R

# audit2why - 拒否の理由を説明
$ sudo ausearch -m avc | audit2why

# sealert - SELinux 問題の詳細分析
$ sudo sealert -a /var/log/audit/audit.log

# ─── ポリシー開発ツール ───

# semodule - ポリシーモジュールの管理
$ sudo semodule -l          # モジュール一覧
$ sudo semodule -i mod.pp   # モジュールインストール
$ sudo semodule -r mod      # モジュール削除
$ sudo semodule -d mod      # モジュール無効化
$ sudo semodule -e mod      # モジュール有効化

# checkpolicy - ポリシーのコンパイルとチェック
$ checkpolicy -M -c 33 policy.conf -o policy.33

13.2 AppArmor ツール

# ─── 状態確認ツール ───

# aa-status - AppArmor の状態を表示
$ sudo aa-status
apparmor module is loaded.
56 profiles are loaded.
  38 - enforce mode.
  18 - complain mode.

# aa-enabled - AppArmor が有効か確認
$ aa-enabled
Yes

# ─── プロファイル管理ツール ───

# aa-enforce - プロファイルをEnforceモードに設定
$ sudo aa-enforce /usr/sbin/nginx
$ sudo aa-enforce /etc/apparmor.d/usr.sbin.nginx

# aa-complain - プロファイルをComplainモードに設定
$ sudo aa-complain /usr/sbin/nginx

# aa-disable - プロファイルを無効化
$ sudo aa-disable /usr/sbin/nginx

# aa-unconfined - プロファイルのないプロセスを表示
$ sudo aa-unconfined
1234 /usr/sbin/mysqld not confined
5678 /usr/bin/python3 not confined

# ─── プロファイル生成ツール ───

# aa-genprof - インタラクティブにプロファイルを生成
$ sudo aa-genprof /usr/sbin/nginx

# aa-logprof - ログからプロファイルを更新
$ sudo aa-logprof

# aa-autodep - 基本的なプロファイルの自動生成
$ sudo aa-autodep /usr/sbin/myapp

# ─── パーサーツール ───

# apparmor_parser - プロファイルのロード/アンロード
$ sudo apparmor_parser -r /etc/apparmor.d/usr.sbin.nginx  # 再ロード
$ sudo apparmor_parser -R /etc/apparmor.d/usr.sbin.nginx  # アンロード
$ sudo apparmor_parser -p /etc/apparmor.d/usr.sbin.nginx  # 構文チェック

# ─── 通知ツール ───

# aa-notify - 拒否通知
$ sudo aa-notify -p -f /var/log/kern.log

14. まとめ

14.1 セキュリティベストプラクティス

1. 多層防御(Defense in Depth):
   - LSM は多層防御の一層として位置づける
   - ファイアウォール + LSM + アプリケーションセキュリティ + 監視

2. 最小権限の原則:
   - プロセスに必要最小限のアクセスのみ許可
   - ブーリアンはデフォルトをoff、必要な場合のみon

3. 段階的な導入:
   - Permissive/Complain モードで開始
   - ログを分析してポリシーを調整
   - 安定したらEnforcingモードに移行

4. 定期的な監査:
   - 監査ログを定期的に確認
   - 不要な例外ルールを削除
   - ポリシーの更新を自動化

5. コンテナセキュリティ:
   - SELinux/AppArmor + seccomp + capabilities の組み合わせ
   - :Z オプションでボリュームラベルを適切に設定
   - rootless コンテナの活用

6. 文書化:
   - カスタムポリシーの変更を記録
   - 変更の理由と影響を文書化

14.2 今後の展望

1. LSM BPF:
   - eBPF を使った動的なセキュリティポリシー
   - 再起動なしのポリシー変更
   - カスタムLSMのユーザー空間からの実装

2. 完全なLSMスタッキング:
   - 複数のメジャーLSMの同時利用
   - SELinux + AppArmor の同時運用
   - コンテナ内部での独立したLSM運用

3. Landlock の進化:
   - より多くのリソースタイプのサポート
   - ネットワーク制限の拡充
   - ファイルシステム操作の細粒度制御

4. コンテナ・クラウドネイティブ対応:
   - Kubernetes での LSM 統合の改善
   - Pod Security Standards との連携
   - サービスメッシュとの統合

14.3 リソースと参考文献

公式ドキュメント:
  - SELinux Project: https://selinuxproject.org/
  - AppArmor Wiki: https://gitlab.com/apparmor/apparmor/-/wikis/
  - Landlock: https://landlock.io/
  - カーネルドキュメント: https://www.kernel.org/doc/html/latest/admin-guide/LSM/

Red Hat ドキュメント:
  - RHEL SELinux ガイド: https://access.redhat.com/documentation/
  
Ubuntu ドキュメント:
  - AppArmor: https://ubuntu.com/server/docs/security-apparmor

RHEL系パッケージ (SELinux):
  - policycoreutils (restorecon, secon, setfiles, ...)
  - policycoreutils-python-utils (semanage, audit2allow, ...)
  - selinux-policy / selinux-policy-targeted
  - setroubleshoot-server (sealert)
  - setools-console (sesearch, seinfo, ...)

Debian/Ubuntu系パッケージ (AppArmor):
  - apparmor
  - apparmor-utils (aa-genprof, aa-logprof, aa-enforce, ...)
  - apparmor-profiles
  - apparmor-profiles-extra

著者注: 本ドキュメントは Linux カーネルのセキュリティモジュールの包括的な技術ガイドである。実際の環境で設定を変更する前に、必ずテスト環境で検証すること。特に SELinux の Enforcing モードの変更は、システムの正常動作に影響を与える可能性がある。


本ドキュメントの内容は Linux カーネル 5.x - 6.x を対象としている。カーネルバージョンやディストリビューションによって、利用可能な機能やコマンドが異なる場合がある。