SECMARK を使用して SELinux でパケットを制御する

## 概要
SECMARK を利用すると、パケットに対しSELinuxのラベルを付与することができ、
SELinux のポリシーでアクセス制限を行うことができる。

つまり、ユーザ毎やアプリケーション毎に細かくパケットを制御することも可能。

## 検証環境
CentOS Linux release 7.3.1611 (Core)

* 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
Max kernel policy version:      28
```

## 検証環境の準備
### Disable firewalld

検証を行う上で firewalld は邪魔なので無効化

```
# systemctl disable --now firewalld
```

### Install packages
```
# yum install setools-console selinux-policy-devel
```

### Create user
```
# useradd -m user1
# semanage login -a -s user_u user1
# useradd -m user2
# semanage login -a -s staff_u user2
# 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       *
system_u             system_u             s0-s0:c0.c1023       *
user1                user_u               s0                   *
user2                staff_u              s0-s0:c0.c1023       *
```

```
[user1@localhost]$ id -Z
user_u:user_r:user_t:s0
```

## 検証
### 未設定状態
```
[user1@localhost]$ curl -I www.google.com
HTTP/1.1 302 Found
Cache-Control: private
Content-Type: text/html; charset=UTF-8
Referrer-Policy: no-referrer
Location: http://www.google.co.jp/?gfe_rd=cr&dcr=0&ei=Z9SuWYz6KLTEXvPYsKAN
Content-Length: 269
Date: Tue, 05 Sep 2017 16:44:23 GMT

[user1@localhost]$ curl -I 54.64.138.248
HTTP/1.1 301 Moved Permanently
Date: Tue, 05 Sep 2017 16:45:21 GMT
Content-Type: text/html; charset=iso-8859-1
Connection: keep-alive
Server: Apache
Location: https://cookpad.com/
```

### Policyの作成
secmark.cil

```
;; Definition of myhttp_client_packet_t
(type myhttp_client_packet_t)
(roletype object_r myhttp_client_packet_t)
(typeattributeset packet_type (myhttp_client_packet_t))

;; Definition of default_http_client_packet_t
(type default_http_client_packet_t)
(roletype object_r default_http_client_packet_t)
(typeattributeset packet_type (default_http_client_packet_t))

;; Rule
(allow user_t myhttp_client_packet_t (packet (send recv)))
(neverallow user_t default_http_client_packet_t (packet (send recv)))
```

この CIL ファイルは以下のことを行っている

* myhttp_client_packet_t の定義
    * roletype は object_r
    * packet_type と関連付ける
* default_http_client_packet_t の定義
    * roletype は object_r
    * packet_type と関連付ける
* user_t が myhttp_client_packet_t:packet の send と recv を許可する(allow)
* user_t が default_http_client_packet_t:packet の send と recv を許可しない(neverallow)

この定義を以下のコマンドで読み込む。

```
# semodule -i secmark.cil
```

### packet にSecurityラベルをつける

iptables コマンドを利用します。

```
# iptables -t security -A OUTPUT -p tcp --dport 80 -j SECMARK --selctx "system_u:object_r:default_http_client_packet_t:s0"
# iptables -t security -A OUTPUT -p tcp -d 54.64.138.248 --dport 80 -j SECMARK --selctx "system_u:object_r:myhttp_client_packet_t:s0"
# iptables -t security -A OUTPUT -p tcp --dport 80 -j CONNSECMARK --save
# iptables -t security -A OUTPUT -m state --state ESTABLISHED,RELATED -j CONNSECMARK --restore
# iptables -t security -vnL
Chain INPUT (policy ACCEPT 1014 packets, 74846 bytes)
 pkts bytes target     prot opt in     out     source               destination

Chain FORWARD (policy ACCEPT 0 packets, 0 bytes)
 pkts bytes target     prot opt in     out     source               destination

Chain OUTPUT (policy ACCEPT 607 packets, 145K bytes)
 pkts bytes target     prot opt in     out     source               destination
   25  1653 SECMARK    tcp  --  *      *       0.0.0.0/0            0.0.0.0/0            tcp dpt:80 SECMARK selctx system_u:object_r:default_http_client_packet_t:s0
    0     0 SECMARK    tcp  --  *      *       0.0.0.0/0            54.64.138.248       tcp dpt:80 SECMARK selctx system_u:object_r:myhttp_client_packet_t:s0
   25  1653 CONNSECMARK  tcp  --  *      *       0.0.0.0/0            0.0.0.0/0            tcp dpt:80 CONNSECMARK save
  545  142K CONNSECMARK  all  --  *      *       0.0.0.0/0            0.0.0.0/0            state RELATED,ESTABLISHED CONNSECMARK restore
```

### 試す

```
[user1@localhost ~]$ curl -I www.google.com
curl: (7) Failed to connect to 172.217.24.132: 接続を拒否されました
[user1@localhost ~]$ curl -I 54.64.138.248
HTTP/1.1 301 Moved Permanently
Date: Tue, 05 Sep 2017 16:54:08 GMT
Content-Type: text/html; charset=iso-8859-1
Connection: keep-alive
Server: Apache
Location: https://cookpad.com/
```

54.64.138.248 へのリクエストだけ許可された。
もちろん `user_t` ではない Context だとポリシーが定義されていないので許可されない。

```
[user2@localhost ~]$ id -Z
staff_u:staff_r:staff_t:s0-s0:c0.c1023
[user2@localhost ~]$ curl -I www.google.com
curl: (7) Failed to connect to 216.58.200.196: 接続を拒否されました
[user2@localhost ~]$ curl -I 54.64.138.248
curl: (7) Failed to connect to 54.64.138.248: 接続を拒否されました
```

unconfined_t なユーザは CentOS(RHEL) の targeted モードだと全て許可するポリシーがデフォルトで定義されているのでパケットは出ていくことができる。

```
[root@localhost ~]# id -Z
unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023

[root@localhost ~]# curl -I www.google.com
HTTP/1.1 302 Found
Cache-Control: private
Content-Type: text/html; charset=UTF-8
Referrer-Policy: no-referrer
Location: http://www.google.co.jp/?gfe_rd=cr&dcr=0&ei=9NauWfGhOILR8gfV1ZegDA
Content-Length: 271
Date: Tue, 05 Sep 2017 16:55:16 GMT

[root@localhost ~]# curl -I 54.64.138.248
HTTP/1.1 301 Moved Permanently
Date: Tue, 05 Sep 2017 16:55:43 GMT
Content-Type: text/html; charset=iso-8859-1
Connection: keep-alive
Server: Apache
Location: https://cookpad.com/
```

## 参考文献
* https://oss.sios.com/security/CIL_Reference_Guide_ja.html
* https://wiki.gentoo.org/wiki/SELinux/Tutorials/Defining_SELinux_users
* http://blog.siphos.be/2015/08/filtering-network-access-per-application/