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 のセキュリティコンテキストは、プロセスを開始したユーザの識別情報ロール、ユーザがプロセス開始時点で持っていたドメイン、によって定義されます。権限は間違いなくドメインに依存しますが、ドメイン間の遷移はそのロールによって制御されます。最後にロール間の遷移の可否は識別情報に依存します。
セキュリティコンテキストと Unix ユーザ

図 14.3 セキュリティコンテキストと Unix ユーザ

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

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

14.5.2. SELinux のセットアップ

SELinux のサポートは Debian の提供する標準的なカーネルに組み込まれています。コア Unix ツールは SELinux をサポートしており、修正は必要ありません。このため、SELinux を有効化することは比較的簡単です。
apt install selinux-basics selinux-policy-default コマンドで、SELinux システムを設定するために必要なパッケージが自動的にインストールされます。
selinux-policy-default パッケージには、標準的なルールが含まれています。デフォルトで、このポリシーは広範囲にわたって提供されるサービスへのアクセスを制限するだけです。ユーザセッションは制限されませんから、SELinux が正当なユーザ操作を妨害することはほとんどありません。しかしながら、これはマシンで実行されているシステムサービスのセキュリティを強化します。古い「strict」ルールと同じポリシーをセットアップするには、行動を制限されていないモジュールを無効化しなければいけません (モジュール管理はこの節でより詳しく説明されています)。
「strict」ポリシーをインストールしたら、すべての利用できるファイルをラベル付けするべきです (これはファイルにタイプを割り当てることを意味します)。この操作は 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 スクリプトがこれらの操作を自動化し、次回起動時にラベル付けを強制する (これは SELinux がまだ動作していなかった時とラベル付けの実行中にラベル付けされていないファイルが新しく作成されることを避ける) 点は注目に値します。

14.5.3. SELinux システムの管理

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

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 識別情報はユーザに与えるロールを定義します。2 種類 (ユーザから識別情報へ、識別情報からロールへ) の対応付けは semanage コマンドを使って設定することが可能です。
コマンドの構文はそのコマンドで管理するすべての概念に類似しているとは言うものの、管理者は semanage(8) マニュアルページを読むべきです。すべてのサブコマンドに対して共通のオプションがあることがわかるでしょう。たとえば -a は追加、-d は削除、-m は修正、-l はリスト、-t はタイプ (またはドメイン) の指定を表します。
semanage login -l はユーザ識別情報と SELinux 識別情報の現在の対応付けをリストします。明確なエントリを持たないユーザは __default__ エントリで表される識別情報を獲得します。semanage login -a -s user_u user コマンドは user_u 識別情報を指定されたユーザに対応付けます。最後に、semanage login -d user は対指定したユーザに関連付けられた対応付けエントリを削除します。
# semanage login -a -s user_u rhertzog
# semanage login -l

Login Name           SELinux User         MLS/MCS Range        Service

__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 ユーザ識別情報と許可されたロールの対応付けをリストします。新しい識別情報を追加した場合、識別情報に対応するロールと、個人ファイル (/home/user/*) にタイプを割り当てるために使われるプレフィックスのラベル付けが必要になります。このプレフィックスは userstaffsysadm のどれか 1 つを選ばなければいけません。「staff」プレフィックスを付けるとファイルのタイプは「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

                Labeling   MLS/       MLS/                          
SELinux User    Prefix     MCS Level  MCS Range             SELinux Roles

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 モジュールには、ファイルラベル付けルール群が含まれますが、さらに SELinux モジュールは特別な場合に応じてラベル付けルールをカスタマイズすることが可能です。たとえば、ウェブサーバに /srv/www/ ファイル階層内のファイルを読むことを許可する場合、semanage fcontext -a -t httpd_sys_content_t "/srv/www(/.*)?" を実行し、その後 restorecon -R /srv/www/ を実行します。最初のコマンドで新しいラベル付けルールを登録し、次のコマンドで現在のラベル付けルールに基づいてファイルタイプを再設定します。
同様に、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 ファイルは最も重要なファイルです。これがルールを定義します。.fc ファイルは「ファイルコンテキスト」を定義します。「ファイルコンテキスト」はこのモジュールに関連するファイルに割り当てるタイプを意味します。.fc ファイルに含まれるデータはファイルのラベル付け中に使われます。最後に、.if ファイルはモジュールのインターフェースを定義します。つまり、これは一連の「公開関数」で、他のモジュールはこの関数を使ってここで作成されたモジュールと情報をやり取りします。

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

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

例 14.2 example.fc ファイル

# myapp executable will have:
# label: 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 マクロを使って) 使用するすべてのタイプを宣言し、権限を取得するために標準的な指示文を使うべきです。しかしながら、他のモジュールが提供するインターフェースを使うことが可能な点に注意してください。次の節では、これらの権限を表現する方法についてより詳しく説明します。

例 14.3 example.if ファイル

## <summary>Myapp example policy</summary>
## <desc>
##      <p>
##              More descriptive text about myapp.  The <desc>
##              tag can also use <p>, <ul>, and <ol>
##              html tags for formatting.
##      </p>
##      <p>
##              This policy supports the following myapp features:
##              <ul>
##              <li>Feature A</li>
##              <li>Feature B</li>
##              <li>Feature C</li>
##              </ul>
##      </p>
## </desc>
#

########################################
## <summary>
##      Execute a domain transition to run myapp.
## </summary>
## <param name="domain">
##      Domain allowed to transition.
## </param>
#
interface(`myapp_domtrans',`
        gen_require(`
                type myapp_t, myapp_exec_t;
        ')

        domtrans_pattern($1,myapp_exec_t,myapp_t)
')

########################################
## <summary>
##      Read myapp log files.
## </summary>
## <param name="domain">
##      Domain allowed to read the log files.
## </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

########################################
#
# Declarations
#

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 local policy
#

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

モジュールは名前とバージョン番号で識別されます。この指示文は必須です。

2

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

3

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

4

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

5

allow 指示文は操作を許可するために使われる基本的な指示文です。1 番目のパラメータはこの操作を実行することを許されたプロセスドメインです。2 番目のパラメータは 1 番目のパラメータで指定したドメインのプロセスが操作することが可能なオブジェクトを定義します。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 ファイルです。
ログエントリを観察することで、その操作を許可するために必要なルールを構築することが可能です。たとえば allow syslogd_t device_t:fifo_file { read write } です。この作業は自動化することが可能です。これが (policycoreutils パッケージに含まれる) audit2allow コマンドの役割です。設定する必要のある内容に応じて、さまざまなオブジェクトが既に正しくラベル付けされている場合にのみ、このアプローチは役に立ちます。いずれにせよ、管理者は必ず、生成されたルールを注意深く確認し、アプリケーションに対する知識に基づいてルールの妥当性を検査しなければいけません。事実上、このアプローチはアプリケーションが本当に必要としている権限よりも多くの権限を与えようとします。ほとんどの場合、新しいタイプを作成し、作成したタイプだけに権限を与えることが適切な解決策と言えます。また、拒否された操作がアプリケーションにとって致命的でない場合もあります。この場合、「dontaudit」ルールを追加するだけに留めることがより良い解決策かもしれません。こうすることで、実際の実行を拒否するのではなくログエントリの記録だけが拒否されます。

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

Once the 3 files (example.if, example.fc, and example.te) match your expectations for the new rules, just run make NAME=devel to generate a module in the example.pp file (you can immediately load it with semodule -i example.pp). If several modules are defined, make will create all the corresponding .pp files.