Apache に関する SELinux の設定 (CentOS)

導入
====
<b>[SELinux](http://ja.wikipedia.org/wiki/SELinux "SELinux - Wikipedia")</b>は、どの<i>プロセス</i>が、どの<i>ファイルやディレクトリ</i>に、どのような<i>操作（読み取りや書き込み）</i>ができるかを設定するLinuxの拡張機能です。

ネットワーク関連のデーモン（サービス）のみを管理する`targeted`ポリシーと、<b>SELinux</b>の機能をフルに利用する`strict`ポリシーがあります。この項目の解説は、`targeted`で動作している場合を対象としています。`sestatus`コマンド実行結果の`Policy from config file:`項目を見れば、現在どのポリシーが設定されているかわかります。[^1]



SELinuxが無効になっていた場合
=============================
`sestatus`コマンド実行結果で`SELinux status: disabled`と表示されていた場合、<b>SELinux</b>が無効になっています。

設定ファイルを編集し、ひとまず`permissive`モードに変更します。`permissive`は、ポリシー違反の場合でもアクセスを遮断せず、ログに記録するだけの動作モードです。[^1]

```ini:/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 - SELinux is fully disabled.
SELINUX=permissive
# ↑この項目を、disabledからpermissiveに変更する

# SELINUXTYPE= type of policy in use. Possible values are:
#	targeted - Only targeted network daemons are protected.
#	strict - Full SELinux protection.
SELINUXTYPE=targeted
```

<b>SELinux</b>では、プロセスやファイルに<i>SELinuxセキュリティコンテキスト</i>というラベルを付けて、どのラベルが付いているかによってアクセス権の有無を判断します。しかし、<b>SELinux</b>が無効の状態ではラベルが削除されているので、ラベルの再構築が必要です。以下のように、ルートディレクトリに空の`.autorelabel`ファイルを作成しておけば、次回起動時にラベルの再構築が行われます。[^2]

```sh
# 再起動時にラベルの再構築を行う
sudo touch /.autorelabel
# 再起動
sudo reboot
```

ラベルの再構築をともなうOSの起動は時間がかかるので、実運用環境ではご注意ください。



Webサーバープロセスからのアクセス許可
=====================================
`/etc/selinux/targeted/contexts/files/file_contexts`には、どこにどのラベルを張り付けると良いか記述されています。このファイルには、Webサーバーに関するラベル付けの設定が初めから含まれているので、Apacheのドキュメントルート等を対象に`restorecon`コマンドを実行するだけでOKです。[^1]

```sh
# /var/wwwのラベルの貼り直し
sudo restorecon -R /var/www
```

以前からWebサーバーを動かしていて、`/tmp`ディレクトリ直下以外の場所にセッションファイルを保存するようにしていたなら、それらのファイルを掃除しておきます。



違反ログから新しいルールを定義
==============================
基本的にはこれで、Apacheに関するポリシー違反は起きないと思います。私の環境ではポリシー違反が起きたので、新しいルールを定義しました。

ポリシー違反のログは、`sudo grep avc: /var/log/messages`を実行、又は<b>[Audit](http://people.redhat.com/sgrubb/audit/ "Audit")</b>が起動していた状態なら`sudo ausearch --message AVC`を実行することで確認できます。[^1]

まず、ログをルールに変換する[audit2allow](http://docs.fedoraproject.org/ja-JP/Fedora/19/html/Security_Guide/sect-Security-Enhanced_Linux-Fixing_Problems-Allowing_Access_audit2allow.html "9.6.3.8. アクセスの許可: audit2allow")コマンドが使えるようにします。

```sh
# audit2allowコマンドが含まれるパッケージを調べる
yum --quiet provides */audit2allow
# policycoreutils-python-2.0.83-19.30.el6.x86_64 : SELinux policy core python utilities
# Repo        : base
# Matched from:
# Filename    : /usr/bin/audit2allow

# 上で返った情報先頭のパッケージ名を指定してインストール
sudo yum install policycoreutils-python
```

`sudo audit2allow --module=httpd --dmesg`を実行、<b>Audit</b>が起動していた状態なら`sudo audit2allow --module=httpd --all`を実行すれば、ログに記録されているポリシー違反に対応するアクセス許可ルールが出力されます。[^3] [^4]

私の環境では以下のような出力になりました。

```:httpd.te
module httpd 1.0;

require {
	type httpd_sys_script_t;
	type httpd_t;
	class process signull;
}

#============= httpd_t ==============
allow httpd_t httpd_sys_script_t:process signull;
```

これは<i>ラベル`httpd_t`が付いたプロセスから、ラベル`httpd_sys_script_t`が付いた`process`への、`signull`を許可する</i>というルールになります。

表示されたルールを確認して問題が無ければ、以下のようにしてルールをインストールします。

```sh
# ルールが定義されたモジュールを作成
# ※Auditが起動していた状態なら、--dmesg → --all
sudo audit2allow --module-package=httpd --dmesg
# 作成されたモジュールをインストール
sudo semodule --install=httpd.pp
```



手動で新しいルールを定義
========================
<b>[VirtualBox](http://ja.wikipedia.org/wiki/VirtualBox "VirtualBox - Wikipedia")</b>や<b>[VMware](http://ja.wikipedia.org/wiki/VMware#.E8.A3.BD.E5.93.81 "VMware - Wikipedia")</b>などの仮想機械で開発環境を構築している場合、共有ディレクトリのラベルは変更することができません。仮想機械上の開発環境において、それらのディレクトリを`/var/www`以下に配置しているなら、Apacheからのアクセスを許可するルールを定義する必要があります。

まず以下のようなType Enforcementポリシーファイルを作成します。このファイルのルール定義は、私の開発環境のポリシー違反ログを`audit2allow`コマンドで変換したものです。`httpd_t`から`vmblock_t`へのアクセスを許可しています。

```:httpd.te
module httpd 1.0;

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

#============= httpd_t ==============
allow httpd_t vmblock_t:dir { getattr search read open };
allow httpd_t vmblock_t:file { getattr read open };
```

ファイル操作を行うCGIスクリプトを利用しているなら、以下のように書き込みや実行も許可する必要があります。

```:httpd.te
module httpd 1.0;

require {
	type httpd_t;
	type vmblock_t;
	class dir { getattr search read open write remove_name add_name };
	class file { getattr read open write lock execute execute_no_trans ioctl append setattr create unlink };
}

#============= httpd_t ==============
allow httpd_t vmblock_t:dir { getattr search read open write remove_name add_name };
allow httpd_t vmblock_t:file { getattr read open write lock execute execute_no_trans ioctl append setattr create unlink };
```

このルールを以下のように2段階でコンパイルし、インストールします。[^5] [^6]

```sh
# 中間ファイルの作成
checkmodule -m -M -o httpd.mod httpd.te
# 中間ファイルをコンパイル
semodule_package --outfile httpd.pp --module httpd.mod
# インストール
sudo semodule --install=httpd.pp
```



SELinuxがpermissiveモードで動いている場合
=========================================
前述の通り、`permissive`モードはログを残すだけでアクセスを遮断しないので、実際にアクセスを遮断する`enforcing`モードに切り替えます。

`sestatus`コマンド実行結果で`Mode from config file: permissive`と表示されていた場合、OS起動時に`permissive`モードになります。設定ファイルを編集して`enforcing`モードで起動するようにしておきます。

```ini:/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 - SELinux is fully disabled.
SELINUX=enforcing
# ↑この項目を、permissiveからenforcingに変更する

# SELINUXTYPE= type of policy in use. Possible values are:
#	targeted - Only targeted network daemons are protected.
#	strict - Full SELinux protection.
SELINUXTYPE=targeted
```

`getenforce`コマンドを実行して`Permissive`と表示された場合、以下のコマンドを実行して動作モードを変更します。[^1]

```sh
# SELinuxをenforcingモードに（OS終了時まで）
sudo setenforce 1
```

`1`ではなく`0`を指定すれば、再び`permissive`モードに変更できます。



【追記】[mod_ssl](http://httpd.apache.org/docs/current/ssl/ "Apache の SSL/TLS 暗号化 - Apache HTTP サーバ")を利用している場合
===============================================================================================================================
[SSLCertificateFileディレクティブ](http://httpd.apache.org/docs/current/mod/mod_ssl.html#sslcertificatefile)で設定する<i>公開鍵ファイル</i>、[SSLCertificateKeyFileディレクティブ](http://httpd.apache.org/docs/current/mod/mod_ssl.html#sslcertificatekeyfile)で設定する<i>秘密鍵ファイル</i>、および[SSLPassPhraseDialogディレクティブ](http://httpd.apache.org/docs/current/mod/mod_ssl.html#sslpassphrasedialog)で設定する<i>パスフレーズを出力するプログラムファイル</i>へのラベル付けが適切に行われている必要があります。

`SSLPassPhraseDialog builtin`の場合
-----------------------------------
<i>公開鍵ファイル</i>、および<i>秘密鍵ファイル</i>の両方に`httpd_config_t`が貼り付けられている必要があります。

両方のファイルが`/etc/httpd`以下のディレクトリに配置されていれば、`sudo restorecon -R /etc/httpd`を実行すればOKです。

`SSLPassPhraseDialog exec:{プログラムファイルのパス}`の場合
-----------------------------------------------------
<i>公開鍵ファイル</i>に`httpd_config_t`が、<i>秘密鍵ファイル</i>と<i>パスフレーズを出力するプログラムファイル</i>に`httpd_sys_script_exec_t`が貼り付けられている必要があります。さらに以下のルールをインストールしなければならないようです。

```:httpd.te
module httpd 1.0;

require {
    type user_devpts_t;
    type httpd_sys_script_t;
    class capability { dac_override };
    class chr_file { read write };
}

#============= httpd_sys_script_t ==============
allow httpd_sys_script_t self:capability { dac_override };
allow httpd_sys_script_t user_devpts_t:chr_file { read write };
```

`semanage`コマンドを使うと、ラベルを永続的に変更できます。[^1]

```sh:設定例
sudo semanage fcontext --add --type httpd_sys_script_exec_t /usr/local/apache/sbin/pp-filter
sudo restorecon /usr/local/apache/sbin/pp-filter
```



【追記】ソケット通信を利用している場合
======================================
PHPなどのプログラムからWhois検索をしたりするために、ソケット通信が必要になりますが、SELinuxは既定でこれを許可しません。

`sudo setsebool -P httpd_can_network_connect on` を実行し、永続的に許可しておきます。[^7]



【追記】データベースを利用している場合
======================================
PHPなどのプログラムからデータベースに接続するには、以下のルールが必要になります。

```
module httpd 1.0;

require {
    type init_t;
    class unix_stream_socket connectto;
}

#============= httpd_t ==============
allow httpd_t init_t:unix_stream_socket connectto;
```



参考ページ
==========
[^1]: [SELinux - PukiWiki](http://www4413u.sakura.ne.jp/wiki/35.html)
[^2]: [こんなに簡単！SELinuxのラベルつけなおし | hiro345](https://www.sssg.org/blogs/hiro345/archives/4272.html)
[^3]: [SELinuxで強化するソリューション第3回　基本操作とポリシーファイルの設定 (3/3) - ITmedia エンタープライズ](http://www.itmedia.co.jp/enterprise/articles/0503/25/news080_3.html)
[^4]: [EZ-NET： SELinux に新しい許可設定を追加する (CentOS 5.4) : SELinux の使い方](http://network.station.ez-net.jp/os/linux/security/selinux/allow/centos/5.4.asp)
[^5]: [SELinuxのポリシー・モジュールを自作する - わんこいん](http://wankomagic.hatenablog.com/entry/2013/05/02/012203)
[^6]: [SELinuxと格闘 その２ jumble/ウェブリブログ](http://jumble.at.webry.info/201203/article_8.html)
[^7]: [[php] phpでfsockopen関数を使うとPermission deniedでうまくいかない件 | 迷走男子！！](http://tazakazushi.net/php-selinux.html)