SELinux (Security Enhanced Linux)

今までは複雑そうだから無効にして逃げてきたけど、
改めてSELinuxについて調べたので、備忘録として残す。
（認識の誤り等あればコメントいただけますと幸いです）

# 0. 背景
### 0.1 DAC（Discretionary Access Control : 任意アクセス制御）
ファイルやディレクトリの所有者に設定されているアクセス権限
これだけだと、万が一乗っ取られた時にシステム全体にリスクが及ぶ。
>$ ls -l /etc/passwd
>**-rw-r--r--.** 1 root root 1437  8月 30 16:57 /etc/passwd

### 0.2 MAC（Mandatory Access Control : 強制アクセス制御）
DACだけではセキュリティとして不十分なので、
アクセス制御をファイルやディレクトリの所有者にて設定するのではなく、
セキュリティ・ポリシー・ファイルで一元的に管理する。
インターネット公開するサーバでは必須。
>$ ls -lZ /etc/passwd
>-rw-r--r--. 1 root root **system_u:object_r:passwd_file_t:s0** 1437  8月 30 16:57 /etc/passwd
<br> 


## 1. 動作モード
### 1.1 動作モードを確認
```
$ getenforce
```
|結果|内容|
|:--|:--|
|Enforcing|有効。ルール違反はアクセス拒否し、監査ログを残す。|
|Permissive|警告モード。ルール違反は監査ログを残すが、アクセス拒否しない。|
|Disable|無効。（非推奨）|

### 1.2 システム起動時の動作モード設定
設定ファイル : /etc/selinux/config
>SELINUX=enforcing|permissive|disable

### 1.3 動作モードの変更
一時的な変更で、OS再起動により/etc/selinux/configの設定値に戻る。
```
# setenforce 0   #Permissive
# setenforce 1   #Enforceing
```
<br>

## 2. セキュリティコンテキストの確認
タイプ（ドメイン）、ユーザ、ロールなどの属性情報を組み合わせてアクセス制御を行う。
以下のようなラベルというフォーマットで表示される。
```
user_u:object_r:unconfined_t:s0         #ユーザ:ロール:タイプ（ドメイン）:レベル
```
ファイルに対するコンテキストをセキュリティコンテキスト、プロセスに対するコンテキストをドメインコンテキストと呼ぶ。

### 2.1 ファイルコンテキストの確認
```
$ ls -lZ /etc/passwd
-rw-r--r--. 1 root root system_u:object_r:passwd_file_t:s0 1437  8月 30 16:57 /etc/passwd
```

### 2.2 ドメインコンテキストの確認
```
$ ps -Z -C httpd
LABEL                               PID TTY          TIME CMD
system_u:system_r:httpd_t:s0       4598 ?        00:00:00 httpd
```
### 2.3 ログインシェルのセキュリティコンテキストの確認
ログインシェルから起動されるプロセスのドメインとロールは、ログインしたユーザによって規定される。
ユーザAでログインしてsuコマンドでユーザBに変わってもユーザAのセキュリティコンテキストが引き継がれる。
```
$ id -Z
unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023
```
<br>

## 3. セキュリティコンテキストの変更
### 3.1 ファイルコンテキストの一時的な変更
chconコマンドで一時的にファイルコンテキストを変更できる。
OSの再起動では元に戻らないが、後述のrestoreconコマンドやパッケージのアップデートなどで元に戻る。
```
# chcon -R -u system_u -t usr_t /home/user/AAA
```
|オプション|説明|
|:--:|:--|
|-R|ディレクトリに対して再帰的にコンテキストを設定|


### 3.2 ファイルコンテキスト設定の確認
semanageコマンドで、セキュリティコンテキストのデフォルト設定を参照・変更できる。
ファイルコンテキストを扱う場合には、fcontextサブコマンドを使う。
```
# semanage fcontext -l
```
### 3.3 ファイルコンテキスト設定の追加
```
# semanage fcontext -a -t usr_t "/home/user/AAA"              #AAAディレクトリのみに適用
# semanage fcontext -a -t usr_t "/home/user/AAA(/.*)?"        #AAAディレクトリ配下すべてに適用
```

### 3.4 ファイルコンテキスト設定の修正
```
# semanage fcontext -m -t usr_t "/home/user/AAA"              #AAAディレクトリのみに適用
# semanage fcontext -m -t usr_t "/home/user/AAA(/.*)?"        #AAAディレクトリ配下すべてに適用
```
### 3.5ファイルコンテキスト設定の削除**
```
# semanage fcontext -d "/home/user/AAA"                       #AAAディレクトリのみに適用
# semanage fcontext -d "/home/user/AAA(/.*)?"                 #AAAディレクトリ配下すべてに適用
```

### 3.6 自分で追加した設定の確認**
```
# semanage fcontext -E
```

### 3.7 ファイルコンテキストの回復
```
# restorecon 
```
|オプション|説明|
|:--:|:--|
|-e|指定したディレクトリを除外|
|-R|ディレクトリに対して再帰的に処理|
|-v|ファイルラベルを変更したらそのファイル名を表示|
|-F|ユーザのみなど部分的に変更されている場合でも強制的に設定|


### 3.8 ファイルコンテキストの再設定
```
```

### 3.9 定期的なファイルコンテキストの確認
システム管理者がファイルを作成したり移動させたときには、標準ルールから外れたコンテキストとなる場合がある。その都度適切なファイルコンテキストになるように配慮するのは難しい。
サービスrestorecondは、ファイルコンテキストを監視し、semanageで設定した標準ルールに自動的に是正してくれる。

## 4. SELinuxブール値

<br>

## 5. avcログ
SELinuxのポリシールールに抵触する処理が実行されると、
    avcログ（/var/log/audit/audit.log）に以下フォーマットで出力される。
>avc: \<result> { \<access_vector> } for pid=\<pid> comm=\<comm> [name=\<name>] [dev=\<dev>] [ino=\<ino>] [src=\<src>] scontext=\<scontext> tcontext=\<tcontext> tclass=\<tclass>

|項目|内容|
|:--|:--|
|\<result>|SELinuxのアクセス結果。"granted"か"denied"|
|\<access_vector>|操作に使用した操作ベクタ|
|\<comm>|実行したコマンドのパス名|
|\<name>|実行したコマンド名|
|\<dev>|アクセス対象のリソースを管理するデバイスの番号|
|\<ino>|アクセス対象のリソースのinode番号|
|\<src>|アクセス対象のリソース|
|\<scontext>|アクセス元プロセスのドメインコンテキスト|
|\<tcontext>|アクセス先リソースのセキュリティコンテキスト|
|\<tclass>|アクセス先リソースのオブジェクトクラス|


<br>

## 6. ポリシーモジュール


ポリシーモジュールの確認
```
# semanage module -l
```
ポリシーモジュールの有効化
```
# semanage module -m --enable <name>
```
ポリシーモジュールの無効化
```
# semanage module -m --disable <name>
```
<br>

## 7. ルールの検索
```
# sesearch 

```

<br>

## 9. 実際に起きた問題への対処


### 9.1 ソースからインストールしたTomcatが起動できない
#### <現象>
+ シェルから～～/tomcat/bin/startup.shスクリプトを実行すると、成功
+ systemctlコマンドでTomcatを起動すると、失敗
+ Tomcatディレクトリ配下のファイルコンテキストは"admin_home_t"
+ Tomcatサービスのユニットファイルのファイルコンテキストは"admin_home_t"
+ /var/log/messagesに以下のエラーメッセージが出力されている。
>setroubleshoot[4779]: SELinux により、～～～～～

+ /var/log/audit/audit.logに以下のエラーメッセージが出力されている。
>avc:  denied  { exec } for  pid=～～ comm="～～" dest=～～ scontext=system_u:system_r:init_t:s0 tcontext=unconfined_u:object_r:admin_home_t:s0 tclass=～～ permissive=0

#### <原因> 
**ドメインコンテキスト"init_t"のプロセスがファイルコンテキスト"admin_home_t"のスクリプトを実行することに対して、SELinuxのポリシールールで許可されていない**

#### <調査>
TomcatのSELinuxポリシーが導入されていることを確認
```
# semanage module -l | grep tomcat
tomcat                    100       pp
```

"init_t"から"tomcat_t"にドメイン遷移するルールを確認
```
# sesearch -T -s init_t -c process | grep tomcat_t
type_transition init_t tomcat_exec_t:process tomcat_t;
```
よって、起動スクリプトstartup.shのファイルコンテキストを"tomcat_exec_t"にすれば、
"init_t"のsystemdプロセスが、"tomcat_exec_t"のstartup.shスクリプトを実行でき、
"tomcat_t"のTomcatプロセスが起動する。

#### <解決策>
+ **起動スクリプトstartup.shのファイルコンテキストを"tomcat_exec_t"に変更する**
+ **その他のファイルのコンテキストも適切に変更する**
yumでインストールした際のファイルコンテキストを参考に設定。


セキュリティコンテキスト設定を追加
```
# semanage fcontext -a -t 'bin_t' '/usr/local/tomcat9/bin(/.*)?'
# semanage fcontext -a -t 'tomcat_exec_t' '/usr/local/tomcat/bin/startup.sh'
（中略）
# semanage fcontext -a -t 'tomcat_unit_file_t' '/usr/lib/systemd/system/tomcat.service'
```

追加したセキュリティコンテキスト設定の確認と反映
```
# semanage fcontext -E
fcontext -a -f a -t bin_t -r 's0' '/usr/local/tomcat/bin(/.*)?'
fcontext -a -f a -t tomcat_exec_t -r 's0' '/usr/local/tomcat/bin/startup.sh'
（中略）
fcontext -a -f a -t tomcat_unit_file_t -r 's0' '/usr/lib/systemd/system/tomcat.service'

# restorecon -FR /usr/local/tomcat
# restorecon -F /etc/systemd/system/tomcat.service
```
これにより、systemctlコマンドでのTomcat起動に成功！

<br>

### 9.2 ApacheとTomcatの連携ができない
#### <現象>
+ Apachehは正常起動しており、単体ならアクセス可能（200）
+ Tomcatは正常起動しており、単体ならアクセス可能（200）
+  ApacheとTomcatの連携後にアクセスすると、503エラー(Service Unavailable)が発生
+ /var/log/messagesに以下のエラーメッセージが出力されている。
>setroubleshoot[4779]: SELinux により、/usr/sbin/httpd による name_connect アクセスが、tcp_socket ポート 8009 で拒否されました。

+ /var/log/audit/audit.logに以下のエラーメッセージが出力されている。
>avc:  denied  { name_connect } for  pid=4602 comm="httpd" dest=8009 scontext=system_u:system_r:httpd_t:s0 tcontext=system_u:object_r:http_port_t:s0 tclass=tcp_socket permissive=0

#### <原因> 
**SELinuxブール値でhttpdのリモートポートへの接続が拒否されていることにより、
httpd（ドメインコンテキスト:httpd_t）がポート8009に接続できない**

ググると、リモートポートへの接続についてのSELinuxブール値は、デフォルトで拒否する設定になっているとのこと。以下にhttpdに関するSELinuxブール値の代表例をいくつか示す。

|ブール値の名前|説明|デフォ|
|:--|:--|:--:|
|httpd_builtin_scripting|PHPコンテンツなどのスクリプトへのアクセスを許可する|on|
|**httpd_can_network_connect**|**リモートポート\(http_port_t)への接続を許可する**|**off**|
|httpd_can_network_connect_db|データベースサーバーへの接続を許可する|off|
|httpd_can_network_relay|リバースプロキシとして使用することを許可する|off|
|httpd_can_sendmail|メールの送信を許可する|off|
|httpd_enable_cgi|CGIスクリプト(httpd_sys_script_exec_t)の実行を許可する|on|
|httpd_enable_homedirs|ユーザのホームディレクトリへのアクセスを許可する|off|

#### <解決策>
**SELinuxブール値"httpd_can_network_connect"をonに変更**
```
# getsebool httpd_can_network_connect
httpd_can_network_connect --> off

# setsebool -P httpd_can_network_connect 1

#getsebool httpd_can_network_connect
httpd_can_network_connect --> on
```
これによりApacheがポート8009に接続でき、Tomcat側のリソースにアクセス成功！