SELinux のモジュールとは
SELinux のポリシールールはいくつかの種類に分かれます。
ポリシーの大部分を占めるプリセットされた "Monothilic ポリシー"、必要に応じて追加できる "Loadable Module ポリシー"、条件によって (Boolean の値が 0 か 1 かによって) 使われるかどうかが決まる "Conditional ポリシー" 等です。
audit2allow コマンド等を使って手動で追加する許可ルールは Loadable Module ポリシーに該当します。
例えば以下のようなログ (audit.log) が ausearch コマンドにより表示されたとします。(-m avc は表示を SELinux による拒否ログに限定するオプション、-ts today は表示を今日のものに限定するオプション)
[root@localhost ~]# ausearch -m avc -ts today ---- time->Sun Jul 15 03:49:01 2018 type=SYSCALL msg=audit(1531594141.821:3548): arch=c000003e syscall=49 success=noexit=-13 a0=3 a1=7ffc6e936700 a2=6e a3=0 items=0 ppid=15845 pid=15846 auid=0 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=(none) ses=426 comm="chronyc" exe="/usr/bin/chronyc" subj=system_u:system_r:logrotate_t: s0-s0:c0.c1023 key=(null) type=AVC msg=audit(1531594141.821:3548): avc: denied { write } for pid=15846 comm="chronyc" name="chrony" dev="tmpfs" ino=14791 scontext=system_u:system_r:logrotate_t:s0-s0:c0.c1023 tcontext=system_u:object_r: chronyd_var_run_t:s0 tclass=dir
上記は SELinux により拒否された操作内容ですが、SELinux のトラブルシュートの単純な解決策の 1 つは、上記のログで拒否された内容を許可するルールを追加することです。
具体的には、許可ルールを Loadable モジュールとしてインストールすることなのですが、結構簡単に実現できます。
まずこのログの内容を紐解きます。
- 操作を拒否されたプロセス (comm) = chronyc
- comm のコンテキスト (scontext, Source Context) = system_u:system_r:logrotate_t:s0-s0:c0.c1023
どこに対する操作なのかというのは ino=14791 がヒントになります。これは i-node 番号ですので、find / -inum 14791 でパスを見つけられます。
[root@localhost ~]# find / -inum 14791
/run/chrony
この環境では、/run/chrony だとわかります。
ls -Z /run | grep chrony で確認してみると、この /run/chrony のコンテキスト (tcontext) は "system_u:object_r:chronyd_var_run_t:s0" であることがわかります。
どのような操作が拒否されたかというのは、tclass と avc denied の後の {} 中カッコを見るとわかります。tclass は SELinux で定義されている操作の種類 (ObjectClass), 中カッコの中身は具体的なパーミッションです。
ここでは tclass=dir つまりディレクトリに対する操作、具体的なパーミッションは書き込み (write) です。
クラスとパーミッションの詳細については下記ページに載っています。
つまり、scontext=logrotate_t から tcontext=chronyd_var_run_t への許可ルールを作ればよいのです。
このあたりの意味が分からない方は、以下のSELinuxの仕組みの基礎ページをご参照下さい。
audit2allowを使ったモジュール追加方法
この方法はとても簡単です。ログの出力を、audit2allow -M [モジュール名 (名称は任意)] オプションをつけてパイプで流し込み、生成された [モジュール名].pp ファイルを semodule -i コマンドでインストールするだけです。
まず audit2allow をインストールします。
[root@localhost ~]# dnf -y install policycoreutils-python-utils
次に audit.log の出力 (ausearch) をパイプで audit2allow に渡します。
以下は chrony-patch という名称で定義する場合の例です。
[root@localhost ~]# ausearch -m avc -ts today | audit2allow -M chrony-patch ******************** IMPORTANT *********************** To make this policy package active, execute: semodule -i chrony-patch.pp [root@localhost ~]# ls chrony-patch.pp chrony-patch.te [root@localhost ~]# cat chrony-patch.te module chrony-patch 1.0; require { type logrotate_t; type chronyd_var_run_t; class dir write; } #============= logrotate_t ============== allow logrotate_t chronyd_var_run_t:dir write; [root@localhost ~]# semodule -i chrony-patch.pp [root@localhost ~]#
まず "audit2allow -M chrony-patch" コマンドこれにより、chrony-patch.te ファイルと chrony-patch.pp ファイルが生成されます。
.teファイルはテキストベースで、人間にわかる形式での許可ルール定義が書かれており、.ppファイルはバイナリで、semodule -i コマンドでモジュールをインストールできる状態で書かれています。
ではモジュールがインストールされている状態を見てみましょう。
[root@localhost ~]# semanage module -l | grep chrony-patch
chrony-patch 400 pp
[root@localhost ~]# sesearch --allow | grep "logrotate_t chronyd_var_run_t"
allow logrotate_t chronyd_var_run_t : dir write ;
(audit2allow を使わない) .teファイルからのモジュールルール追加方法
これは少し中級者向けですが、.teファイルを自ら作成し、コンパイルする方法です。
フォーマットは前述の通りで、まずは module [モジュール名] [モジュールバージョン]、その後に許可ルールで使うタイプやクラスを require { } で定義、最後に許可ルール、という流れです。
問題はコンパイル方法ですが、これには selinux-policy-devel パッケージが必要になります。
# dnf -y install selinux-policy-devel
そしてこの .te ファイルがある場所で make します。
[root@localhost ~]# make -f /usr/share/selinux/devel/Makefile Compiling targeted chrony-patch module /usr/bin/checkmodule: loading policy configuration from tmp/chrony-patch.tmp /usr/bin/checkmodule: policy configuration loaded /usr/bin/checkmodule: writing binary representation (version 19) to tmp/chrony-patch.mod Creating targeted chrony-patch.pp policy package rm tmp/chrony-patch.mod.fc tmp/chrony-patch.mod [root@localhost ~]# ls -l 合計 23 -rw-------. 1 root root 1424 6月 17 20:03 anaconda-ks.cfg -rw-r--r--. 1 root root 0 7月 15 21:26 chrony-patch.fc -rw-r--r--. 1 root root 23 7月 15 21:26 chrony-patch.if -rw-r--r--. 1 root root 7006 7月 15 21:26 chrony-patch.pp -rw-r--r--. 1 root root 191 6月 22 22:22 chrony-patch.te drwxr-xr-x. 2 root root 75 7月 15 21:26 tmp [root@localhost ~]#
すると .pp ファイルが生成されますので、先ほどと同じように semodule -i でインストールします。
# semodule -i chrony-patch.pp
追加したモジュールの内容を確認する
semodule -i でインストールしたモジュールは、 /etc/selinux/targeted/active/modules/400/ 配下に格納されます。"400"というのは優先度で、semodule で -X オプションで指定しない限りはデフォルトで優先度は400になります。
[root@localhost ~]# ls -l /etc/selinux/targeted/active/modules/400/chrony-patch/
合計 12
-rw-------. 1 root root 107 7月 28 23:55 cil
-rw-------. 1 root root 1912 7月 28 23:55 hll
-rw-------. 1 root root 2 7月 28 23:55 lang_ext
[root@localhost ~]#
上記ディレクトリに格納されたモジュールは cil と hll という中間言語 (バイナリ) に変換されていますので、ポリシーの中身をテキストベースで見ることはできません。
では .pp ファイルや .te ファイルを無くしてしまった場合はどのように追加したモジュールの確認をすればよいのでしょうか?
.pp ファイルを .te ファイルに変換はできなそうですが、以下のように sedismod コマンドを使うことで、似た形で出力することはできました。
[root@localhost ~]# sedismod chrony-patch.pp Reading policy... libsepol.policydb_index_others: security: 0 users, 1 roles, 2 types, 0 bools libsepol.policydb_index_others: security: 0 sens, 0 cats libsepol.policydb_index_others: security: 2 classes, 0 rules, 0 cond rules libsepol.policydb_index_others: security: 0 users, 1 roles, 2 types, 0 bools libsepol.policydb_index_others: security: 0 sens, 0 cats libsepol.policydb_index_others: security: 2 classes, 0 rules, 0 cond rules Binary policy module file loaded. Module name: chrony-patch Module version: 1.0 Select a command: 1) display unconditional AVTAB 2) display conditional AVTAB 3) display users 4) display bools 5) display roles 6) display types, attributes, and aliases 7) display role transitions 8) display role allows 9) Display policycon 0) Display initial SIDs a) Display avrule requirements b) Display avrule declarations c) Display policy capabilities l) Link in a module u) Display the unknown handling setting F) Display filename_trans rules f) set output file m) display menu q) quit Command ('m' for menu): 1 unconditional avtab: --- begin avrule block --- decl 1: allow [logrotate_t] [chronyd_var_run_t] : [dir] { add_name write }; allow [logrotate_t] [chronyd_var_run_t] : [sock_file] { create }; Command ('m' for menu): a avrule block requirements: --- begin avrule block --- decl 1: commons: classes: sock_file{ create } dir{ add_name write } roles : types : logrotate_t chronyd_var_run_t users : bools : levels : cats : Command ('m' for menu): q [root@localhost ~]#
なお、.ppファイルも無くしてしまった場合は、semodule -E [モジュール名]で引っ張ることができます。
[root@localhost ~]# semodule -E chrony-patch Module 'chrony-patch' does not exist at the default priority '400'. Extracting at highest existing priority '400'. [root@localhost ~]# ls chrony-patch.pp
コメント