# 如何處理 user/group 及其設計
目前 Yunikorn 取得 user 的方法是透過 **pod 的 config yaml 內的 label 屬性**得到的,以下為範例:
```yaml
metadata:
labels:
yunikorn.apache.org/username: "john"
```
但是這樣的情況會有冒充身分的問題,且身分組別並不一定與 K8s 所指的 user 相同,因此在以下會討論到的重點為:
- 目標:
- 建立一種穩定的方式去取得 k8s 的 user 或 group ,並將其與 yunikorn 設定的 user 對應
- 評估在 shim 裡對於 group 的解析度
- 定義在 k8s 上的物件的 user 或 group
- 非目標:
- 不考慮授權或身分驗證的部分,因為那是 yunikorn 或 k8s 的一部分
- 不改變 shim 與 core 對於身分組資訊的溝通方法
## k8s 內的 user 及 group
k8s 內的 user 分為兩種,一種是 normal user(human) 另一種是 service account(pod內)
而由於 k8s 所需要部署的 yaml 文件內部資訊指儲存了 ServiceAccount ,因此無法直接從 pod 的 config 檔取得對應的 user
至於 user 所屬的 group 則是只分為兩種, `system:authenticated` 及`system:unauthenticated`,但 group 與 user 同理,無法由部署文件得知
## 當前可行方案與挑戰
因為無法從部署文件得知 pod 所屬之 user,因此只能在部署前的更早階段就完成「抓取 user」的動作

如上圖所示,由 Admission Controller 去取得 k8s 的 user 設定,並在新增(Admission Controller 有新增、修改、刪除等等權力) 一個 pod 時,**攔截**並對其 config 檔做更動
但對於「非直接新增」 pod 的方法,如 deployment 、 job 等等方法, Admission Controller 所抓取到得 user 資訊為 deployment 或 job 之 ServiceAccount
針對這個問題, Yunikorn 給出的解法是「新增 API 取得『需監聽的 object』」
下例為 Admission Controller 抓取「直接新增」之 pod :
```yaml
INFO webhook/admission_controller.go:180 AdmissionReview
{
"Namespace": "default",
"UID": "3f8c8df5-0272-4c85-874f-0aa1ece74fbc",
"Operation": "CREATE",
"UserInfo": {
"username":"minikube-user",
"groups":[
"system:masters",
"system:authenticated"
]
}
}
```
下例為未使用 API 時, Admission Controller 抓取「非直接新增」之 pod:
```yaml
INFO webhook/admission_controller.go:177 AdmissionReview
{
"Namespace": "default",
"UID": "be94ea46-c1d2-442a-a46a-60589d582abd",
"Operation": "CREATE",
"UserInfo": {
"username":"system:serviceaccount:kube-system:job-controller",
"uid":"32abc062-eb53-4dca-9669-55251d687939",
"groups":[
"system:serviceaccounts",
"system:serviceaccounts:kube-system",
"system:authenticated"
]
}
}
```
下例為使用 API 時, Admission Controller 抓取「非直接新增」之 pod 的結果與上例的差別:
```yaml
INFO webhook/admission_controller.go:483 Deployment validation - pass through
{
"User": {
"username":"minikube-user",
"groups":[
"system:masters",
"system:authenticated"
]
}
}
```
此方法之可行性請參考 [Git Commit](https://github.com/pbacsko/incubator-yunikorn-k8shim/commit/478d688675a1ca98afb895672295d61b47172d19)
由於此方法的大致方向為「攔截並修改其部署檔案」,因此出現另一個問題為「Admission Controller 是否會覆蓋其原本的 user 或 ServiceAccount」,這個敘述將在[之後](#規避身分冒充問題)詳細定義
## 以 Yunikorn 取得 group
Yunikorn core 是有群組查找功能的,有兩種方法:基於操作系統的簡單查找及虛擬查找
「基於操作系統的簡單查找」就像使用 api 去取得 container 內的資訊,進而得出 group
「虛擬查找」就像在 container 內模擬一個新的 unix user 進而得出 group
此功能是可插拔的,並且以虛擬查找作為預設方式,但由於 contianer 內的作業系統常常為不完整的,因此群組查找通常都是失敗的
Yunikorn core 只有在 shim 沒給出 group 的資訊時才會執行查找,但如一開始說的, shim 預設只傳送 username 給 core ,所以基本上 core 都會執行群組查找;而由於群組查找通常都失敗,因此 group 的取得應該在 shim 那邊做完
## user 資訊物件
在 Admission Controller 取得 user、group 等資訊後,使用 annotation 將資訊加進部署文件內(不使用 label 的原因是可用字長太短)
而 annotation 的名字為 `yunikorn.apache.org/user.info`
在 scheduler interface 內的 `UserGroupInfo` 定義如下
```go
message UserGroupInformation {
// the user name
string user = 1;
// the list of groups of the user, can be empty
repeated string groups = 2;
}
```
用 json 可以表達 user 及 group,且 group 的順序不會亂掉,因此 annotation 內使用 json 表達,以下為例子:
```yaml
yunikorn.apache.org/user.info: "
{
username: \"joe\",
groups: [
\"developers\",
\"system:authenticated\"
]
}"
```
`system:authenticated` 的部分會在 k8s 的授權階段中加入,所以不會特別加
由於 `yunikorn.apache.org/user.info` 這個 annotation 是 yunikorn 所需要的,因此需避免 user 修改已建立的 pod 的部署文件時的更動,畢竟 k8s **不保證**更動時會通知到 shim
避免更動有以下兩個方法:
在**更動**後,將 annotation 修改為自己需要的
在**驗證**時,如果 annotation 被修改過,則駁回
更動的部分, Admission Controller 已在 pod "create" 時監聽,其方法可擴展至 "update"
驗證的部分則在 configMap 註冊一個 hook ,其方法可擴展至 pod
建議為**使用驗證方法**
對於向後兼容性,目前的 label 方法將會持續到下個主要版本(2.0.0),在此之前,1.x 版本都還在支援的範圍裡面
但如果部署文件裡有舊的方法(label)和新的方法(annotation)時, Yunikorn 將以新方法為準,且**不支援兩者合併**
## 規避身分冒充問題
這個問題由上面提到的 Admission Controller 不該覆蓋掉 user 所給定的 user 資訊延伸而出
對於給定的 user 資訊應該加以管理,因此以上面的圖做為基底,改為下圖

對於 controller 部署的 pod ,會有以下兩種結果
開啟 `--use-service-account-credentials` 時,使用的 user 為經過 k8s 認證的 ServiceAccount
(預設)關閉時,使用的 user 資訊變為 `kube-controller-manager`
對於以上兩種情況,設置文件應該都要支援,但推薦只用上面那一種
對於外部創建(如 job 或 deployment)的 pod ,Admission Controller 也應新增 annotation,但對於安全性,與上面 controller 不同的是,此功能應該支持對 user 或 group 進行限制,因此對於外部創建的 pod 之配置應與上述 controller 部署的 pod 分開配置
## 設置文件
依據上面的敘述,會有以下的屬性需要新增,注意,前綴都包含 `ADMISSION_CONTROLLER_`
改變以下屬性**並不會**觸發自動更新
|Name | Description | Default|
| -------- | -------- | -------- |
| `BYPASS_AUTH` | Allow external users to create pods with user information set. Type: boolean | false
|BYPASS_CONTROLLERS | Allow controller users to create pods with user information set. Type: boolean | true|
SYSTEM_USERS | Regular expression for the allowed controller service account list. Type: string | "system:serviceaccount:kube-system:\*"|
EXTERNAL_USERS | Regular expression for the allowed external user list. Type: string | "" |
EXTERNAL_GROUPS | Regular expression for the allowed external group list. Type: string | "" |
特定屬性的決策過程以如下 pseudo code 表達:
```
if BYPASS_AUTH
if user label and annotation not be setted
add annotation
else
add annotation
```
而以上過程又會根據 `EXTERNAL_USERS` 及 `EXTERNAL_GROUPS` 的值來決定是否覆蓋已指定的 user 資訊
---
[官方文檔連結](https://yunikorn.apache.org/docs/design/user_group)