[Kubernetes]SecurityContextの動作を確認する

# はじめに
今回はSecurityContextの動作を確認したいと思います。SecurityContextは、個々のPodまたはコンテナに対して特権やアクセス制御などを定義するセキュリティ設定です。

[Configure a Security Context for a Pod or Container](https://kubernetes.io/docs/tasks/configure-pod-container/security-context/)

設定できる項目のうち、今回は代表的な項目として以下の動作を確認します。なお、PodSecurityContextとSecurityContext（コンテナ）で同じ項目が設定されている場合、SecurityContextの値が優先されます。

|設定項目|概要|PodSecurityContext|SecurityContext
|:---|:---|:---|:---
|runAsUser|実行ユーザ|レ|レ
|runAsGroup|実行グループ|レ|レ
|fsGroup|マウントしたPVやVolumeのファイルシステムのグループ|レ|
|runAsNonRoot|rootで実行しない|レ|レ
|readOnlyRootFilesystem|rootファイルシステムをReadOnlyにする||レ
|capabilities|Capabilitiesを追加/削除する||レ

その他の設定項目は、以下をご確認ください。

[PodSecurityContext v1 core](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.18/#podsecuritycontext-v1-core)
[SecurityContext v1 core](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.18/#securitycontext-v1-core)

# PodSecurityContext
PodSecurityContextはPod内の全てのコンテナに対してセキュリティを設定します。

## 実行ユーザ、グループの設定
spec.securityContext配下に設定値を記載します。今回はCentOS 7と8のコンテナがありますので、それぞれで想定通りに設定されているかを確認します。

```securityContext01.yaml
apiVersion: v1
kind: Pod
metadata:
  name: security-context01
spec:
  securityContext:
    runAsUser: 1000
    runAsGroup: 3000
    fsGroup: 2000
  volumes:
  - name: sec-ctx-vol
    emptyDir: {}
  containers:
  - name: centos7
    image: centos:7
    command: [ "sh", "-c", "sleep 1h" ]
    volumeMounts:
    - name: sec-ctx-vol
      mountPath: /data/demo
  - name: centos8
    image: centos:8
    command: [ "sh", "-c", "sleep 1h" ]
    volumeMounts:
    - name: sec-ctx-vol
      mountPath: /data/demo
```

```
$ kubectl apply -f securityContext01.yaml
pod/security-context01 created
```

### 確認

まずはCentOS 7にログインして確認します。

```
$ kubectl exec -it security-context01 -c centos7 /bin/bash
bash-4.2$ id
uid=1000 gid=3000 groups=3000,2000                         #・・・1.
bash-4.2$ ps -ef                                           #・・・2.
UID        PID  PPID  C STIME TTY          TIME CMD
1000         1     0  0 13:15 ?        00:00:00 sleep 1h
1000         6     0  0 13:16 pts/0    00:00:00 /bin/bash
1000        12     6  0 13:16 pts/0    00:00:00 ps -ef
bash-4.2$ ls -l /data/
total 0
drwxrwsrwx. 2 root 2000 6 Jun 25 13:15 demo                #・・・3.
bash-4.2$ touch /data/demo/testfile                        #・・・4.
bash-4.2$ ls /data/demo/
testfile
bash-4.2$ exit
exit
```
1. uidとgidがそれぞれrunAsUser/runAsGroupで設定したIDになっていますね。また、groupsにはfsGroupで設定したIDも含まれています。
2. プロセスを実行しているユーザがrunAsUserで指定したIDになっていますね。
3. VolumeをマウントしたファイルシステムのグループがfsGroupで指定したIDになっています。指定しない場合、グループも「root」になります。実行ユーザを変更した場合は、マウントしたボリュームに権限がない場合がありますので、その場合にfsGroupを指定します。
4. 書き込みもできます。

次にCentOS 8にログインして同様に確認します。

```
$ kubectl exec -it security-context01 -c centos8 /bin/bash
bash-4.4$ id
uid=1000 gid=3000 groups=3000,2000
bash-4.4$ ps -ef
UID        PID  PPID  C STIME TTY          TIME CMD
1000         1     0  0 13:15 ?        00:00:00 /usr/bin/coreutils --coreutils-prog-shebang=sleep /usr/bin/sleep 1h
1000         6     0  0 13:18 pts/0    00:00:00 /bin/bash
1000        12     6  0 13:18 pts/0    00:00:00 ps -ef
bash-4.4$ ls -l /data/
total 0
drwxrwsrwx. 2 root 2000 22 Jun 25 13:18 demo
bash-4.4$ touch /data/demo/testfile2
bash-4.4$ ls /data/demo/
testfile  testfile2
bash-4.4$ exit
exit
```
CentOS 7と同様ですね。

## rootユーザの実行制限
続いて、rootユーザで実行するのを制限するようにします。
securityContextの設定値を以下のマニフェストのように変更します。

```securityContext02.yaml
apiVersion: v1
kind: Pod
metadata:
  name: security-context02
spec:
  securityContext:
#    runAsUser: 1000
#    runAsGroup: 3000
#    fsGroup: 2000
    runAsNonRoot: true
  volumes:
  - name: sec-ctx-vol
    emptyDir: {}
  containers:
  - name: centos7
    image: centos:7
    command: [ "sh", "-c", "sleep 1h" ]
    volumeMounts:
    - name: sec-ctx-vol
      mountPath: /data/demo
```

上記のマニフェストをapplyすると、エラーになります。
詳細を確認すると、CentOSはrootでの起動が必要になるようです。CentOSの他にも、NginxやRedisも同様のエラーで起動に失敗しました。

```
$ kubectl describe pod security-context02
Name:         security-context02
Namespace:    default

Events:
  Type     Reason     Age              From                   Message
  ----     ------     ----             ----                   -------
  Normal   Scheduled  10s              default-scheduler      Successfully assigned default/security-context02 to k8s-worker02
  Normal   Pulled     7s (x3 over 9s)  kubelet, k8s-worker02  Container image "centos:7" already present on machine
  Warning  Failed     7s (x3 over 9s)  kubelet, k8s-worker02  Error: container has runAsNonRoot and image will run as root
```

# SecurityContext
続いてSecurityContextでコンテナに対して個別にセキュリティを設定します。

## 実行ユーザ、グループの設定
spec.containers.securityContext配下に設定値を記載します。2つのコンテナがありますが、CentOS 7のみに設定して、CentOS 8は設定せずにデフォルトのままとします。

```securityContext03.yaml
apiVersion: v1
kind: Pod
metadata:
  name: security-context03
spec:
  containers:
  - name: centos7
    image: centos:7
    command: [ "sh", "-c", "sleep 1h" ]
    securityContext:
      runAsUser: 1000
      runAsGroup: 3000
  - name: centos8
    image: centos:8
    command: [ "sh", "-c", "sleep 1h" ]
```

```
$ kubectl apply -f securityContext03.yaml
pod/security-context03 created
```

CentOS 7にログインして、各値を確認します。

```
$ kubectl exec -it security-context03 -c centos7 /bin/bash
bash-4.2$ id
uid=1000 gid=3000 groups=3000
bash-4.2$ ps -ef
UID        PID  PPID  C STIME TTY          TIME CMD
1000         1     0  0 14:15 ?        00:00:00 sleep 1h
1000         6     0  0 14:16 pts/0    00:00:00 /bin/bash
1000        12     6  0 14:16 pts/0    00:00:00 ps -ef
bash-4.2$
bash-4.2$ exit
exit
```

PodSecurityContextと同様にユーザとグループが指定したIDになっていて、プロセスを実行しているユーザも設定したユーザとなっています。

次にCentOS 8にログインして確認します。

```
$ kubectl exec -it security-context03 -c centos8 /bin/bash
[root@security-context03 /]# id
uid=0(root) gid=0(root) groups=0(root)
[root@security-context03 /]# ps -ef
UID        PID  PPID  C STIME TTY          TIME CMD
root         1     0  0 14:15 ?        00:00:00 /usr/bin/coreutils --coreutils-prog-shebang=sleep /usr/bin/sleep 1h
root         6     0  0 14:16 pts/0    00:00:00 /bin/bash
root        20     6  0 14:16 pts/0    00:00:00 ps -ef
```

こちらはデフォルトのrootでログインし、プロセスを実行しています。

## ReadOnlyのrootファイルシステム
readOnlyRootFilesystemパラメータをtrueに設定して、Podをデプロイします。

```securityContext04.yaml
apiVersion: v1
kind: Pod
metadata:
  name: security-context04
spec:
  containers:
  - name: centos7
    image: centos:7
    command: [ "sh", "-c", "sleep 1h" ]
    securityContext:
      readOnlyRootFilesystem: true
  - name: centos8
    image: centos:8
    command: [ "sh", "-c", "sleep 1h" ]
```

```
$ kubectl apply -f securityContext04.yaml
pod/security-context04 created
```
デプロイしたら、コンテナにログインし書き込みができないことを確認します。

```
$ kubectl exec -it security-context04 -c centos7 touch /testfile
touch: cannot touch '/testfile': Read-only file system
command terminated with exit code 1
$ kubectl exec -it security-context04 -c centos8 touch /testfile
$ kubectl exec -it security-context04 -c centos8 ls /
bin  etc   lib    lost+found  mnt  proc  run   srv  testfile  usr
dev  home  lib64  media       opt  root  sbin  sys  tmp       var
```
ReadOnlyに設定したCentOS 7には書き込みができませんが、設定していないCentOS 8には書き込めることがわかります。


## Capabilitiesの設定
Capabilitiesによってコンテナに対して[Linux capabilities](https://man7.org/linux/man-pages/man7/capabilities.7.html)個別に追加/削除ができます。
ここでは、CentOS 7に対して「NET_ADMIN」と「SYS_TIME」を追加します。

```securityContext05.yaml
apiVersion: v1
kind: Pod
metadata:
  name: security-context05
spec:
  containers:
  - name: centos7
    image: centos:7
    command: [ "sh", "-c", "sleep 1h" ]
    securityContext:
      capabilities:
        add: ["NET_ADMIN", "SYS_TIME"]
  - name: centos8
    image: centos:8
    command: [ "sh", "-c", "sleep 1h" ]
```

```
$ kubectl apply -f securityContext05.yaml
pod/security-context05 created
```

CentOS 7にログインして確認します。

```
$ kubectl exec -it security-context05 -c centos7 /bin/bash
[root@security-context05 /]#  capsh --print | grep net_admin
Current: = cap_chown,cap_dac_override,cap_fowner,cap_fsetid,cap_kill,cap_setgid,cap_setuid,cap_setpcap,cap_net_bind_service,cap_net_admin,cap_net_raw,cap_sys_chroot,cap_sys_time,cap_mknod,cap_audit_write,cap_setfcap+eip
Bounding set =cap_chown,cap_dac_override,cap_fowner,cap_fsetid,cap_kill,cap_setgid,cap_setuid,cap_setpcap,cap_net_bind_service,cap_net_admin,cap_net_raw,cap_sys_chroot,cap_sys_time,cap_mknod,cap_audit_write,cap_setfcap
[root@security-context05 /]#  capsh --print | grep sys_time
Current: = cap_chown,cap_dac_override,cap_fowner,cap_fsetid,cap_kill,cap_setgid,cap_setuid,cap_setpcap,cap_net_bind_service,cap_net_admin,cap_net_raw,cap_sys_chroot,cap_sys_time,cap_mknod,cap_audit_write,cap_setfcap+eip
Bounding set =cap_chown,cap_dac_override,cap_fowner,cap_fsetid,cap_kill,cap_setgid,cap_setuid,cap_setpcap,cap_net_bind_service,cap_net_admin,cap_net_raw,cap_sys_chroot,cap_sys_time,cap_mknod,cap_audit_write,cap_setfcap
[root@security-context05 /]# exit
exit
```

個別に追加した「NET_ADMIN」と「SYS_TIME」が設定されていることがわかります。

次にCentOS 8を確認します。

```
$ kubectl exec -it security-context05 -c centos8 /bin/bash
[root@security-context05 /]# capsh --print | grep net_admin
[root@security-context05 /]# capsh --print | grep sys_time
[root@security-context05 /]# exit
exit
```

こちらには設定されていませんね。

# まとめ
今回確認した以外にもSecurityContextには設定できる項目があります。設定すればそれだけセキュリティ的には強固になりますが、場合によってはコンテナが起動しないこともありますので、慎重に設計、設定する必要がありますね。