containerにおけるSELinuxの役割について

# 背景

Youki (Rustでruncを再実装するOSS）にcontributeする中で、本家のruncで実装されているSELinuxに関する機能が、Youkiでは実装されていないことがわかった。
そこで、SELinuxに関する機能をYoukiに導入することになったのだが、Rustで利用できるSELinuxのcrateが無かった。そこで、SELinux crateをRustで再実装することになり、そのプロジェクトにアサインしてもらった。
しかし、その時点で僕はSELinuxについて何も知らなかったので、SELiuxについて色々と調べたことをまとめた。

# SELinuxとは何か？

security-enhanced Linuxの略称。MAC制御を行うことができる。通常のセキュリティに加えてSELinuxを設定することで、システムセキュリティを更に強化できる。

Labelとpolicyを組み合わせたセキュリティ制御が特徴である。process・file・networkなどのobject、process・userなどのsubjectのそれぞれに、labelが付与される。label同士のルールをsecurity policyに定義することで、subject・object間のアクセスをコントロールする。

### メリット

- SELinuxのルールはlabelで決定するので、ルート権限などの概念は考慮する必要が無い。
- 権限昇格攻撃による被害を軽減することができる。process・fileなどはdomainで互いに分離されているので、あるprocessが乗っ取られても、被害を最小限に抑えることができる。
- 管理者がアクセス制御を行うため、一般ユーザーに変更される心配がない。
- ウイルス対策ソフトウェアなどと独立した機能として、OSの安全性を高めることができる。

### DAC, MAC

- DAC: 任意アクセス制御。一般ユーザー自身がシステム上のアクセス制御を行う。
- MAC: 強制アクセス制御。管理者がアクセス制限を行う。

### Subject, Object

- Subject: process、userなどを指す。
- Object: file、directoryなどを指す。

## SELinuxの動作モード

OSは、SELinuxの動作モードを選択する。config file(etc/selinux/config)を書き換えると、動作モードを変更できる。/sys/fs/selinux/enforceにも動作モードの設定がある。前者のファイルを書き換える場合は再起動が必要だが、後者の場合は、再起動せずに変更することができる。

- Enforcing: SELinux有効。ルール外の動作は全て止める。
- Permissive: SELinux有効。ただしルール外の動作はログに記録するだけ。
- Disable: SELinux無効。

### Mount

SELinuxがenableされると、SELinuxファイルシステムが特定のディレクトリ以下でマウントされる。マウントされたSELinuxディレクトリ内で、ポリシー、状態管理が行われる。

## label (SELinux context)

SELinuxが有効の場合、subject・objectは、通常のfile permission (例: 777)のほかに、label(SELinux context)を持つ。labelは4つのpartから構成される。subject, objectのどちらにも、以下の形式で付与される。

```
User:Role:Type:level
```

- User: Userごとに付与されるラベル。例：`system_u`
- Role: Userに対して紐づいたRole名。RBACとして機能する。例：`object_r`
- Type: subjectのdomain, およびobjectのtypeを定義する。末尾に`_t`が付与される。例：`etc_t`。etc内のファイルに`etc_t`が付与される、のようにルールが適用される。
- Level: MLS, MCSに利用される属性。例：`s0`

例：`system_u:object_r:bin_t:s0:c1,c2`

## Type enforcement (domain)

あるdomainを持つobjectには、policy ruleが許可されたsubjectしかアクセス出来ない。例えば、`httpd_t`というドメインで実行されているプロセスは、`httpd_sys_content_t`を持つファイルにアクセスできるが、`mysql_db_t`というドメインを持つファイルにはアクセスできない。

あるsubject・objectをdomainで制約したくない場合、`unconfined`というdomainを設定することができる。このdomainを使えば、特定のdomainに制約されることなく、多くの操作を行うことができる。

### xattr

標準のファイル属性に加えて、追加のメタデータをファイルに格納する仕組み。SELinuxは、xattrを利用してファイルにlabelを設定する。`security.selinux`という拡張属性(キー)を利用している。xattrは、ファイルのindex node (inode)に書き加えられる。inodeにはファイルのパーミッション・所有者・更新日時などの情報が保存されている。

### procfs

Process filesystemのことで、processに関する情報が蓄えられている。processのlabelは、xattrではなくprocfsのファイルに書き込む。

## MLS enforcement

Multi level securityの略称。同じTypeに属するが別の挙動をさせたいsystemに、levelを設定することができる。例えば、A, Bは同じType `httpd_t`に属するが、Aだけにアクセスさせたいファイルがある場合、Aには強いlevelを設定することで、A・Bのアクセス権を区別できる。`s0`から`s15`までの16段階のlevelがあり、`s15`が最も機密レベルが高い。

## security Policy

SELinuxに関するルールを定義する。あるdomainは、あるtypeにアクセスできる、などを定める。以下の形式でルールを設定する。classは、どのリソースに対するルールなのかを表す。file, directory, simbolic link, device, ports, cursorなどのリソースがclassの種類として存在する。
以下の形式で書かれる。

```
allow <domain> <type>:<class> { <permissions> };
```

以下はルール例。

```
ftp_home_dir                   (off  ,  off)  Allow ftp to home dir
smartmon_3ware                 (off  ,  off)  Allow smartmon to 3ware
mpd_enable_homedirs            (off  ,  off)  Allow mpd to enable homedirs
xdm_sysadm_login               (off  ,  off)  Allow xdm to sysadm login
xen_use_nfs                    (off  ,  off)  Allow xen to use nfs
mozilla_read_content           (off  ,  off)  Allow mozilla to read content
ssh_chroot_rw_homedirs         (off  ,  off)  Allow ssh to chroot rw homedirs
mount_anyfile                  (on   ,   on)  Allow mount to anyfile
```

## Domain transition

domain transitionは、あるprocessのlabelを変えられる。例えば、あるprocessがあるコードを実行して新しくprocessが生成される時に、別のlabelとして実行させることができる。dyntransitionとも記載される。

# Dockerなどのcontainerで、SELinuxがどのように使われるのか？

Host, containerを互いに保護するために利用される。

DockerはSELinuxの機能のうち、主に、type enforcement, MCS(Multi-category security)分離、という２つの機能を利用している。type enforcementは、Host・container間の保護に利用される。MCSはcontainer同士の保護に利用される。
Dockerでは、SELinuxがdefaultで有効化されているわけでは無いので、有効化したい場合は設定を変更する必要がある。

### Type enforcement

subject, object間でのアクセス制御に使われる。label同士のやり取りをpolicyで設定する。

コンテナのdefault typeは、`svirt_lxc_net_t`である。このtypeは、usr, etc以下のほぼ全てのファイルをread, execできるが、var, home, rootなど、他ファイルへのアクセスは制限される。ネットワーク使用は許可されている。コンテナ内の全てのファイルは`svirt_sandbox_file_t`ラベルが付与される。`svirt_lxc_net_t` は`svirt_sandbox_file_t`および`docker_var_lib_t`とラベルされたファイルにはアクセスできる。`docker_var_lib_t`はdocker volumeの使用に関連する。

### MCS(multi category security)分離

この機能は、Docker, OpenShiftなどのコンテナ権限管理に利用されることが多い。

MCS分離は`svirt`とも呼ばれる。コンテナごとにuniqueな値(Docker起動時のPID)が、SELinux labelのlevel fieldに付与される。つまり、コンテナは、そのprocessがコンテナであることを表すlabelと、「どのコンテナなのか」を表すlabelが付く。

processのMCS labelは、開きたいファイルのMCS labelに対応している必要がある。例えば、`s0:c1,c2`のlabelを持つprocessは`s0, s0:c1, s0:c2, s0:c1,c2`にアクセスできる。`s0:c1,c3`にはアクセスできない。
labelは複数のカテゴリを推奨される。そのため、`s0:c1`, `s0:c2`は好ましく無い。

# go-selinuxでカバーしている機能

containerd organization内にあるレポジトリ。

subject・objectのlabel操作を行う関数・動作モードを変更する関数などをサポートしている。
あくまで、containerd関連のプロジェクトがSELinux機能を使うサポート用のOSSである。そのため、SELinuxが持つ、label・policyを元にアクセス制御するシステム自体を実装しているわけでは無い。

このレポジトリを絶賛Rustに移植している。

### 参考にしたリンク

https://www.digitalocean.com/community/tutorials/an-introduction-to-selinux-on-centos-7-part-1-basic-concepts

https://projectatomic.io/docs/introduction/

https://opensource.com/business/13/11/selinux-policy-guide

https://blog.fenrir-inc.com/jp/2016/09/selinux.html

https://docs.redhat.com/ja/documentation/red_hat_enterprise_linux/8/html-single/using_selinux/index#managing-confined-and-unconfined-users_using-selinux

https://ja.wikipedia.org/wiki/Procfs