Linux Kernel Security Modules
Linux Security Modules (LSM) 総合技術ガイド - SELinux, AppArmor, その他のセキュリティフレームワーク
最終更新日: 2026-04-10 対象カーネルバージョン: Linux 5.x - 6.x 対象読者: システム管理者、セキュリティエンジニア、DevOps/SREエンジニア
目次
- はじめに
- LSMフレームワークのアーキテクチャ
- SELinux 詳細解説
- SELinux 実践的設定
- AppArmor 詳細解説
- AppArmor 実践的設定
- その他のLSM: Smack, TOMOYO, Yama
- Landlock: 非特権サンドボックス
- LSMの比較
- LSMスタッキング
- 実践例: Webサーバー、コンテナ、サービスのセキュリティ強化
- トラブルシューティング
- ツールリファレンス
- まとめ
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 カーネルにおけるセキュリティモジュールの標準化されたインターフェースとして開発された。
| 年 | イベント |
|---|---|
| 2001 | Linux Security Modules プロジェクト開始 |
| 2003 | Linux 2.6.0 に LSM フレームワーク統合、SELinux マージ |
| 2006 | AppArmor が開発開始(Immunix/Novell) |
| 2009 | TOMOYO Linux がメインラインカーネルに統合 |
| 2009 | Smack が Linux 2.6.25 に統合 |
| 2010 | AppArmor が Linux 2.6.36 にマージ |
| 2012 | Yama LSM が Linux 3.4 に統合 |
| 2017 | LSM スタッキングの改善が始まる |
| 2021 | Landlock が Linux 5.13 に統合 |
| 2023 | LSM 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 を対象としている。カーネルバージョンやディストリビューションによって、利用可能な機能やコマンドが異なる場合がある。