Product SiteDocumentation Site

14.5. SELinux の紹介

14.5.1. 原理

SELinux (Security Enhanced Linux) は Linux の LSM (Linux Security Modules) インターフェース上に設けられた強制アクセス制御システムです。具体的に言えば、カーネルはそれぞれのシステムコールの前にシステムコールを発行したプロセスが指定された操作に対する権限を与えられているか SELinux に問い合わせます。
SELinux はまとめてポリシーとして知られているルール群を使い、操作を許可したり禁止したりします。これらのルールの作成は難しいです。幸いなことに、設定作業の大部分を避けるために 2 種類の標準的なポリシー (targetedstrict) が提供されています。
SELinux を使うと、権限管理が伝統的な Unix システムとは全く違ったものになります。プロセスの権限は SELinux のセキュリティコンテキストに依存します。SELinux のセキュリティコンテキストはプロセスを開始したユーザの SELinux ユーザ名SELinux ロール、ユーザがプロセス開始時点で持っていた SELinux ドメインによって定義されます。正しく言えばプロセスの権限は SELinux ドメインに依存しますが、SELinux ドメイン間の遷移は SELinux ロールによって制御されます。最後に SELinux ロール間の遷移の可否は SELinux ユーザ名に依存します。
セキュリティコンテキストと Unix ログイン名

図 14.3 セキュリティコンテキストと Unix ログイン名

具体的に言えば、ログイン時にユーザはデフォルトのセキュリティコンテキストを割り当てられます (セキュリティコンテキストはユーザに与える SELinux ロールに依存します)。セキュリティコンテキストは現在の SELinux ドメインを定義し、ここで定義された SELinux ドメインが新しい子プロセスに割り当てられます。ユーザが現在の SELinux ロールと SELinux ロールに対応する SELinux ドメインを変更したい場合、newrole -r role_r -t domain_t を実行しなければいけません (通常 SELinux ロールと SELinux ドメインは一対一に対応しているため、-t パラメータは省略されることが多いです)。newrole コマンドはユーザに自分のパスワードを入力させることで認証を行います。この機能のおかげで、プログラムが自動的に SELinux ロールを切り替えることを禁止できます。SELinux ロールの変更を行えるのは、ユーザが SELinux ポリシーに基づいて SELinux ロールの変更を許可されている場合に限ります。
権限がすべてのオブジェクト (ファイル、ディレクトリ、ソケット、デバイスなど) に適用されないのは明らかです。権限はオブジェクトによって異なります。これを実現するために、それぞれのオブジェクトは SELinux タイプ と結び付けられています (これをラベリングと呼びます)。このため SELinux ドメインの権限はオブジェクトの SELinux タイプ (および、与えられた SELinux タイプによって間接的にラベリングされたすべてのオブジェクト) に対する一連の許可された (されていない) 操作を使って表現されます。
デフォルトで、プログラムはプログラムを起動したユーザに割り当てられた SELinux ドメインを継承しますが、標準的な SELinux のポリシーは多くの重要なプログラムが専用の SELinux ドメインで実行されることを要求しています。これを成し遂げるために、重要なプログラムの実行ファイルは専用の SELinux タイプでラベリングされています (たとえば、sshssh_exec_t でラベリングされています。ssh プログラムが起動すると ssh プログラムは ssh_t SELinux ドメインに自動的に切り替わります)。この自動 SELinux ドメイン遷移メカニズムによって、それぞれのプログラムに対して必要な権限だけを認めることが可能です。これが SELinux の基本原則です。
SELinux ドメイン間の自動遷移

図 14.4 SELinux ドメイン間の自動遷移

14.5.2. SELinux のセットアップ

SELinux のサポートは Debian の提供する標準的なカーネルに組み込まれています。コア Unix ツールは SELinux をサポートしており、修正は必要ありません。このため、SELinux を有効化することは比較的簡単です。
apt install selinux-basics selinux-policy-default コマンドで、SELinux システムを設定するために必要なパッケージが自動的にインストールされます。
selinux-policy-default パッケージには、標準的なルールが含まれています。デフォルトで、このポリシーは広範囲にわたって提供されるサービスへのアクセスを制限するだけです。ユーザセッションは制限されませんから、SELinux が正当なユーザ操作を妨害することはほとんどありません。しかしながら、このポリシーはマシンで実行されているシステムサービスのセキュリティを強化します。古い「strict」ルールと同じポリシーをセットアップするには、unconfined モジュールを無効化しなければいけません (モジュール管理に関してはこの節でさらに詳しく説明しています)。
selinux-policy-default パッケージをインストールしたら、すべての利用できるファイルをラベリングするべきです (これはファイルに SELinux タイプを割り当てることを意味します)。この操作は fixfiles relabel を使って手作業で開始しなければいけません。
これで SELinux システムの準備が整いました。SELinux を有効化するには、selinux=1 security=selinux パラメータを Linux カーネルに追加する必要があります。audit=1 パラメータを指定した場合、SELinux のログ記録が有効化され、すべての拒否された操作が記録されます。最後に enforcing=1 パラメータを指定した場合、ルールをアプリケーションに強制します。enforcing=1 パラメータを指定しなかった場合、SELinux はデフォルトの permissive モードで動作します。permissive モードの場合、拒否された操作はログ記録され、実行されます。このため、GRUB ブートローダ設定ファイルを変更して必要なパラメータを追加するべきです。これを簡単に行うには、/etc/default/grub の中の GRUB_CMDLINE_LINUX 変数に必要なパラメータを追加し、update-grub を実行します。SELinux は再起動後に動作状態になります。
selinux-activate スクリプトを使うことで、GRUB ブートローダ設定ファイルに対する変更操作が自動化され、次回起動時にラベリングが強制されます (強制的にラベリングすることで SELinux がまだ動作していなかった時とラベリングの実行中にラベリングされていないファイルが新しく作成されることを避けることが可能です)。この点は注目に値します。

14.5.3. SELinux システムの管理

selinux-policy-default パッケージに含まれる SELinux ポリシーはモジュール式のルール群で、selinux-policy-default パッケージはインストール時にインストール済みのサービスに基づいて対応するモジュールを自動的に検出して有効化します。そのため、システムは SELinux ポリシーをすぐに利用できるようになります。しかしながら、SELinux ポリシーを設定した後にサービスをインストールした場合、対応するモジュールを手作業で有効化する必要があります。これを行うのが semodule コマンドです。さらに、管理者はそれぞれのユーザに与える SELinux ロールを定義する能力を持っていなければいけません。これは semanage を使って行います。
このため semodulesemanage コマンドは /etc/selinux/default/ に保存されている現在の SELinux 設定を変更するために使われます。/etc/ に配置されている SELinux 以外の設定ファイルと異なり、SELinux の設定ファイルはすべて手作業で修正されなければいけません。管理者は SELinux 設定ファイルを編集するために設計されたプログラムを使うべきです。

14.5.3.1. SELinux モジュールの管理

利用できる SELinux モジュールは /usr/share/selinux/default/ ディレクトリに保存されています。現在の設定の中で SELinux モジュールの 1 つをインストールするには、semodule -i module.pp.bz2 を使うべきです。pp.bz2 拡張子は bzip2 で圧縮されたポリシーパッケージを意味しています。
現在の設定からモジュールを削除するには semodule -r module を使います。最後に、semodule -l コマンドは現在インストールされているモジュールとそのバージョンをリストします。モジュールを選択的に有効化するには semodule -e、無効化するには semodule -d を使います。
# semodule -i /usr/share/selinux/default/abrt.pp.bz2
# semodule -l
abrt    1.5.0   Disabled
accountsd       1.1.0   
acct    1.6.0   
[...]
# semodule -e abrt
# semodule -d accountsd
# semodule -l
abrt    1.5.0
accountsd       1.1.0   Disabled
acct    1.6.0   
[...]
# semodule -r abrt
# semodule -l
accountsd       1.1.0   Disabled
acct    1.6.0   
[...]
semodule-n オプションを付けない限り、すぐさま新しい設定を読み込みます。semodule プログラムがデフォルトで現在の設定に対して操作を行う点は注目に値します (現在の設定は /etc/selinux/config 内の SELINUXTYPE 変数によって表されます)。しかし -s オプションを使えば、他の設定に対して操作を行うことも可能です。

14.5.3.2. ユーザの身元管理

ユーザはログイン時に毎回、SELinux ユーザ名を割り当てられます。SELinux ユーザ名はユーザに与える SELinux ロールを定義します。semanage コマンドを使えば、2 種類 (Unix ログイン名から SELinux ユーザ名へ、SELinux ユーザ名から SELinux ロールへ) の対応付けを設定することが可能です。
semanage コマンドはサブコマンドごとに管理する要素が決められています。サブコマンドの構文はすべてのサブコマンド間で類似しているとは言うものの、管理者は semanage(8) マニュアルページを読むべきです。すべてのサブコマンドに対して共通のオプションが存在します。たとえば -a は追加、-d は削除、-m は修正、-l はリスト、-t は SELinux タイプ (または SELinux ドメイン) の指定を表します。
semanage login -l は Unix ログイン名と SELinux ユーザ名の現在の対応付けをリストします。明確なエントリを設定されていない Unix ログイン名を持つユーザは __default__ という Unix ログイン名に対応する SELinux ユーザ名を割り当てられます。semanage login -a -s user_u user コマンドを使うことで、user で指定した Unix ログイン名を持つユーザに user_u で指定した SELinux ユーザ名を割り当てます。最後に、semanage login -d useruser で指定した Unix ログイン名を持つユーザに関連付けられたエントリを削除します。
# semanage login -a -s user_u rhertzog
# semanage login -l

ログイン名                SELinux ユーザー         MLS/MCS 範囲           サービス

__default__          unconfined_u         SystemLow-SystemHigh *
rhertzog             user_u               SystemLow            *
root                 unconfined_u         SystemLow-SystemHigh *
system_u             system_u             SystemLow-SystemHigh *
# semanage login -d rhertzog
semanage user -l は SELinux ユーザ名と許可された SELinux ロールの対応付けをリストします。新しい SELinux ユーザ名を追加する場合、SELinux ユーザ名に対応する SELinux ロールと、個人ファイル (/home/user/*) に SELinux タイプを割り当てるために使われるラベリングプレフィックスが必要になります。このプレフィックスは userstaffsysadm のどれか 1 つを選ばなければいけません。「staff」プレフィックスを選んだ場合、ファイルの SELinux タイプは「staff_home_dir_t」になります。新しい SELinux ユーザ名を作成するには semanage user -a -R roles -P prefix identity を使います。最後に、SELinux ユーザ名を削除するには semanage user -d identity を使います。
# semanage user -a -R 'staff_r user_r' -P staff test_u
# semanage user -l

                ラベリング      MLS/       MLS/
SELinux ユーザー    プレフィックス    MCS レベル    MCS 範囲                         SELinux ロール

root            sysadm     SystemLow  SystemLow-SystemHigh  staff_r sysadm_r system_r
staff_u         staff      SystemLow  SystemLow-SystemHigh  staff_r sysadm_r
sysadm_u        sysadm     SystemLow  SystemLow-SystemHigh  sysadm_r
system_u        user       SystemLow  SystemLow-SystemHigh  system_r
test_u          staff      SystemLow  SystemLow             staff_r user_r
unconfined_u    unconfined SystemLow  SystemLow-SystemHigh  system_r unconfined_r
user_u          user       SystemLow  SystemLow             user_r
# semanage user -d test_u

14.5.3.3. ファイルコンテキスト、ポート、ブール値の管理

各 SELinux モジュールにはファイルラベリングルール群が含まれますが、特別な場合に応じてラベリングルールをカスタマイズすることも可能です。たとえば、ウェブサーバに /srv/www/ ファイル階層内のファイルを読むことを許可する場合、semanage fcontext -a -t httpd_sys_content_t "/srv/www(/.*)?" を実行し、その後 restorecon -R /srv/www/ を実行します。semanage fcontext -a で新しいラベリングルールを登録し、restorecon で現在のラベリングルールに基づいてファイルの SELinux タイプを再設定します。
同様に TCP/UDP ポートをラベリングして、ポート番号とそのポートのリッスンを許可するデーモンを対応付けることが可能です。たとえば、ウェブサーバがポート 8080 番をリッスンすることを可能にしたい場合、semanage port -m -t http_port_t -p tcp 8080 を実行します。
一部の SELinux モジュールでは、ブール値オプションを使ってデフォルトルールの挙動を微調整することが可能です。getsebool ユーティリティを使ってブール値オプションを調査します (getsebool boolean は 1 つのオプションを表示し、getsebool -a はすべてのオプションを表示します)。setsebool boolean value コマンドはブール値オプションの現在の値を変更します。-P オプションを付けるとこの修正が永続的なものになります。つまり、新しい値がデフォルトになり、再起動後も適用されることになります。以下の例では、ウェブサーバにホームディレクトリに対するアクセス権を与えています (ユーザが個人的なウェブサイトを ~/public_html/ の下に作る場合、この設定を使うと便利です)。
# getsebool httpd_enable_homedirs
httpd_enable_homedirs --> off
# setsebool -P httpd_enable_homedirs on
# getsebool httpd_enable_homedirs 
httpd_enable_homedirs --> on

14.5.4. ルールの適用

SELinux ポリシーはモジュール式なので、モジュールの用意されていない (場合によっては特注の) アプリケーション用に新しいモジュールを開発する方法を知っておくと良いでしょう。新しいモジュールはリファレンスポリシーを満足させなければいけません。
新しいモジュールを作成するには、selinux-policy-dev および selinux-policy-doc パッケージが必要です。selinux-policy-doc パッケージには、標準的なルールに関する文書 (/usr/share/doc/selinux-policy-doc/html/) と新しいモジュールを作成するためのテンプレートとして使えるサンプルファイルが含まれます。これらのファイルをインストールし、さらにしっかりと勉強してください。
$ cp /usr/share/doc/selinux-policy-doc/Makefile.example Makefile
$ cp /usr/share/doc/selinux-policy-doc/example.fc ./
$ cp /usr/share/doc/selinux-policy-doc/example.if ./
$ cp /usr/share/doc/selinux-policy-doc/example.te ./
.te ファイルは最も重要なファイルです。.te ファイルがルールを定義します。.fc ファイルは「ファイルコンテキスト」を定義します。「ファイルコンテキスト」はこのモジュールに関連するファイルに割り当てる SELinux タイプを意味します。.fc ファイルに含まれるデータはファイルのラベリング中に使われます。最後に、.if ファイルはモジュールのインターフェースを定義します。つまり、モジュールのインターフェースは一連の「公開関数」で、他のモジュールはこの関数を使ってここで作成されたモジュールと情報をやり取りします。

14.5.4.1. .fc ファイルの書き方

以下の例を読めば、.fc ファイルの構造を十分に理解することが可能です。複数のファイルおよび完全なディレクトリツリーに対して同じセキュリティコンテキストを割り当てるために、正規表現を使うことが可能です。

例 14.2 example.fc ファイル

# myapp 実行ファイルのファイルコンテキスト定義:
# ラベル: system_u:object_r:myapp_exec_t
# MLS sensitivity: s0
# MCS categories: <none>

/usr/sbin/myapp         --      gen_context(system_u:object_r:myapp_exec_t,s0)

14.5.4.2. .if ファイルの書き方

以下の例では、1 番目のインターフェース (「myapp_domtrans」) はアプリケーションを実行できるユーザを制御します。2 番目のインターフェース (「myapp_read_log」) はアプリケーションのログファイルに対する読み込み権限を制御します。
それぞれのインターフェースは .te ファイルに組み込むことが可能な有効なルール群を生成しなければいけません。そんなわけで、管理者は (gen_require マクロを使って) 使用するすべての SELinux タイプを宣言し、権限を取得するために標準的な指示文を使うべきです。しかしながら、他のモジュールが提供するインターフェースを使うことが可能な点に注意してください。次の節では、これらの権限を表現する方法についてより詳しく説明します。

例 14.3 example.if ファイル

## <summary>myapp ポリシーの一例</summary>
## <desc>
##      <p>
##              ここには myapp に関する追加説明を書きます。
##              <desc> タグの中では書式指定をするために
##              <p>、<ul>、<ol> html タグを使えます。
##      </p>
##      <p>
##              本ポリシーは myapp の以下に示す機能を動作させるために必要です。
##              <ul>
##              <li>機能 A</li>
##              <li>機能 B</li>
##              <li>機能 C</li>
##              </ul>
##      </p>
## </desc>
#

########################################
## <summary>
##      myapp を実行するために SELinux ドメインを遷移させます。
## </summary>
## <param name="domain">
##      遷移を許可する SELinux ドメイン。
## </param>
#
interface(`myapp_domtrans',`
        gen_require(`
                type myapp_t, myapp_exec_t;
        ')

        domtrans_pattern($1,myapp_exec_t,myapp_t)
')

########################################
## <summary>
##      myapp のログファイルを読み込みます。
## </summary>
## <param name="domain">
##      myapp のログファイルの読み込みを許可する SELinux ドメイン。
## </param>
#
interface(`myapp_read_log',`
        gen_require(`
                type myapp_log_t;
        ')

        logging_search_logs($1)
        allow $1 myapp_log_t:file r_file_perms;
')

14.5.4.3. .te ファイルの書き方

それでは example.te を見てみましょう。
policy_module(myapp,1.0.0) 1

########################################
#
# SELinux タイプと SELinux ドメインの宣言
#

type myapp_t; 2
type myapp_exec_t;
domain_type(myapp_t)
domain_entry_file(myapp_t, myapp_exec_t) 3

type myapp_log_t;
logging_log_file(myapp_log_t) 4

type myapp_tmp_t;
files_tmp_file(myapp_tmp_t)

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

allow myapp_t myapp_log_t:file { read_file_perms append_file_perms }; 5

allow myapp_t myapp_tmp_t:file manage_file_perms;
files_tmp_filetrans(myapp_t,myapp_tmp_t,file)

1

モジュールは名前とバージョン番号で識別されます。policy_module マクロは必須です。

2

モジュールによって新しい SELinux タイプが導入される場合、type 文を使って新しい SELinux タイプを必ず宣言してください。多くの無駄な権限を与えるのでなく、必要な SELinux タイプをすべて作成してください。遠慮はいりません。

3

ここでは domain_type および domain_entry_file インターフェースを使って、myapp_exec_t とラベリングされた実行ファイルによって使われるプロセスの SELinux ドメインとして myapp_t SELinux タイプを定義しています。こうすることで、暗黙のうちにオブジェクトに exec_type 属性が追加されます。このおかげで、他のモジュールは myapp_exec_t とラベリングされたプログラムを実行する権限を取得することが可能になります。たとえば userdomain モジュールを使うことで、user_tstaff_tsysadm_t SELinux ドメインを持つプロセスが自分を実行することが可能になります。他の閉じ込められたアプリケーションの SELinux ドメインは、その SELinux ドメインに割り当てられたルールが同様の権限を取得しない限り (たとえば dpkg_t SELinux ドメインを持つ dpkg がこの場合に相当します)、myapp_exec_t とラベリングされたプログラムを実行する権限を持ちません。

4

logging_log_file はリファレンスポリシーによって提供されるインターフェースです。これは指定された SELinux タイプでラベリングされたファイルはその SELinux タイプに対応するルールから恩恵を受ける義務があるログファイルであることを表します (たとえば、logrotate がログファイルを処理することを可能にするために、logrotate に権限を与える場合に使います)。

5

allow 指示文は操作を許可するために使われる基本的な指示文です。1 番目のパラメータはこの操作を実行することを許されたプロセスの SELinux ドメインです。2 番目のパラメータは 1 番目のパラメータで指定した SELinux ドメインのプロセスが操作することが可能なオブジェクトを定義します。2 番目のパラメータは「type:class」の形で定義します。ここで type は SELinux タイプで class はオブジェクトの種類 (ファイル、ディレクトリ、ソケット、名前付きパイプなど) です。最後に、3 番目のパラメータはパーミッション (許可された操作) を表現します。
パーミッションは許可された操作の一式として定義され、以下のテンプレートに従います。すなわち { operation1 operation2 } です。しかしながら、最も役に立つパーミッションを表すマクロを使うことも可能です。/usr/share/selinux/devel/include/support/obj_perm_sets.spt には、最も役に立つパーミッションのマクロが説明されています。
以下のウェブページでは、オブジェクトクラスと与えられるパーミッションの比較的包括的なリストが載せられています。
さらに、対象のアプリケーションやサービスが正しく動くために必要な最低限のルールセットを見つけ出さなければいけません。これを行うには、アプリケーションの動作方法とアプリケーションが管理および生成するデータの種類に関する詳しい知識が必要です。
しかしながら、経験的なアプローチが使えます。対応するオブジェクトに対する正しいラベリングが終わっていれば、アプリケーションを permissive モードで使うことが可能です。そして permissive モードでは、禁止されるであろう操作はログ記録されて実行されます。このログを解析することで、許可する操作を識別することが可能になります。以下は permissive モードでアプリケーションを動かした場合に記録されるログエントリの例です。
avc:  denied  { read write } for  pid=1876 comm="syslogd" name="xconsole" dev=tmpfs ino=5510 scontext=system_u:system_r:syslogd_t:s0 tcontext=system_u:object_r:device_t:s0 tclass=fifo_file permissive=1
このメッセージをより詳しく理解するために、それぞれの要素について勉強しましょう。

表 14.1 SELinux ログエントリの解析

メッセージ説明
avc: denied操作が拒否されました。
{ read write }この操作には readwrite パーミッションが必要です。
pid=1876PID 1876 のプロセスがこの操作を実行しました (または実行を試行しました)。
comm="syslogd"プロセスは syslogd プログラムのインスタンスです。
name="xconsole"対象のオブジェクトは xconsole と名付けられました。場合によってはこれの代わりにフルパスを含む「path」変数が設定されていることもあります。
dev=tmpfs対象のオブジェクトをホストしているデバイスは tmpfs (メモリ内ファイルシステム) です。実ディスクの場合、オブジェクトをホストしているパーティション (たとえば「sda3」) になります。
ino=5510オブジェクトは inode 番号 5510 で識別されています。
scontext=system_u:system_r:syslogd_t:s0これは操作を実行したプロセスのセキュリティコンテキストです。
tcontext=system_u:object_r:device_t:s0これは対象オブジェクトのセキュリティコンテキストです。
tclass=fifo_file対象オブジェクトは FIFO ファイルです。
SELinux ログエントリを観察することで、その操作を許可するために必要なルールを構築することが可能です。たとえば例に挙げた操作を許可するルールは allow syslogd_t device_t:fifo_file { read write } のようになります。この作業は自動化することが可能です。これが audit2allow コマンド (policycoreutils パッケージに含まれます) の役割です。設定する必要のある内容に応じてさまざまなオブジェクトが既に正しくラベリングされている場合にのみ、このアプローチは役に立ちます。いずれにせよ、管理者は必ず生成されたルールを注意深く確認し、アプリケーションに対する知識に基づいてルールの妥当性を検査しなければいけません。事実上、このアプローチはアプリケーションが本当に必要としている権限よりも多くの権限を与えようとします。ほとんどの場合、新しい SELinux タイプを作成し、作成した SELinux タイプだけに権限を与えることが適切な解決策と言えます。また、拒否された操作がアプリケーションにとって致命的でない場合もあります。この場合、「dontaudit」ルールを追加するだけに留めることがより良い解決策かもしれません。こうすることで、実際の実行を拒否するのではなくログエントリの記録だけが拒否されます。

14.5.4.4. ファイルのコンパイル

3 個のファイル (example.ifexample.fcexample.te) が新しいルールに関する管理者の期待と一致したら、make NAME=devel を実行して example.pp ファイルの形でモジュールを生成します (semodule -i example.pp を使ってすぐさまこのモジュールをインストールすることが可能です)。複数のモジュールを定義した場合、make はすべての対応する .pp ファイルを生成します。