SELinuxシリーズ
本記事は、SELinuxシリーズの5記事目です。
- Linuxプロセスアクセス制御の概要
- SELinuxの概要
- SELinux Type Enforcement
- SELinuxの実践
- (参考) SELinuxのRBAC、UBAC、MLS、MCS ←今ココ
- (参考) SELinux Module Policyのソースコード読解、ビルド
- 参考URL
1〜3記事目は、4記事目を理解するための前提知識をカバーしています。
4記事目が最も重要で、SELinuxの具体的な操作方法やコマンド、トラブルシューティング手順を紹介しています。
5記事目以降は参考情報です。
SELinuxの関連記事は、SELinuxタグから探せます。
一連の記事はFedora環境を前提として書いています。
FedoraやRHELに類するディストリビューションであればほぼ同等の挙動になると思いますが、他のディストリビューションでは挙動に差異がある可能性があるのでご注意ください。
お伝えしたいこと
本記事は、SELinuxのアクセス制御の中でも、TE (Type Enforcement) を除く以下の技術について概要を説明します。
- RBAC (Role-Based Access Control)
- UBAC (User-Based Access Control)
- MLS (Multi-Level Security)
- MCS (Multi-Category Security)
正直、これらの技術について知る必要は基本ありません。
なぜなら、targeted policyにおいて実質的に有効なのはTEとMCSのみであり、MCSはKVMやコンテナ仮想化技術でしか使われていないためです。
KVMやコンテナ仮想化技術を使う方にとってはMCSは重要かもしれませんが、大半の方はTEだけ知っていれば十分だと思います。
言ってしまえば、targeted policyを使うのであれば、本記事に書いてある情報はほぼ不要です。
とはいえ、targeted policyを使っている方にとっても以下の情報は気になるかもしれません。
よろしければ、気になるトピックのみ拾い読みいただければと思います。
- TE以外のアクセス制御技術の概要
- targeted policyではTEとMCS以外実質無効であると判断できた理由
- SELinuxシリーズ
- お伝えしたいこと
- RBAC (Role-Based Access Control)
- UBAC (User-Based Access Control)
- MLS (Multi-Level Security)
- MCS (Multi-Category Security)
- (参考) Constraint Statement
- (参考) mls policyに変更する方法
- まとめ
- 次の記事
RBAC (Role-Based Access Control)
RBACの一般論
一般論からお話すると、RBACとは下図のようなアクセス制御です。
具体例として、インフラ自動化システムを想定したRBACイメージを取り上げます。
RBACにおいては、UserとRoleをそれぞれ別に定義します。
Userはログインに使うもので、ユーザー名と認証キー (ログインパスワード) を持ちます。
Roleは役割を表すもので、各種リソースへの読み書き実行権限を保有します。
そして、UserとRoleを紐付けることで、Userはシステムへのアクセス権限を得るという仕組みです。
Userに対して直接権限を付与ということはなく、必ずRoleに対して権限を付与するのがRBACの原則です。
※製品によっては、Roleへの権限付与を原則としつつもUserへの権限付与を許可しているものもあると思います
組織の規模にも依りますが、Userの数は数百〜数万程度の想定です。
それに対してRoleの数は数〜数十程度の想定です。
組織変更や昇格によってUserの役割が変わったときはUserとRoleの紐付けを変更します。
そして組織やシステムの統廃合などによってリソース構成が変わったときは、Role自体の定義や構成を見直します。
このように、様々な理由からユーザーや権限の構成が変わることがあります。
RBACはUser-Role間の紐付けやRole定義を見直すだけでその変化に追従できるという設定変更の容易さ、そして設計思想のわかりやすさにメリットがあります。
組織やシステムの変更のたびに、数万のユーザー全ての権限設定を個別に修正する必要はありません。
また権限構造がわかりやすいので、設計のブラックボックス化によるセキュリティホールも撲滅しやすいのではないかと思います。
さて、一般論はこの辺にしておきます。
次のセクションから、SELinuxにおけるRBACについて説明します。
SELinuxにおけるRBAC
SELinuxにおけるRBAC (Role-Based Access Control) とは、プロセスが持つSELinux Roleに基づいて、追加のアクセス制御を行う機能です。1,2
例えば、以下のようなSecurity Contextがあったとします。
id -Z # unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023
こちらのコマンド出力は、targeted policyの環境においてログインしたときのSecurity Contextがunconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023
であることを表しています。
ここで真ん中のunconfined_r:unconfined_t
の部分から、unconfined_r
Roleとunconfined_t
Typeが紐付いていると読み取れます。
Roleは定義の段階で紐付け可能なTypeが決まっています。
Linuxのリソースが特定のRoleを持った段階で、そのリソースが取りうるTypeはRoleと紐づいたもののみに限定されます。
SELinux Type Enforcement - #Security Contextの決まり方でも触れたように、子プロセスが発行されるときは親プロセスのSecurity Contextを引き継ぐのが原則です。
つまり、原則としては今回のユーザーが発行するプロセスは基本的にunconfined_r
というRoleを持ちます。
そして、子プロセスはunconfined_r
と紐付け可能なTypeしか持てないということになります。
例えtype_transitionルールによってそのプロセスが別のTypeを持つ条件が揃っていたとしても、そのTypeがunconfined_r
と共存できない限りType Transitionには失敗します。
まとめると、SELinuxにおけるRBACとは、リソースが持つRole (役割) によって取りうるType (権限) を制限するアクセス制御です。
targeted policyにおけるRBAC
本セクションでは、targeted policyに限定したお話をします。
ログインシェルのRoleの決まり方
SELinux Type Enforcement - #Security Contextの決まり方で触れましたが、Roleは以下のように決まります。
- プロセス
- 親プロセスから引き継ぐ
- Role Transitionの条件を満たせば、Roleが変わることもある
- default_roleルールは実質定義されていないので関係ない
- ファイル
- 基本全てobject_rになる
しかし、ログインシェルのSecurity Contextについては別の仕組みがあります。
本セクションでは、その仕組みを簡単に説明します。
まずは、以下の概要図を見てください。
Linuxログインユーザー、SELinux User、Role、Typeの紐付けが示されています。
こちらの概要図は、私がコマンドをいくつか実行しながらざっくり調べて描いたものですので、「だいたいあってる」といった感覚で見てください。
targeted policyのログインシェルの文脈において関係ないものは全て黒く網掛けしました。
(※) 細かいことを言えばtargeted policyでもunconfined_r
からsystem_r
にRole Transitionすることはありますし、ファイルは形式的にobject_r
を持ちますが...
次のセクションに続きます。
targeted policyにおいてRBACが実質無効である理由
前のセクションより、targeted policyにおいてはどのLinuxユーザーでログインしてもunconfined_u
Userとunconfined_r
Roleにひも付きます。
一般ユーザーでも、rootユーザーであってもです。
「targeted policyでは実質的にRBACが無効化されている」根拠はそこにあります。
全てのユーザーがrootと同じroleに属するということは、roleによる権限を区別していないということです。
言い換えれば、RBACの観点では全てのユーザーがrootと同等の権限を持っています。
ちなみに、unconfined_r
自身はそれほど多くのTypeと紐づいていません。
unconfined_r
は圧倒的多数のTypeと紐づくsystem_r
にRole Transitionするためのルールが多数定義されていることで、実質的に管理者と同等数のTypeとの紐付けが可能となっています。
Role Transitionとは、新規プロセスが発行されるタイミングで、条件次第でRoleが変化するルールのことです。
以上が、targeted policyにおいてはRBACが実質的に無効化されている理由でした。
以降のRBACに関するセクションは、targeted policyを使う方にとっては関係ない内容ですので、気になる方のみご覧いただければと思います。
(参考) Role構成の調べ方
#ログインシェルのRoleの決まり方 (targeted policyの例)で示した概要図をどのように調べたか、コマンド実行ログを交えて補足します。
似たような説明は、Gentoo Wiki - SELinux/Users and loginsにもあります。
まず、先に結論を示しておきます。
targeted policyの環境にログインすると、unconfined_u:unconfined_r:unconfined_t
というSecurity Contextになります。
id -Z
unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023
どうしてこの結果になるか、各種紐付け設定を見ながら説明していきます。
LinuxログインユーザーとSELinux Userの紐付けは、semanage login -l
で確認できます3。
sudo semanage login -l # Login Name SELinux User MLS/MCS Range Service # __default__ unconfined_u s0-s0:c0.c1023 * # root unconfined_u s0-s0:c0.c1023 *
root
ユーザーでLinuxにログインすると、SELinux Userはunconfined_u
になります。
__default__
は「その他全てのユーザー」を表し4、unconfined_u
が紐づきます。
つまり、targeted policyにおいてはどのユーザーでログインしても必ずunconfined_u
になります。
semanage login
にオプションを渡して実行すればこの紐付け設定を変えることができますが、mls policyでRBACをフル活用する場合を除いて変える機会はないでしょう。
続いて、SELinux UserとRoleの紐付け情報をsemanage user -l
で確認します。
Labeling MLS/ MLS/ SELinux User Prefix MCS Level MCS Range SELinux Roles guest_u user s0 s0 guest_r root user s0 s0-s0:c0.c1023 staff_r sysadm_r system_r unconfined_r staff_u user s0 s0-s0:c0.c1023 staff_r sysadm_r system_r unconfined_r sysadm_u user s0 s0-s0:c0.c1023 sysadm_r system_u user s0 s0-s0:c0.c1023 system_r unconfined_r unconfined_u user s0 s0-s0:c0.c1023 system_r unconfined_r user_u user s0 s0 user_r xguest_u user s0 s0 xguest_r
1つのSELinux Userに複数のSELinux Roleが紐付いている箇所があります。
例えば、root
Userにはstaff_r
、sysadm_r
、system_r
、unconfined_r
の4つが紐付いています。
実際にログインしたときにどのログインユーザーに紐づくかは、以下のファイルによって制御されているようです。
/etc/selinux/{SELINUXTYPE}/contexts/*_contexts
/etc/selinux/{SELINUXTYPE}/contexts/users/*
これらの設定ファイルにより、unconfined_u
にログインした場合にはsystem_r
ではなくunconfined_r:unconfined_t
が優先的に紐付けられます。
この設定ファイルにより、ログインシェルに紐づくRoleだけでなくTypeも決まります。
SSH経由でunconfined_u
にログインする状況を想定して、いくつか関連するファイルを見てみましょう。
/etc/selinux/targeted/contexts/default_contexts
/etc/selinux/targeted/contexts/openssh_contexts
/etc/selinux/targeted/contexts/users/unconfined_u
まずはdefault_contexts
の抜粋です。
man default_contextsによると、1列目がログインプロセスのContext、2列目以降がログインシェルに割り当てるRoleとTypeの組み合わせです。
2列目以降はスペース区切りで複数の組み合わせが示されていますが、何度か実験して挙動を確認したところ、左に書いてあるものが優先されるようです。
ここでは、user
> staff
> sysadm
> unconfined
の順ですね。
system_r:sshd_t:s0 user_r:user_t:s0 staff_r:staff_t:s0 sysadm_r:sysadm_t:s0 unconfined_r:unconfined_t:s0
続いてopenssh_contexts
を見てみます。
何を意味するかはわかりませんが、紐付け情報ではなさそうなのでスルーします。
privsep_preauth=sshd_net_t
続いて、users/unconfined_u
を見てみます。
ここには、unconfined
しか書かれていません。
system_r:sshd_t:s0 unconfined_r:unconfined_t:s0
ということで、default_contexts
とusers/unconfined_u
にSSHログインに関するルールが書いてありました。
どちらが優先されるかはmanにも書いてありませんでしたが、mls policyの環境で実際に試してみたところusers/unconfined_u
の方が優先されました。
(※) ファイルを書き換えてsshでログインし、id -Z
でSecurity Contextを確認するとわかります
mls policyにおけるRBAC
mls policyではどう変わるのか、簡単に示したいと思います。
まずは概要図を示します。
targeted policyとの主な違いは、以下のとおりです。
unconfined_r
は使われない- Linuxユーザーのrootは、SELinuxユーザーの
root
と紐づく - その他ユーザーは、
user_u
と紐づく root
とstaff_u
にauditadm_r
とdbadm_r
の紐付けが追加されている
guest_u
やxguest_u
など、全く紐付いていないSELinux Userもあります。
これらについては、semanage user
コマンドによって必要に応じてLinux Userとの紐付けを追加して使う想定のようです。
以下にmls policyにおけるコマンド実行結果を示します。
rootユーザーにSSHログインすると、デフォルトではstaff_r
になります。
したがって、rootと言えども管理者権限はなく、色々とできません。
staff_r
にはsudo権限があるので、sudoerファイルを編集してsudoによってroleを手動で変える運用が基本のようです5。
sudoerをどのように編集するかまでは特に調べていません。
id -Z # root:staff_r:staff_t:s0-s15:c0.c1023
非rootユーザーは、user_r
に割り当てられます。
id -Z # user_u:user_r:user_t:s0
ちなみに、user_r
はstaff_r
とは異なりsudoに対応していないので、sudoコマンドを実行しても何も変わりません。
targeted policyの環境であれば一般ユーザーでも実行できるjournalctl
が、mls policyにおいてはsudo -i
(≒su -
) してもできません。
SELinux Roleが変わっていないため、SELinux的には権限昇格できていないからです。
DAC (Discretionary Access Control) では許可されても、MAC (Mandatory Access Control) では拒否されます。
$ id uid=1000(endy) gid=1001(endy) groups=1001(endy),10(wheel),1000(shared) context=user_u:user_r:user_t:s0 $ id -Z user_u:user_r:user_t:s0 $ sudo -i [sudo] password for endy: # id uid=0(root) gid=0(root) groups=0(root) context=user_u:user_r:user_t:s0 # id -Z user_u:user_r:user_t:s0 # journalctl No journal files were opened due to insufficient permissions.
sudoの設定はしていないものの、SELinuxユーザーと紐づく範囲であればnewrole -r
でRoleを付け替えることは可能です。
紐付けはsemanage user -l
で確認できます。
まずはrootユーザーにsu
などを使わず "直接" ログインして、staff_u
が割り当てられた状態にします。
その後、newrole -r
コマンドでsysadm_r
に昇格します。
staff_u
はsysadm_r
とも紐付いているので、これが可能となります。
# id -Z root:staff_r:staff_t:s0-s15:c0.c1023 # newrole -r sysadm_r Password: # id -Z root:sysadm_r:sysadm_t:s0-s15:c0.c1023 # journalctl (ログが出る)
最後に、その他の設定を列挙します。
LinuxユーザーとSELinux Userとの紐付けです。
sudo semanage login -l # Login Name SELinux User MLS/MCS Range Service # __default__ user_u s0-s0 * # root root s0-s15:c0.c1023 *
SELinux UserとRoleの紐付けです。
semanage user -l # Labeling MLS/ MLS/ # SELinux User Prefix MCS Level MCS Range SELinux Roles # guest_u user s0 s0 guest_r # root user s0 s0-s15:c0.c1023 auditadm_r secadm_r staff_r sysadm_r system_r # staff_u user s0 s0-s15:c0.c1023 auditadm_r secadm_r staff_r sysadm_r system_r # sysadm_u user s0 s0-s15:c0.c1023 sysadm_r # system_u user s0 s0-s15:c0.c1023 system_r # user_u user s0 s0 user_r # xguest_u user s0 s0 xguest_r
/etc/selinux/mls/contexts/default_contexts
には、sshログイン時にはデフォルトでuser_r
> staff_r
が書かれています。
# (一部抜粋)
system_r:sshd_t:s0 user_r:user_t:s0 staff_r:staff_t:s0
/etc/selinux/mls/contexts/openssh_contexts
には、targeted policyと同様、重要な情報はありませんでした。
/etc/selinux/mls/contexts/users/root
には、以下のようにコメントアウトされた設定がありました。
# (一部抜粋) # Uncomment if you want to automatically login as sysadm_r #system_r:sshd_t:s0 unconfined_r:unconfined_t:s0 sysadm_r:sysadm_t:s0 staff_r:staff_t:s0 user_r:user_t:s0
指示通りに上記行のコメントを外すと、sshでrootユーザーにログインしたときに最初からsysadm_r:sysadm_t
が割り当てられます。
users/root
の設定は、default_contexts
よりも優先されました。
(参考) Roleの意味の調べ方
Roleの意味は、#ログインシェルのRoleの決まり方と#mls policyの例の概要図に記載の通りです。
以下に情報ソースを貼っておきます。
RHEL8のマニュアルにtargeted policy, mls policyのRoleの権限の一覧表が書いてあったので、参考にしました。
selinux-policy-docs
パッケージをインストールしていれば、一部のRoleの説明をmanでも確認できます。
しかし、manの情報は部分的にしか参考になりません。
- man unconfined_selinux
- man user_selinux
- man sysadm_selinux
- man staff_selinux
- man guest_selinux
- man xguest_selinux
- man auditadm_selinux
- man secadm_selinux
- man webadm_selinux
- man dbadm_selinux
system_u
とobject_r
については、The SELinux Notebookで言及されていました。
psコマンドを実行すると、システムが自動起動しているデーモンやスレッドがsystem_u:system_r
を持っていることがわかります。
ps -eZ | head -3 # LABEL PID TTY TIME CMD # system_u:system_r:init_t:s0 1 ? 00:00:01 systemd # system_u:system_r:kernel_t:s0 2 ? 00:00:00 kthreadd
適当なフォルダでls -Z
を実行すると、全てのファイルがobject_r
を持っていることがわかります。
ls -Z1 / # system_u:object_r:bin_t:s0 bin # system_u:object_r:boot_t:s0 boot # system_u:object_r:device_t:s0 dev # system_u:object_r:etc_t:s0 etc # system_u:object_r:home_root_t:s0 home # system_u:object_r:lib_t:s0 lib # system_u:object_r:lib_t:s0 lib64 # system_u:object_r:lost_found_t:s0 lost+found # system_u:object_r:mnt_t:s0 media # system_u:object_r:mnt_t:s0 mnt # system_u:object_r:usr_t:s0 opt # system_u:object_r:proc_t:s0 proc # system_u:object_r:admin_home_t:s0 root # system_u:object_r:var_run_t:s0 run # system_u:object_r:bin_t:s0 sbin # system_u:object_r:var_t:s0 srv # system_u:object_r:sysfs_t:s0 sys # system_u:object_r:tmp_t:s0 tmp # system_u:object_r:usr_t:s0 usr # system_u:object_r:var_t:s0 var
最後の手段として、アクセス制御ルールをRoleと紐づくTypeなどで検索すると、Roleの役割がざっくりわかります。
# dbadmの役割を調べる sesearch -A -ds -s dbadm_t # Role Transitionの傾向を見る sesearch --role_trans
(参考) RoleとTypeの紐付け制御の実機検証
Roleの定義に含まれないTypeは、Roleと紐付けられないことを実機で確認します。
今回の検証には、プロセスに任意のSecurity Contextを付与するruncon
コマンドを使います。
runcon
は現在のSELinux Userから変更しようとするとエラーになるので、現在のSELinux Userの範囲で試します。
mls policyの環境で非rootユーザーにログインします。
デフォルトでuser_r
が割り当てられます。
後続の検証のため、allow Statementでエラーになってもアクセス拒否されないようにPermissiveにしておきます。
id -Z # user_u:user_r:user_t:s0 setenforce 0 getenforce # Permissive
引き続き、mls policyの環境でrunconを使ってTypeを変えてみましょう。
以下の例では、user_r
と紐付いているssh_home_t
を指定しています。
明らかにデタラメなTypeなので本来であればアクセス拒否されそうですが、Permissiveにしてあるので問題なくpsコマンドが通ります。
ssh_home_t
が紐付いていることは、予めseinfo -xr user_r | grep ssh_home_t
で確認済みです。
runcon -t ssh_home_t ps -ZC ps # LABEL PID TTY TIME CMD # user_u:user_r:ssh_home_t:s0 9264 pts/1 00:00:00 ps
次の例では、user_r
と紐付かないunconfined_t
を割り当てようとしています。
すると、invalid context: ‘user_u:user_r:unconfined_t:s0’
とエラーになります。
この検証結果から、Roleの定義時点で対象のTypeと紐付いていない場合、そのRoleとそのTypeは同じリソース上で共存できないということになります。
そしてこの制限はアクセス拒否ではなくSELinuxのその他のエラーなので、Permissive設定であってもエラーになります。
runcon -t unconfined_t ps -ZC ps # runcon: invalid context: ‘user_u:user_r:unconfined_t:s0’: Invalid argument
UBAC (User-Based Access Control)
UBACの概要
UBAC (User-Based Access Control) は、RBACのオプション的な存在です。
RBACをもってしても、Roleが全く同じSELinux Userが複数存在した場合、それらのUser間の独立性は保たれません。
UBACはSubjectとObjectでSELinux Userが異なる場合に、仮にRoleが同じ場合でも特定のアクセスを拒否する実装です6。
特定ユーザーが侵入されたとしても、UBACの制御によって異なるSELinux Userのリソースを覗き見したり改変したりすることができなくなります。
UBACは、SELinuxのBase Policyをビルドするタイミングで有効/無効を切り替えることができます7。
Fedoraが管理しているSELinux Policyでは、UBACは有効化されています8。
UBACが有効化されると、ソースコード上の随所でif文がTrueになり、追加のconstraintルールが定義されます9。
(※) ソースコードを読む必要はありません
UBACが定義する具体的なルールについては、#UBAC関連のConstraintにて紹介します。
targeted policyにおいてUBACが実質無効である理由
#UBAC関連のConstraintに記載の通り、UBACはSubjectとObjectのSELinux Userが不一致の場合に特定のアクセスを拒否する制御です。
RBACの#ログインシェルのRoleの決まり方にて説明したとおり、targeted policyでは常にログインシェルがunconfied_u
というSELinux Userになります。
つまり、常に同じSELinux Userになるので、UBACのルールによってアクセス拒否されることは基本ありません。
MLS (Multi-Level Security)
MLSとは
MLS (Multi-Level Security) とは、Security ContextのSensitivity Levelを利用したアクセス制御です。10,11,12
TE、MCSと比較して、最も粒度の細かいアクセス制御を実装します。
mls policyにおいては、Sensitivity Levelはs0からs15まであります。
s0が最もSensitivity Levelが低く、s15が高いです。
s15のファイルは、いわゆるトップシークレットで、絶対に流出を防ぎたいファイルです。
そしてs15のプロセスは、高セキュリティ環境で動作することを許可されており、トップシークレットのファイルを読み取れます。
MLSの原則は "no read up, no write down" です。
例えば、あるプロセスのSensitivity Levelがs5だったとします。
このプロセスは自分よりSensitivity Levelの高い、s6以上のファイルからデータを読み取れません。
また、自分よりSensitivity Levelの低い、s4以下のファイルにデータを書き込めません。
自分以下のレベルのファイルへの読み込み、自分以上のレベルのファイルへの書き込みについて、どのように許可/拒否するかはディストリビューションによって差異があるようです。
RHELについては次のセクションで触れます。
MLSは、情報の機密性に応じてファイルのSensitivity Levelをレベル分けし、高レベルのファイルからの情報流出を防ぐことを重視したいときに使う技術です。
そう聞くと「顧客情報や企業秘密を守りたい」という一般的な用途にマッチしそうな気もしますが、大抵の場合は他のアクセス制御で十分といわれています。
MLSを使う団体は、しばしば「軍隊」と表現されます。
私達が扱うには、恐らく難しいものなのでしょう。
RHELにおけるMLS
RHEL8の場合、Sensitivity Levelに基づくアクセス許可は原則として以下のように実装されているようです13。
- プロセスは自身よりレベルの高いファイルを読み取れない (no read up)
- プロセスは自身と同じレベルのファイルにしか書き込めない (write equality)
上記ルールは、原則にあった「プロセスは自身よりも低いレベルのファイルに書き込めない (no write down)」よりも更に強力です。
プロセスが自身より高いレベルのファイルを書き換えることを防ぐので、レベルの低いプロセスによる改ざんも防止するということになります。
詳細なルールは、#MLS関連のConstraintにてサンプルを示していますが、MLSがデフォルトでどのようにルール定義されているかの理解は難しすぎるので諦めました。
targeted policyにおいてUBACが実質無効である理由
targeted policyにおいては、Sensitivity Levelがs0しか存在しないためです。
単一の値では大小関係によってルール設定のしようがないので、targeted policyにおいてはMLSは実質無効と判断しました。
以下がtargeted policyにおけるSensitivity Levelの表示結果です。
seinfo --sensitivity # Sensitivities: 1 # s0
同じコマンドをmls policyで実行すると、以下のようになります。
seinfo --sensitivity # Sensitivities: 16 # s0 # s1 # s2 # s3 # s4 # s5 # s6 # s7 # s8 # s9 # s10 # s11 # s12 # s13 # s14 # s15
Sensitivity Levelの表記
Gentoo Wikiを参考に、Sensitivity Level表記の例を示します。
s0
→ low=s0
, high=s0
s0-s0
→ low=s0
, high=s0
※上と同じs0-s2
→ low=s0
, high=s2
また、mcstransd
デーモンが動作している場合、/etc/selinux/{SELINUXTYPE}/setrans.conf
に従って表示が変わる場合があります。
ファイルの書式は、man setrans.confに書いてあります。
targeted policyにおいては、SystemLowもSystemHighもs0になります。
cat /etc/selinux/targeted/setrans.conf # s0=SystemLow # s0-s0:c0.c1023=SystemLow-SystemHigh # s0:c0.c1023=SystemHigh
mls policyにおいては、また別の意味を持ちます。
SystemLow=s0、SystemHigh=s15です。
cat /etc/selinux/mls/setrans.conf # s0=SystemLow # s15:c0.c1023=SystemHigh # s0-s15:c0.c1023=SystemLow-SystemHigh # s1=Unclassified # (以下略)
MCS Categoryも含めてalias名が定義されていますが、MCS Categoryの表記方法については#Categoryの表記を参照してください。
MCS (Multi-Category Security)
MCS (Multi-Category Security) は、SubjectとObjectの間でCategoryの値を含むか含まないかを比較し、それによってアクセス制御を行います。
TEと比較すると、Typeに加えてCategoryによっても区別されるので、より粒度の細かいアクセス制御が実装されます。
MLSと比較すると、Sensitivity Levelには明確な大小関係があるのに対し、Category間には何の関係もありません。
重要なのはCategoryが一致するかしないかです。
更に言うとCategoryは複数持てるので、含むか含まないかとなります。
MCSのCategoryもmlsconstrainのdom
、domby
の結果に影響するものの、本質的にはMLSとMCSは全く別の概念です。
MCSはtargeted policyのようにMLSが無効 (s0しかない) 環境でも動作します。
デフォルトでは、MCS関連の設定としてはCategoryが1024個作られているのみです。
このCategoryに対して、特に意味づけはされていません。
コマンド実行結果は、targeted policyでもmls policyでも変わりません。
seinfo --category # Categories: 1024 # c0 # c1 # (中略) # c1022 # c1023
categoryに対して意味づけを行うのは、MCSを実装しているアプリケーションによる制御です。
イメージとしては、複数のコンテナプロセスを起動した場合、Type Enforcementでは各プロセスに同じTypeが割り当てられます。
一方、MCSの動作としては各プロセスに異なるCategoryを割り当てます。
結果として、MCSは複数のコンテナを区別し、TEよりも細かい粒度でアクセス制御できるようになります14。
targeted policyにおいてMCSが実質無効である理由
targeted policyにおいてMCSを利用しているのは、RHEL8のマニュアルによると以下のみとのことです。
- OpenShift
- virt (sVirt)
- sandbox
- network labeling
- containers (container-selinux)
上記製品とSELinuxを併用していない限りは、SELinuxを意識する必要はありません。
もし上記製品を利用する場合には、各製品のドキュメントからMCSの扱い方を調べることになると思います。
Categoryの表記
Gentoo Wikiを参考に、Category表記の例を示します。
Categoryの表記 | 意味 |
---|---|
c0 |
c0 |
c0,c15 |
c0 とc15 |
c4.c8 |
c4 〜c8 (c4,c5,c6,c7,c8 ) |
c2,c6.c9 |
c2 とc6 〜c9 (c2,c6,c7,c8,c9 ) |
(参考) Constraint Statement
Constraint Statementは、UBAC、MLSでは特に重要な意味を持ちます。
RBACでも部分的に使われています。
TEの世界では、allow Statementによって各種アクセス許可を定義していました。
Constraint Statementは、このallowに加えて、「allowによってXXを許可する。ただし、許可するのはXXXの場合のみ」の「ただし〜」の条件を定義します。
allow StatementはSubject/ObjectのTypeという条件しか指定できませんでしたが、Constraint StatementはSELinux User, Role, Rangeなども含めてより細かい条件設定が可能です。
Constraint StatementのSyntaxは、以下の構造です15。
constrain class perm_set expression | expr ...;
Constraintの対象をclass
(Object Class) とperm_set
(Permissions) で絞り込み、対象のPermissionsを許可する追加の条件式をexpression
の部分に書きます。
expressionsは、「左辺」、「演算子」、「右辺」の3つの部位で構成されます。
左辺/右辺には以下が入ります。
l1,l2,h1,h2のlow、highの意味については、#Sensitivity Levelの表記にて説明します。
これら4つの演算対象は、正確にはMLS関連のConstraintsを規定するmlsconstrain
Statementで使うものですが、constrain
とmlsconstrain
は構文が一緒なのでここで併せて説明しています。
演算対象 | 意味 |
---|---|
u1 | SubjectのUser |
r1 | SubjectのRole |
t1 | SubjectのType |
l1 | Subjectの "Low" Range |
h1 | Subjectの "High" Range |
u2 | ObjectのUser |
r2 | ObjectのRole |
t2 | ObjectのType |
l2 | Objectの "Low" Range |
h2 | Objectの "High" Range |
演算子は==
(等しい)、!=
(異なる) などが登場します。
他にも、式と式の間をand
やor
で連結して複雑な条件式を組むことがしばしばあります。
後続のセクションで具体例を出してみますが、「こういったものがあるんだなぁ」というぐらいの理解で十分かなと思います。
RBAC関連のConstraint
r1とr2でgrepすると、Role関連のConstraintを確認できます。
Type Transitionなど一部の処理は、SubjectとObjectのRoleが一致していなければ許可されないようなルール付けがされています (r1 == r2
)。
しかし、OR条件でcan_change_process_identity
Attributeに含まれていればOKなど、いくつか例外も認めているようです。
seinfo --constrain | grep -P 'r1|r2' # constrain process dyntransition (r1 == r2 or ( t1 == can_change_process_identity ) and ( t2 == process_user_target )); # constrain process { transition dyntransition noatsecure rlimitinh siginh } (r1 == r2 or ( t1 == can_change_process_role ) and ( t2 == process_user_target ) or ( t1 == cron_source_domain ) and ( t2 == cron_job_domain ) or ( t1 == can_system_change ) and ( r2 == system_r ) or ( t1 == process_uncond_exempt ));
UBAC関連のConstraint
UBAC関連のConstraintは、u1 == u2
という条件式によって実装されています。
SubjectとObjectのSELinux Userが異なる場合に、特定のアクセスを拒否します。
seinfo --constrain | grep -P 'u1|u2' # constrain alg_socket { create relabelto relabelfrom } (u1 == u2 or ( t1 == can_change_object_identity )); # constrain appletalk_socket { create relabelto relabelfrom } (u1 == u2 or ( t1 == can_change_object_identity )); # (以下略)
MLS関連のConstraint
MLS関連のConstraintは、mlsconstraint
Statementによって記述されます。
dom
、domby
という見慣れない演算子が出てきますが、これはdom
が>= (larger than or equal to)
、domby
が<= (less than or equal to)
です。
また、RangeにはMCSのCategoryも含みますが、Category間には大小関係の概念はありません。
dom
の場合は、左辺のCategoryが右辺のCategoryと同じか内包している関係を表します。
domby
の場合は、その逆です。
上記のdom
とdomby
の意味は、各種ドキュメントでdominates
とdominated by
について説明している言い回しから推定しました。16,17
MLSのSensitivity Levelは、LowからHighまで範囲で値を取りうるため、以下のようにl1とh2を比較したりと条件が複雑に見えます。
そしてあまりにもルールが多いため、これを理解するのは諦めました。
seinfo --constrain | grep mlsconstrain # mlsconstrain association polmatch (l1 dom l2 and ( h1 domby h2 )); # mlsconstrain association recvfrom (l1 dom l2 and ( l1 domby h2 ) or ( t1 == mlsnetreadtoclr ) and ( h1 dom l2 ) or ( t1 == mlsnetread ) or ( t2 == unlabeled_t ));
MCS関連のConstraint
MCS関連のConstraintは存在しません。
Categoryに基づいたアクセス制御は、MCSを認識するアプリケーション側の実装で行います。
詳細は、#MCS (Multi-Category Security)を参照してください。
(参考) mls policyに変更する方法
実用上使うことはないと思いますが、やり方だけ触れておきます。
試す場合は、クローンバックアップを取得の上、VMなどでお試しください。
今回はmlsの例で説明しますが、minimum policyに変更したい場合はmlsの部分をminimumに読み替えれば同じ手順で対応できます。
まず、有効化するpolicyに応じて必要なパッケージをインストールします。
これによって/etc/selinux/mls
配下にSecurity Policyなど、mls policyの動作に必要なファイルがインストールされます。
sudo dnf install selinux-policy-mls
続いて、/etc/selinux/config
を以下のように書き換えます。
SELINUX=permissive SELINUXTYPE=mls
いきなりenforcingにするのは危険なので、permissiveにしておきます。
permissiveは、アクセス制御違反が発生したときに監査ログは出すが、実際にはアクセス拒否しないモードです。
Linuxの起動に必要な処理がSELinuxによって拒否され、起動自体に失敗するという最悪の自体を防ぐための保険として、permissiveにします。
(※) SELinux起因でLinuxが起動できなくなったときは、Boot EntryからKernel Command Line Parameterにenforcing=0
を追記することで、SELinuxをPermissiveモードにした上でLinuxを起動できます。その後は任意の手順でLinuxを復旧できます。手順の詳細は、SELinuxの実践 - #一時的に無効化する方法を参照してください。
続いて、/.autorelabel
ファイルを配置します。
mls policyにおいてはType以外のSecurity Contextも重要になるので、-F
を指定することでTypeだけでなくUser, Role, Rangeも全てRelabel対象にします。
sudo fixfiles -F onboot
この状態でOS再起動すると、Linuxブート中にrelabel処理が走ります。
SELINUXTYPEを変えてOS再起動しただけでは、File Contextは書き換わりません。
relabelせずにOS再起動すると、想定外のFile Contextが割り当てられていることによってアクセス拒否が大量に発生します。
SELINUXTYPEを変えるとき、SELinuxをdisabledからenforcingへ変更する時は、必ずrelabelするようにしてください。
relabelについて、詳細はSELinux Type Enforcementの「File Contextとrelabel」を参照してください。
最後に、OS再起動します。
sudo reboot
relabel処理を経て、OSが起動してきます。
journalctl -qen all -t audit -g denied
やjournalctl -qen all -t setroubleshoot
でSELinux周りの監査ログの状況を確認しつつ、問題なさそうであればSELinuxを有効化します。
(※) 正直、大量のアクセスエラーが出ます。でも大丈夫です。検証用のVMでしたらそのまま有効化しちゃってください
setenforce 1
mls policyの検証をする場合、/etc/selinux/config
にてSELINUX=enforcing
にしないで運用するのがおすすめです。
mls policyでLinuxの起動処理が正しく完了するか調べたいのであれば別ですが、そうでなければ必要のときのみEnforcingにし、何があっても電源OFF/ONでPermissiveに戻せる状態にしておくと安心だと思います。
実際、上述の手順でrelabelも含めて対応したとしても、mls policyにおいては大量の監査ログが発生します。
以上でpolicy変更は完了です。
ここで紹介した手順は、以下のリンクを参考にしました。
RHEL8 - Using SELinux - Switching the SELinux policy to MLS
まとめ
RBAC、UBAC、MLS、MCSの概要について紹介しました。
RBAC、UBAC、MLSについては、targeted policyにおいては実質無効である理由についても触れました。
結局のところ、targeted policyにおいてはTEとMCS以外を使うことはありません。
MCSも以下の製品にしか使われないので、MCSも関係なければTEのみ意識すれば十分です。
- OpenShift
- virt (sVirt)
- sandbox
- network labeling
- containers (container-selinux)
「SELinuxはTEを覚えましょう」というのが本記事の一番言いたいことです。
次の記事
SELinuxのアクセス制御ルールをソースコードから書かなければならない場面がごく稀に存在します。
そういった状況に対処できるよう、基本的なファイル構成やソースコード記述のお作法について概要レベルで紹介します。
基本的にはKernel Policy LanguageとM4をカバーします。
CILについては、2022年1月現在ほとんど使われていないので、概要のみ紹介します。
-
Gentoo Wiki - SELinux/Role-based access control - #RBAC in SELinux↩
-
GitHub - fedora-selinux/selinux-policy - constraints - #L29↩
-
The SELinux Notebook - Multi-Level and Multi-Category Security↩
-
Gentoo Wiki - SELinux/Tutorials/SELinux Multi-Level Security↩
-
RED HAT BLOG - Why you should be using Multi-Category Security for your Linux containers↩
-
The SELinux Notebook - Multi-Level and Multi-Category Security - #Managing Security Levels via Dominance Rules↩
-
Gentoo Wiki - SELinux/Tutorials/SELinux Multi-Level Security - #How MLS restrictions work↩