理解OpenShift(1):網絡之 Router 和 Route
理解OpenShift(5):從 Docker Volume 到 OpenShift Persistent Volume
** 本文基於 OpenShift 3.11,Kubernetes 1.11 進行測試 ***
OpenShift 支持 RBAC(Role Based Access Controll),基於角色的訪問控制。它涉及諸多概念,本文嘗試着做一些概念上的梳理,很多細節還需要進一步研究。
1. 主要概念及其之間的聯系
1.1 用戶(User)
我試着把一個OpenShift 環境中的所有用戶分為三大類:
- 應用用戶:部署在集群之中的應用自己的用戶。一般來說每個應用都有自己的用戶管理系統,與平台無關。也有一些應用,比如 Jenkins,支持與OpenShift 用戶系統集成,也就是Jenkins允許用戶在通過了OpenShift 用戶認證后對其進行訪問。這部分不是本文的討論范圍之內。
- OpenShift 用戶:訪問OpenShift 資源的用戶。根據其特征,又將其分為三個子類:
- Regular user:代表一個自然人用戶,比如部署應用的一個開發者。
- System user:OpenShift 系統用戶,大部分在集群創建時被自動創建,比如每個node都有一個system user。因為這部分主要和OpenShift自身系統相關,與一般用戶關系不太大,因此本文不會具體介紹這部分。
- Service account:服務賬戶。這是跟一個項目關聯的特殊系統用戶,每個用戶被一個 ServiceAccount 對象表示,通常是指 pod 中運行主進程的用戶。后文會有具體介紹。
- 操作系統用戶:訪問操作系統資源的用戶,又分為容器內的操作系統用戶,和宿主機上的操作系統用戶。
1.2 身份(Identity)與認證(Authentication)
認證是確認用戶身份是否合法的過程。這可以有多種實現方式,比如用戶名/密碼、令牌(token)、證書等。不同類型的用戶有不同的身份管理方式:
- 對於 regular user,每個用戶有一個身份(identity)用於認證。OpenShift 以插件形式支持多種 identity provider,比如在測試環境中常用的 htpasswd,生產環境中常用的 LDAP 等。這些 provider 中會保存用戶身份信息,比如用戶名和密碼。useridentitymapping 對象將 user 對象和 identity 對象聯系在一起。
- 對於 service account,一方面它需要訪問 OpenShift 集群資源比如 API 和內部鏡像倉庫中的鏡像,另一方面它可能需要訪問 pod 中和宿主機上的操作系統資源,比如宿主機上的文件或網絡。對於前者,每個 service account 使用 secret 來進行身份認證,包括用戶 API 訪問的 token 和用於從鏡像倉庫拉取代碼的 secret。對於后者,本來有 user namespace(用戶命名空間)來支持,但是似乎OpenShift 還不支持該功能。
1.3 角色(Role)/權限(Permission)與授權(Authorization)
授權是對被認證了的用戶訪問受控制的資源的權限進行控制。按照資源類型,OpenShift 將授權管理方式分為兩大類:
- 對於 OpenShift 集群資源,比如 pod,deployment,route 等,通過 role (角色)進行控制。從范圍(scope)上分,role 可分為集群角色(clusterRole)和項目角色(role)。每個角色定義了受控制的對象(subject)、允許的操作(verb)和范圍(集群還是項目)。用戶(user)和角色(role/clusterRole)之間通過 rolebinding/clusterrolebinding 對象進行綁定。
- 對於操作系統資源,這只針對服務賬戶。宿主機上的用戶訪問宿主機上的資源,這由宿主機操作系統進行控制。pod 中的用戶(serviceaccount)訪問pod內和宿主機上操作系統資源,由 scc(security context constraints)進行控制。
2. 身份 (Identity) 與認證(Authentication)
就像人的身份證一樣,identity 是一個 user 的身份信息,該信息用於用戶認證。OpenShift 自己並沒有實現用戶身份信息庫,而是通過靈活支持多種 identity provider,來實現各種不同的身份管理方案。每種實現都以不同的方式保存着用戶的身份信息,比如保存在LDAP 中,保存在 htpasswd 文件中,保存在 OpenStack Keystone 中。
因此,OpenShift 對 user 身份的校驗是通過這些配置了的 identity provider 進行的。因此,還需要 OpenShift user 和這些 provider 里面的 identity 的映射關系。OpenShfit 支持四種映射管理,claim,lookup,generate,add。具體請參考官網 https://docs.openshift.com/container-platform/3.11/install_config/configuring_authentication.html#identity-providers_parameters。
上圖中以 htpasswd 這種 identity provider 為例,說明了從零創建一個 openshift user,在 htpasswd 中創建 user 及其密碼,然后創建 openshift identity 對象以及 useridentitymapping 對象的過程。
用戶獲取用於身份認證的token的過程:
- 所有申請 OAuth token 的請求都要發到 <master>/oauth/authorize和
<master>/oauth/token
。每個申請 OAuth token 的請求中都必須帶有 OAuth client 標識,這是一個 OAuthClient 對象。具體請參閱官方文檔。 - OpenShift master 節點上內置有一個 OAuth server。用戶從 OAuth 獲取 token 后再用它去訪問 API 就可以認證通過了。當一個 user 申請一個 OAuth token 時,OAuth 使用配置的 identity provider 去確定該申請用戶的身份。它在確定該用戶所映射到的 identity后,會為該用戶創建一個 token,然后返回該token。
3. 角色(Role)和授權(Authorization)
前文說了,角色用於控制對 OpenShift 資源的訪問權限,它分為項目角色和集群角色。
OpenShift 系統默認會創建很多的集群角色。常用的角色的簡單描述如下:
admin |
|
basic-user |
|
cluster-admin |
|
cluster-status |
|
edit |
|
self-provisioner |
|
view |
|
可以查看所有角色,比如 system:router 角色:
可以使用 oc adm policy 命令向用戶或用戶授予角色:
user、group、role、rolebinding 之間的關系:
更多對 role 的說明,可參見官方文檔。
4. Service Account 用戶
OpenShift 的 service account 比較復雜,和很多概念都有關聯。官方文檔在 https://docs.openshift.com/container-platform/3.11/dev_guide/service_accounts.html。該文檔對為什么需要這個概念的說明是:當一個自然人用戶訪問 OpenShfit API 時,OpenShift 對它進行用戶認證和權限控制。但是,有時候做操作的並不是自然人用戶,比如:
- Replication Controller 調用 API 去創建或者刪除 pod
- 容器中的應用調用 API
- 外部應用調用 API 去進行監控或者整合
為了這種訪問,OpenShift 創造了 Service Account (簡稱sa)概念。每個項目(project)默認都會自動創建3個sa user:
Service Account | Usage |
---|---|
builder |
用於 build pod。它被授予了 system:image-builder 角色,因此被允許向內部鏡像倉庫中的任意 image stream 上push 鏡像。 |
deployer |
用於 deployment pod。被授予了 system:deployer 角色,因此被允許查看和修改集群中的 RC 及其 pod。 |
default |
當一個 pod 沒有顯式指定 service account 默認都會使用該 sa user。 |
4.1 身份
sa 利用 secret(token)來保存其身份信息。默認情況下,每個 sa 用戶都有兩種token,即訪問 OpenShift API 的token和訪問內部鏡像倉庫的token。以系統默認的 『builder』 sa user 為例,它包含一個用於拉取鏡像的token secret,兩個訪問API 的token secret,三個secret 中只有兩個能被以卷的形式掛接給pod:
可以按需求修改一個 sa 賬戶的這些token。可參考 https://docs.openshift.com/enterprise/3.0/dev_guide/image_pull_secrets.html 為service account 創建和添加新的用於拉取鏡像的token secret。具體請參考官方文檔。
其中,sa 的 API token 會被掛接到 pod 的 目錄的 token 文件,從而使得 pod 中的應用可以讀取該 token 去訪問 OpenShift API:
image pull secret 是如何掛載到 pod 的,我還沒有找到。而且在 pod spec 中,只看到 API token secret 的 mountpoint,而並沒有 imagePullSecret 的 mountpoint:
4.2 權限 - 訪問OpenShift 集群資源的權限
和自然人 user 類似,對 sa 用戶訪問OpenShift 集群資源的權限控制是通過 role 進行的。前面的表格表明,項目的兩個默認 sa builder 和 deployer 都被授予了相應的 role,從而能訪問指定的資源。而默認的 sa 用戶,只被授予了 /system:image-puller 角色。這意味着默認的 sa 用戶只能拉取鏡像,而不能訪問集群其它資源。
下圖是項目 stage 中的 rolebinding:
用戶組 system:serviceaccounts:stage 中包括該項目中的所有 sa 用戶。利用 default sa user 來做下實驗。先獲取其 API token,然后登錄進 OpenShift 集群:
調用 API 獲取 pod,結果失敗:
向 default sa user 授予 cluster-reader 角色:
然后就可以做集群資源查詢操作了。
4.3 權限 - 訪問系統資源的權限
pod 中的應用除了有訪問 OpenShift API 和內部鏡像倉庫之外,還有一些系統資源訪問要求。比如:
- 要求以任意用戶甚至是 root 來運行 pod 中的主進程
- 要求訪問宿主機上的文件系統
- 要求訪問宿主機上的網絡
對於這些操作系統資源的訪問權限,OpenShift 利用 scc 來進行控制。這就要求:
- 在 scc 中進行權限控制。這部分在后面介紹。
- 在 servie account 和 sa 之間建立聯系。每個 scc 都有指定使用它的用戶列表。所有通過身份認證了的用戶都只在 restricted 這個 scc 的用戶列表之中,包括 service account。因此,pod 默認使用的是 restricted scc。要使它使用其它的scc,就要將它的 service account user 加入到要使用的 scc 的用戶列表之中。這在 scc 部分具體介紹。
5. Security Context Constraint(SCC)
前面說過,SCC 用於控制訪問系統資源的權限,那說明只有 service account 才需要使用 scc。沒在文檔中看到自然人用戶 user 使用 scc 的例子。
Linux 中的權限控制很是復雜,這里就不說明了,我自己也沒怎么弄清楚。OpenShift scc 將系統權限分為幾大類,具體見上圖中的『權限』部分,然后可以創建 scc 對象來精細地控制對每種權限的控制。
5.1 OpenShift默認的 scc
因為這非常繁瑣,因此 OpenShift 默認創建了幾個典型的 scc,列表如下。上圖中的『系統預定義scc』部分有簡要說明,這里不再重復。
其中,如果 pod 所使用的 scc 的 runAsUser
策略被設置為了 MustRunAsRange,那么 pod 所使用的 uid 和 supplemental-group id 必須在某區間之內。
集群管理員可以在 scc 中設置區間,比如:
但是,OpenShift 默認提供的 scc 都沒有設置該區間,而是使用 pod 所在的 project 中定義的區間之內。比如:
[root@master2 cloud-user]# oc describe project dev Name: dev Created: 3 weeks ago Labels: <none> Annotations: openshift.io/description= openshift.io/display-name= openshift.io/requester=demo openshift.io/sa.scc.mcs=s0:c16,c0 openshift.io/sa.scc.supplemental-groups=1000000000/10000 openshift.io/sa.scc.uid-range=1000000000/10000
每個 project 會被分配不同的 ID 區間。當未指定 uid 和 supplemental gid 時,會默認使用區間的最小值。
每個 pod 中,運行主進程的用戶都有三個屬性:
- uid 即 user id,上面例子中uid 為 100000000,使用的是 project 定義的 uid 區間的第一個值。其策略分為三種:
- MustRunAs - 需要配置一個區間,要么在 project 中配置,要么在 scc 中指定。默認使用區間的第一個值。指定特定值的話,會檢查是否在該區間內。
- MustRunAsNonRoot - 要求指定非 root user id,不提供默認值。
- RunAsAny - 允許所有用戶,不提供默認值。
- gid 即用戶的primary group id(主組ID)。上面例子中 gid 為 0,組名字為 root。從 https://github.com/kubernetes/enhancements/issues/213 上看,目前 Kubernetes 還不支持設置 gid,其值固定為 0.
fsGroup
定義 pod 的 文件系統 group ID,用於塊存儲,比如 Ceph RDB 和 iSCSI。supplementalGroups
ID 用於共享存儲,也是組/group ID 的一種,用於共享存儲,比如 NFS 和 GlusterFS。上面例子中的 supplementalGroup ID 為 10000000,取的也是默認值。這兩種 group ID 支持 MustRunAs 和 RunAsAny 兩種策略。會在下一篇存儲部分詳細介紹。
備注:Supplemental group(附加組)也是Linux 組的一種。當一個進程運行在 Linux 中時,它會擁有一個 UID,一個 GID,以及一個或多個附加組ID。具體請查閱Linux 相關文檔。
如果某 scc 設置的 RunAsUser 策略為 MustRunAsRange,它會要求配置合法的 uid 區間(要么在 project 中配置,要么在 scc 中指定)。如果你要指定特定的 uid ,你可以在 pod 定義 yaml 中使用 securityContext: runAsUser <uid> 來指定特定的 user id,但是該 id 必須在區間之內。否則會報錯:
Error from server (Forbidden): error when creating "testcontainervolume-restricted.yaml": pods "test-pod-volume-restricted2" is forbidden: unable to validate against any security context constraint: [spec.containers[0].securityContext.securityContext.runAsUser: Invalid value: 65534: must be in the ranges: [1000000000, 1000009999]]
5.2 容器使用的默認 scc
根據創建 pod 的用戶不同,pod 使用不同的默認 scc:
- 非 cluster admin 角色的 openshift 用戶創建的沒有顯式指定 service account 和 scc 的 pod,其默認使用的 sa user 為 default,默認使用的 scc 為 restricted。
- cluster admin 用戶,根據 scc 優先級,anyuid 將是這種 pod 默認使用的 scc。
SCC 優先級:一個 sa user 可以被加到多的 scc 當中。當一個 service account user 有多個 SCC 可用時,其 scc 按照下面的規則排序
- 最高優先級的scc排在最前面。默認地,對於 cluster admin 角色的用戶的 pod,anyuid scc 會被授予最高優先級,排在最前面。這會允許集群管理員能夠以任意 user 運行 pod,而不需要指定 pod 的 SecurityContext 中的 RunAsUser 字段。
- 如果優先級相同,scc 將從限制最高的向限制最少的順序排序。
- 如果優先級和限制都一樣,那么將按照名字排序。
每個 scc 有其用戶/用戶組列表。以 restricted 為例,所有通過身份驗證的用戶都在列表中;而 anyuid,只有 cluster-admins 用戶組中的用戶在里面。
5.3 修改容器使用的scc
要授權 pod 有除了 restricted 定義的權限之外的權限,主要有兩種做法:
- 一是將其 service account 放到目標 scc 的用戶列表中。此時建議創建一個新的 service account,而不要使用已有的,考慮到對現有 pod 的影響。比如因為 registry 和 router 服務的 pod 需要使用 host networkinig 網絡模式,因此有為它們創建單獨的 sa user 並且加入到了 hostnetwork scc 的用戶列表中。
- 二是將 service account 加入到目標 scc 的用戶組中。
官方建議采用第一種。一個很常用的例子是運行要使用 root 用戶的容器。很多的Docker 鏡像都使用的是 root 用戶。但是,openshift restricted scc 不允許使用 root 用戶,而要使用一個用戶區間內的用戶:
修復步驟:
$ oc create serviceaccount useroot $ oc adm policy add-scc-to-user anyuid -z useroot $ oc patch dc/myAppNeedsRoot --patch '{"spec":{"template":{"spec":{"serviceAccountName": "useroot"}}}}'
備注:
- 第二個語句中的 -z userroot 用於指定當前project 中的 sa user,它和使用 system:serviceaccount:<project>:userroot 效果相同。
- 在 pod 中設置 service account,其屬性名字為 serviceAccount,不是 dc 中的 serviceAccount。
說明:
- 第一步創建名為 userroot 的 sa user
- 第二步將該 sa user 加入到 anyuid scc 的 user 列表中
- 第三步在應用的 DeploymentConfig 中指定 serviceAccountName 為 userroot
感謝您的閱讀,歡迎關注我的微信公眾號: