えんでぃの技術ブログ

えんでぃの技術ブログ

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

SELinux Type Enforcement

SELinux_logo

SELinuxシリーズ

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

  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のアクセス制御において必ず必要となるType Enforcementの理論を説明します。
SELinuxの運用で使う具体的なコマンドやテクニックについては、次の記事であるSELinuxの実践にて紹介します。

SELinuxで実装可能なアクセス制御は、以下のとおり複数存在します。

  • TE (Type Enforcement)
  • RBAC (Role-Based Access Control)
  • UBAC (User-Based Access Control)
  • MCS (Multi-Category Security)
  • MLS (Multi-Level Security)

SELinuxのデフォルト構成 (targeted policy) においては、TEとMCS以外のアクセス制御は実質的に全て無効化されています。
また、MCSを利用するアプリケーションもごく一部のみです (※)
したがって、ほとんどの場合はTEのみ覚えれば十分です。
(※) MCSを使っているのは、主にKVMやコンテナ技術などの仮想化周りです

本記事においてもRBAC、UBAC、MCS、MLSについては概要レベルで軽く触れますが、後半からはTEのみにフォーカスして詳しく説明します。

SELINUXTYPE (policy)

SELINUXTYPE (policy) とは

SELinuxの設定ファイルが/etc/selinux/configにありますが、ここでSELinuxの大まかな設定を変えることができます。
その設定項目の一つがSELINUXTYPEです。
SELINUXTYPEは、単にpolicyと呼ばれることもあります (※)1,2
(※) アクセス制御ルールの定義ファイルであるSecurity Policyと、SELINUXTYPEを表すpolicyは似て非なる言葉なので、混同しないようご注意ください。

SELINUXTYPEが変わることで具体的に何が起こるかというと、読み込まれるSecurity Policyファイルそのものが変わります。
Security Policyのファイルパスは、以下のとおりです。
/etc/selinux/SELINUXTYPE/policy/policy.NN

つまりSELINUXTYPEが変わると、SELinuxのアクセス制御ルールが全く異なる内容に変化します。

SELINUXTYPEは、以下の3種類のいずれかです3
セキュリティは、minimum < targeted < mlsの順に強くなります。
デフォルトはtargetedで、基本的に変更することはありません。
これより先では、特に明記しない限りtargeted policyを前提として説明します。

policy 詳細
minimum
  • MCS policy
  • MCSとTEのみ有効
  • 最低限のModule Policyのみをインストールする
  • ごく一部のプロセスのアクセス制御ルールのみを定義する
targeted
  • MCS policy
  • MCSとTEのみ有効
  • minimumよりも多くのModule Policyをインストールし、より多くのプロセスのアクセス制御ルールを定義する
mls
  • MLS policy
  • TE, RBAC/UBAC, MCS/MLSが全て有効
  • 最も厳しいアクセス制御を実装する
  • デフォルトルールからカスタマイズすることが前提

SELINUXTYPEの確認方法

SELINUXTYPEは、OS起動時に読み込まれる/etc/selinux/configで設定します。
/etc/selinux/configの一部を以下に抜粋します。

# SELINUXTYPE= can take one of these three values:
#     targeted - Targeted processes are protected,
#     minimum - Modification of targeted policy. Only selected processes are protected. 
#     mls - Multi Level Security protection.
SELINUXTYPE=targeted

実際のステータス値としてのSELINUXTYPEは、sestatusで確認できます。
以下の出力は、targetedとして動作しているときのものです。

sestatus
# SELinux status:                 enabled
# SELinuxfs mount:                /sys/fs/selinux
# SELinux root directory:         /etc/selinux
# Loaded policy name:             targeted
# Current mode:                   enforcing
# Mode from config file:          enforcing
# Policy MLS status:              enabled
# Policy deny_unknown status:     allowed
# Memory protection checking:     actual (secure)
# Max kernel policy version:      33

minimum, targeted, mlsの違い

3つのpolicyの違いを下表にまとめました。

SELINUXTYPE TE RBAC UBAC MCS MLS
minimum × × ×
targeted × × ×
mls
  • ×:デフォルトは実質無効
  • △:デフォルトは一部有効
  • ◯:デフォルトで有効

基本はデフォルト値のtargeted policyを使います。
targeted policyはMCSとTEがありますが、基本的にはTEのみ意識すれば十分です。

上表で「×」でマークされた機能が実質無効である根拠については、後続記事の(参考) SELinuxのRBAC、UBAC、MLS、MCSで説明します。
本記事については、上表が正しい前提で話を進めます。

(参考) TEの概要

TE (Type Enforcement)とは、SubjectとObject割り当てられたTypeと呼ばれる識別子に基づき、アクセス可否を制御するSELinuxの最も基本的なアクセス制御実装です。

今の時点では、「SELinuxのアクセス制御は基本的にTEで実装されており、他のアクセス制御は別の観点を追加することでセキュリティを高めている」と理解ください。

TEについて、詳細は#TE (Type Enforcement)以降のセクションで説明します。

(参考) MCSの概要

MCSは、プロセスやリソースをCategoryに分けてアクセス制御を行う技術です4
CategoryはTypeとは独立して定義されます。

MCSは、TEでは同じTypeが割り当てられる関係のプロセスに対して、異なるCategoryを割り当ててより細かくアクセス制御します。

例えばコンテナを複数起動した場合、各コンテナプロセスには同じType、異なるCategoryが割り当てられます5
これによって、MCSはTEよりも細かくコンテナ関連のアクセス制御を実装できます。

MCSを使っている主な技術は、以下のとおりです6

  • OpenShift
  • virt (sVirt)
  • sandbox
  • network labeling
  • containers (container-selinux)

上記から、コンテナやKVMなどの仮想化技術を使っているのでない限りは、MCSを意識する必要はほぼありません。

sVirtの場合、MCSのCategory割り当てのロジックは、SELinuxではなくアプリケーション側に実装されているようです。
Categoryの割り当ては基本的にsVirtの機能で自動的に行われますが、ユーザーが手動で設定することもできるようです7

(参考) RBAC、UBAC、MLSの概要

RBAC、UBAC、MLSもMCSと同様、TEが利用するTypeとは別の識別子に基づいてアクセス制御する技術です。
いずれもデフォルトのtargeted policyでは実質無効化されているため、ここでは扱いません。

これらの用語に関する説明は、後続記事の(参考) SELinuxのRBAC、UBAC、MLS、MCSを参照してください。

(参考) targeted policyの概要

#TE (Type Enforcement)以降のセクション、及び後続記事では、targeted policyを前提に説明を進めます。
この後の説明を読んでいく中で、targeted policyの動きについては自然と理解が深まるはずです。

現時点では、targeted policyについて以下の認識を持っていただければ十分です。

  • デフォルト構成
  • TEとMCSが有効。ほかは全て無効
  • 初期状態のLinuxであればアクセス拒否しないよう十分作り込まれている (mls, minimumでは大量のエラーが出る)

(参考) mls policyの概要

mls policyは、TE、MCS、RBAC、UBAC、MLSの全てが有効化で、最もセキュリティの高い構成です。

一部の企業では使用されていますが、targeted policyでもある程度の効果が得られる点、mls policyの運用の難易度の高さからあまり使われていない印象です。
RHEL8のマニュアルでも、利用する団体の例として「軍隊」が挙げられているほどです8

以降は、mls policyがどのように難しいのかを説明します。

まず、MLSが最小限のデフォルトの設定しかなく、自身でカスタマイズする前提の機能であることが難しさのポイントとして挙げられます。

MLSは、プロセスやファイルなどのリソースにSensitivity Levelを割り当て、セキュリティ強度の上下関係によってアクセス制御する技術です。
デフォルトで最低レベルのs0から最高レベルのs15までLevel自体は作成されますが、後はユーザー自身の手で以下のような設計・実装を行う必要があります。

  • 各種リソースをどのようなLevelをつけるか 9
  • Levelの違いによるアクセス許可/拒否の条件をどのように定義するか (※)

(※) デフォルトでも最低限の基本ルールはあります

他にも以下の点で、mls policyは難しいと思います。

  • RBACの権限設計の難易度が高い
  • RBAC前提の運用フローや手順の設計が大変
  • そもそもmls policyを運用している人が少ないので情報が見つかりにくい
  • mls policyは、targeted policyとは異なりデフォルトで大量のアクセス拒否エラーが出る (※)

(※) RBACの設定起因だと思うのですが、私の実体験としてログイン直後の~/.bash_profileの読み込みがPermission Deniedになりました。他にも大量のエラーが数秒おきに発生しました

mls policyが必要な明確な要件がある場合は別ですが、多くの場合はmls policyを使いません。
targeted policyを使います。

(参考) minimum policyの概要

※このセクションでは、まだ説明していない用語を使用します。Base PolicyとModule Policyについては、後続記事の(参考) SELinux Module Policyのソースコード読解、ビルド - Base PolicyとModule Policyにて説明します。Type Transitionについては、後続セクションの#Type Transitionで説明します。

minimum policyは、Base Policy以外は何もインストールされていないモードです10
httpdなどの代表的なミドルウェアに対するSELinuxのルールは、ほぼ定義されていません。
Linux Kernelやsyslogdなど一部の基本機能のみがアクセス制御対象となり、他のプロセスにはデフォルトのTypeが割り当てられます。

具体的には、ユーザー自身が手動で起動したプロセスには、多くの場合unconfined_t (unconfined = 制限されない) Typeがアサインされてアクセス許可されます。
これはtargeted policyと同様です。

systemdによって自動起動されるようなプロセスの大半は、Type Transitionが働かず親プロセスのsystemdと同じinit_tが割り当てられ、アクセスに失敗するケースが多いです。

なお、minimum policyでModule Policyを一覧表示しようとするとエラーになります。
この出力からも、ほぼBase Policyしか有効化されていないのだと理解できます。

sudo semodule -l
# No modules.

minimum policyの主なユースケースは、SELinuxの開発者が最低限のModule Policyのみ有効化した構成で単体試験するときです。

我々ユーザーがminimum policyを利用することは基本ありません。
なぜなら、targeted policyやmls policyで初期インストールされたModule Policyを後からアンインストールすることはできません。
やるとしたら、私達が独自に開発したModule Policyを追加インストールするのみです。
したがって、仮に独自開発したModule Policyの動作確認をしたい要件があったとしても、わざわざminimum policyで最小構成にする必要がないのです。

私達がminimum policyを使うことはまずありません。
使うとしたらtargeted policyか、mls policyです。

(参考) SELINUXTYPEの変更方法

後続記事のSELinuxのRBAC、UBAC、MLS、MCS - (参考) mls policyに変更する方法にて手順を紹介しています。

mls policyに変更する手順を扱っていますが、他のpolicyについても同様です。

TE (Type Enforcement)

TE (Type Enforcement) は、SubjectとObjectに対してTypeと呼ばれる識別子を割り当て、Typeに基づいてアクセス可否を制御するSELinuxの最も基本的なアクセス制御実装です。

特にデフォルトのtargeted policyにおいては、TEとMCSしか使われません。
そしてMCSは一部のアプリケーションでしか使われません。
つまり、SELinuxのアクセス制御はほぼTEのみで実装されています。

TEでは、以下のようなルールを実装します。
関連するキーワードをカッコ付きで付記していますが、これらの用語については後続のセクションで説明します。

  • Typeの定義 (Type, Attribute)
  • Typeの割り当て方に関する規則 (Type Transition, File Context)
  • Typeに基づくアクセス許可ルール (allow Statement)

一番重要なのは、最後のallow Statementです。
allow Statementが、SubjectのTypeとObjectのTypeの組み合わせに対し、許可されたActionを紐付けて定義します。
そして、allow Statementで明示的に許可されなかったアクセスパターンは、暗黙的に全て拒否されます。

この後のセクションで、TEによるアクセス制御ルールがどのように定義されているのか、順を追って紹介します。

allow Statement

allow Statementは、SELinuxが許可するアクセスを定義します。
ここで許可されなかったアクセスは、全てデフォルトで拒否されます。

setools-consoleパッケージがインストールされていれば、sesearch -Aで表示できます。

デフォルトのallow Statementを一部抜粋します。
このアクセス許可ルールでは、sshdプロセス (Subject) がfile_typeに属するディレクトリ群 (Object) に対してgetattr, open, search権限を許可しています。

allow sshd_t file_type:dir { getattr open search };

allow Statementの構文は、以下のとおりです11
ACLのように、source, target を並べて書きます。
(※) source, target, type, class, permissionなどの用語は、この後順を追って説明します。

allow source_type target_type : class perm_set;

type, class, perm_setを複数並べたいときは、{}で囲ってスペース区切りで列挙します。
今回は、perm_set{ getattr open search }と複数指定されています。

なお、SELinuxにはdeny Statementは存在しません。
既に許可されたアクセスパターンは、後から拒否することはできません。
そして、許可されなかったアクセスパターンがデフォルトで拒否されます12

今回のルールについては、以下のような対応関係になっています。

Syntax Syntaxの意味 今回の値
source_type SubjectのType sshd_t
target_type ObjectのType (※) file_type
class Object Class dir
perm_set 許可するAction getattr
open
search

(※) target_typeにselfというキーワードが指定された場合、「source_typeと同じ」という意味になります

少しややこしいですが、allow StatementのSyntaxでは言葉遣いが若干変わります。
以下の言葉は同じ意味を持ちます。

  • Source = Subject
  • Target = Object
  • Class = Object Class
  • Permissions = Actions

更に新しい用語としてTypeとObject Classが出てきました。
この後のセクションで、関連用語も含めつつ順に説明していきます。

  • Security Context (Label)
  • Type
  • Attribute
  • Object Class
  • Common

Security Context (Label)

Typeの説明をするために、まずはSecurity Contextという概念について紹介します。

Security Contextとは、SELinuxが有効な場合に全てのSubjectとObjectに割り当てられる識別子 (文字列) です13
SubjectとObjectとはすなわち、全てのプロセス、ファイル、ネットワークソケット、ファイルシステム、データベース、ユーザーなど、SELinuxのアクセス制御に関わる全てのリソースです。

Security Contextは、Labelとも呼ばれます。
プロセスにSecurity Contextを割り当てることをLabeling (ラベル付け) と表現することもあります。

Security Contextのフォーマットを以下に示します。
Security Contextは、コロンで区切られた文字列で表現されます。

user:role:type[:range]

各要素の意味は、以下のとおりです。

要素名 意味
user
  • SELinux User
  • xxx_uと表記される
  • Linuxユーザーとは別物
  • Linuxユーザーは、ログイン時に1つのSELinux Userと紐づく
  • SELinuxユーザーは1つか複数のroleと紐づく
  • UBACとRBACに使われる
role
  • SELinux Role (役割)
  • xxx_rと表記される
  • roleは1つか複数のtypeと紐づく
  • RBACに使われる
type
  • SELinux Type
  • xxx_tと表記される
  • SubjectとObjectを識別する
  • アクセス許可設定はTypeを指定して定義する
range
  • Sensitivity Level (※) とCategoryの組み合わせ
  • 表記例: s0s0-s0:c0.c1023など
  • range全体、またはCategoryのみが省略されることもある
  • LevelはMLS、CategoryはMCSで使われる

※Sensitivity Levelは、Security Levelや、単にLevelと表記されることもあります

上述の要素の中で重要なのは、typeのみです。
他の要素は無視して結構です。

なぜなら、targeted policyでは基本的にTEしか使われず、TEで使うのは基本Typeのみであるためです。
RBAC/UBAC、MLS/MCSを使う場合のみ、Type以外の要素が重要になります。

例えば、sshdプロセスのSecurity Contextは、以下のように表されます。
Security Contextは非常に長いですが、この中で重要な情報はsshd_tのみです。

ps -o label,uid,command -C sshd
# LABEL                                   USER COMMAND
# system_u:system_r:sshd_t:s0-s0:c0.c1023 root sshd: /usr/sbin/sshd -D [listener] 0 of 10-100 startups

SELinuxでアクセス制御を実装する際、プロセス名やファイル名を直接指定するのではなく、Security ContextやObject Classを指定してルールを定義します。

例えば今回取り上げた#allow Statementでは、プロセスやファイルが持つTypeを指定して許可設定を記載していました。
冒頭のallow Statementを再掲します。

allow sshd_t file_type:dir { getattr open search };

Type

Typeは、前述のSecurity Contextの3番目の要素です。

Typeの定義は、setools-consoleパッケージが入っていればseinfoコマンドで確認できます。

-xをつけるとより詳細な情報がわかります。
今回はsshd_tに絞って出力していますが、Type名を指定しなければ全て表示されます。

seinfo -t sshd_t

# Types: 1
#    sshd_t

seinfo -xt sshd_t

# Types: 1
#    type sshd_t, polydomain, nsswitch_domain, login_pgm, can_change_object_identity, can_change_process_identity, can_change_process_role, corenet_unlabeled_type, domain, kernel_system_state_reader, netlabel_peer_type, privfd, daemon, syslog_client_type, pcmcia_typeattr_1, ssh_server, unconfined_login_domain, userdom_home_manager_type;

この後もseinfoは何度か出てきますが、以降のセクションでは-xありの出力のみ掲載します。
-xなしの出力は、シンプルに一覧表示するときには便利ですが、先の実行例のように出力を絞っている場合には掲載する意味がないためです。

Type名は以下の命名規則で定義するのが通例です。

  • _tで終わる
  • プロセスのTypeは、プロセス名_t
  • プロセスを起動するための実行ファイルのTypeは、プロセス名_exec_t
  • ログファイルは、プロセス名_log_t
  • ポート番号は、プロトコル名_port_t

Attribute

上述のseinfo -xt sshd_tの出力において、type sshd_tの右に並んでいるのはAttributeと呼ばれるものです。14,15,16
Attributeは、Typeの集合体です。

単一のAttributeに1つ、または複数のTypeを紐付けて定義します。
定義したAttributeは、Typeと同様に扱うことができます。
なお、AttributeにはTypeのような命名規則はありません。

Attributeの定義は以下のコマンドで確認できます。
seinfo -xaは、AttributeとTypeの紐付けを「Attributeごとに」表示します。
紐付けを「Typeごとに」表示するseinfo -xtと情報量は同じですが、表示方法が異なります。

seinfo -xa unconfined_login_domain

# Type Attributes: 1
#    attribute unconfined_login_domain;
#      chroot_user_t
#      crond_t
#      local_login_t
#      remote_login_t
#      rshd_t
#      sshd_t
#      sulogin_t

allow StatementにAttributeを指定することで、複数Typeを含むアクセス許可ルールを少ない文字数で表現できます。

例えば上述のunconfined_login_domainをsource_typeに指定すると、{ chroot_user_t crond_t local_login_t remote_login_t rshd_t sshd_t sulogin_t } を指定したのと同じ意味になります。
そして、後から別のTypeをunconfined_login_domainに追加すると、そのTypeも既存のallow Statementによるアクセス許可の対象に含まれます。

Object Class (OC)

Object Classは、Objectを用途によってざっくり分類する概念です17
単にclassと呼ばれることもあります。

Object Classは、(定義上) TypeやAttributeとは紐付きません18
ニュアンスとしてはObject ClassとType/Attributeに関連性があると意識しても良いのですが、ソースコード的にはそのように書かれていません。

Object Classは、Permissionsと紐付きます。

試しにObject Classの定義を一部見てみましょう。

seinfo -xc dir

# Classes: 1
#    class dir
# inherits file
# {
#  add_name
#  reparent
#  search
#  rmdir
#  remove_name
# }

上記の出力から、dir (ディレクトリ) というObject Classは、add_name, reparent, search, rmdir, remove_nameというPermissionsと紐付いていることがわかります。

また、定義の中にinherits fileという文字列がありますが、これも重要です。
ここで出てくるfileは、Commonと呼ばれるものです。
Commonについては、次のセクションで説明します。

Common

CommonもActionと紐づく概念です。19, 20
Commonがallow Statementで直接指定されることはありません。
CommonはObject ClassのinheritキーワードによってActionを継承させることで、間接的に作用します。

Commonは複数のObject Classと紐づくことで、Object Classの定義を簡潔に表現するのに使われます。

以下のコマンドで、file Commonの定義を確認しましょう。

seinfo -x --common file

# Commons: 1
#    common file
# {
#  watch_mount
#  watch_with_perm
#  ioctl
#  setattr
#  swapon
#  open
#  mounton
#  map
#  append
#  getattr
#  audit_access
#  watch_sb
#  create
#  unlink
#  write
#  lock
#  execmod
#  rename
#  relabelfrom
#  link
#  watch_reads
#  quotaon
#  execute
#  relabelto
#  watch
#  read
# }

file Commonに定義された上記のPermissionsは、fileを継承するdir Classにも紐付けられます。
したがって、dir Object Classの定義上は5つのPermissionsしか定義されていませんでしたが、実際にはfile Commonと紐づく26のPermissionsも追加で扱えます。

ここで、冒頭のallow Statementを再掲します。

allow sshd_t file_type:dir { getattr open search };

Object Classとしてdirが指定された時点で、Object Classの定義上31通り (dir classの5通り + file commonの26通り) のPermissionsの実行に絞り込まれています。
更にallow Statementによって、sshd_tがfile_typeに対して実行できるActionは3つに絞り込まれた、ということになります。

最後に2点補足します。

(1)
Object ClassやCommonと紐づくPermissionsの意味は、以下のリンクで調べることができます。
同じPermission名でもCommonやObject Classによって意味が異なるので、該当箇所のPermissionsを確認するようにしてください。
例えば、同じgetattrでもdirとsocketでは若干意味が異なります。
The SELinux Notebook - Appendix A - Object Classes and Permissions

(2)
CommonとObject Classの一覧を確認すると、SELinuxがざっくり何をアクセス制御できるのかを俯瞰できて便利です。

seinfo --common

# Commons: 7
#    cap
#    cap2
#    database
#    file
#    ipc
#    socket
#    x_device

seinfo -c
# (行数多いので一部のみ抜粋)

# Classes: 134
  #  alg_socket
  #  capability
  #  capability2
  #  context
  #  db_database
  #  db_table
  #  dbus
  #  dir
  #  fd
  #  fifo_file
  #  file
  #  filesystem
  #  icmp_socket
  #  ipc
  #  kernel_service
  #  process
  #  process2
  #  proxy
  #  sock_file
  #  socket
  #  system
  #  tcp_socket
  #  tun_socket
  #  udp_socket
  #  unix_dgram_socket
  #  unix_stream_socket
  #  x_keyboard
  #  x_pointer
  #  x_server

Security Contextの確認

Security Contextの確認方法を下表に整理します。
多くの場合、-ZオプションをつけることでSecurity Contextを表示できます。
特に重要なのはps -Zls -Zseinfo --portconなので、この3つは確実に覚えてください。

確認対象 コマンド
プロセス ps -Z
ファイル ls -Z
ユーザー (ログインシェルのプロセス) id -Z
ネットワークソケット ss -Z
seinfo --portcon

Security Contextが割り当てられているリソースとして、他にもファイルシステムやデータベースなどあると思いますが、代表的なコマンドは上記のみです。
他のリソースのSecurity Contextを調べる方法もあるかもしれませんが、私が調べた範囲では見つかりませんでした。

以下に具体例を示します。

プロセスのLabel表示 (ps -Z)

まずは、プロセスの表示コマンドです。
-eオプションにより、全ユーザーのプロセスを表示できます。
他にも-oによって表示列を指定したり、-Cによって特定プロセスのみ表示することも可能です。
grepawkで頑張らなくても良いのです

ps -eZ
# LABEL                               PID TTY          TIME CMD
# system_u:system_r:init_t:s0           1 ?        00:00:00 systemd
# (以下略)

ps -o label,user,cmd -C sshd
# LABEL                                   USER CMD
# system_u:system_r:sshd_t:s0-s0:c0.c1023 root sshd: /usr/sbin/sshd -D [listener] 0 of 10-100 startups

ファイルのLabel表示 (ls -Z)

ファイルのLabel表示は、ls -Zで行います。

ls -Z /etc/hosts
# system_u:object_r:net_conf_t:s0 /etc/hosts

getfattrstatでも確認できますが、ls -Zがあるのでほぼ使いません。
ちなみに、getfattrは、ファイルシステムの拡張アトリビュート (Extended Attribute) を確認するコマンドです。
この出力から、ファイルのLabelは拡張アトリビュートとして保持されていることが読み取れます。
拡張アトリビュートについて詳細が気になる方は、man xattrをご覧ください。

# getfattr -n security.selinux /etc/hosts でも良い
getfattr -m - -d /etc/hosts
# getfattr: Removing leading '/' from absolute path names
# # file: etc/hosts
# security.selinux="system_u:object_r:net_conf_t:s0"

stat /etc/hosts
#   File: /etc/hosts
#   Size: 158          Blocks: 8          IO Block: 4096   regular file
# Device: fd00h/64768d Inode: 1048867     Links: 1
# Access: (0644/-rw-r--r--)  Uid: (    0/    root)   Gid: (    0/    root)
# Context: system_u:object_r:net_conf_t:s0
# Access: 2022-01-02 01:22:36.647398431 +0900
# Modify: 2021-07-16 17:35:49.000000000 +0900
# Change: 2021-11-24 15:36:03.671000000 +0900
#  Birth: 2021-11-24 15:36:03.670000000 +0900

(参考) ユーザーのLabel表示 (id -Z)

ログインユーザーのLabelは、id -Zで表示します。
より正確には、ログインシェルプロセスのLabelを表示します。

id -Z
# unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023

SELinuxが有効な環境では、単にidを実行するだけでもSecurity Contextが表示されます。
しかしid -Zの方が見やすいので、あまり使いません。

id
# uid=1000(endy) gid=1001(endy) groups=1001(endy),10(wheel),1000(shared) context=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023

id -Zで表示されるSecurity Contextは、ログインシェルのSecurity Contextと同じです。
つまり、id -Zは以下のpsコマンドと同じ情報を表示しています。

ps -Z | grep bash | grep -v grep
# unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 26530 pts/0 00:00:01 bash

targeted policyにおいては、id -Zで表示されるのは基本unconfined_tのみです。
どのユーザーでも結果は同じなので、このコマンドを叩く機会はそう多くないでしょう。

ネットワークソケットのLabel表示

ネットワークソケットのLabel表示には、ss -Zを使います。

以下の実行例では、sedによって、先頭行と_tを含む行のみ抽出しています。
_tはTypeの一部であり、Security Contextを持つSocketのみを表示するために指定しています。

grepではなくsedを使っているのは、先頭行も抽出するためです。
grep -e 条件1 -e 条件2のようにOR条件で抽出しても良いですが、sedの方が条件指定が簡単です (sed1行目を表示する条件式が1pなので)。

ssコマンドはtcp, udpを含めて様々なタイプのソケットを表示するコマンドですが、ヒットしたのはu_str (Unix Stream) のみでした。
Unix Streamは、平たく言えばローカル通信用で、IPC (Inter-Process Communication) に使われるようです。

すなわち、ssコマンドではネットワーク外部通信用のソケット (tcp, udpなど) のSecurity Contextを表示しません。
割り当てられているのは、IPCに関わる一部のソケットのみでした。
そして、そのソケットもunconfined_tを持つため、実質的にアクセス制御されていないことがわかります。

したがって、targeted policyにおいてはss -Zというコマンドを覚える必要はありません。

ss -nZ | sed -ne 1p -e /_t/p
# Netid State Recv-Q Send-Q Local Address:Port Peer Address:Port Process
# u_str ESTAB 0      0      * 20153            * 20914           users:(("systemd",pid=935,proc_ctx=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023,fd=20))
# u_str ESTAB 0      0      * 20869            * 20130           users:(("systemd",pid=935,proc_ctx=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023,fd=2),("systemd",pid=935,proc_ctx=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023,fd=1))

TCP/UDP用のソケットと紐づくSecurity Contextをステータス値として確認するコマンドはありませんでしたが、Typeとの紐付け設定はseinfo --portconで確認できます。
semanage port -aによってローカルポリシーでポート番号のSecurity Contextを追加していたとしても、このコマンドでちゃんと表示されます。

seinfo --portconは、実運用でもよく使う重要なコマンドです。

seinfo --portcon

# Portcon: 652
#    portcon sctp 1-511 system_u:object_r:reserved_port_t:s0
# (以下略)

# ポート番号の指定も可能
# seinfo --portcon 22

# Portcon: 4
#    portcon sctp 1-511 system_u:object_r:reserved_port_t:s0
#    portcon tcp 1-511 system_u:object_r:reserved_port_t:s0
#    portcon tcp 22 system_u:object_r:ssh_port_t:s0
#    portcon udp 1-511 system_u:object_r:reserved_port_t:s0

seinfo --portconと同等の情報を得られるコマンドとしてsudo semanage port -lもありますが、root権限が必要なので使い勝手としてはあまりよくありません。
参照用途のみであれば、seinfoに軍配が上がります。
semanage port -aでポート番号をカスタマイズしている場合は、semanage port -lCでカスタマイズ値のみ表示できるのが便利なぐらいです。

sudo semanage port -l | sed -ne 1p -e /ssh/p
# SELinux Port Type              Proto    Port Number
# ssh_port_t                     tcp      22

Security Contextの決まり方

Security Contextの決まり方は、以下のリンクに書いてあります。
The SELinux Notebook - Computing Security Contexts

本セクションでは、上記リンクで説明されている内容を整理し、噛み砕いてお伝えしたいなと思います。
対象は、targeted policyに関わるプロセスとファイルに絞ります。

プロセスとファイルのSecurity Contextの決まり方は、大きく分けて2種類あります。

  1. Security Policyで定義されたルールで制御する
  2. SELinuxと連携しているアプリケーションがlibselinux APIのライブラリ関数を使って制御する

1のSecurity Policyによる制御では、大まかに言うと以下の順序でSecurity Contextが決まります。

  1. 基本的には親要素のSecurity Contextを引き継ぐ
  2. 特殊なルールによって、親とは異なるSecurity Contextを持つ状況がある
  3. 親要素がない場合など、上記仕組みで決まらない場合には別途定められたデフォルト値に従う

2のlibselinux APIについてはアプリケーション側のソースコード上の実装となるので、Security Contextの決まり方はアプリケーションの作りに依存します。
私の経験では、今までこのようなアプリケーションを見たことはありません。
もしそういったアプリケーションを扱う機会が出てきたら意識しましょう。
本記事では、libselinux APIについては取り扱わないこととします。

では、次のセクションから具体的な内容に入っていきましょう。

プロセスのContext計算

プロセスのSecurity Contextは、以下の流れで決まります21
libselinux APIが関わる部分はあまり意識しないで良い部分なので、文字をグレーにして目立たなくしています。

  1. プロセスがforkされた時、forkされた子プロセスは親プロセスのSecurity Contextを引き継ぐ
  2. プロセスがexecされた時、execされた子プロセスは以下のルールでSecurity Contextが変化する
    • Transition Ruleの条件を満たす場合、そのルールに従って決まる (※1)
    • libselinuxのsetexeccon(3)でSecurity Contextが変化する (※アプリ依存)
    • Default Ruleが指定されている場合、そのルールに従ってデフォルト値がセットされる (※2)
  3. 上記挙動を上書きしてlibselinux APIのsetcon(3) でSecurity Contextが上書きされる。非推奨な実装 (※アプリ依存)

2点補足します。

  • Default Ruleは、本記事で扱うtargeted policyでは実質意識不要です。詳細は#(参考) Default Ruleで補足します
  • ファイル実行によってプロセスが生成する時、fork > execの順に行われます。したがって、上記の1と2の処理は一瞬の間に両方行われます。新規プロセスのSecurity Contextはfork時の親プロセスのものを最初に持ちますが、次の瞬間exec時に変化することがあるとご理解ください

重要な部分を抽出すると、以下のように理解すれば十分です。

  • 親プロセスのSecurity Contextを引き継ぐ
  • ただしType Transitionのルールに該当する場合、Security Contextが書き換わる

forkやexecという用語については、以下の記事を参考にしてください。
前提知識として、exec系のライブラリ関数はexecveというシステムコールと関係することを頭に入れた上でお読みいただけるとスムーズです。

endy-tech.hatenablog.jp

残りのDefault Rule、Transition Ruleといった用語については、ファイルのContext計算のセクションの後でまとめて説明します。

ファイルのContext計算

ファイルのSecurity Contextは、以下の流れで決まります22
Type以外の情報はあまり重要ではないので、グレーで目立たなくしてあります。

  • Userは、ファイルを作成するプロセス (Subject) から引き継ぐ
  • Roleは、role_transitionとdefault_roleの影響を受ける。これらのルールがない場合は、デフォルトでobject_rになる
  • Typeは、type_transitionとdefault_typeの影響を受ける。これらのルールがない場合は、デフォルトで親ディレクトリと同じTypeになる
  • range_transitionとdefault_rangeの影響を受ける。これらのルールがない場合は、デフォルトでファイルを作成したプロセスのlow/current levelになる

勘違いしやすいポイントについて補足しますが、ファイル作成時にはsudo semanage fcontext -lで表示されるFile Contextの値は関係ありません。
ここで表示される値は、restoreconコマンドを実行した時のrelabel処理時に、どのSecurity Contextを割り当てるか判断するために参照されます。
restorecon以外にも、/.autorelabelをつけてOS再起動したとき、fixfiles restorefixfiles relabelコマンドを実行した時、restorecondデーモンがファイル作成時に自動的にrelabelする際などが該当します。
relabelについては、後続の#File Contextとrelabelで説明します。

(参考) Default Rule

この機能をtargeted policyで使うことはありません。
興味のある方以外は、本セクションを丸ごとスキップしても結構です。

プロセスやファイルなどのリソースが生成するとき、プロセスは親プロセス、ファイルは上位ディレクトリのSecurity Contextを引き継ぐのが基本です。
Default Ruleは、このデフォルト挙動を若干変える効果を持ちます23

Default Ruleが発動する条件は単純で、Objectが特定のObject Classだった場合に常に発動します。

default系のStatementには以下のバリエーションがあります。
それぞれuser, role, type, rangeのデフォルト値の決定方法を上書きするのに使われます。

  • default_user
  • default_role
  • default_type
  • default_range

この中で重要なのはdefault_typeのみです。
なぜなら、targeted policyにおいては (MCSと) TEのみ意識すれば良いためです。

その前提で、targeted policyで定義されているDefault Ruleを確認してみましょう。

seinfo --default
# Default rules: 7
#    default_range blk_file target low;
#    default_range chr_file target low;
#    default_range dir target low;
#    default_range fifo_file target low;
#    default_range file target low;
#    default_range lnk_file target low;
#    default_range sock_file target low;

上記出力を見ると、default_typeルールがないことがわかります。

さらに、我々ユーザーが新たにDefault Ruleを追加することはそもそも不可能です。
それは、Module Policyのソースコードにはdefault_typeを記述できないためです。

default_typeの仕様を確認すると、Module Policyの欄に "No" と書かれていることからも読み取れます。

default_typeを変更する影響は非常に大きいので、私達ユーザーが書き換えるようなパラメータではないということだと思います (※)

(※) 厳密にはGitHubからSELinuxソースコード全体をダウンロードし、Base Policyを編集して全体をビルドし直せば、Default Ruleだけでなく何でも書き換えられます。しかし、そこまですることはまずないでしょう。

targeted policyにおいて、Default RuleがTypeを書き換えることはありません。
したがって、私達がDefault Ruleの仕様を気にする必要は全くありません。

Default Ruleは、MLSを使う方のみ気にするようにしましょう。

Transition Rule

Transition RuleもDefault Ruleと同様に、プロセスやファイルが新規作成されたとき、デフォルトのSecurity Contextを指定するルールです。
両者の役割は基本同じですが、Default Ruleと比較してType Transitionはより細かくSecurity Contextを書き換える条件を指定できます。

Transition Ruleには、以下のバリエーションがあります。
それぞれrole、type、rangeの変化に関するルールを記述するStatementです。

  • role_transition24
  • type_transition25
  • range_transition26

これらの中で、重要なのはTypeを変化させるtype_transitionのみです。

Type Transitionについては、#Type Transitionのセクションにて詳細に説明します。

(参考) SID (Security ID)

SID (Security ID) を知ることでSecurity Contextの決まり方について更に理解が進むので、ここで紹介しておきます。
SELinuxを運用する上でSIDは必ずしも必要にはならないので、興味のある方のみご覧いただければと思います。

SIDとは、Security Contextのデフォルト値を決めるパラメータです27
Initial SIDと呼ばれることもあります。

デフォルト値と言えば、プロセスの場合は親プロセス、ファイルの場合は親ディレクトリのSecurity Contextが引き継がれるのでした。
SIDは、上記ルールでもカバーできないような状況でのデフォルト値を定義する、言ってみれば最後のデフォルト値です。

例えば、PPID (Parent Process ID) が0、つまり親プロセスを持たないsystemdプロセスや、SELinuxが無効化された状態で作成されたためSecurity Contextを持たないファイルなどに対して、SIDが利用されます。

SIDのSyntaxは以下の通りです。
やや冗長ですが、1行目でsid_idを定義し、2行目でsid_idにSecurity Contextを紐付けます。

sid sid_id;
sid sid_id context;

SIDの定義を以下に示します。
SID名の後にSecurity Contextが紐付いています。

seinfo -x --initialsid

# Initial SIDs: 27
#    sid any_socket system_u:object_r:unlabeled_t:s0
#    sid devnull system_u:object_r:null_device_t:s0
#    sid file system_u:object_r:unlabeled_t:s0
#    sid file_labels system_u:object_r:unlabeled_t:s0
#    sid fs system_u:object_r:fs_t:s0
#    sid icmp_socket system_u:object_r:unlabeled_t:s0
#    sid igmp_packet system_u:object_r:unlabeled_t:s0
#    sid init system_u:object_r:unlabeled_t:s0
#    sid kernel system_u:system_r:kernel_t:s0
#    sid kmod system_u:object_r:unlabeled_t:s0
#    sid netif system_u:object_r:netif_t:s0
#    sid netmsg system_u:object_r:netlabel_peer_t:s0
#    sid node system_u:object_r:node_t:s0
#    sid policy system_u:object_r:unlabeled_t:s0
#    sid port system_u:object_r:port_t:s0
#    sid scmp_packet system_u:object_r:unlabeled_t:s0
#    sid security system_u:object_r:security_t:s0
#    sid sysctl system_u:object_r:sysctl_t:s0
#    sid sysctl_dev system_u:object_r:unlabeled_t:s0
#    sid sysctl_fs system_u:object_r:unlabeled_t:s0
#    sid sysctl_kernel system_u:object_r:unlabeled_t:s0
#    sid sysctl_modprobe system_u:object_r:unlabeled_t:s0
#    sid sysctl_net system_u:object_r:unlabeled_t:s0
#    sid sysctl_net_unix system_u:object_r:unlabeled_t:s0
#    sid sysctl_vm system_u:object_r:unlabeled_t:s0
#    sid tcp_socket system_u:object_r:unlabeled_t:s0
#    sid unlabeled system_u:object_r:unlabeled_t:s0

各種SIDが使われる条件は、ざっくり以下の通りです28

  • kernelが起動したプロセスやスレッドはkernel SIDのSecurity Contextが割り当てられる
  • file classやdir classがSecurity Contextを持たない場合、file SIDのSecurity Contextが割り当てられる
  • (その他のSIDも同様)
  • Security Contextが (値なしではなく) 不正な値を持っていた場合、unlabeled SIDのSecurity Contextが割り当てられる (滅多にないと思います)

kernel SIDが使われるケースは、例えばこんな状況です29

  1. kernelが起動したsystemd/initプロセス (PID 1) や、その他いくつかのスレッドはkernel SIDのkernel_t Typeが割り当てられる
  2. ただしsystemd/initプロセスは、その後Type Transitionしてinit_t Typeに変わる

file SIDが使われるケースは、例えばこんな状況です30

  1. SELinuxを一時的に無効化した (disabled)
  2. ファイルを作成した。SELinuxは無効なのでラベル付けされない
  3. SELinuxを有効化した。有効化にはOS再起動を伴うので、ファイルシステムも再度読み込まれる
  4. 2で作成したファイルはSecurity Contextを持たないため、file SIDのSecurity Contextであるunlabeled_t Typeが割り当てられる

ただ、最近のディストリビューションではSELinuxをdisabledからenforcingなどに変更したタイミングで自動的にrelabelされるので、File Contextが<<none>> (relabelしない)として定義されていない限りは上記のような状況にはならないと思います31

Type Transition

Type Transitionとは、プロセスやファイルが生成した時にSecurity Contextの一部であるTypeを変化させるアクセス制御ルールです32

Type Transitionがないと、プロセスは全てデフォルトのkernel_tかsystemdと同じinit_tになってしまいます。
実際にはプロセスによって異なるTypeを持っていますが、これはType Transitionの一種であるDomain Transitionの働きによるものです。

Type Transitionは2種類に分類できます。
名前は異なりますが、何のTypeが変化するかで呼び方を分けているだけで、本質は変わりません。

  • Domain Transition: 新規生成するプロセスのTypeが変化するType Transition
  • Object Transition: 新規生成するプロセス以外 (主にファイル) のTypeが変化するType Transition

以降のセクションで、Domain TransitionとObject Transitionについて図解と具体例を交えつつ説明します。

Domain Transition

Domain Transitionの概要

Domain Transitionとは、新規発行された子プロセスが親プロセスとは別のSecurity Contextに遷移する処理のことです。
(※) Default Ruleとは別物なので、区別してください

詳細な説明に入る前に、Domainという言葉をイメージしてみましょう。
以下の図も活用しつつ、説明を進めます。

domain_transition_example

Domainとは、一般に「範囲」を意味する言葉です。
SELinuxにおいては、Subjectであるプロセスのアクセス可能な範囲のことをDomainと呼びます。33,34,35
上図で言うと、init_t Domainとsshd_t Domainが存在します。
それぞれのDomainは、init_tとsshd_tをSubjectとしてアクセス可能なObjectを内包しています。
言い換えると、init_tとsshd_tをSubjectとして、矢印でつながった先をObjectとするallow Statementが存在する範囲のことをDomainと呼びます。

Domain Transitionとは、子プロセスが親プロセスのTypeを引き継がず別Typeを持つことで、異なるDomainに遷移することです。
これにより、子プロセスのsshdは、親プロセスのsystemdとは異なるsshdならではのリソースにアクセスできるようになります。

Entry Pointという言葉も重要です。
Entry Pointとは、子プロセスが別ドメインへ移動する際の入口役となる実行ファイルのことです。
上図ではsshd_exec_tがsshd_t DomainへのEntry Pointということになります。

最後に、図中に出てくるexecute, read, getattr, entrypoint, transitionは、Domain Transitionを実行するためにallow Statementで許可する必要があるPermissionです。
これらのPermissionに加え、type_transition Statementが別途定義されていることでDomain Transitionが可能となります。
Domain Transitionに必要なルール定義については、この後更に詳細に説明します。

Domain Transitionに必要な制御ルール

上記の具体例で言うと、Domain Transitionを行うのに必要なアクセス制御ルールは4行あります36
Domain Transitionを実行するtype_transitionルール1行と、必要なアクセスを許可するallowルール3行です。

前セクションの図において、実線の黒い矢印で表現されている部分に相当します。

# execute
allow init_t sshd_exec_t : file { execute read getattr };

# entrypoint
allow sshd_t sshd_exec_t : file entrypoint;

# transition
allow init_t sshd_t : process transition;
type_transition init_t sshd_exec_t : process sshd_t;

実際にsesearchでルール検索すると、ちゃんとヒットします。
initrc_domainなどAttributeを使って表現しているものもあるので表記が若干異なりますが、アクセス制御ルールとしてはちゃんと内包されています。

sesearchコマンドのオプションは次の記事で詳しく扱いますので、今は「実機でもちゃんとエビデンスが取れているんだな」とご理解いただければ十分です。

sesearch -A -s init_t -t sshd_exec_t -c file -p execute,read,getattr
# allow init_t file_type:file { getattr relabelfrom relabelto };
# allow initrc_domain direct_init_entry:file { execute getattr map open read };

sesearch -A -s sshd_t -t sshd_exec_t -c file -p entrypoint
# allow sshd_t sshd_exec_t:file { entrypoint execute execute_no_trans ioctl lock map open read };

sesearch -A -s init_t -t sshd_t -c process -p transition
# allow initrc_domain daemon:process transition;

sesearch --type_trans -s init_t -t sshd_exec_t
# type_transition init_t sshd_exec_t:process sshd_t;

今回はsystemdプロセスからsshdプロセスが発行される例を取り上げて説明しましたが、Domain Transitionのルールの書き方は一般に同じです。
一般化して、initをparentに、sshdをchildに置き換えたルールも以下に添付しておきます。

# execute
allow parent_t child_exec_t : file { execute read getattr };

# entrypoint
allow child_t child_exec_t : file entrypoint;

# transition
allow parent_t child_t : process transition;
type_transition parent_t child_exec_t : process child_t;

一般化した図も添付します。

domain_transition

type_transition Statement

type_transition Statementは、Type Transition (Domain/Object Transition) を実行する制御ルールです。

type_transitionのSyntaxは以下のとおりです37

type_transition source_type target_type : class default_type;

Syntax上の各要素の意味は、以下のとおりです。

要素 意味
source_type TransitionのトリガーとなったActionのSubject
(※) Transitionの条件1
target_type TransitionのトリガーとなったActionのObject
(※) Transitionの条件2
class Transition対象のTypeのObject Class
default_type Transition後のType
(※) 新規生成するプロセスやファイルのデフォルト値

例えば、先ほどのDomain Transitionの例をモデルにします。

Transitionのトリガーは、以下のallow Statementに対応するexecute命令でした。
以下のallow Statementは、init_tがsshd_exec_tを実行 (execute) することを許可するアクセス制御ルールです。
Subjectがinit_t、Objectがsshd_exec_tです。

allow init_t sshd_exec_t : file { execute read getattr };

それに対応するtype_transition Statementは以下のとおりです。
allow Statementと縦に並べるとわかりやすいですが、上述のallow StatementとSubjectとObjectが揃っています。
type_transitionのSubjectとObjectには、トリガーとなったexecuteのSubjectとObjectを指定します。

type_transition init_t sshd_exec_t : process sshd_t;

Transition後のTypeはsshd_tですが、これに関してはそのままの意味なので説明は不要だと思います。

最後にObject Classがprocessですが、これはTransition先のsshd_tがプロセスであることと対応しています。

なお、Domain TransitionとObject Transitionは全く同じ構文ですが、簡単な見分け方があります。
それは、Object ClassがprocessならDomain Transition、それ以外 (fileなど) ならObject Transitionということです。

Object Classがprocessということは、ファイル実行によって新規生成したプロセスのDomainが変わったということです。
このことから、このTransitionはObject Transitionではなく、Domain Transitionであると言えます。

Object Transition

Object Transitionの概要

Object Transitionとは、新規発行されたプロセス以外のリソース (ファイル、ディレクトリ、データベースのSchemaなど) が親要素とは別のSecurity Contextに遷移する処理のことです。
今回は、ファイルやディレクトリに着目して説明を進めます。

ファイルやディレクトリは、Type Transitionが発生しない場合は、生成したファイルの親ディレクトリのTypeを引き継ぐのがデフォルトの動作です。
Object Transitionが発生すると、生成したファイルやディレクトリが別のTypeを持ちます。
これによって、新規生成したファイル/ディレクトリは親ディレクトリと異なるallow Statementに一致するようになり、異なるアクセス制御パターンを実装できるようになります。

Object Transitionの具体例を下図に示します。

object_transition_example

NetworkManagerが/var/log/wicd.*にログファイルを作成するとします。
Object Transitionがなければ、/var/log/と同じTypeを引き継いで/var/log/wicd.*var_log_tを割り当てようとします。
しかし、今回の場合はObject Transitionによって/var/log/wicd.*というObjectにNetworkManager_log_tが割り当てられました。

上述の挙動を実現するためには、Domain Transitionと同様にtype_transitionルールと、付随するいくつかのallowルールが必要です。
詳細は次のセクションで説明します。

Object Transitionに必要な制御ルール

上記の具体例で言うと、Object Transitionを行うのに必要なアクセス制御ルールは3行あります38
Object Transitionを実行するtype_transitionルール1行と、必要なアクセスを許可するallowルール2行です。

前セクションの図において、実線の黒い矢印で表現されている部分に相当します。

allowルールによって、NetworkManagerプロセスが親ディレクトリにファイル追加する権限 (add_name)と、対象のファイルを作成する権限 (create) を中心にアクセス許可しています。

Object Transitionの場合は、type_transitionに親ディレクトリへのadd_nameと同じSubjectとObjectをセットすることに気をつけてください。
ファイルのcreateの方ではありません。

# add_name
allow NetworkManager_t var_log_t:dir { add_name write search };

# create
allow NetworkManager_t NetworkManager_log_t:file { create write getattr };

# transition
type_transition NetworkManager_t var_log_t:file NetworkManager_log_t;

target policyの実機上では、以下のようにルール定義されていました。

# add_names
sesearch -A -s NetworkManager_t -t var_log_t -c dir -p add_name,write,search
# allow NetworkManager_t var_log_t:dir { add_name ioctl lock read remove_name write };
# allow domain var_log_t:dir { getattr open search };

# create
sesearch -A -s NetworkManager_t -t NetworkManager_log_t -c file -p create,write,getattr
# allow NetworkManager_t NetworkManager_log_t:file { create open setattr };
# allow application_domain_type logfile:file { append getattr ioctl lock };
# allow daemon logfile:file { append getattr ioctl lock };

# transfer
sesearch --type_trans -s NetworkManager_t -t var_log_t -c file
# type_transition NetworkManager_t var_log_t:file NetworkManager_log_t;

今回はNetworkManagerプロセスからログファイルを自動生成する例を取り上げて説明しましたが、Object Transitionのルールの書き方は一般に同じです。
一般化して、NetworkManagerをprocessに、/var/log/parent/dirに置き換えたルールも以下に添付しておきます。

# add_name
allow process_t parent_dir_t:dir { add_name write search };

# create
allow process_t process_yyy_t:file { create write getattr };

# transition
type_transition process_t parent_dir_t:file process_yyy_t;

一般化した図も添付します。

object_transition

Name Transition

Name TransitionはObject Transitionの一種です。

Name Transitionとは、TypeとObject Classだけでなく、ファイル名も含めてTransitionする条件を細かく制御する機能です。

Name Transitionのルール設定例を以下に示します。

sesearch --type_trans -s init_t -t etc_t -c file
# (一部のみ抜粋)
# type_transition init_t etc_t:file syslog_conf_t rsyslog.conf;
# type_transition init_t etc_t:file passwd_file_t passwd;

syslog_conf_tpasswd_file_tなどのTransition後のType指定の後に、rsyslog.confpasswdなどのファイルが追加で指定されています。
このように最後のファイル名を指定することで、作成するファイル名によってTransition後のTypeを区別する書き方が可能となります。

今回の例では、両ルール共にSubject、Object、Classは全て同じです。
通常のObject Transitionでは、このような状況においては2つ以上のTypeを使い分けることはできません。
Name Transitionは、SubjectとObjectのTypeが同じで、それでもTransitionルールを区別したいときに使います。

name_transition_example

Name Transition用のtype_transitionのSyntaxは、以下の構造です39
#type_transition Statementで取り上げたDomain/Object Transitionの構文の末尾にobject_nameが増えています。
object_nameには、ファイル名を完全一致で指定します。

type_transition source_type target_type : class default_type object_name;

Name Transitionの場合も、Object Transitionと同様のallowルールが追加で必要です。

(参考) Typeの意味の見分け方

sshd_tというTypeがあったとします。
このTypeがObjectとなった場合の典型的なObject Classと、具体的にどのようなリソースと紐づくかをすぐに言い当てることはできますでしょうか?

今回は簡単なので、すぐにわかるかもしれません。
しかし、5000強あるTypeとAttribute全てについて用途を言い当てることは難しいと思います。

そこで、ここでは用途を見分けるちょっとしたコツを紹介したいと思います。

Typeの見分け方

Typeは、以下の段階を踏んで見分けていくのが早いと思います。

  • 名前から判断する
  • manで検索する (selinux-policy-docパッケージのインストール推奨)
  • allow Statementから推測する

どういったアプローチか、1つずつ説明します。

名前から判断する

絶対ではありませんが、Typeにはおおよその命名規則があります。
この命名規則から、多くの場合プロセス名とObject Classを判断できます。

Type名 具体例 用途 (class)
プロセス名_t sshd_t sshdプロセス (file)
プロセス名_exec_t sshd_exec_t sshdのentry point、実行ファイル (file)
プロセス名_log_t zabbix_log_t zabbixのログファイル (file)
プロセス名_conf_t syslog_conf_t syslogの設定ファイル (file)
プロセス名_port_t syslogd_port_t syslogdのポート番号 (tcp_socket, udp_socket)

Object Classを判断できたら、後はclassごとに特化した確認コマンドを実行すればより具体的な情報がわかります。

プロセスの場合は、2通りのアプローチがあります。

1つ目の方法は、プロセスが起動している場合に使えます。
プロセス起動中という条件はあるものの、簡単に調べがつきます。

# sshd_t = /usr/sbin/sshd
ps -eo user,label,command | grep sshd_t | grep -v grep
# root     system_u:system_r:sshd_t:s0-s0:c0.c1023 sshd: /usr/sbin/sshd -D [listener] 0 of 10-100 startups

2つ目の方法は、allow Statementから追いかける方法です。
プロセスが起動していなくても調査可能です。

今回はプロセスとわかっているので、entrypointのルールから調べています。
もしプロセスと知らない場合はtype_transitionルールから探すのが確実ですが、これについては後述します。

# sshd_exec_tがentry point
sesearch -A -s sshd_t -p entrypoint
# allow sshd_t sshd_exec_t:file { entrypoint execute execute_no_trans ioctl lock map open read };

# sshd_exec_tは、/usr/sbin/sshd
sudo semanage fcontext -l | grep :sshd_exec_t:
# /usr/sbin/gsisshd  regular file  system_u:object_r:sshd_exec_t:s0 
# /usr/sbin/sshd     regular file  system_u:object_r:sshd_exec_t:s0

余談ですが、関連ファイルが1つでも分かればパッケージを特定して追加の情報を得ることも可能です。

# パッケージはopenssh-server
dnf provides /usr/sbin/sshd
# openssh-server-8.7p1-2.fc35.x86_64 : An open source SSH server daemon
# Repo        : @System
# Matched from:
# Filename    : /usr/sbin/sshd

# openssh-serverとは何か?
dnf info openssh-server
# Installed Packages
# Name         : openssh-server
# Version      : 8.7p1
# Release      : 2.fc35
# Architecture : x86_64
# Size         : 1.0 M
# Source       : openssh-8.7p1-2.fc35.src.rpm
# Repository   : @System
# From repo    : fedora
# Summary      : An open source SSH server daemon
# URL          : http://www.openssh.com/portable.html
# License      : BSD
# Description  : OpenSSH is a free version of SSH (Secure SHell), a program for logging
#              : into and executing commands on a remote machine. This package contains
#              : the secure shell daemon (sshd). The sshd daemon allows SSH clients to
#              : securely connect to your SSH server.

ファイルの場合はFile Contextを確認すれば大体わかります。
zabbix_log_t/var/log/zabbix.*とわかりました。
Zabbixのログファイルという予想は正しそうです。

sudo semanage fcontext -l | grep :zabbix_log_t:
# /var/log/zabbix.*  all files  system_u:object_r:zabbix_log_t:s0 

TCP/UDP Socketの場合は、ポート番号のSecurity Contextを確認すればポート番号を特定できます。
もしサービスが起動していれば、ssから追加の情報を得られます。
そうでなくても、/etc/servicesからポート番号と対応する和名を調べることができます。

(※) /etc/servicesは、現在Activeなポート番号とプロセス名の最新の紐付けを示すファイルではなく、あくまでLinuxがポート番号の和名を表示するためのマッピング情報が静的に記述されているだけのファイルです。参考:man services

sudo semanage port -l | grep syslogd_port_t
# syslogd_port_t                 tcp      601, 20514
# syslogd_port_t                 udp      514, 601, 20514

grep -P ' (601/tcp|20514/tcp|514/udp|601/udp|20514/udp) ' /etc/services
# syslog-conn     601/tcp                 # Reliable Syslog Service
# syslog-conn     601/udp                 # Reliable Syslog Service

manで検索する

このやり方は非常に簡単で、頭を使わずにできます。
man -Kwに検索ワードを指定するだけです。

唯一の弱点は、manに載っていないと使えないことです。

この方法はTypeに限らず、Attribute、Booleanなど何でも使えます。
それどころか、SELinuxに限らず一般的に使えます。

-Kは、man全文からのキーワード検索をするオプションです。
検索にヒットしたmanを順に開いていきます。

-wは検索結果の返し方をチューニングするオプションです。
検索にヒットしたmanファイルのフルパスを一覧表示します。
-Kのみだと検索がたくさんヒットしたときに全体俯瞰しづらいので、-wと組み合わせます。

man -Kw watchdog_t
# /usr/share/man/man8/watchdog_selinux.8.gz
# /usr/share/man/man8/freeipmi_bmc_watchdog_selinux.8.gz

# 載ってないこともある
man -Kw vmware_device_t
# No manual entry for vmware_device_t

watchdog_tについてより詳しく知るには、man watchdog_selinuxman freeipmi_bmc_watchdog_selinuxを実行すれば良いとわかります。

allow Statementから推測する

私達が気にするTypeの大半はプロセスかファイルです。
そしてプロセスかファイルは、systemdなど一部を除いてほぼ全てがDomain/Object TransitionしてそのTypeに変化しています。

つまり、Type Transitionルールを検索すれば何かしらのヒントを得られます。

対象がプロセスだった場合は簡単です。
以下の出力から「sshd_tinit_tからsshd_exec_tをentry pointとしてDomain Transitionしたプロセスである」というところまで一発でわかります。

sesearch --type_trans -D sshd_t
# (一部のみ抜粋)
# type_transition init_t sshd_exec_t:process sshd_t;

後は#名前から判断するの例と同様に、sshd_exec_tのFile Contextを確認して詳細を調べていけます。

対象がファイルやディレクトリだった場合も同様に簡単です。
以下の出力から、「locale_tzabbix_script_tプロセスがetc_tディレクトリ配下にファイルを生成した時にObject Transitionした、ファイルである」ことがわかります。
Object Classがfileなのでファイルと判断しましたが、ここがdirの場合はディレクトリです。

sesearch --type_trans -D locale_t
# type_transition zabbix_script_t etc_t:file locale_t clock;

ファイルであることがわかったので、後は#名前から判断すると同様にFile Contextを調べれば詳細を確認できます。

対象がファイルでもプロセスでもなかった場合は、type_transitionルールにヒットしないと思われます。
こういったTypeの正体を突き止めるのは困難を極めます。
基本的にはmanやGoogleを検索し、わからなければサポートに問い合わせなければ厳しいと思います。

そもそもこういったTypeを調べる場面は想像することも難しいほどレアだと思います。
もしあるとしたら、エラーが発生した場合でしょうか。
その場合は、アプリケーションのエラーログやsetroubleshootのログも合わせて総合的に調査方針を検討する必要があると思います。

File Contextとrelabel

ファイルのSecurity Contextを計算する場面

#ファイルのContext計算にて、ファイルが新規作成されるときのTypeは以下のように決まると説明しました。
以下の他にもDefault Ruleなどの存在はありますが、実際にはほとんど意識する必要がないので割愛しています。

  • 作成されたファイルの親ディレクトリと同じTypeを持つ
  • ただし、Object Transitionの条件を満たす時、type_transitionルールに従ってTypeが決定する

実は、ファイル作成以外にもファイルのSecurity Contextを計算・変更する状況があります。

  1. chconコマンドによって、対象のファイルを任意のSecurity Contextに変更したとき
  2. relabel処理が走ったとき

1のchconコマンドは、テストしたいときを除いて滅多に実行することはありません。
ここで取り上げたいのは、2のrelabel処理についてです。

次のセクションでrelabelとは何かについて説明します。

relabelとは

relabelとは、ファイルを再度ラベル付けする処理のことです。40,41
relabel対象のファイルのSecurity Contextを確認し、File Contextの内容と差分があればFile Contextの定義通りの値に変更します。

File Context (fcontext) とは

File Context (fcontext) とは、ファイルパスとSecurity Contextの紐付け定義です。
relabel処理は、File Contextの紐付け通りになるようにファイルのSecurity Contextを書き換えます。

File Contextは、allow Statementやtype_transition Statementなどと同様に、Module Sources (ソースコード) にて定義されます。
また、semanage fcontextコマンドによってユーザーの手で追加のFile Contextを定義することも可能です。

semanage fcontext -lでFile Contextの定義を一覧表示できます。
File Contextは、ファイルパスはPerl互換の正規表現で定義できます42
grepと組み合わせて、特定Typeと紐づくFile Contextを調べるのによく使います。

sudo semanage fcontext -l

# SELinux fcontext  type       Context
# /                 directory  system_u:object_r:root_t:s0 
# /.*               all files  system_u:object_r:default_t:s0
# (以下略)

sudo semanage fcontext -l | grep :user_home_dir_t:
# /home/[^/]+       directory      unconfined_u:object_r:user_home_dir_t:s0 
# /home/[^/]+       symbolic link  unconfined_u:object_r:user_home_dir_t:s0 

逆にファイルパスを元に対応するSecurity Contextを知りたい場合は、matchpathconコマンドが便利です。
selabel_lookup -kでも同じことはできますが、matchpathconの方が楽に覚えられると思います。

matchpathcon /etc/selinux/config
# /etc/selinux/config  system_u:object_r:selinux_config_t:s0

selabel_lookup -k /etc/selinux/config
# Default context: system_u:object_r:selinux_config_t:s0

これらのコマンドは、引数に渡したPATHをFile Contextと突き合わせてSecurity Contextを返しています。
したがって、matchpathconとselabel_lookup -kの引数はフルパスで指定しないと想定する結果が返ってこないので注意してください。

pwd
# /etc/selinux

matchpathcon config
# config   <<none>>

relabelが発生する場面

relabel処理は、以下の状況で発生します。

  • /.autorelabelを配置してOS再起動
  • restoreconコマンドの実行
  • fixfiles restoreコマンドの実行
  • fixfiles relabelコマンドの実行 (ほぼ使わない)

他にも、ドキュメントには書かれていませんがSELINUXTYPE (targeted/minimum/mls) が変わった時にも自動的にrelabel処理が走りました。
また、RHEL8の場合はSELinuxがdisabledからpermissive/enforcingに変化したときにもrelabel処理が走ります43

次のセクションから、relabelを実行する操作を1つずつ説明します。

/.autorelabel

/.autorelabelファイルを配置してからOS再起動すると、Linux起動中に以下の処理が走ります44

  1. fixfiles restore
  2. rm /.autorelabel

1つ目の処理で、ファイルシステム全体をrelabelします。
2つ目の処理で/.autorelabelファイルを削除し、次回以降の再起動ではrelabelが走らないようにします。

(※) man selinux_configによると空の/.autorelabelを配置するだけでfixfiles -F restoreを実行しそうな説明が書いてありますが、実際にはfixfiles restoreの実行になります。空の/.autorelabelを配置して再起動してもSELinux UserはRelabelされませんでした。fixfiles -F restoreを実行するには、fixfiles -F onbootを実行するか、-Fと書いてある/.autorelabelを配置する必要があります

OS再起動は伴うものの、/.autorelabelを使うのがrelabelのやり方として最も確実とSELinuxのマニュアルに書かれています45
とはいえ、ちょっとした修正であれば#restorecon#fixfiles restoreを利用して問題ありません。
OS再起動をすれば、/devなども含めてより確実にrelabelできるということと理解しています。

/.autorelabelを使うのは、SELinuxを無効 (disabled) から有効 (enforcing/permissive) に変更したときです。
SELinuxが無効の状態で作成されたファイルはSecurity Contextが割り当てられません。
Security Contextが割り当てられていないファイルは、以下のように見えます。

getenforce
# Disabled

touch x
ls -Z x
# ? x

この状態でSELinuxを有効化すると、ファイルに想定外のSecurity Contextが割り当てられることで、SELinuxによるアクセス拒否が大量に発生してしまう恐れがあります46

ただ、実際にはRHEL8の場合は無効から有効に切り替えたタイミングで/.autorelabelが無くてもrelabelが走る仕様です。

とはいえ、過信は禁物です。
SELinuxをdisabledからenforcingに切り替える際は、必ず/.autorelabelを配置してからOS再起動しましょう。
また、滅多にやらないと思いますがSELINUXTYPEの値を変更する際にも/.autorelabelを配置すべきです。

/.autorelabelの使い方の例を以下に示します。
/.autorelabelの配置にはtouchコマンドを使っても良いのですが、今回の例では専用コマンドのfixfiles onbootを使います。
個人的には、専用コマンドの方がタイプミスの心配が少なくて良いかなと思います。
通常のユースケースではType Enforcementしか使わないので、-Fの指定は必要ないと思います。

# sudo touch /.autorelabelと同等
sudo fixfiles onboot

sudo reboot

targeted policyからmls policyに変更する場合など、TE以外のRBAC、UBAC、MLS、MCSなどを使う場合にはType以外のSecuirty Contextも重要になってきます。
以下の例では-Fを指定することで、TypeだけでなくUser,Role,Rangeもrelabel対象としています。
(※) TE以外を使うことは基本的にないので、あくまで参考情報です

# sudo bash -c 'echo -n -F > /.autorelabel'と同等
sudo fixfiles -F onboot

sudo reboot

restorecon

特定のファイルのSecurity Contextを修正したい時、restoreconは便利です47
restoreconは、特定ファイルやディレクトリに対象を絞ったrelabelを実行するコマンドです。
対象ファイルのSecurity ContextをFile Contextに合わせて変更します。

よくあるシナリオとしては、ある場所に作成したファイルをmvで移動したことで、Security Contextがおかしくなった時にrestoreconで修正します。
以下に具体例を示します。

ホームディレクトリ配下にファイルxを作成します。
作成したファイルはuser_home_t Typeを持ちます。

touch /home/endy/x
ls -Z /home/endy/x
# system_u:object_r:user_home_t:s0 /home/endy/x

作成したファイルをmvで移動します。
mvはcpとは異なり、オーナーやタイムスタンプなどのメタ情報を保持します。
これは拡張ファイル属性として保持されるSecurity Contextも例外ではなく、これも保持されます。
しかしmatchpathconが示すとおり、File Contextの値はsystem_u:object_r:usr_t:s0です。

sudo mv /home/endy/x /opt/x

ls -Z /opt/x
# unconfined_u:object_r:user_home_t:s0 /opt/x

matchpathcon /opt/x
# /opt/x   system_u:object_r:usr_t:s0

ここで、restoreconを実行して/opt/xのSecurity ContextをFile Contextの値に合わせて変更します。

restoreconは実行しても標準出力を出しませんが、-vをつけることで変更内容を表示するようになります。
また、-nをつけるとテストモード実行のようにSecurity Contextを変更しません。
-n-vとセットで使うことが多いです。
以下で試してみましょう。

restorecon -nv /opt/x
# Would relabel /opt/x from unconfined_u:object_r:user_home_t:s0 to unconfined_u:object_r:usr_t:s0

ls -Z /opt/x
# unconfined_u:object_r:user_home_t:s0 /opt/x

変更内容は表示されますが、実際には変更されていません。
では、実際に変更してみます。
Typeがusr_tに変化しました。
しかし、Userは変化していないです。

restorecon /opt/x

ls -Z /opt/x
# unconfined_u:object_r:usr_t:s0 /opt/x

matchpathcon /opt/x
# /opt/x   system_u:object_r:usr_t:s0

このように、restoreconはデフォルトでTypeしか変更しません。
-Fをつけて実行すれば、User/Role/Rangeも含めて変更します。
一旦chconで元の状態に戻した上で、今度は-F付きで試してみましょう。

(※) chconは、ファイルに任意のSecurity Contextをセットするコマンドです。今回のようなテスト用途でたまに使います

chcon -t user_home_t /opt/x
ls -Z /opt/x
# unconfined_u:object_r:user_home_t:s0 /opt/x

restorecon -F /opt/x
ls -Z /opt/x
# system_u:object_r:usr_t:s0 /opt/x

restoreconの挙動確認はこれで一旦おしまいです。
/opt/xはもう使わないので、削除しておきます。

sudo rm /opt/x

fixfiles

fixfilesは、内部的にはrestoreconを呼び出しています。
このことは、man fixfilesの以下の文言から読み取れます。

-v     Modify verbosity from progress to verbose. (Run restorecon with -v instead of -p)

fixfilesは、restoreconと比較して大規模なrelabelを行うのに使われます。
デフォルトでrestorecon -Rのように再帰的に実行する上、実行対象のパスを指定しなければ暗黙的に/が指定されます。
つまり、シンプルにfixfiles restoreとだけ実行すると、ファイルシステム全体がrelabelされます。

/etc/selinux/fixfiles_exclude_dirsディレクトリパスを列挙しておくと、そのディレクトリについてはrelabelの対象外とするような制御も可能です。
初期状態ではfixfiles_exclude_dirsファイル自体が存在しないので、対象外のディレクトリは存在しないのがデフォルト動作となります (Fedora35で確認)。

fixfilesは以下のサブコマンドを持ちます。 以降のセクションで一つ一つ説明します。

サブコマンド 意味
onboot /.autorelabelを作成する
check
またはverify
relabelせず、relabelが必要なファイルを列挙する
(≒restorecon -nv)
restore ファイルシステム全体をrelabelする (≒restorecon -Rp /)
relabel restoreとほぼ同じ。
ただし、実行前に/tmp配下を削除できる (≒rm -rf /tmp/*; restorecon -Rp /)

fixfiles onboot

fixfiles onbootは、touch /.autorelabelと同じ意味を持ちます。
fixfiles -F onbootで、echo -n '-F' > /.autorelabelと同じ意味を持ちます。

基本的にはTypeしか使わないので、-Fをつけることはあまりないと思います。

ls /.autorelabel
# ls: cannot access '/.autorelabel': No such file or directory

sudo fixfiles onboot
# System will relabel on next boot

ls /.autorelabel
# /.autorelabel

この後OS再起動すると、#/.autorelabelで説明したとおりfixfiles restore相当のrelabelが実行されます。

fixfiles check (または、fixfiles verify)

fixfiles checkは、relabelを実行したときにSecurity Contextが書き換わるファイルを列挙します。
実際にはSecurity Contextを書き換えません。
fixfiles verifyも全く同じ意味です。
restorecon -Rnvとほぼ同等の挙動です。

試しに、ファイルxのSecurity Contextを書き換えた上でfixfiles checkを実行してみます。

touch /home/endy/x
chcon -u sysadm_u -t tmp_t /home/endy/x
ls -Z /home/endy/x
# sysadm_u:object_r:tmp_t:s0 /home/endy/x

sudo fixfiles check
# Checking / /boot /dev /dev/hugepages /dev/mqueue /dev/pts /dev/shm /run /run/user/1000 /sys /sys/fs/cgroup /sys/fs/pstore /sys/kernel/debug /sys/kernel/debug/tracing /sys/kernel/tracing /tmp
# Would relabel /home/endy/x from sysadm_u:object_r:tmp_t:s0 to sysadm_u:object_r:user_home_t:s0

fixfilesrestoreconと同様、デフォルトではTypeしか確認しません。
-Fを指定することで、User, Role, Rangeを全て確認してくれます。
-Fの挙動は、onboot, restore, relabelなど他のサブコマンドについても同様です。

また、fixfiles checkの後に対象のファイルやディレクトリを指定することで、チェック対象を絞り込むことができます。
ディレクトリを指定した場合は配下のファイルやディレクトリを再帰的にチェックします。
fixfiles restoreも同様にファイルやディレクトリを指定できます。
fixfiles relabelは、常に/が暗黙的に選択され、全てのファイルをrelabelします。

では、これらの特徴を試してみましょう。
対象が/home配下に絞られているので処理が早く終わります。
また、TypeだけでなくUserもrelableするというチェック結果になっていることがわかります。

sudo fixfiles -F check /home/
# Would relabel /home/endy/x from sysadm_u:object_r:tmp_t:s0 to unconfined_u:object_r:user_home_t:s0

fixfiles checkの動作確認はここでおしまいです。
使い終わったファイルを削除しておきます。

rm /home/endy/x

fixfiles restore

fixfiles restoreは、ファイルシステム全体をrelabelします。
このコマンドを単体で使うと、OS再起動することなくrelabelが可能です。

OS再起動を伴わない分/.autorelabelを使った方法よりも気軽に実行しやすいです。
しかし、man selinuxでも触れられている通り/.autorelabelを使った方法が "Best" とのことなので、もしリリース前など、気軽に再起動できる状況であればOS再起動を伴う方法の方が手堅いとは思います。

以下に実行例を示します。

単にfixfiles restoreを実行すると、Typeのみrelabelします。
また、restoreconと同様に-vを指定しないと何が書き換わったかわかりません。
実行時間短縮のため、/home/endy/配下に対象を絞って実行します。

touch /home/endy/x
chcon -u system_u -t tmp_t /home/endy/x
ls -Z /home/endy/x
# system_u:object_r:tmp_t:s0 /home/endy/x

sudo fixfiles restore /home/endy/
ls -Z /home/endy/x
# system_u:object_r:user_home_t:s0 /home/endy/

一旦Typeを元に戻して、今度は-F-vを指定して実行します。
今度は-Fの指定によってTypeだけでなくUser/Role/Rangeも含めてrelabelされます。
また、-vによって実行結果が詳細に出力されます。

chcon -t tmp_t /home/endy/x
ls -Z /home/endy/x
# system_u:object_r:tmp_t:s0 /home/endy/x

sudo fixfiles -Fv restore /home/endy
# Relabeled /home/endy/x from system_u:object_r:tmp_t:s0 to unconfined_u:object_r:user_home_t:s0

(参考) fixfiles relabel

fixfiles relabelを実行すると、fixfiles restoreと同様に対象をrelabelします。
ただし、実行前に/tmpを削除するか聞かれます。

fixfiles relabelユースケースは、正直思い浮かびません。
「基本使わない」と覚えておけば良いと思います。

/tmp配下を削除しないよう指定すると、fixfiles restoreと同様に動作します。

sudo fixfiles relabel

#     Files in the /tmp directory may be labeled incorrectly, this command
#     can remove all files in /tmp.  If you choose to remove files from /tmp,
#     a reboot will be required after completion.

#     Do you wish to clean out the /tmp directory [N]? n
# Relabeling / /boot /dev /dev/hugepages /dev/mqueue /dev/pts /dev/shm /run /run/user/1000 /sys /sys/fs/cgroup /sys/fs/pstore /sys/kernel/debug /sys/kernel/debug/tracing /sys/kernel/tracing /tmp
# / 100.0%
# /boot 100.0%
# /dev 100.0%
# /dev/hugepages 100.0%
# Warning no default label for /dev/mqueue
# /dev/mqueue 100.0%
# /dev/pts 100.0%
# /dev/shm 100.0%
# /run 100.0%
# /run/user/1000 100.0%
# /sys 100.0%
# /sys/fs/cgroup 100.0%
# /sys/fs/pstore 100.0%
# /sys/kernel/debug 100.0%
# Warning no default label for /sys/kernel/debug/tracing
# /sys/kernel/debug/tracing 100.0%
# /sys/kernel/tracing 100.0%
# /tmp 100.0%

# Cleaning up labels on /tmp

/tmp配下を削除するよう指定すると、fixfiles restoreと同様に動作します。
-fを指定することで、/tmpを削除するか聞かれずに無条件で削除するよう選択します。

/tmp配下を削除した後は、Linuxが正しく動作するように一度再起動する必要があります。

sudo fixfiles -f relabel
# Cleaning out /tmp
# Relabeling / /boot /dev /dev/hugepages /dev/mqueue /dev/pts /dev/shm /run /run/user/1000 /sys /sys/fs/cgroup /sys/fs/pstore /sys/kernel/debug /sys/kernel/debug/tracing /sys/kernel/tracing /tmp
# / 100.0%
# /boot 100.0%
# /dev 100.0%
# /dev/hugepages 100.0%
# Warning no default label for /dev/mqueue
# /dev/mqueue 100.0%
# /dev/pts 100.0%
# /dev/shm 100.0%
# /run 100.0%
# /run/user/1000 100.0%
# /sys 100.0%
# /sys/fs/cgroup 100.0%
# /sys/fs/pstore 100.0%
# /sys/kernel/debug 100.0%
# Warning no default label for /sys/kernel/debug/tracing
# /sys/kernel/debug/tracing 100.0%
# /sys/kernel/tracing 100.0%
# /tmp 100.0%

# Cleaning up labels on /tmp

ls /tmp
# (出力なし)

sudo reboot

fixfiles restorefixfiles relabelの違いは、/tmpを削除するか否かです。
正直、/tmpを削除するメリットはいまいちわかりません。

fixfiles relabelを使うことはほぼないと思います。

Boolean

Booleanの概要

SELinuxのModule Policyには、いくつかのBooleanが定義されています48
BooleanはPolicy Source (ソースコード) 上で条件分岐 (if文) として登場し、Booleanが1か0かによって挙動が変わるようにロジックが組まれています。
Booleanが1の場合はTrueを、0の場合はFalseを表します。

このBooleanの値を変更することで、ソースコードを変更すること無く既存のアクセス制御ルールの挙動を変更することができます。

ソースコード上は3種類のBooleanの定義方法がありますが、私達が使う段階においては特に機能に差はありません (※1)49

ソースコード上の定義方法によっては、BooleanのことをTunableと呼ぶこともあります。
私達が扱うBooleanの大半はTunableになります (※2)

(※1) The SELinux Notebookからgen_boolgen_tunabletunable_policyなど、各種Booleanを定義するための構文を参照できます。これらの構文を見比べると、結局のところ同じ使い方をしています。Boolean変数名を宣言して、その先で変数を利用したif文を記述するという使い方です。こういった理由から、各種Booleanは記述場所や記述方法が異なるのみで、機能的な差異は特にないと判断しました。

(※2) Fedoraが管理するSELinux Policyのソースコード上で、Global Booleanを定義するgen_boolよりもTunable Booleanを定義するgen_tunableとその中身を記述するtunable_policyの方が圧倒的にヒット数が多かったためです

Boolean関連コマンド

Booleanの値を調べるには、以下のいずれかのコマンドを使います。

  • getsebool -a
  • sudo semanage boolean -l

Booleanの意味を調べるには、以下のコマンドが有効です。

  • sesearch -A --type_trans -b <boolean>

Booleanの値を1 (True) に変更するには、以下のいずれかのコマンドを使います。

  • sudo setsebool -P <boolean> 1
  • sudo semanage boolean -m -1 <boolean>

Booleanの値を0 (False) に変更するには、以下のいずれかのコマンドを使います。

  • sudo setsebool -P <boolean> 0
  • sudo semanage boolean -m -0 <boolean>

Booleanのより詳細な使い方については、後続記事のSELinuxの実践の「Boolean有無の確認」セクションを参照してください。

(参考) アクセス制御ルールはどこに書いてあるのか

アクセス制御ルールが書いてある場所

アクセス制御ルールの情報は、Security Policyと呼ばれる単一のバイナリファイルに格納されていることは既に説明したとおりです。
このバイナリファイルを作る工程として、開発者はソースコード (Module Sources) を書いています。
したがって、SELinuxのTypeの定義やallow Statementなどの情報は、ソースコードを追うことでも確認することができます。

下図において、右下にある.te.fc.ifの拡張子を持つファイルが主なソースコードです。
これらのソースコードを専用のコマンドによるコンパイル・パッケージ化・インストールという工程を経てSecurity Policyファイルが生成します。

how_security_policies_are_built

ソースコードを読む必要はあるのか?

実用上、ソースコードを読む必要はありません。

必要な定義情報は、全てseinfoやsesearchなどで検索することができます。
わざわざ読みづらいソースコードから理解する必要は全くありません。

非常にレアなケースとして、自分でソースコードを書いてルール定義したくなる場面もあるかもしれません。
ただ、相当なレアケースですので一旦は忘れて良いと思います。

重要なのはseinfoやsesearchコマンドを使いこなすことです。

このあたりのテクニック面のお話は、まとめて次の記事で説明します。
SELinuxの実践

ソースコード周りの話については、別途専用の記事を用意してあります。
もし 不幸にも ソースコードを書く必要な状況が出てきたら、目を通していただければと思います。
(参考) SELinux Module Policyのソースコード読解、ビルド

まとめ

SELinuxの最も重要な要素であるTE (Type Enforcement) について一通りの理論を説明しました。
かなり盛りだくさんの内容となりましたが、実運用をする上では全てを暗記する必要はありません。

本記事で扱った基礎理論をベースに、次の記事ではSELinuxのエラーを修正するための実践的なノウハウを紹介します。
ノウハウ理解のために知識の補完が必要な場合は、改めて本記事を辞書的に活用いただければ幸いです。

次の記事

SELinuxの実運用に必要な知識として、アクセス拒否エラーの切り分け手順と原因別の対処方を紹介します。

endy-tech.hatenablog.jp


  1. The SELinux Notebook - Types of SELinux Policy - #Policy Functionality Based on Name or Type

  2. The SELinux Notebook - The Reference Policy - #Policy Functionality

  3. The SELinux Notebook - The Reference Policy - #Policy Functionality

  4. Red Hat - SELinux Coloring Book - #MCS Enforcement ※MCSはTEよりも少し細かい制御

  5. RED HAT BLOG - Why you should be using Multi-Category Security for your Linux containers

  6. RHEL8 - Using SELinux - Configuring Multi-Category Security for data confidentiality

  7. RHEL7 - Virtualization Security Guide - sVirt Labeling

  8. RHEL8 - Using SELinux - Using Multi-Level Security (MLS)

  9. Gentoo Wiki - SELinux/Tutorials/SELinux Multi-Level Security - #MLS on a default installation

  10. Dan Walsh’s Blog - selinux-policy-minimum

  11. The SELinux Notebook - Access Vector Rules

  12. Gentoo Wiki - SELinux/Quick introduction - #SELinux security subsystem

  13. The SELinux Notebook - Security Context

  14. The SELinux Notebook - Type Statements - #attribute

  15. SELinux by Example: Using Security Enhanced Linux - 5.2.2. Types and Attributes ※書籍

  16. SELinux by Example: Using Security Enhanced Linux - 5.3.1.2. Using Attributes in AV Rules ※書籍

  17. The SELinux Notebook - Objects - #Object Classes and Permissions

  18. The SELinux Notebook - Object Class and Permission Statements - #class-2

  19. The SELinux Notebook - Object Class and Permission Statements - #Associating Permissions to a Class

  20. The SELinux Notebook - Object Class and Permission Statements - #common

  21. The SELinux Notebook - Computing Security Contexts - #Process

  22. The SELinux Notebook - Computing Security Contexts - #Files

  23. The SELinux Notebook - Default Object Rules

  24. The SELinux Notebook - Role Statements - #role_transition

  25. The SELinux Notebook - Type Statements - #type_transition

  26. The SELinux Notebook - MLS Statements - #range_transition

  27. The SELinux Notebook - Security ID (SID) Statement

  28. SELinux by Example: Using Security Enhanced Linux - 10.6. Initial Security Identifiers ※書籍

  29. The SELinux Notebook - Computing Security Contexts - #Process

  30. The SELinux Notebook - Computing Security Contexts - #Files

  31. RHEL8 - Using SELinux - Permanent changes in SELinux states and modes

  32. The SELinux Notebook - Domain and Object Transitions

  33. The SELinux Notebook - SELinux Overview - #Is SELinux useful → “SELinux can confine an application within its own ‘domain’ and allow it to have the minimum privileges required to do its job.”

  34. The SELinux Notebook - Mandatory Access Control → “processes run in domains and the actions on objects are controlled by policy.”

  35. The SELinux Notebook - Type Enforcement → “Basically if the type identifier is used to reference a subject it is referring to a Linux process or collection of processes (a domain or domain type)”

  36. The SELinux Notebook - Domain and Object Transitions - #Domain Transition

  37. The SELinux Notebook - Type Statements ※同じページにtype_changeやtype_memberがありますが、これらはlibselinux APIを使う類のルールなので基本意識不要です

  38. The SELinux Notebook - Domain and Object Transitions - #Object Transition

  39. The SELinux Notebook - Type Statements - #type_transition

  40. man7.org - selinux(8) - #FILE LABELING

  41. man7.org - restorecon(8)

  42. man7.org - semanage-fcontext(8)

  43. RHEL8 - Using SELinux - Permanent changes in SELinux states and modes

  44. man7.org - selinux_config (5)

  45. man7.org - selinux(8) - #FILE LABELING

  46. RHEL8 - Using SELinux - Permanent changes in SELinux states and modes

  47. man7.org - restorecon(8)

  48. Gentoo Wiki - SELinux/Booleans

  49. The SELinux Notebook - The Reference Policy - #Booleans, Global Booleans and Tunable Booleans