はじめに
注意事項
この記事は何らかの理由でSELinuxを利用しなければならない時に発生する、意図せずプログラムが動かなくなる問題を解決するための手段を書いたものである。
作業対象のOSは作業中いつでも停止可能であるものとする。SELinuxの設定作業中に停止不可能とか無茶なので。
また、すべての操作はrootユーザで行っている。SELinuxは「管理者による強制的なアクセス制御」なのでrootユーザが操作しなければならない。
内容は主にCentOS 7で確認し、CentOS 6やFedora 22も一部確認に使用している。 とりあえずSELinuxが利用できる状態になっていないとこの記事の意味がないので、まずはSELinuxの状態の確認方法と、利用できる状態にする方法を説明する。 SELinuxの状態には次の3つある。 SELinuxの状態を確認するコマンドはいくつかある。 前記の通り、状態がenforcingの時だけSELinuxが働いてくれるので、まずはenforcingにしよう。 現在の状態がdisabledの場合、直接disabledからenforcingに変更するとシステムが起動しないことがあり得るので、enforcingにする前にまずは一旦permissiveにする。 permissiveに状態を変化させるには /etc/selinux/config のSELINUX=行が”SELINUX=permissive”となるように書き換え、その後OSを再起動する。 permissiveからenforcingにするには次を実行する。 逆にenforcingからpermissiveにする場合は このようにSELinuxの状態をpermissiveからenforcing、あるいはその逆にenforcingからpermissiveにする場合はOS再起動は不要だが、OS再起動をすると /etc/selinux/config のSELINUX=行の状態に戻ってしまうので、OS再起動後もenforcingのままにするには /etc/selinux/config が”SELINUX=enforcing”となるように書き換える。 /etc/selinux/config ではなく /etc/sysconfig/selinux を書き換えろ、としている文書もあるが、実際には /etc/sysconfig/selinux は /etc/selinux/config のシンボリックリンクである。 また、 /etc/selinux/config のSELINUX=行以外を誤って変更してはならない。 さて、ここからが本題である。 なぜSELinuxによってプログラムがこちらの意図に反して動かなくなることがあるのだろうか。 TEはchmodで設定するファイルのパーミッションとは別に行われるもう一つのアクセス制御であり、SELinuxがenforcingの場合、ファイルのパーミッションによるアクセス制御とTEによるアクセス制御との両方を通過しないとアクセスは成功しないというわけだ。 TEによるアクセス制御はrootユーザにも適用される。あるプロセスの脆弱性を突かれてrootユーザへの権限昇格を許すなどしても、そのプロセスのドメインに許可されていないリソースへのアクセスを防ぐ、というのがTEの狙いである。 SELinuxの世界では、すべてのプロセスとリソースはSELinuxコンテキストと呼ばれるものでラベル付けされている。 psコマンドに-Zオプションを付ければプロセスのSELinuxコンテキストを見ることができる。 lsコマンドに-Zオプションを付ければファイルなどのSELinuxコンテキストを見ることができる。 SELinuxコンテキストは:で区切られた「(_uで終わるもの):(_rで終わるもの):(_tで終わるもの):(s数字で始まるもの):(c数字で始まるもの。一部のみ)」の部分である。 この記事で語る範囲内の話ではSELinuxコンテキストのうち_tで終わるものしか使わないし使われないしなので他は忘れて良い。 TEはホワイトリスト方式であり、ルールが定義されていないすべてのアクセスは「アクセスを許可せず、アクセスが試みられた場合監査ログに記録する」ことになっている。 アクセスを許可する・許可しないという観点と監査ログに記録する・記録しないという観点で、2×2で指定なしも含めて合わせて4種類というわけだ。 個々のルールをSELinuxではアクセスベクタルールと呼んでいる。 つまりSELinuxによってプログラムがこちらの意図に反して動かなくなる状態とは、selinux-policy-xxパッケージによってallowあるいはauditallowされたアクセスベクタルール以外のことを実行しようとした状態、というわけだ。 プログラムがこちらの意図に反して動かない場合、まずはそれがSELinuxによってアクセスが拒否されたせいかどうかを見極める必要がある。 ファイルのパーミッションによるアクセス制御はTEによるアクセス制御より先に判定される。 permissiveに変更するのにはもう一つ意味がある。 もちろんpermissiveにするのは一時的なことであり、問題を解決するためのアクションを取った後 SELinuxによって問題が発生した場合、監査ログを確認することによってその問題がどのようなものかを知ることができる。 見るべきはtype=AVCで始まる行である。次のような意味になり、どのような問題が発生したのかを判別できる。 監査ログは またausearchには豊富な絞り込みオプションがあり、例えば-tsオプションは指定した日時以降の監査ログだけを出力する。 ところで、アクセスが拒否されているのに監査ログには残らないdontauditというアクセスベクタルールの種類があることを説明した。 しかしdontauditルールが無効化されていると、次の項で説明するsetroubleshootが働かなくなるようなので使い分けが必要になる。 監査ログを見て問題の原因が判明すれば、次のようなアクションを行い解決を目指すことになる。 個々の説明は後程行うが、これらのうちどのアクションを選択すれば良いかの指針を与えてくれるsetroubleshoot(実行ファイル名はsetroubleshootd)というものがある。 setroubleshootを利用するにはsetroubleshoot-serverパッケージをインストールする。4 permissive状態にしてあれば問題が複数あったなら、このようなログも複数現れるはずだ。 1つも現れない場合、auditdサービスをreloadしてからもう一度監査ログを出力させてみる。それでも現れなければOS再起動してみる。 この場合、書かれているように このログの中で一番最初に書かれている*****に囲まれて書かれているプラグイン名を調べる。 このプラグイン名から次に行うアクションを決定する。 SELinuxブール値とは、アクセスベクタルールをいくつかまとめて管理者が有効化、あるいは無効化できるようにしたものである。 あるSELinuxブール値をオンにするためには 先程のsetroubleshootのログでは、1つ目のcatchall_booleanに つまりこの問題はhttpd_can_network_connect_cobblerかhttpd_can_network_connectかどちらか1つのSELinuxブール値を有効にすれば解決できる、ということだ。 何故どちらか1つなのか。 次はhttpd_can_network_connect_cobblerブール値の内容である。 また次はhttpd_can_network_connectブール値の内容である。 sesearchの出力結果の読み方は次の通りである。 httpd_can_network_connectブール値の内容のうちの1行はこうなっている。 これはhttpd_can_network_connect_cobblerブール値の唯一の1行の内容と比べると、”タイプ”の項がcobbler_port_tではなく「port_type」となっているところだけが相違点である。 つまり、httpd_can_network_connectブール値のうちの1行である 従ってhttpd_can_network_connect_cobblerブール値を有効にしてできることはhttpd_can_network_connectブール値を有効にすることでも達成できる、というわけである。 で、どちらを有効にすべきかとなると、権限が少ない方とかより相応しい方とかで判断することになるだろう。 「リソースのタイプを正しいものに直す」編の1つ目として、アクセスされたファイルのタイプがデフォルト設定とは異なるパターンがある。 次はhttpdをstartし、/tmp/1.htmlを作って これは/var/www/html/1.htmlの規定のタイプがhttpd_sys_content_tであるにもかかわらず別のものになっているのが問題の原因であり、 もっとも解決にはディレクトリに対して あるディレクトリやファイルの規定のSELinuxコンテキストは この問題はファイルをcp -a 6 やmvで別のディレクトリに持っていくと元のSELinuxコンテキストのままになることが原因になって起こることが多い。 対策として /etc/selinux/restorecond.conf に列記されたパスを自動的に規定のSELinuxコンテキストに修正してくれるrestorecondサービスを使用するのも良い。 「リソースのタイプを正しいものに直す」編の2つ目は1つ目と似ているが、こちらはファイルの既定のタイプすらプロセスのドメインからアクセス禁止となっている場合である。 次は/var/www/htmlではなく/space/1をドキュメントルートにしてhttpdをstartし、 これは そこで元々のドキュメントルートである /var/www/html のタイプを調べて、同じものを今のドキュメントルートに適用すれば良いだろう。 その後、 なお、自分で追加した規定のタイプは ちなみにファイルが作られる時、そのファイルのSELinuxコンテキストは規定のものではなく、そのファイルを置いたディレクトリと同じものになる。 「リソースのタイプを正しいものに直す」編の最後として、利用するポートを標準的ではないポート番号に変更した場合の対応を紹介する。 次は公開ポートを8888にしてhttpdをstartしようとしたがTEのアクセス制御に引っ掛かり失敗した時のsetroubleshootの出力内容である。 対応方法は書かれている通り、 最後に紹介するのは最終手段とすべき、監査ログからアクセスベクタルールを生成し適用する方法となる。 次はreposyncというコマンドを実行した時のsetroubleshootのログ出力 7 だが、「Plugin catchall (100. confidence) suggests」を返してきた場合他に手はないのでこの方法を採ることになる。 書かれているように ただmypolという名前では目的がわかりづらいため適当な名前を付けた方が良いだろう。同じ名前のポリシーモジュールが複数あってもすべてを生かすようには取り込めない、という理由もある。 audit2allowは監査ログファイル /var/log/audit/audit.log をパイプで受け取る以外にもausearchの結果もパイプで受け取ることができる。 設定可能なクラスと操作の種類は次を見れば書かれている。 なお、audit2allowで生成されたteファイルには、ブール値で対応可能なアクセスベクタルールがあった場合それを教えてくれるので、そのような場合はそれに従うと良いだろう。 ここまで見てきた通りSELinuxを使っていて問題が発生した場合、基本的な対応パターンは次のようなものになる。 経験上、permissiveの時は発生しないのにenforcingの時には発生する問題もあったりするが、その場合もsetroubleshootのログを確認して問題解決するまで対応を続けるだけである。 これで問題が解決すれば実は監査ログからアクセスベクタルールを生成する場合以外は監査ログファイル /var/log/audit/audit.log を確認する必要はあまりなかったりする。 ただ、これだけで問題が解決しない場合がある。 dontauditルールは「アクセスを許可せず、アクセスが試みられた場合監査ログに記録しない」ルールである。 その場合は setroubleshootに頼らない場合、次のような方法で対応できる。 監査ログにはプロセスのドメイン、リソースのタイプ、リソースのクラス、アクセス拒否された操作が記録されている。 監査ログのpathにはアクセスされたパスが書かれているので、それに対してrestoreconして解決すればタイプの誤りだったことがわかる。 標準的なディレクトリやポート以外を使っているために問題が発生している場合は、管理者は標準外を使っていることを大抵知っているだろうからそれに応じた対応ができる。 どれにも当てはまらない場合は監査ログからアクセスベクタルールを生成する。 対応が完了したら 最後に。 これまで示してきたやり方のほとんどは正しく対症療法であり、未知の問題の発生が不定期で多種多様に渡るなど、頑張ってSELinuxに対応しようしても現実的に無理、ということもあるだろう。 ただ、問題を起こすのがどのドメインかがわかっていればそのドメインだけをpermissiveにし、他のドメインについては引き続きenforcingでSELinuxを使用し続ける、という対応を取ることもできる。 次のコマンドで特定のドメインをpermissiveにすることができる。この変更は永続的でありOS再起動をしても解除されない。 permissiveになっているドメインは次で確認できる。 ドメインをpermissiveでなくすには次のコマンドを実行する。 古いdnfでは 実際にはもう1つ、「他のルールによるallow, auditallowを許さない」、neverallowという特別なルールもある。 ↩ manを読む限り正確には00:00:01である可能性が高い。なお、指定した時刻ちょうどは検索結果に含まれる。 ↩ setroubleshoot-serverはパッケージ名にserverという文字列を含むが、デスクトップ環境用のGUIアプリケーションを含まない、程度の意味合いだと推測される。デスクトップ環境用のGUIアプリケーションを含んだパッケージはsetroubleshoot。 ↩ 調べがつかなかったが「&&」は逆ポーランド記法の位置に書かれたAND演算子と思われる。 ↩ 正確には-aオプションが含有する「–preserve=context」の影響である。 ↩ reposyncを実行した時はいつでもこうなるわけではない。プロセスのドメインは親プロセスと通常は同じになるので、親プロセスのドメインにアクセスベクタルールの不備があればこうなる、というだけである。 ↩
SELinuxの管理で使用する各種のコマンドは初期からインストールされているものは少なく、またコマンド名がそれを含むrpmパッケージ名と一致しないものが多い。
このような場合はyum install *bin/<コマンド名>
でインストールすることができる。Fedora 22ではdnf install /usr/*/<コマンド名>
とする。SELinuxを利用できる状態にする
SELinuxの状態の確認
もっともこの辺りは各種媒体で良く見かけるものであり(主にSELinuxを無効化するために、だが)、良く知っているならば読み飛ばしてもらって問題ないだろう。
echo $?
で取得できる。SELinuxの状態変更:disabledからpermissiveへ
すでにenforcingなら先に読み進めて構わない。SELinuxの状態変更:permissiveからenforcingへ
# setenforce 1
setenforce 0
である。/etc/selinux/configを書き換える際の注意
シンボリックリンクである /etc/sysconfig/selinux に対してsedを–follow-symlinksオプションや–copy(-C)オプションを付けずに実行する、あるいはcp -aで上書きするなどすると、 /etc/sysconfig/selinux はシンボリックリンクから実ファイルに変化し /etc/selinux/config には反映されないため設定を変更したことにはならない。
誤って変更した場合に起こることの例は http://qiita.com/MurabitoK/items/d44fc01b9e1ac2fdcfab のようなことで、ここにはその復旧方法も書かれている。なぜSELinuxによってプログラムは意図せず動かなくなるのか
Type Enforcement
それはSELinuxがenforcingの場合、プロセスからファイルやディレクトリ、ソケットなど(SELinuxの用語ではこれらをまとめてリソース、種類1つ1つをクラスと呼ぶ)にアクセスできるのが許可された場合だけになるからである。
この仕組みをType Enforcement(以下、TE)と呼ぶ。ドメインとタイプ
# ps -eZ
LABEL PID TTY TIME CMD
system_u:system_r:init_t:s0 1 ? 00:00:02 systemd
system_u:system_r:kernel_t:s0 2 ? 00:00:00 kthreadd
(長いので中略)
system_u:system_r:sshd_t:s0-s0:c0.c1023 4840 ? 00:00:00 sshd
system_u:system_r:sshd_t:s0-s0:c0.c1023 4844 ? 00:00:00 sshd
unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 4846 ? 00:00:00 sshd
unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 4849 pts/0 00:00:00 bash
unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 4852 ? 00:00:00 sshd
unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 4860 ? 00:00:00 sftp-server
unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 4898 pts/0 00:00:00 sudo
unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 4899 pts/0 00:00:00 bash
system_u:system_r:kernel_t:s0 31494 ? 00:00:00 bioset
# ls -Z /tmp
-rwx------. root root system_u:object_r:initrc_tmp_t:s0 ks-script-6Tm7iL
-rwx------. root root system_u:object_r:initrc_tmp_t:s0 ks-script-wKtOJm
-rwxrwxrwx. vagrant vagrant unconfined_u:object_r:user_tmp_t:s0 script.sh
-rwxrwxr-x. vagrant vagrant unconfined_u:object_r:user_tmp_t:s0 vagrant-shell
-rw-r--r--. root root unconfined_u:object_r:user_tmp_t:s0 vboxguest-Module.symvers
-rw-------. root root system_u:object_r:initrc_tmp_t:s0 yum.log
この中で_tで終わるものがプロセスやリソースのタイプである。それぞれのプロセスやリソースはタイプを1つだけ持つ。
プロセスのタイプは特にドメインと呼び、リソースのタイプとは区別するので、ここでも以下そう呼ぶことにする。TEによるアクセス制御
ルールの設定は「どのドメインが、どのタイプの、どのクラスに、どの操作を」という単位ごとに次の3種類の指定を行うことができる。2
アクセスベクタルールが1つも定義されていない状態ではまったく何も動かないので、selinux-policy-xxパッケージ(xxは /etc/selinux/config のSELINUXTYPE=行の値)によって提供されるルールによってシステムを動かすことになる。SELinuxのせいで動かないと思った時に何をすれば良いか
その問題がSELinuxのせいかどうかを見分ける
そのためにはSELinuxの状態をsetenforce 0
で一時的にpermissiveにし、それで動くようになればSELinuxが原因であると言える。
従ってファイルのパーミッションを解決した後になってSELinuxによる問題が顕在化することもある。
enforcingの状態ではSELinuxによる複数の問題があった場合、最初に当たった問題の時点で処理が終了し、監査ログにそれしか残らないのでその分を解決してもまたすぐに次の問題に当たることになる。
permissiveにした場合、複数の問題が監査ログに残るので一括して解決することができる。setenforce 1
でenforcingに戻して実際に問題が解決しているかどうかを確認する必要がある。監査ログを確認する
監査ログはauditdサービスによってデフォルトでは /var/log/audit/audit.log に出力されている。type=AVC msg=audit(1436807141.396:399): avc: denied { open } for pid=2363 comm="httpd" path="/var/www/html/1.html" dev="dm-4" ino=19923324 scontext=system_u:system_r:httpd_t:s0 tcontext=unconfined_u:object_r:user_tmp_t:s0 tclass=file
type=SYSCALL msg=audit(1436807141.396:399): arch=c000003e syscall=2 success=no exit=-13 a0=7fbf95c62290 a1=80000 a2=0 a3=7ffe31bbf530 items=0 ppid=1277 pid=2363 auid=4294967295 uid=48 gid=48 euid=48 suid=48 fsuid=48 egid=48 sgid=48 fsgid=48 tty=(none) ses=4294967295 comm="httpd" exe="/usr/sbin/httpd" subj=system_u:system_r:httpd_t:s0 key=(null)
ausearch -m avc
で見ることもできる。発生日時がわかりやすいフォーマットで表示される分こちらの方が良いと思う。time->Tue Jul 14 02:05:41 2015
type=SYSCALL msg=audit(1436807141.396:399): arch=c000003e syscall=2 success=no exit=-13 a0=7fbf95c62290 a1=80000 a2=0 a3=7ffe31bbf530 items=0 ppid=1277 pid=2363 auid=4294967295 uid=48 gid=48 euid=48 suid=48 fsuid=48 egid=48 sgid=48 fsgid=48 tty=(none) ses=4294967295 comm="httpd" exe="/usr/sbin/httpd" subj=system_u:system_r:httpd_t:s0 key=(null)
type=AVC msg=audit(1436807141.396:399): avc: denied { open } for pid=2363 comm="httpd" path="/var/www/html/1.html" dev="dm-4" ino=19923324 scontext=system_u:system_r:httpd_t:s0 tcontext=unconfined_u:object_r:user_tmp_t:s0 tclass=file
この日時はmm/dd/YYYY HH:MM:SSのフォーマットで指定する。これらをクォートで囲んではならない。日付を省略した場合は「今日の指定した時刻」、時刻を省略した場合は「指定日の0時ちょうど3」として扱われる。日付の代わりにtodayとかyesterdayとかで指定もできる。
監査ログに記録されていないことには対処できないので、enforcing時にアクセス拒否されるのに監査ログには事象が記録されていないようならdontauditルールを無効化してしまうのが良い。
次のコマンドですべてのdontauditルールを無効化することができる。# semodule -DB
すべてのdontauditルールを再度有効化するには次のコマンドを実行する。# semodule -B
setroubleshoot
setroubleshootは監査ログが出力されるタイミングでその対応方法を導き出し /var/log/messages に出力してくれるものである。
インストール後、TEのアクセス制御に引っかかり監査ログが出力された時に /var/log/messages (Fedora 22ではjournalctlを見た方が良い)の中に1つのタイムスタンプに対する出力として次のようなログが現れる。Jul 14 22:06:36 localhost python: SELinux is preventing /usr/sbin/httpd from name_connect access on the tcp_socket port 25151.
***** Plugin catchall_boolean (47.5 confidence) suggests ******************
If you want to allow httpd to can network connect cobbler
Then you must tell SELinux about this by enabling the 'httpd_can_network_connect_cobbler' boolean.
Do
setsebool -P httpd_can_network_connect_cobbler 1
***** Plugin catchall_boolean (47.5 confidence) suggests ******************
If you want to allow httpd to can network connect
Then you must tell SELinux about this by enabling the 'httpd_can_network_connect' boolean.
Do
setsebool -P httpd_can_network_connect 1
***** Plugin catchall (6.38 confidence) suggests **************************
If you believe that httpd should be allowed name_connect access on the port 25151 tcp_socket by default.
Then you should report this as a bug.
You can generate a local policy module to allow this access.
Do
allow this access for now by executing:
# grep httpd /var/log/audit/audit.log | audit2allow -M mypol
# semodule -i mypol.pp
また、CentOS 6だと上記のような行は現れず次のような文字列を含む行が現れる。For complete SELinux messages. run sealert -l 7d8bffe6-d07f-4971-84a5-9fe70ebeba76
sealert -l 7d8bffe6-d07f-4971-84a5-9fe70ebeba76
を実行すれば先程示したようなログが表示される。
上記ログの例ならそのプラグイン名は「catchall_boolean」である。
次項でプラグイン名ごとに何をすべきかを示す。問題解決のために実行すべきアクション
catchall_boolean → SELinuxブール値の変更
あるプログラムにおいてすべての機能を使わない場合、使わない機能のためのアクセスベクタルールは許可しないまま、使う機能のためのアクセスベクタルールだけを許可する、というようにできるだけ少ないアクセス権限で運用することを可能にするために設定されている。setsebool -P <SELinuxブール値名> 1
を、オフにするためにはsetsebool -P <SELinuxブール値名> 0
を実行する。
この-PオプションはOS再起動後も変更を永続化するためのオプションである。setsebool -P httpd_can_network_connect_cobbler 1
を、2つ目のcatchall_booleanにsetsebool -P httpd_can_network_connect 1
を実行しろと書かれている。
あるSELinuxブール値で有効化/無効化できるアクセスベクタルールのリストはsesearch --allow --auditallow -C -b <SELinuxブール値名>
で取得できる。
–allowオプションはallowルールを表示、–auditallowオプションはauditallowルールを表示、-Cオプションは後で説明する、-bオプションはSELinuxブール値名で絞り込むというオプションである。# sesearch --allow --auditallow -C -b httpd_can_network_connect_cobbler
Found 1 semantic av rules:
DT allow httpd_t cobbler_port_t : tcp_socket name_connect ; [ httpd_can_network_connect_cobbler ]
# sesearch --allow --auditallow -C -b httpd_can_network_connect
Found 17 semantic av rules:
DT allow httpd_t port_type : tcp_socket name_connect ; [ httpd_can_network_connect ]
DT allow httpd_sys_script_t netif_t : netif { tcp_recv tcp_send udp_recv udp_send ingress egress } ; [ httpd_enable_cgi httpd_can_network_connect && ]
DT allow httpd_suexec_t client_packet_type : packet { send recv } ; [ httpd_can_network_connect ]
DT allow httpd_suexec_t node_t : node { tcp_recv tcp_send udp_recv udp_send recvfrom sendto } ; [ httpd_can_network_connect ]
DT allow httpd_sys_script_t port_type : tcp_socket { recv_msg send_msg name_connect } ; [ httpd_enable_cgi httpd_can_network_connect && ]
DT allow httpd_sys_script_t port_type : udp_socket { recv_msg send_msg } ; [ httpd_enable_cgi httpd_can_network_connect && ]
DT allow httpd_suexec_t httpd_suexec_t : tcp_socket { ioctl read write create getattr setattr lock append bind connect listen accept getopt setopt shutdown } ; [ httpd_can_network_connect ]
DT allow httpd_suexec_t httpd_suexec_t : udp_socket { ioctl read write create getattr setattr lock append bind connect getopt setopt shutdown } ; [ httpd_can_network_connect ]
DT allow httpd_suexec_t netif_t : netif { tcp_recv tcp_send udp_recv udp_send ingress egress } ; [ httpd_can_network_connect ]
DT allow httpd_sys_script_t client_packet_type : packet { send recv } ; [ httpd_enable_cgi httpd_can_network_connect && ]
DT allow httpd_sys_script_t httpd_sys_script_t : tcp_socket { ioctl read write create getattr setattr lock append bind connect listen accept getopt setopt shutdown } ; [ httpd_enable_cgi httpd_can_network_connect && ]
DT allow httpd_sys_script_t httpd_sys_script_t : udp_socket { ioctl read write create getattr setattr lock append bind connect getopt setopt shutdown } ; [ httpd_enable_cgi httpd_can_network_connect && ]
DT allow httpd_sys_script_t node_t : tcp_socket node_bind ; [ httpd_enable_cgi httpd_can_network_connect && ]
DT allow httpd_sys_script_t node_t : udp_socket node_bind ; [ httpd_enable_cgi httpd_can_network_connect && ]
DT allow httpd_sys_script_t node_t : node { tcp_recv tcp_send udp_recv udp_send recvfrom sendto } ; [ httpd_enable_cgi httpd_can_network_connect && ]
DT allow httpd_suexec_t port_type : tcp_socket { recv_msg send_msg name_connect } ; [ httpd_can_network_connect ]
DT allow httpd_suexec_t port_type : udp_socket { recv_msg send_msg } ; [ httpd_can_network_connect ]
DT allow httpd_t port_type : tcp_socket name_connect ; [ httpd_can_network_connect ]
このように”タイプ”や”ドメイン”の項にありながら_tで終わっていないものはいくつかのタイプやドメインをひとまとめにしてグループ化したものであり、アトリビュートと呼ばれる。
1つのアトリビュートに含まれるタイプやドメインはseinfo -a<アトリビュート名> -x
で取得できる。seinfo -aport_type -x | grep -w cobbler_port_t
を実行すると、port_typeアトリビュートにはcobbler_port_tタイプも含まれていることがわかる。DT allow httpd_t port_type : tcp_socket name_connect ; [ httpd_can_network_connect ]
はhttpd_can_network_connect_cobblerブール値の唯一の1行DT allow httpd_t cobbler_port_t : tcp_socket name_connect ; [ httpd_can_network_connect_cobbler ]
を包含することがわかる。semanage boolean -l
でSELinuxブール値の一覧を取得できるので、その最後に書かれている簡単な説明を読んで決めても良い。restorecon → アクセスされたファイルのタイプがデフォルト設定とは異なる
cp -a /tmp/1.html /var/www/html
を実行した後curl http://localhost/1.html
を実行したがTEのアクセス制御に引っ掛かりエラーになった時のsetroubleshootの出力内容である。***** Plugin restorecon (92.2 confidence) suggests ************************
If you want to fix the label.
/var/www/html/1.html default label should be httpd_sys_content_t.
Then you can run restorecon.
Do
# /sbin/restorecon -v /var/www/html/1.html
/sbin/restorecon -v /var/www/html/1.html
を実行すると解決する、と言っている。restorecon -RFv /var/www/html/
としてしまった方が良いだろう。
restoreconはディレクトリやファイルのSELinuxコンテキストを永続的に規定のものに変更するコマンドである。
-Rオプションはディレクトリ内を再帰的に実行する、-Fオプションは強制的に変更する、-vオプションはSELinuxコンテキストを変更したディレクトリやファイルを表示する。matchpathcon <パス>
で取得できる。パスには*などを使用できる。semanage fcontext -l
で規定のSELinuxコンテキストの一覧が取得できるが、こっちはちょっと読みづらい。
修正してくれるタイミングはおそらくinotifyイベントが発生した時、つまりコピーや移動などを含むファイル作成時や変更時、後はサービス起動時である。
ただし、ハードリンクに対しては働かないことに注意。catchall_labels → 参照ディレクトリを標準以外のものにした場合の対応
curl http://localhost/a.html
を実行したがTEのアクセス制御に引っ掛かりエラーになった時のsetroubleshootの出力内容である。***** Plugin catchall_labels (83.8 confidence) suggests *******************
If you want to allow httpd to have getattr access on the a.html file
Then you need to change the label on /space/1/a.html
Do
# semanage fcontext -a -t FILE_TYPE '/space/1/a.html'
where FILE_TYPE is one of the following: NetworkManager_exec_t, (長すぎるので省略).
Then execute:
restorecon -v '/space/1/a.html'
semanage fcontext -a -t FILE_TYPE '/space/1/a.html
を実行して規定のSELinuxコンテキストを変更してからrestorecon -v '/space/1/a.html
を実行しろ、と言っている。
FILE_TYPEはhttpd_tドメインがアクセスを許可されたタイプであるNetworkManager_exec_t以降から選べば良いのだが大量にありすぎてこの中から選ぶのは困難である。matchpathcon /var/www/html
を実行すると、そのタイプがhttpd_sys_content_tであることがわかる。
なので今のドキュメントルートの規定のタイプをhttpd_sys_content_tにすれば良いが、ディレクトリだけではなくsemanage fcontext -a -t httpd_sys_content_t "/space/1(/.*)?"
としてディレクトリ内のファイルの規定のタイプにも適用されるようにする。この変更は永続的である。restorecon -RFv /space/1
で今のドキュメントルートディレクトリとその中のファイルのタイプを規定のものに変更すれば完了である。semanage fcontext -l -C
で見ることができる。
-lオプションは一覧を表示、-Cオプションは管理者が追加したものだけを表示するオプションである。bind_ports → 利用するポートを標準的でないポート番号に変更した場合の対応
(ソケットもリソースの一種である)***** Plugin bind_ports (92.2 confidence) suggests ************************
If you want to allow /usr/sbin/httpd to bind to network port 8888
Then you need to modify the port type.
Do
# semanage port -a -t PORT_TYPE -p tcp 8888
where PORT_TYPE is one of the following: http_cache_port_t, http_port_t, jboss_management_port_t, jboss_messaging_port_t, ntop_port_t, puppet_port_t.
semanage port -a -t PORT_TYPE -p tcp 8888
を実行すれば良い。この変更は永続的である。
PORT_TYPEはhttpd_tドメインがアクセスを許可されたタイプであるhttp_cache_port_t, http_port_t, jboss_management_port_t, jboss_messaging_port_t, ntop_port_t, puppet_port_tのうちの1つを選べば良いことを示している。
これらの中で意味合いが合っているタイプを選べば良いだろう。各タイプをラベルにしているポートは例えばsemanage port -l | grep -w http_cache_port_t
で得ることができるので、それを参考にするのも良いだろう。catchall → 監査ログからアクセスベクタルールを生成し適用する
最終手段とすべき、というのは最初から自作しているドメインならともかく、selinux-policy-xxパッケージから提供されているドメインでこれをしないとならないことは本来ないはずでSELinuxブール値などで対処すべきだからである。
とはいえselinux-policy-xxパッケージから提供されているものにも結構不備はあるのである。***** Plugin catchall (100. confidence) suggests **************************
If you believe that python2.7 should be allowed read access on the history directory by default.
Then you should report this as a bug.
You can generate a local policy module to allow this access.
Do
allow this access for now by executing:
# grep reposync /var/log/audit/audit.log | audit2allow -M mypol
# semodule -i mypol.pp
grep reposync /var/log/audit/audit.log | audit2allow -M mypol
でアクセスベクタルールのコンパイル済みファイル(これをポリシーモジュールと呼ぶ)であるmypol.ppを生成し、semodule -i mypol.pp
を実行すれば監査ログファイル /var/log/audit/audit.log をgrep reposync
で抽出したものをすべて永続的にallowするようになる。
例えば2015-07-14 12:00:00以降の実行ファイル名がreposyncである監査ログだけをポリシーモジュールにしたい場合、ausearch -m avc -ts 07/14/2015 12:00:00 -c reposync | audit2allow -M reposync
とすれば良い。audit2allow -M (ポリシーモジュール名)
はコンパイル済みの(ポリシーモジュール名).pp以外にソースである(ポリシーモジュール名).teも生成する。
selinux-policy-develパッケージがインストールされていれば、teファイルを必要に応じて手で修正した後、make -f /usr/share/selinux/devel/Makefile (ポリシーモジュール名).pp
で別途コンパイルを行うこともできる。コンパイル後にsemodule -i (ポリシーモジュール名).pp
で取り込むことになる。
これはまだ監査ログには現れていないがいずれ登場するのがわかっている許可したい動作を、先んじて許可するためなどに使用できる。
http://selinuxproject.org/page/ObjectClassesPerms対応のまとめ
setenforce 0
でpermissiveにするsetenforce 1
でenforcingに戻して問題が解消しているかを確認する
つまり監査ログファイルを見ることによっても、setroubleshootのログを見ることによっても問題解決ができないのである。
(監査ログに残らなければsetroubleshootも働かない)semodule -DB
でdontauditルールを無効にして対応することになる。
ただしdontauditルールを無効にした場合もsetroubleshootが働かなくなるので、setroubleshootに頼らない方法で問題解決をしなければならない。setroubleshootに頼らずに問題解決を行う
sesearch --allow --auditallow -C -s <ドメイン> -t <タイプ> -c <クラス> -p <操作>
を実行することにより、対応するSELinuxブール値があるかどうかを知ることができる。
対応するSELinuxブール値が見つかればそれを使って解決すれば良いだろう。semodule -B
でdontauditルールを再度有効化する。SELinuxを諦める……が、敗戦処理にもやり方がある
もちろんsetenforce 0
を実行し、 /etc/selinux/config を書き換えてpermissiveにしてしまえばSELinuxをすぐに諦めることができる。# semanage permissive -a <ドメイン>
# semanage permissive -l
# semanage permissive -d <ドメイン>
参考文献
dnf install *bin/<コマンド名>
でも通ったがFedora 22のdnfでは通らない。バグっぽいが https://github.com/rpm-software-management/dnf/pull/238/files のコメント見ると仕様扱いかなあ。 ↩