OpenShiftのデフォルトユーザーはなにができるか、なぜできるのか

# はじめに
OpenShift Originを最近触っています。
各種チュートリアルでは新しいProject（≒K8sのNamespace）を作成しているのですが
「そもそも人によってProjectの作成を制限するにはどうすれば良いのか」と疑問が浮かんだので権限周りを調べました。

- デフォルトのユーザーはなにができるのか
- なぜそれができるのか

を軸に調べていきます。

※デフォルトユーザーは「クラスタ管理者が、明示的に権限を与えていないユーザー」とします
※ClusterRole/ClusterRoleBindingのあたりの説明は行いません。一番下にリンクがありますのでそちらからどうぞ。

# 環境
OpenShift Origin 3.9
（ユーザー周りはLDAP連携等いろいろありますが、今回はhtpasswdで簡単にすませました）

# なにができるか
## 権限の確認 
早速適当なユーザーを作成し、何ができるかを確認してみます。

自分がどのような操作が可能なのかは、`oc auth can-i --list`で確認できます。
（`Command "can-i" is deprecated, use 'oc auth can-i'`というメッセージが出てくるのですが、`oc auth can-i`の方には--listがなさそうです。誰か一覧を出す方法を教えてください。）

```bash
VERBS              NON-RESOURCE URLS        RESOURCE NAMES   API GROUPS                     RESOURCES
[get]              [/]                      []               []                             []
[get]              [/]                      []               []                             []
[get]              [/]                      []               []                             []
[get]              [/.well-known]           []               []                             []
[get]              [/.well-known]           []               []                             []
[get]              [/.well-known]           []               []                             []
[get]              [/.well-known/*]         []               []                             []
[get]              [/.well-known/*]         []               []                             []
[get]              [/.well-known/*]         []               []                             []
[get]              [/api]                   []               []                             []
[get]              [/api]                   []               []                             []
[get]              [/api]                   []               []                             []
[get]              [/api/*]                 []               []                             []
[get]              [/api/*]                 []               []                             []
[get]              [/api/*]                 []               []                             []
[get]              [/apis]                  []               []                             []
[get]              [/apis]                  []               []                             []
[get]              [/apis]                  []               []                             []
[get]              [/apis/*]                []               []                             []
[get]              [/apis/*]                []               []                             []
[get]              [/apis/*]                []               []                             []
[get]              [/healthz]               []               []                             []
[get]              [/healthz/*]             []               []                             []
[get]              [/oapi]                  []               []                             []
[get]              [/oapi]                  []               []                             []
[get]              [/oapi]                  []               []                             []
[get]              [/oapi/*]                []               []                             []
[get]              [/oapi/*]                []               []                             []
[get]              [/oapi/*]                []               []                             []
[get]              [/osapi]                 []               []                             []
[get]              [/osapi]                 []               []                             []
[get]              [/osapi]                 []               []                             []
[get]              [/osapi/]                []               []                             []
[get]              [/osapi/]                []               []                             []
[get]              [/osapi/]                []               []                             []
[get]              [/swagger-2.0.0.pb-v1]   []               []                             []
[get]              [/swagger-2.0.0.pb-v1]   []               []                             []
[get]              [/swagger-2.0.0.pb-v1]   []               []                             []
[get]              [/swagger.json]          []               []                             []
[get]              [/swagger.json]          []               []                             []
[get]              [/swagger.json]          []               []                             []
[get]              [/swaggerapi]            []               []                             []
[get]              [/swaggerapi]            []               []                             []
[get]              [/swaggerapi]            []               []                             []
[get]              [/swaggerapi/*]          []               []                             []
[get]              [/swaggerapi/*]          []               []                             []
[get]              [/swaggerapi/*]          []               []                             []
[get]              [/version]               []               []                             []
[get]              [/version]               []               []                             []
[get]              [/version]               []               []                             []
[get]              [/version/*]             []               []                             []
[get]              [/version/*]             []               []                             []
[get]              [/version/*]             []               []                             []
[create get]       []                       []               []                             [buildconfigs/webhooks]
[create get]       []                       []               [build.openshift.io]           [buildconfigs/webhooks]
[create]           []                       []               []                             [builds/docker]
[create]           []                       []               [build.openshift.io]           [builds/docker]
[create]           []                       []               []                             [builds/jenkinspipeline]
[create]           []                       []               [build.openshift.io]           [builds/jenkinspipeline]
[create]           []                       []               []                             [builds/optimizeddocker]
[create]           []                       []               [build.openshift.io]           [builds/optimizeddocker]
[create]           []                       []               []                             [builds/source]
[create]           []                       []               [build.openshift.io]           [builds/source]
[get list]         []                       []               []                             [clusterroles]
[get list]         []                       []               [authorization.openshift.io]   [clusterroles]
[get list watch]   []                       []               [rbac.authorization.k8s.io]    [clusterroles]
[get list watch]   []                       []               [servicecatalog.k8s.io]        [clusterserviceclasses]
[get list watch]   []                       []               [servicecatalog.k8s.io]        [clusterserviceplans]
[delete]           []                       []               []                             [oauthaccesstokens]
[delete]           []                       []               [oauth.openshift.io]           [oauthaccesstokens]
[delete]           []                       []               []                             [oauthauthorizetokens]
[delete]           []                       []               [oauth.openshift.io]           [oauthauthorizetokens]
[create list]      []                       []               []                             [projectrequests]
[create list]      []                       []               [project.openshift.io]         [projectrequests]
[list watch]       []                       []               []                             [projects]
[list watch]       []                       []               [project.openshift.io]         [projects]
[create]           []                       []               [authorization.k8s.io]         [selfsubjectaccessreviews]
[create]           []                       []               []                             [selfsubjectrulesreviews]
[create]           []                       []               [authorization.k8s.io]         [selfsubjectrulesreviews]
[create]           []                       []               [authorization.openshift.io]   [selfsubjectrulesreviews]
[get list]         []                       []               [storage.k8s.io]               [storageclasses]
[impersonate]      []                       []               [authentication.k8s.io]        [userextras/scopes.authorization.openshift.io]
[get]              []                       [~]              []                             [users]
[get]              []                       [~]              [user.openshift.io]            [users]
```

なにやら大量に出てきましたが、明示的に権限を付与せずともある程度の操作は可能なようです。
今回はRESOUCESに絞ってみていきたいと思います。
表にすると下記のような形に。(○は権限あり）

|API GROUPS|RESOURCES|get|list|watch|create|delete|impersonate|
|---|---|---|---|---|---|---|---|
||buildconfigs/webhooks|○|||○|||
|build.openshift.io|buildconfigs/webhooks|○|||○|||
||builds/docker||||○|||
|build.openshift.io|builds/docker||||○|||
||builds/jenkinspipeline||||○|||
|build.openshift.io|builds/jenkinspipeline||||○|||
||builds/optimizeddocker||||○|||
|build.openshift.io|builds/optimizeddocker||||○|||
||builds/source||||○|||
|build.openshift.io|builds/source||||○|||
||clusterroles|○|○|||||
|authorization.openshift.io|clusterroles|○|○|||||
|rbac.authorization.k8s.io|clusterroles|○|○|○||||
|servicecatalog.k8s.io|clusterserviceclasses|○|○|○||||
|servicecatalog.k8s.io|clusterserviceplans|○|○|○||||
||oauthaccesstokens|||||○||
|oauth.openshift.io|oauthaccesstokens|||||○||
||oauthauthorizetokens|||||○||
|oauth.openshift.io|oauthauthorizetokens|||||○||
||projectrequests||○||○|||
|project.openshift.io|projectrequests||○||○|||
||projects||○|○||||
|project.openshift.io|projects||○|○||||
|authorization.k8s.io|selfsubjectaccessreviews||||○|||
||selfsubjectrulesreviews||||○|||
|authorization.k8s.io|selfsubjectrulesreviews||||○|||
|authorization.openshift.io|selfsubjectrulesreviews||||○|||
|storage.k8s.io|storageclasses|○|○|||||
|authentication.k8s.io|userextras/scopes.authorization.openshift.io||||||○|
||users|○||||||
|user.openshift.io|users|○||||||

※`users`はget可能になっていますが、ResouceNamesに`[~]`とあるように、自分自身の情報しか取得できません。
※他にもVerbはあるのですが、いったん出てきたものだけあげています。

OpenShift(Kubernetes)お馴染みのPodなどのリソースが表示されていないので、この状態では参照もできません。

ポイントとなるのは`ProjectRequests`が`Create`になっている点です。
このおかげでユーザーはProjectを作成することができます。

「なにができるのか」は上の表で概要はつかめるので、「なぜできるのか」を追っていきます。

# なぜできるのか

## ClusterRoleBindingの確認
上記をみる限り、自動でなにかしらのClusterRoleがBindingされているようなので`oc get clusterrolebinding.rbac -o wide`で確認してみます。

```bash
Error from server (Forbidden): clusterrolebindings.rbac.authorization.k8s.io is forbidden: User "[ユーザ名]" cannot list clusterrolebindings.rbac.authorization.k8s.io at the cluster scope: User "[ユーザ名]" cannot list all clusterrolebindings.rbac.authorization.k8s.io in the cluster
```

おっと、デフォルトのユーザーのままだったので上記のようなエラーが出てしまいました。
表を見る限りClusterRoleの参照はできますが、ClusterRoleBindingの参照は（can-iで確認した通り）できません。
`oc login -u [ユーザ名]`で参照可能なユーザに変更し、再度コマンドを実行します。

```bash
NAME    AGE       ROLE                  USERS       GROUPS       SERVICEACCOUNTS
admin   6h        ClusterRole/admin                              openshift-infra/template-instance-controller
...
```

たくさん情報が表示されると思いますが、`USERS`のところには作成したユーザー名は表示されていませんでした。
その代わり、`GROUPS`欄にいくつかグループが表示されているのがわかると思います。

ざっくりした予想ですが、

- **ユーザーが自動的に属するグループがある**
- **そのグループに対してClusterRoleがBindingされている**

となっていそうです。

## 仮想グループ
OpenShiftでは、明示的に指定したグループのほかにも、仮想グループというものが自動的にプロビジョニングされ、ユーザに適用されます。

|仮想グループ|説明|
|---|---|
|system:authenticated|認証されたすべてのユーザーに自動的に関連付けられます。|
|system:authenticated:oauth|OAuthアクセストークンで認証されたすべてのユーザーに自動的に関連付けられます。|
|system:unauthenticated|認証されていないすべてのユーザーに自動的に関連付けられます。|

今回は認証済みユーザについて調査しているので、`system:authenticated`と`system:authenticated:oauth`関連をみていけばわかりそうです。
先ほどの`ClusterRoleBinding`の出力で`GROUPS`に`system:authenticated`または`system:authenticated:oauth`があるものを抜き出してみます。

|ClusterRoleBinding|ClusterRole|GROUPS|
|---|---|---|
|basic-users|basic-user|system:authenticated|
|cluster-status-binding|cluster-status|system:authenticated|
self-access-reviewers|self-access-reviewer|system:authenticated|
self-provisioners|self-provisioner|system:authenticated:oauth|
servicecatalog-serviceclass-viewer-binding|servicecatalog-serviceclass-viewer|system:authenticated|
system:basic-user|system:basic-user|system:authenticated|
system:build-strategy-docker-binding|system:build-strategy-docker|system:authenticated|           
system:build-strategy-jenkinspipeline-binding|system:build-strategy-jenkinspipeline|system:authenticated|                
system:build-strategy-source-binding|system:build-strategy-source|system:authenticated|
system:discovery|system:discovery|system:authenticated|
system:discovery-binding|system:discovery|system:authenticated|
system:oauth-token-deleters|system:oauth-token-deleter|system:authenticated|
system:scope-impersonation|system:scope-impersonation|system:authenticated|
system:webhooks|system:webhook|system:authenticated|

`ClusterRole`を確認していけば、どのようなResourceにどのようなVerbが許可されているのかわかります。
`oc describe clusterrole.rbac [ClusterRole名]`で確認し、権限の表に「どのClusterRoleが権限を担っているのか」を追加してみます。

|API GROUPS|RESOURCES|get|list|watch|create|delete|impersonate|ClusterRole|
|---|---|---|---|---|---|---|---|---|
||buildconfigs/webhooks|○|||○|||system:webhook|
|build.openshift.io|buildconfigs/webhooks|○|||○|||system:webhook|
||builds/docker||||○|||system:build-strategy-jenkinspipeline|
|build.openshift.io|builds/docker||||○|||system:build-strategy-jenkinspipeline|
||builds/jenkinspipeline||||○|||system:build-strategy-jenkinspipeline|
|build.openshift.io|builds/jenkinspipeline||||○|||system:build-strategy-jenkinspipeline|
||builds/optimizeddocker||||○|||system:build-strategy-jenkinspipeline|
|build.openshift.io|builds/optimizeddocker||||○|||system:build-strategy-jenkinspipeline|
||builds/source||||○|||system:build-strategy-source|
|build.openshift.io|builds/source||||○|||system:build-strategy-source|
||clusterroles|○|○|||||basic-user|
|authorization.openshift.io|clusterroles|○|○|||||basic-user|
|rbac.authorization.k8s.io|clusterroles|○|○|○||||basic-user|
|servicecatalog.k8s.io|clusterserviceclasses|○|○|○||||servicecatalog-serviceclass-viewer|
|servicecatalog.k8s.io|clusterserviceplans|○|○|○||||servicecatalog-serviceclass-viewer|
||oauthaccesstokens|||||○||system:oauth-token-deleter|
|oauth.openshift.io|oauthaccesstokens|||||○||system:oauth-token-deleter|
||oauthauthorizetokens|||||○||system:oauth-token-deleter|
|oauth.openshift.io|oauthauthorizetokens|||||○||system:oauth-token-deleter|
||projectrequests||○||○|||basic-user(list), self-provisioner(create)|
|project.openshift.io|projectrequests||○||○|||basic-user(list), self-provisioner(create)|
||projects||○|○||||basic-user|
|project.openshift.io|projects||○|○||||basic-user|
|authorization.k8s.io|selfsubjectaccessreviews||||○|||system:basic-user|
||selfsubjectrulesreviews||||○|||basic-user|basic-user|
|authorization.k8s.io|selfsubjectrulesreviews||||○|||system:basic-user|
|authorization.openshift.io|selfsubjectrulesreviews||||○|||basic-user|
|storage.k8s.io|storageclasses|○|○|||||basic-user|
|authentication.k8s.io|userextras/scopes.authorization.openshift.io||||||○|system:scope-impersonation|
||users|○||||||basic-user|
|user.openshift.io|users|○||||||basic-user|

※`ClusterStatus`、`system:discovery`はNon-Resource URLs関連のため、上記表には登場せず。

きれいに埋まりました。

# まとめ
- **デフォルトのユーザーはなにができるのか**  
表を参照
- **なぜそれができるのか**  
ユーザーは自動的にOpenShiftの仮想グループに属しており、
その仮想グループにClusterRoleがBindingされているから。

# さらなる疑問-TODO
**「デフォルトユーザのままだと結局Podが作れない？」**
**「チュートリアルだとPodとかService作れるけど、どこかで権限が付与されている？」**  
Projectの作成時、Projectローカルなadmin権限が割り当てられてるような気がしています。

**「can-iではProjectにlist可と書いてあるのに、defaultやkube-publicが見えない」**
自分もよくわかりません。誰か教えてください...。
なにかKubernetesの基本的なところを見落としている気がします。

**「どのResouceがなにをあらわしているのか。実際に運用するときに制限を検討した方がいいResourceは？」**
Resourceは有名なものしかよくわかりません...どれを制限すべきかは難しいところです。

**「クラスタ管理用、プロジェクト管理用、参照用みたいなClusterRoleプリセットはある？」**
公式でいくつか用意されています。

[ARCHITECTURE 4.2.4. Cluster Roles and Local Roles](https://access.redhat.com/documentation/en-us/openshift_container_platform/3.9/html-single/architecture/index)

|ClusterRole|説明|
|---|---|
|admin|プロジェクトマネージャーです。ローカルバインディングで使用されている場合、admin ユーザーにはプロジェクトのリソースを閲覧し、クォータを除くプロジェクトのすべてのリソースを変更する権限があります。|
|basic-user|プロジェクトおよびユーザーについての基本的な情報を取得できるユーザーです。|
|cluster-admin|プロジェクトですべてのアクションを実行できるスーパーユーザーです。ローカルバインディングでユーザーにバインドされる場合、クォータおよびプロジェクトのすべてのリソースに対するすべてのアクションに対するフルコントロールがあります。|
|cluster-status|基本的なクラスターのステータス情報を取得できるユーザーです。|
|edit|プロジェクトのほとんどのオブジェクトを変更できるが、ロールまたはバインディングを表示したり、変更したりする機能を持たないユーザーです。|
|self-provisioner|独自のプロジェクトを作成できるユーザーです。|
|view|変更はできないが、プロジェクトでほとんどのオブジェクトを確認できるユーザーです。これらのユーザーはロールまたはバインディングを表示したり、変更したりできません。|

**「○○のcreateを"拒否"したい」**
「どれを許可するか」が考え方のベースのはずなので、既につけられている権限をなにかしらで拒否するというのはできないような気がします。

**「独自のClusterRoleを作りたい」**  
参考の「How to customize OpenShift RBAC permissions」をどうぞ。

**「K8sとの違いはある？」**  
調べていませんが、多分そんなに違いはないと思いたいです...。

# 参考
非常に勉強になった記事。必読。

[OpenShiftのRBACを完全に理解する - nekop's blog](https://nekop.hatenablog.com/entry/2017/12/12/182149)
[How to customize OpenShift RBAC permissions - RedHat Developer Blog](https://developers.redhat.com/blog/2017/12/04/customize-openshift-rbac-permissions/)

# おまけ
「Projectの作成を制限したい」というのは公式ドキュメントで方法が紹介されています。
https://docs.openshift.com/container-platform/3.9/admin_guide/managing_projects.html

```bash
oc adm policy remove-cluster-role-from-group self-provisioner system:authenticated:oauth
```
`system:authenticated:oauth`仮想グループからself-provisionerのRoleを削除しています。
（ちょっと乱暴な気も...「クラスタを更新したら元に戻ってた」とかありそう）