【OpenShift Pipeline】Taskを実行するPodのユーザーは何になる？

Taskを実行するPodのユーザーがrootだったり非rootだったりとまちまちだったため、Security Context Constraints（SCC）の復習も兼ねて確認してみました。

:::note warn
まだまだ勉強中の身のため、おかしな点があればぜひツッコミお願い致します。
:::

## 背景

WorkspaceをTask間で共有する際にユーザーがいい加減だと、例えば

- 何かのリポジトリをcloneしてWorkspaceに置く
- 次Taskでそのリポジトリに何か書き込みをする

といった状況で、Permission Deniedが頻出するようになります。

じゃあ一体どのユーザーでWorkspaceにアクセスしているんだ…？と気になったことが発端です。


## 検証環境

Red Hat OpenShift on IBM Cloud
OCPバージョンは4.7


## 検証内容

`alpine:3.14`および`quay.io/ibmgaragecloud/cli-tools:v14`をベースイメージとしたTaskをそれぞれ作成し、コマンド`id`を実行します。
なぜ`cli-tools`というイメージも確認するかというと、DockerfileでUIDの指定があるからです。Dockerfileは[こちら](https://github.com/cloud-native-toolkit/image-cli-tools/blob/v14/Dockerfile)


## 検証手順

- 検証用のプロジェクト作成。

```
yuki@LAPTOP-R0PDAE4U:~$ oc new-project check-uid
Now using project "check-uid" on server "https://c100-e.jp-tok.containers.cloud.ibm.com:30812".

You can add applications to this project with the 'new-app' command. For example, try:

    oc new-app rails-postgresql-example

to build a new example application in Ruby. Or use kubectl to deploy a simple Kubernetes application:

    kubectl create deployment hello-node --image=k8s.gcr.io/serve_hostname
```

- `alpine`および`quay.io/ibmgaragecloud/cli-tools:v14`をベースイメージとしたTask作成。どちらとも`id`を実行するだけ。

```
yuki@LAPTOP-R0PDAE4U:~$ oc apply -f - <<EOF
> apiVersion: tekton.dev/v1beta1
> kind: Task
> metadata:
>   name: alpine
> spec:
>   steps:
>   - image: alpine:3.14
>     name: check-user
>     script: |
>       id
> EOF
task.tekton.dev/alpine created
```

```
yuki@LAPTOP-R0PDAE4U:~$ oc apply -f - <<EOF
> apiVersion: tekton.dev/v1beta1
> kind: Task
> metadata:
>   name: cli-tools
> spec:
>   steps:
>   - image: quay.io/ibmgaragecloud/cli-tools:v14
>     name: check-user
>     script: |
>       id
> EOF
task.tekton.dev/cli-tools created
```

- Pipeline作成。上記2つのTaskを順に実行するだけ。

```
yuki@LAPTOP-R0PDAE4U:~$ oc apply -f - <<EOF
> apiVersion: tekton.dev/v1beta1
> kind: Pipeline
> metadata:
>   name: check-user
> spec:
>   tasks:
>   - name: alpine
>     taskRef:
>       name: alpine
>       kind: Task
>   - name: cli-tools
>     taskRef:
>       name: cli-tools
>       kind: Task
>     runAfter:
>     - alpine
> EOF
pipeline.tekton.dev/check-user created
```

- Pipeline起動。

```
yuki@LAPTOP-R0PDAE4U:~$ tkn pipeline start check-user
PipelineRun started: check-user-run-ltcg7

In order to track the PipelineRun progress run:
tkn pipelinerun logs check-user-run-ltcg7 -f -n check-uid
```

- 以下に記載したPipelineRunのログを確認すると、`alpine`ではuid=0、`cli-tools`（DockerfileでUIDの指定がある）ではuid=10000になっていることが分かります。

```
yuki@LAPTOP-R0PDAE4U:~$ tkn pipelinerun logs check-user-run-ltcg7 -f -n check-uid
[alpine : check-user] + id
[alpine : check-user] uid=0(root) gid=0(root) groups=0(root),1(bin),2(daemon),3(sys),4(adm),6(disk),10(wheel),11(floppy),20(dialout),26(tape),27(video),1000870000

[cli-tools : check-user] + id
[cli-tools : check-user] uid=10000(devops) gid=0(root) groups=1000(sudo),1000870000
```

どうやら、実行ユーザーがイメージ内で指定されていないとrootユーザーで実行される様子。

OpenShiftはデフォルトだとランダムなUIDを割り振るので、なぜrootユーザーになるのか考えてみます。


## なぜrootユーザーになるのか？

### Taskを実行するPodのServiceAccount

コンテナを実行する権限は、SCCをServiceAccount(SA)に付与し、そのSAをPodで使うようにすることでコントロールできます。

ということで、まずTaskを実行したPodの中身を覗いてみます。

```
yuki@LAPTOP-R0PDAE4U:~$ oc get po check-user-run-ltcg7-alpine-q6ddt-pod-px8b8 -o yaml | grep -i serviceaccount
（略）
  serviceAccount: pipeline
  serviceAccountName: pipeline
```

`pipeline`というServiceAccountを使用しているようです。


### ServiceAccountに紐づくSCC

このServiceAccountにどんなSCCが付与されているか確認します。

確認方法はRolebindingをまず見てみれば良さそうです。SCCを使用するRoleを定義して、それをSAにバインドすることでSCC付与となるようです。いつも`oc adm policy add-scc-to-user`で付与していましたが、この動作が裏で行われているってことなんですかね。。
（参考：https://access.redhat.com/documentation/ja-jp/openshift_container_platform/4.9/html/authentication_and_authorization/role-based-access-to-ssc_configuring-internal-oauth）

`pipeline` SAに`edit` ClusterRoleと`pipelines-scc-clusterrole` ClusterRoleがバインドされていました。

```
yuki@LAPTOP-R0PDAE4U:~$ oc get rolebinding -o wide
NAME                          ROLE                                      AGE     USERS                    GROUPS                             SERVICEACCOUNTS
（略）
edit                          ClusterRole/edit                          7d19h                                                               check-uid/pipeline
pipelines-scc-rolebinding     ClusterRole/pipelines-scc-clusterrole     7d19h                                                               check-uid/pipeline
```

`edit` ClusterRoleは一旦置いておき、`pipelines-scc-clusterrole` ClusterRoleの中身を見てみます。

```
yuki@LAPTOP-R0PDAE4U:~$ oc get clusterrole pipelines-scc-clusterrole -o yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
（略）
  name: pipelines-scc-clusterrole
（略）
rules:
- apiGroups:
  - security.openshift.io
  resourceNames:
  - pipelines-scc
  resources:
  - securitycontextconstraints
  verbs:
  - use
```

`pipelines-scc`というSCCが出てきました。これを使用するroleが`pipeline` SAにバインドされているということで、`pipeline` SAに`pipelines-scc` SCCが付与されていることになります。


### `pipeline-scc`の詳細

```console
$ oc get scc pipelines-scc
NAME            PRIV    CAPS         SELINUX     RUNASUSER   FSGROUP     SUPGROUP   PRIORITY   READONLYROOTFS   VOLUMES
pipelines-scc   false   <no value>   MustRunAs   RunAsAny    MustRunAs   RunAsAny   10         false            ["configMap","downwardAPI","emptyDir","persistentVolumeClaim","projected","secret"]
```

どのユーザーでコンテナを動かしてよいか、を示しているのは`RUNASUSER`です。
ここが`RunAsAny`となっているため、root含めどんなユーザーでも動かせる設定になっています。
（参考：https://access.redhat.com/documentation/ja-jp/openshift_container_platform/4.9/html-single/authentication_and_authorization/index#authorization-SCC-strategies_configuring-internal-oauth ）

Pod内のYAML定義を眺めてもユーザーの指定はなかったため、その場合はDockerfile内のユーザー指定がそのまま反映されるようです。
（参考：https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#securitycontext-v1-core の「runAsUser」の項目）

`runAsUser`が`runAsAny`のSCCが付与された状態で、Podの定義やDockerfile内にユーザー指定がない場合はどうやらrootになるようです（Docker上だと特に何も指定しなければrootで動くという認識はあるのですが、それらしきドキュメントが見当たらず。。）


## まとめ

冒頭の

> Taskを実行するPodのユーザーがrootだったり非rootだったりとまちまちだった

というのは、`pipeline-scc`というSCCの力により、

- UIDの指定がどこかに（Dockerfileなど）あればそのユーザーで実行される
- どこにもなければrootで実行される

という動作によるものであると言えそうです。
Workspaceを使ってPipelineを動かすならば、実行ユーザーも意識する必要があります。

ちなみにPipelineRunで実行ユーザーを指定できるようです。
https://tekton.dev/docs/pipelines/pipelineruns/#specifying-a-pod-template
これをやっておけばPermission Deniedで怒られなくなる…はず？いつか試します。