導入
SELinuxは、どのプロセスが、どのファイルやディレクトリに、どのような操作(読み取りや書き込み)ができるかを設定するLinuxの拡張機能です。
ネットワーク関連のデーモン(サービス)のみを管理するtargeted
ポリシーと、SELinuxの機能をフルに利用するstrict
ポリシーがあります。この項目の解説は、targeted
で動作している場合を対象としています。sestatus
コマンド実行結果のPolicy from config file:
項目を見れば、現在どのポリシーが設定されているかわかります。1
SELinuxが無効になっていた場合
sestatus
コマンド実行結果でSELinux status: disabled
と表示されていた場合、SELinuxが無効になっています。
設定ファイルを編集し、ひとまずpermissive
モードに変更します。permissive
は、ポリシー違反の場合でもアクセスを遮断せず、ログに記録するだけの動作モードです。1
# 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
SELinuxでは、プロセスやファイルにSELinuxセキュリティコンテキストというラベルを付けて、どのラベルが付いているかによってアクセス権の有無を判断します。しかし、SELinuxが無効の状態ではラベルが削除されているので、ラベルの再構築が必要です。以下のように、ルートディレクトリに空の.autorelabel
ファイルを作成しておけば、次回起動時にラベルの再構築が行われます。2
# 再起動時にラベルの再構築を行う
sudo touch /.autorelabel
# 再起動
sudo reboot
ラベルの再構築をともなうOSの起動は時間がかかるので、実運用環境ではご注意ください。
Webサーバープロセスからのアクセス許可
/etc/selinux/targeted/contexts/files/file_contexts
には、どこにどのラベルを張り付けると良いか記述されています。このファイルには、Webサーバーに関するラベル付けの設定が初めから含まれているので、Apacheのドキュメントルート等を対象にrestorecon
コマンドを実行するだけでOKです。1
# /var/wwwのラベルの貼り直し
sudo restorecon -R /var/www
以前からWebサーバーを動かしていて、/tmp
ディレクトリ直下以外の場所にセッションファイルを保存するようにしていたなら、それらのファイルを掃除しておきます。
違反ログから新しいルールを定義
基本的にはこれで、Apacheに関するポリシー違反は起きないと思います。私の環境ではポリシー違反が起きたので、新しいルールを定義しました。
ポリシー違反のログは、sudo grep avc: /var/log/messages
を実行、又はAuditが起動していた状態ならsudo ausearch --message AVC
を実行することで確認できます。1
まず、ログをルールに変換するaudit2allowコマンドが使えるようにします。
# 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
を実行、Auditが起動していた状態ならsudo audit2allow --module=httpd --all
を実行すれば、ログに記録されているポリシー違反に対応するアクセス許可ルールが出力されます。3 4
私の環境では以下のような出力になりました。
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;
これはラベルhttpd_t
が付いたプロセスから、ラベルhttpd_sys_script_t
が付いたprocess
への、signull
を許可するというルールになります。
表示されたルールを確認して問題が無ければ、以下のようにしてルールをインストールします。
# ルールが定義されたモジュールを作成
# ※Auditが起動していた状態なら、--dmesg → --all
sudo audit2allow --module-package=httpd --dmesg
# 作成されたモジュールをインストール
sudo semodule --install=httpd.pp
手動で新しいルールを定義
VirtualBoxやVMwareなどの仮想機械で開発環境を構築している場合、共有ディレクトリのラベルは変更することができません。仮想機械上の開発環境において、それらのディレクトリを/var/www
以下に配置しているなら、Apacheからのアクセスを許可するルールを定義する必要があります。
まず以下のようなType Enforcementポリシーファイルを作成します。このファイルのルール定義は、私の開発環境のポリシー違反ログをaudit2allow
コマンドで変換したものです。httpd_t
からvmblock_t
へのアクセスを許可しています。
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スクリプトを利用しているなら、以下のように書き込みや実行も許可する必要があります。
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
# 中間ファイルの作成
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
モードで起動するようにしておきます。
# 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
# SELinuxをenforcingモードに(OS終了時まで)
sudo setenforce 1
1
ではなく0
を指定すれば、再びpermissive
モードに変更できます。
【追記】mod_sslを利用している場合
SSLCertificateFileディレクティブで設定する公開鍵ファイル、SSLCertificateKeyFileディレクティブで設定する秘密鍵ファイル、およびSSLPassPhraseDialogディレクティブで設定するパスフレーズを出力するプログラムファイルへのラベル付けが適切に行われている必要があります。
SSLPassPhraseDialog builtin
の場合
公開鍵ファイル、および秘密鍵ファイルの両方にhttpd_config_t
が貼り付けられている必要があります。
両方のファイルが/etc/httpd
以下のディレクトリに配置されていれば、sudo restorecon -R /etc/httpd
を実行すればOKです。
SSLPassPhraseDialog exec:{プログラムファイルのパス}
の場合
公開鍵ファイルにhttpd_config_t
が、秘密鍵ファイルとパスフレーズを出力するプログラムファイルにhttpd_sys_script_exec_t
が貼り付けられている必要があります。さらに以下のルールをインストールしなければならないようです。
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
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;