えんでぃの技術ブログ

えんでぃの技術ブログ

ネットワークエンジニアの視点で、IT系のお役立ち情報を提供する技術ブログです。

SELinuxのRBAC、UBAC、MLS、MCS

SELinux_logo

SELinuxシリーズ

本記事は、SELinuxシリーズの5記事目です。

  1. Linuxプロセスアクセス制御の概要
  2. SELinuxの概要
  3. SELinux Type Enforcement
  4. SELinuxの実践
  5. (参考) SELinuxのRBAC、UBAC、MLS、MCS ←今ココ
  6. (参考) SELinux Module Policyのソースコード読解、ビルド
  7. 参考URL

1〜3記事目は、4記事目を理解するための前提知識をカバーしています。
4記事目が最も重要で、SELinuxの具体的な操作方法やコマンド、トラブルシューティング手順を紹介しています。

5記事目以降は参考情報です。

SELinuxの関連記事は、SELinuxタグから探せます。

一連の記事はFedora環境を前提として書いています。
FedoraRHELに類するディストリビューションであればほぼ同等の挙動になると思いますが、他のディストリビューションでは挙動に差異がある可能性があるのでご注意ください。

お伝えしたいこと

本記事は、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以外実質無効であると判断できた理由

RBAC (Role-Based Access Control)

RBACの一般論

一般論からお話すると、RBACとは下図のようなアクセス制御です。
具体例として、インフラ自動化システムを想定したRBACイメージを取り上げます。

rbac_general

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の紐付けが示されています。
こちらの概要図は、私がコマンドをいくつか実行しながらざっくり調べて描いたものですので、「だいたいあってる」といった感覚で見てください。

login_targeted

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__は「その他全てのユーザー」を表し4unconfined_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_rsysadm_rsystem_runconfined_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_contextsusers/unconfined_uSSHログインに関するルールが書いてありました。
どちらが優先されるかは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と紐づく
  • rootstaff_uauditadm_rdbadm_rの紐付けが追加されている

login_mls

guest_uxguest_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_rstaff_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_usysadm_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の情報は部分的にしか参考になりません。

system_uobject_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のdomdombyの結果に影響するものの、本質的には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 c0c15
c4.c8 c4c8 (c4,c5,c6,c7,c8)
c2,c6.c9 c2c6c9 (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で使うものですが、constrainmlsconstrainは構文が一緒なのでここで併せて説明しています。

演算対象 意味
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

演算子==(等しい)、!=(異なる) などが登場します。

他にも、式と式の間をandorで連結して複雑な条件式を組むことがしばしばあります。

後続のセクションで具体例を出してみますが、「こういったものがあるんだなぁ」というぐらいの理解で十分かなと思います。

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によって記述されます。
domdombyという見慣れない演算子が出てきますが、これはdom>= (larger than or equal to)domby<= (less than or equal to)です。

また、RangeにはMCSのCategoryも含みますが、Category間には大小関係の概念はありません。
domの場合は、左辺のCategoryが右辺のCategoryと同じか内包している関係を表します。
dombyの場合は、その逆です。

上記のdomdombyの意味は、各種ドキュメントでdominatesdominated 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 deniedjournalctl -qen all -t setroubleshootSELinux周りの監査ログの状況を確認しつつ、問題なさそうであれば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月現在ほとんど使われていないので、概要のみ紹介します。

endy-tech.hatenablog.jp