SELinuxをRedmine 3.4 + Passenger + CentOS 7の環境で有効にする
「Redmine 3.4をCentOS 7.3にインストールする手順」でインストールしたRedmineの環境で、SELinuxを有効にするための手順です。
このサイトで公開しているCentOS用のRedmineインストール手順ではSELinuxを無効にしています。
「SELinuxが有効な状態の手順を掲載してほしい」との要望はこれまで何度も寄せられていました。ただ、Redmine.JP Blogに掲載してたくさんの方にご覧いただくのであれば、「とりあえず動く」手順ではなく、SELinuxの専門家の方にも納得いただけるものでなければならないと考えていました。
そこで、セキュアOSのコミュニティなどでご活躍でSELinuxに造詣が深い石川さん(@ishikawa84g)に相談したところ、記事の執筆を快く引き受けていただきました。石川さん、ありがとうございました。
現時点ではいくつか未確認の内容が残っていますが、今後解決を進めていきます。SELinuxの活用やRedmineサーバ構築の参考にしていただけると幸いです。
はじめに
本来SELinuxを使う上ではディストリビューション用に調整されたパッケージとそのパッケージ用に調整されたSELinux Policyを使用することが望ましいです。 Phusion Passenger(以下、Passenger)を専用のドメインで動作するにあたり、ディストリビューションが提供するパッケージを使用したいところですが、以下の理由から難しくなっています。
- CentOS 7ではPassengerのパッケージを提供していない
- gemでインストールするため、用意されたポリシーではラベル付けが適切に行えない
- Phusion Passenger公式から提供されているパッケージに含まれているSELinux Passenger moduleの品質が極めて低い
- 本来はこのパッケージを利用したいが利用できない。利用する場合手順が複雑化する。
(SELinux Passenger module自体に若干の改良の余地はありますが) ここではRedmine 3.4をCentOS 7.3にインストールする手順の通りにインストールしたRedmineをSELinuxが有効な環境で動作させるための手順を記載します。
SELinuxの用語
用語 | 説明 |
---|---|
Disabled mode | SELinuxが無効化されてる状態 |
Permissive mode | SELinuxのポリシー違反を監視するが実際のアクセス拒否を行わずにアクセス拒否ログだけを出力する状態 |
Enforcing mode | SELinuxが有効な状態。ポリシーに反するアクセスは全て拒否されログに記録する |
Label | SELinux上のユーザ、ロール、タイプ、レベルをひとまとめにした識別子 |
Domain | プロセスに割り当てられたラベル |
File Context | ファイルに割り当てられたラベル |
SELinuxの有効化
「Redmine 3.4をCentOS 7.3にインストールする手順」を使用して構築した環境ではSELinuxの状態がDisabledに設定されています。SELinuxがDisabledの状態で作成されたファイルにはSELinuxのラベルが付与されません。SELinuxを使用するためには全てのファイルにSELinuxのラベルを付与する必要があります。これを ファイルシステムの再ラベル付け と呼びます。
再ラベル付けをすることでSELinuxが利用できるようになります。再ラベル付け後、初めてシステムにアクセスをするとポリシー違反によっていくつかの必要な処理が拒否される場合があります。アクセス拒否が起動中に発生するとシステム自体が起動できなくなる可能性があります。これを回避するため、SELinuxのモードを必ず Permissive モードに設定します。全ての設定が完了した後で、SELinuxのモードを改めて Enforcing モードに変更します。
SELinuxの起動モードを変更する
OS起動時のSELinuxのモードをPermissiveモードに変更します。
$ sudo sed -i "s/\(^SELINUX=\).*/\1permissive/" /etc/selinux/config
再起動時に再ラベル付けを行う
/.autorelabelというファイルを作成すると次回OS起動時に再ラベル付けを行います。
$ sudo fixfiles onboot
$ sudo shutdown -r 0
起動後の状態を確認する
SELinuxの状態確認
再起動後、SELinuxの状態を確認します。
$ sestatus SELinux status: enabled SELinuxfs mount: /sys/fs/selinux SELinux root directory: /etc/selinux Loaded policy name: targeted Current mode: permissive ← 現在のモードがPermissiveモードになっていることを確認 Mode from config file: permissive ← 設定ファイル上の設定がPermissiveモードになっていることを確認 Policy MLS status: enabled Policy deny_unknown status: allowed Max kernel policy version: 28
Apache及びPassengerのドメインの確認
ps
コマンドに Zオプション
を付与するとプロセスのドメインを確認できます。以下の例ではApache、Passengerいずれもhttpd_tドメインで動作していることがわかります。
$ ps axuZ | grep -e [P]ass -e [h]ttpd
system_u:system_r:httpd_t:s0 root 857 0.0 0.1 294516 6476 ? Ss 23:23 0:00 /usr/sbin/httpd -DFOREGROUND
system_u:system_r:httpd_t:s0 root 1102 0.0 0.1 362928 4776 ? Ssl 23:23 0:00 Passenger watchdog
system_u:system_r:httpd_t:s0 root 1105 0.0 0.3 666752 11772 ? SNl 23:23 0:00 Passenger core
system_u:system_r:httpd_t:s0 apache 1117 0.0 0.1 294516 4396 ? S 23:23 0:00 /usr/sbin/httpd -DFOREGROUND
system_u:system_r:httpd_t:s0 apache 1118 0.0 0.1 294516 4184 ? S 23:23 0:00 /usr/sbin/httpd -DFOREGROUND
system_u:system_r:httpd_t:s0 apache 1119 0.0 0.1 294516 4396 ? S 23:23 0:00 /usr/sbin/httpd -DFOREGROUND
system_u:system_r:httpd_t:s0 apache 1120 0.0 0.1 294516 4140 ? S 23:23 0:00 /usr/sbin/httpd -DFOREGROUND
system_u:system_r:httpd_t:s0 apache 1341 1.5 2.5 388816 99128 ? Sl 23:39 0:02 Passenger AppPreloader: /var/lib/redmine
system_u:system_r:httpd_t:s0 apache 1360 0.2 3.2 458384 127492 ? Sl 23:39 0:00 Passenger RubyApp: /var/lib/redmine
system_u:system_r:httpd_t:s0 apache 1372 0.0 0.1 294516 4400 ? S 23:39 0:00 /usr/sbin/httpd -DFOREGROUND
system_u:system_r:httpd_t:s0 apache 1373 0.0 0.1 294516 4144 ? S 23:39 0:00 /usr/sbin/httpd -DFOREGROUND
system_u:system_r:httpd_t:s0 apache 1374 0.0 0.1 294516 4404 ? S 23:39 0:00 /usr/sbin/httpd -DFOREGROUND
system_u:system_r:httpd_t:s0 apache 1375 0.0 0.1 294516 4404 ? S 23:39 0:00 /usr/sbin/httpd -DFOREGROUND
system_u:system_r:httpd_t:s0 apache 1376 0.0 0.1 294516 4144 ? S 23:39 0:00 /usr/sbin/httpd -DFOREGROUND
system_u:system_r:httpd_t:s0 apache 1419 0.0 0.1 294516 4144 ? S 23:40 0:00 /usr/sbin/httpd -DFOREGROUND
アクセス拒否ログの確認
ausearch
コマンドを使用するとSELinuxのポリシー違反を起こしたアクセスを確認できます。本手順ではRedmineにアクセスした際、これらのログが出力されないよう設定を行います。
以下の出力例ではhttpd_tドメインに対して許可されていないアクセスが拒否されています。(ログ出力のみ)
$ sudo ausearch -m avc | head -n 5
----
time->Mon Dec 4 22:48:24 2017
type=PROCTITLE msg=audit(1512395304.256:213): proctitle=50617373656E6765724167656E74007761746368646F6700202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020
type=SYSCALL msg=audit(1512395304.256:213): arch=c000003e syscall=1 success=yes exit=6 a0=4 a1=7f1fc9665000 a2=6 a3=22 items=0 ppid=9408 pid=9410 auid=4294967295 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=(none) ses=4294967295 comm="PassengerAgent" exe="/usr/local/lib/ruby/gems/2.4.0/gems/passenger-5.1.12/buildout/support-binaries/PassengerAgent" subj=system_u:system_r:httpd_t:s0 key=(null)
type=AVC msg=audit(1512395304.256:213): avc: denied { sys_resource } for pid=9410 comm="PassengerAgent" capability=24 scontext=system_u:system_r:httpd_t:s0 tcontext=system_u:system_r:httpd_t:s0 tclass=capability
SELinux設定の準備
SELinuxの設定を行うにあたり、設定に必要なツールをインストールします。
SELinux操作用各種ツールのインストール
以下のパッケージをインストールします。
$ sudo yum -y install policycoreutils-python setools-console selinux-policy-devel
よく使うコマンドとここでの役割
コマンド | 説明 |
---|---|
semanage | デフォルトのFile Contextを設定する。または表示する |
sesearch | ドメインに許可されたアクセス情報を表示する |
semodule | 新しく定義したモジュールを追加する。または表示する |
getsebool | 定義されたBoolean値を表示する |
setsebool | 定義されたBoolean値を有効/無効に設定する |
restorecon | 特定のディレクトリまたはファイルに対して再ラベル付けを行う |
SELinux設定の事前確認
設定の方針
Passengerをpassenger_tドメインで動作させる。
- Redmineをhttpd_tのままで動作させる方法は対象外とする。
passenger_tが必要とするアクセスは基本的にディストリビューションによって容易されているポリシーを使用し、新規にラベルを作成したりモジュールを作成することは極力避ける。
- やむを得ない場合はlocal moduleのようなものを作成する。
File Contextを設定する場合は、最小特権の観点から適切と思われるものを使用する。
- 基本的にはPassenger moduleで提供されるラベルを使用するが最小特権の原則に沿わないものは使用しない。
- そのような状況では、定義済みのPolicyの中から適切なものを使用する。
各ディレクトリの役割
説明がないディレクトリは読み取りのみが行われます。(ファーエンドテクノロジー提供情報)
現在、Redmineは /var/lib/redmine
に配置されています。このディレクトリのFile Contextはそのまま var_lib_t
を使用し、必要な権限がある場合のみFile Contextを変更します。
※一応、条件に合うFile Contextを設定しますが、Passenger module側で適切なFile Contextを定義した方が良いように見えます。
passenger_tのvar_lib_tに対するアクセス許可情報は以下の通りです。コマンド結果から分かる通り、 passenger_tのディレクトリに対するアクセス許可は定義がありますが、ファイルに対するアクセス許可は定義されていません。
$ sesearch -A -C -s passenger_t -t var_lib_t
Found 4 semantic av rules:
allow domain base_file_type : dir { getattr search open } ;
allow domain var_lib_t : dir { getattr search open } ;
allow passenger_t var_lib_t : dir { getattr search open } ;
allow nsswitch_domain var_lib_t : dir { ioctl read getattr lock search open } ;
ここで言う「比較的適切と考えられるFile Context」は現在のSELinux Passenger module (Version 1.1.1)の定義にある中で適切と思われるものを指定しています。
ディレクトリ | 用途 | 比較的適切と考えられるFile Context |
---|---|---|
.bundle/ | Ruby gem管理用 | httpd_sys_content_t |
.svn/ | svnコマンドでRedmineを取得した場合に生成される | passenger_var_lib_t |
app/ | Redmine本体 | httpd_sys_content_t |
bin/ | Redmine本体 | var_lib_t |
config/ | 設定ファイル | httpd_sys_content_t |
db/ | データベース関係ファイル (データベースとしてSQLiteを使っているときのみ書き込みあり) | passenger_var_lib_t |
doc/ | ドキュメント (本番運用時は参照されない) | var_lib_t |
extra/ | Redmine本体 | var_lib_t |
files/ | アップロードされた添付ファイルを格納 (書き込みあり) | passenger_var_lib_t |
lib/ | Redmine本体 | var_lib_t |
log/ | アプリケーションのログ (書き込みあり) | passenger_log_t |
plugins/ | プラグインインストール用ディレクトリ | httpd_sys_content_t |
public/ | 画像、CSSなど (plugin_assets/ に対して書き込みあり) | httpd_sys_content_t , passenger_var_lib_t |
script/ | Redmine本体 | httpd_sys_content_t |
test/ | テストコード。(本番運用時は参照されない) | var_lib_t |
tmp/ | テンポラリ (書き込みあり) | passenger_tmp_t |
vendor/ | Redmine本体 | lib_t |
SELinuxの設定
Passengerをpassenger_tドメインで動作させる
ApacheからPassengerが呼び出された際、passenger_tにドメイン遷移するようにFile Contextを定義します。
$ sudo semanage fcontext -a -s system_u -t passenger_exec_t -r s0 -f f "/usr/local/lib/ruby/gems(/.*)?/gems/passenger-(.*/)buildout/support-binaries/PassengerAgent"
Aapche Module(mod_passenger)のFile Contextを変更する
gemでインストールしているため、Apache module(mod_passenger.so)のFile Contextが意図しないものに設定されています。これをApache module用のFile Contextを設定します。
$ sudo semanage fcontext -a -s system_u -t httpd_modules_t -r s0 -f f "/usr/local/lib/ruby/gems(/.*)?/gems/passenger-(.*/)buildout/apache2/mod_passenger\.so"
passenger_tからRedmineにアクセスする設定を行う。
各ディレクトリとその配下のファイルに必要な権限
passenger_tが動作する際、アクセスが発生するディレクトリとその際必要になるアクセス権限は以下の通りです。必要となる権限には以下に示すディレクトリは配下に配置されたファイルに対するアクセスも含みます。(個別に定義しても良いですが大変だと思いますのでざっくりとです。)
ディレクトリ | 必要となる主な権限 |
---|---|
app | |
config | read, getattr, ioctl, open |
db | |
files | write, create, unlink, など |
lib | |
log | append |
plugins | read |
public | |
public/plugin_assets | |
tmp | |
vendor | execute |
.bundle | open, ioctl |
.svn | lock, write |
- ここでは適切と考えられるFile Contextに以下を使用しています。
- httpd_sys_content_t: read権限が必要となるディレクトリ
- passenger_var_lib_t: read権限以外にwrite権限が必要になるディレクトリ
- lib_t: execute権限が必要となるディレクトリ。再考の余地あり。
各ファイルに必要な権限
passenger_tが動作する際、アクセスが発生するファイルとそのファイルに対して設定する比較的適切と考えられるFile Contextは以下の通りです。
ファイル | 比較的適切と考えられるFile Context |
---|---|
config.ru | httpd_sys_content_t |
Gemfile | httpd_sys_content_t |
Gemfile.lock | httpd_sys_content_t |
README | httpd_sys_content_t |
File Contextの設定
上記を元にFile Contextを定義します。
$ sudo semanage fcontext -a -s system_u -t httpd_sys_content_t -r s0 "/var/lib/redmine/\.bundle(/.*)?"
$ sudo semanage fcontext -a -s system_u -t passenger_var_lib_t -r s0 "/var/lib/redmine/\.svn(/.*)?"
$ sudo semanage fcontext -a -s system_u -t httpd_sys_content_t -r s0 "/var/lib/redmine/app(/.*)?"
$ sudo semanage fcontext -a -s system_u -t httpd_sys_content_t -r s0 "/var/lib/redmine/config(/.*)?"
$ sudo semanage fcontext -a -s system_u -t passenger_var_lib_t -r s0 "/var/lib/redmine/db(/.*)?"
$ sudo semanage fcontext -a -s system_u -t passenger_var_lib_t -r s0 "/var/lib/redmine/files(/.*)?"
$ sudo semanage fcontext -a -s system_u -t passenger_var_lib_t -r s0 "/var/lib/redmine/lib(/.*)?"
$ sudo semanage fcontext -a -s system_u -t passenger_log_t -r s0 "/var/lib/redmine/log(/.*)?"
$ sudo semanage fcontext -a -s system_u -t httpd_sys_content_t -r s0 "/var/lib/redmine/plugins(/.*)?"
$ sudo semanage fcontext -a -s system_u -t httpd_sys_content_t -r s0 "/var/lib/redmine/public(/.*)?"
$ sudo semanage fcontext -a -s system_u -t passenger_var_lib_t -r s0 "/var/lib/redmine/public/plugin_assets(/.*)?"
$ sudo semanage fcontext -a -s system_u -t passenger_tmp_t -r s0 "/var/lib/redmine/tmp(/.*)?"
$ sudo semanage fcontext -a -s system_u -t lib_t -r s0 "/var/lib/redmine/vendor(/.*)?"
$ sudo semanage fcontext -a -s system_u -t httpd_sys_content_t -r s0 -f f "/var/lib/redmine/config\.ru"
$ sudo semanage fcontext -a -s system_u -t httpd_sys_content_t -r s0 -f f "/var/lib/redmine/Gemfile"
$ sudo semanage fcontext -a -s system_u -t httpd_sys_content_t -r s0 -f f "/var/lib/redmine/Gemfile\.lock"
再ラベル付け
semanage
コマンドで定義したFile Contextを再ラベル付けをし、適用します。ここではfixfiles
コマンドは使用せず、restorecon
コマンドを使用し、オンラインで再ラベル付けを行います。
$ sudo restorecon -RF /var/lib/redmine /usr/local/lib/ruby/gems
$ sudo ls -Zl /var/lib/redmine/
total 36
drwxr-xr-x. 6 system_u:object_r:httpd_sys_content_t:s0 apache apache 67 Dec 5 00:37 app
-rw-r--r--. 1 system_u:object_r:var_lib_t:s0 apache apache 860 Dec 5 00:38 appveyor.yml
drwxr-xr-x. 2 system_u:object_r:var_lib_t:s0 apache apache 58 Dec 5 00:38 bin
drwxr-xr-x. 5 system_u:object_r:httpd_sys_content_t:s0 apache apache 4096 Dec 8 02:55 config
-rw-r--r--. 1 system_u:object_r:httpd_sys_content_t:s0 apache apache 160 Dec 5 00:38 config.ru
-rw-r--r--. 1 system_u:object_r:var_lib_t:s0 apache apache 241 Dec 5 00:38 CONTRIBUTING.md
drwxr-xr-x. 3 system_u:object_r:passenger_var_lib_t:s0 apache apache 38 Dec 5 00:45 db
drwxr-xr-x. 2 system_u:object_r:var_lib_t:s0 apache apache 113 Dec 5 00:38 doc
drwxr-xr-x. 5 system_u:object_r:var_lib_t:s0 apache apache 58 Dec 5 00:38 extra
drwxr-xr-x. 3 system_u:object_r:passenger_var_lib_t:s0 apache apache 35 Dec 8 02:57 files
-rw-r--r--. 1 system_u:object_r:httpd_sys_content_t:s0 apache apache 3622 Dec 5 00:38 Gemfile
-rw-r--r--. 1 system_u:object_r:httpd_sys_content_t:s0 apache apache 4864 Dec 5 00:44 Gemfile.lock
drwxr-xr-x. 7 system_u:object_r:passenger_var_lib_t:s0 apache apache 111 Dec 5 00:38 lib
drwxr-xr-x. 2 system_u:object_r:passenger_log_t:s0 apache apache 45 Dec 5 00:45 log
drwxr-xr-x. 2 system_u:object_r:httpd_sys_content_t:s0 apache apache 20 Dec 5 00:38 plugins
drwxr-xr-x. 8 system_u:object_r:httpd_sys_content_t:s0 apache apache 214 Dec 5 00:38 public
-rw-r--r--. 1 system_u:object_r:var_lib_t:s0 apache apache 275 Dec 5 00:38 Rakefile
-rw-r--r--. 1 system_u:object_r:var_lib_t:s0 apache apache 205 Dec 5 00:38 README.rdoc
drwxr-xr-x. 2 system_u:object_r:var_lib_t:s0 apache apache 32 Dec 5 00:38 script
drwxr-xr-x. 10 system_u:object_r:var_lib_t:s0 apache apache 170 Dec 5 00:37 test
drwxr-xr-x. 9 system_u:object_r:passenger_tmp_t:s0 apache apache 106 Dec 5 00:38 tmp
drwxr-xr-x. 3 system_u:object_r:lib_t:s0 apache apache 20 Dec 5 00:42 vendor
sysfsへのアクセスを許可する
Passengerは起動時、/sys/devices/system/cpu
にアクセスします。以下はaudit.logを抜粋したものです。
※何かは不明です。どうしてか理由を知っている人は教えてください。
$ sudo grep sysfs /var/log/audit/audit.log
type=AVC msg=audit(1512722470.559:351): avc: denied { read } for pid=3556 comm="ruby" name="cpu" dev="sysfs" ino=37 scontext=system_u:system_r:passenger_t:s0 tcontext=system_u:object_r:sysfs_t:s0 tclass=dir```
これを許可するため、redmine_localモジュールを作成します。
$ mkdir ~/redmine
$ cd ~/redmine
$ cat > ~/redmine/redmine_local.te << _EOF_
module redmine_local 1.0;
require {
type passenger_t;
type sysfs_t;
class dir read;
}
allow passenger_t sysfs_t:dir read;
_EOF_
$ make -f /usr/share/selinux/devel/Makefile
$ sudo semodule -i redmine_local.pp
$ sudo semodule -l | grep redmine_local
redmine_local 1.0
上記に加えメール送信をするためのアクセスを許可する
メール送信機能を使用する場合は上記Policyに加えて、メールが送信できるように以下のPolicyを使用します。
※なぜかpassenger_tからcert9.db, nssdbに対して書き込みが発生します。どうしてか理由を知っている人は教えてください。
※メール送信の際、httpd_can_sendmail相当のBoolean値を有効にする必要がありますが、Passenger moduleには存在しないため、代わりにBoolean値nis_enableを有効にします。
$ mkdir ~/redmine
$ cd ~/redmine
$ cat > ~/redmine/redmine_local.te << _EOF_
module redmine_local 1.0;
require {
type passenger_t;
type sysfs_t;
type cert_t;
class file write;
class dir { read write };
}
allow passenger_t cert_t:dir write;
allow passenger_t cert_t:file write;
allow passenger_t sysfs_t:dir read;
_EOF_
$ make -f /usr/share/selinux/devel/Makefile
$ sudo semodule -i redmine_local.pp
$ sudo semodule -l | grep redmine_local
redmine_local 1.0
$ sudo setsebool -P nis_enabled 1
組み込んだmoduleは以下のコマンドで取り除くことができます。不要になった場合は適宜削除ください。
$ sudo semodule -r redmine_local
Apacheを再起動する
Apacheを再起動し、PassengerAgentがpassenger_tドメインで動作していることを確認します。
$ sudo systemctl restart httpd.service
$ ps axuZ | grep -e [P]ass -e [h]ttpd
SELinuxを有効にする
OS起動時のSELinuxのモードをEnforcingモードに変更します。
$ sudo sed -i "s/\(^SELINUX=\).*/\1enforcing/" /etc/selinux/config
SELinuxの状態を確認します。
$ sestatus SELinux status: enabled SELinuxfs mount: /sys/fs/selinux SELinux root directory: /etc/selinux Loaded policy name: targeted Current mode: enforcing ← 現在のモードがEnforcingモードになっていることを確認する Mode from config file: enforcing ← 設定ファイル上の設定がEnforcingモードになっていることを確認する Policy MLS status: enabled Policy deny_unknown status: allowed Max kernel policy version: 28
おわりに
以上でSELinuxの設定は完了です。ここでは、以下のアクセスについてのみ確認しています。
- Apache起動時の
- Redmineへのアクセス
- Redmineへのログイン
- プロジェクトの作成
- チケットの作成(ファイル付き)
- チケット作成・更新時のメール送付
- Plugin(Redmine view customize plugin)の簡単な動作確認
これら以外のアクセスについては確認を行っていないため、処理が拒否される場合があります。その場合は ausearch
コマンドなどを利用し、適切なFile Contextを設定する必要があります。
Appendix
SELinuxについての情報は以下をご覧ください。
- SELINUX ユーザーおよび管理者のガイド
- 動かしてわかるSELinuxセキュリティの基本
- SELinux System Administration - Second Edition Kindle版
以上