このappさ。動かないんだぜ。

問題文  

Webアプリケーションをデプロイするために動作環境を用意したのだが、/opt/service/以下にある app アプリケーションを実行しても、正しく起動しないらしい。 また、担当者だったトラブル太郎さんは会社を去ってしまった。

原因を解明し、トラブルを修正してくれ。また、同じトラブルが再起動後にも発生しないようにしてくれ。

初期状態  

  • /opt/service に app , flag , flag.txt がある
  • /opt/service 以下で ./app を正しく実行できない

終了状態  

VMを再起動しても以下が成り立つ

  • ./app でアプリケーションが起動できる
  • curl localhost:3000 すると以下のように表示される(一行目は現在時刻/GMT)
2020-02-29 04:40:55
test
Great!!!!

補足  

  • セキュリティレベルが下がるようなことをしてはいけません
  • セキュリティを保つため、appや関連するファイルへ不必要な権限を与えてはいけません(プロセスをroot権限で動作させたり、ケイパビリティ与えるなど)
  • なぜ正しく動かなかった原因を報告してください。またその原因をどうやって特定したか具体的に報告してください。
  • 解決するにあたって、何をしたか(どのようなコマンドを打ったか、どこのファイルをどう編集したか等)具体的に報告してください
  • /opt/service 以下のファイルの内容を変更してはいけません

解説・解法  

1. 導入・ロール昇格  

SELinuxによって適切に権限が振られてない問題です。

まず、sshして/opt/service/appを実行してみます。

2020/02/29 07:17:42 ReadFile() error=open flag.txt: permission denied

以上のように表示されるため、flag.txtのパーミッションを確認しますが、オーナーは自分で、かつ権限は644です。ここで、おそらくSELinuxによってアクセス権限がないことがわかります。

/var/log/audit/audit.log を開こうとすると権限がなく、またauditdを起動しようとしても、systemctlが発行できないことに気づきます。 またgetenforceするとenforcingです。

ここまでくればSELinux/MACが原因と確定できます。

idコマンドを打つと、自分のSELinuxコンテキストが staff_u:staff_r:staff_t:s0-s0:c0.c1023 とわかります。CentOSの場合、 staff_uはデフォルトでstaff_r sysadm_r system_r unconfined_rの4つのロールに属しているので、まずはadmin用であるsysadm_rへロール昇格をします。

ロール昇格は、通常sudonewroleで行えるので、まずはsudoで試してみます。

$ sudo id -Z
staff_u:staff_r:staff_t:s0-s0:c0.c1023

ロールもドメインも変化していません。次はnewroleを試します。

$ newrole -r sysadm_r
$ id -Z
staff_u:sysadm_r:sysadm_t:s0-s0:c0.c1023

昇格できました。これで作業が始められます。

2. myapp_t へ myapp_flag_t の読み込み権限を与える  

無事staff_rからsysadm_rへ昇格できたので、auditdを再開させます。

$ sudo service auditd start
Permission denied
$ sudo systemctl start auditd
Permission denied
$ sudo tail -f /var/log/audit/audit.log
No such file or directory

auditが起動できず、またログも出ていないため、どのドメインの権限が不足しているかわかりません。audit2allowも使用できません。なので、selinuxからポリシーを吐き出させて静的に解析します。

$ ls -lZ /opt/service
-rwxr-xr-x. ictsc ictsc system_u:object_r:myapp_exec_t:s0 app
-rwxr-xr-x. ictsc ictsc system_u:object_r:usr_t:s0 flag
-rw-r--r--. ictsc ictsc system_u:object_r:myapp_flag_t:s0 flag.txt

ls -lZで、flag.txtファイルのコンテキストmyapp_flag_tがわかるので、myapp_flag_tに対する権限を持っているドメインを探す必要があります。

semodule -E myappでポリシーを抽出、dismodで中のポリシーを確認します。

アプリケーションはmyapp_tドメインで動作するとわかるので、flag.txt(myapp_flag_t)への読み込み許可(read open)を与えるポリシーを生成します。

$ cat hoge.te
policy_module(hoge, 1.0)

require {
        type myapp_t;
        type myapp_flag_t;
}

allow myapp_t myapp_flag_t:file { read open };
$ cp /usr/share/selinux/devel/Makefile .
$ make
$ sudo semodule -i hoge.pp

これで、app自体は動くようになります。

$ exit
$ ./app

3. myapp_tflagの実行権限を与える  

curlを投げて見ると、出力が足りません。

$ curl localhost:3000
2020-02-29 07:58:35
test

appがどのような動作をしているかわからないのでstraceをインストールし見てみます。

$ newrole -r sysadm_r
$ sudo yum install strace.x86_64
$ strace ./app

appがflagを実行しようとしています。 ただ、この状況を解決するために、myapp_tドメインにusr_t全てに対する権限を与えてしまいそうになりますが、その場合myapp_tがすべてのusr_tラベルを持つファイルを実行できてしまうため、セキュリティレベルが下がります。

なので、flagバイナリのコンテキストを押さえ込みます。新しくドメインを作ってもいいですが、既にあるmyapp_exec_tを使いましょう。

$ sudo semanage fcontext -a -t myapp_exec_t /opt/service/flag
$ sudo restorecon -FR /opt/service

restoreconができなかったので、自分に権限を与えます。 以下をteファイルへ追記して再インストールします。ちなみにこのポリシーは、audit2allowが使えないので自力でrefpolicyから推測します。

allow sysadm_sudo_t myapp_flag_t:file getattr;
allow sysadm_sudo_t usr_t:file relabelfrom;
allow sysadm_sudo_t myapp_exec_t:file relabelto;

また、requireに追記することも忘れないようにしまししょう。

以下に現在のteファイルを示します。

$ cat hoge.te
policy_module(hoge, 1.0)

require {
        type myapp_t;
        type myapp_flag_t;
        type myapp_exec_t;
        type usr_t;
        type sysadm_sudo_t;
}

allow myapp_t myapp_flag_t:file { read open };
allow sysadm_sudo_t myapp_flag_t:file getattr;
allow sysadm_sudo_t usr_t:file relabelfrom;
allow sysadm_sudo_t myapp_exec_t:file relabelto;

先ほどと同じ手順で、コンテキストをインストールできたら、appを起動します。

$ make
$ sudo semodule -i hoge.pp
$ exit
$ ./app

curlします。

$ curl localhost:3000
2020-02-29 08:16:38
test
Great!!!!

正しく起動できました。

まとめです。 appが正しく動かない原因は2つ。

  • app (myapp_t) が flag.txt を読めない
  • app (myapp_t) が flag を実行できない

前者はポリシーの作成、後者はfcontextを行いラベル変更で解決できました。

所感  

この問題で一番大事になる部分は、SELinuxの管理を行える権限がないstaff_rロールから、newroleコマンドでsysadm_rへロール昇格ができるかどうか、という点でした。 (結構、setenforceコマンドが実行できないのは問題の不備ではないのかという質問を頂きました)

また、惜しかったのは、問題の中で「不必要な権限を与えてはいけない」と書いてあるのに、usr_tドメインへのexecuteアトリビュート権限をmyapp_tドメインへ与えていたチームです。/opt/service/appは正しく動きますが、問題の指示とあっていないため満点にはしませんでした。

SELinuxは、結構マイナーで触る機会がない技術であるため、ここまで解かれるとは思っていませんでした。作問者として、大変嬉しく思います。