我們知道可以通過 RBAC 爲操作 kubectl 的用戶或組來進行權限控制,但是我們往往是通過 kubernetes 集羣的超級管理員手動爲這些用戶進行分配的,並沒有一個開箱即用的 kubectl 身份驗證工具。
現在參加 kubernetes 進階課程 的學生比較多,其中可能有一部分學生暫時還沒有一套可用的集羣環境,那麼我們就可以爲這部分學生授權訪問我們的集羣,但是如果這麼多學生都手動去給他們創建身份認證必然非常麻煩。
那麼我們可以用什麼辦法來可以很方便的爲用戶進行授權訪問 kubernetes 集羣呢?
Kubernetes 身份認證
其實前面關於 RBAC 的文章中我們就已經和大家介紹了關於 kubernetes 身份認證的一些信息,kubernetes 本身並不維護任何用戶賬戶信息,當然也就沒辦法進行任何身份認證。kubernetes 集羣有兩種類型的帳號:User Account 和 Service Account。
-
User Account:是給用戶來使用的,全局唯一的,和集羣的 namespace 沒有關係。
- Service Account:是給應用程序來使用的,給那些運行在 kubernetes 集羣中的程序訪問 API server 使用的。
關於如何手動創建 RBAC 爲 User Account 和 Service Account 用戶進行權限控制,可以去查看之前的文章:Kubernetes RBAC 詳解,這裏不再重複了。
對於普通用戶使用的 User Account,kubernetes 並不提供管理機制,而是通過信任某個獨立的外部身份認證系統,來把身份認證交給這個外部系統來完成,所以,大部分情況下 User Account 的認證是發生在 Kubernetes 系統之外的。kubernetes 自然是支持和外部用戶管理和認證服務進行集成的。例如,一個可分發私鑰的服務,一個用戶管理服務(比如 Google Accounts),甚至可以是一個存儲了用戶名和密碼列表的外部文件也可以。但是不管外部系統怎麼管理,kubernetes 集羣內部並沒有存儲任何代表用戶的對象信息,也就是說,集羣在創建之初,在沒有配置外部認證機制的情況下,沒有任何用戶賬戶存在,當然,你也無法創建任何用戶。所以 kubernetes 不能設置信任單個用戶,而是去信任某個第三方的用戶管理系統,一旦信任了某個系統,那麼該系統中所有的用戶都可以訪問 kubernetes 集羣了,當然具體有什麼權限就是授權管理的事情了,而授權是由 kubernetes 自身控制的。
OIDC 認證
kubernetes 的認證策略有很多種,其中,通過一個不記名令牌 (Bear Token) 來識別用戶是一種相對安全又被各種客戶端廣泛支持的認證策略。不記名令牌,代表着對某種資源,以某種身份訪問的權利,無論是誰,任何獲取該令牌的訪問者,都被認爲具有了相應的身份和訪問權限。身份令牌(ID Token)就是一種不記名令牌,它本身記錄着一個權威認證機構對用戶身份的認證聲明,同時還可以包含對這個用戶授予了哪些權限的聲明,kubernetes 接受和識別的正是這種 ID Token。
要想得到 ID Token,就需要經過一個權威機構的一套身份認證流程,OpenID Connect(OIDC)就是這樣一套認證、授權 ID Token 的協議,我們這裏需要使用到的工具包括下面幾個:
-
dex-k8s-authenticator - 一個用來生成 kubectl 配置信息的應用
-
Dex - 一個 OIDC 提供器
-
GitHub - 通過 GitHub 來提供用戶授權認證
- Cert manager - 用來進行自動化 HTTPS
授權流程
下面是 kubernetes 通過 OIDC 進行認證授權的流程圖:
-
用戶通過訪問 dex-k8s-authenticator 應用進行登錄請求(dex-login.qikqiak.com)
-
dex-k8s-authenticator 應用跳轉請求到 Dex(dex-k8s.qikqiak.com)
-
Dex 跳轉到 GitHub 授權頁面
-
GitHub 將響應信息回傳給 Dex
-
Dex 轉發響應信息給 dex-k8s-authenticator
-
用戶通過 GitHub 獲得 ID Token
-
dex-k8s-authenticator 添加 ID Token 到 kubeconfig
-
kubectl 傳遞 ID Token 到 KubeAPIServer
-
KubeAPIServer 返回響應結果給 kubectl
- 用戶從 kubectl 獲取相關信息。
準備工作
首先,我們當然需要有一個可用的 kubernetes 集羣,由於我們這裏會通過 Helm 來進行應用安裝,所以需要提前準備好 Helm 相關環境,可以查看前面的文章瞭解更多關於 Helm 的信息。
然後,由於要爲很多用戶進行授權,所以我們這裏需要在 GitHub 上面創建一個組織,我們直接對這個組織下面的一個團隊進行授權即可,我們這裏的組織名稱爲:max-k8s,團隊名稱爲:team-red,當然我們也可以對組織下的所有用戶進行授權,但是通過團隊來進行授權顯然更加靈活。前往 GitHub 組織設置頁面(https://github.com/organizations/max-k8s/settings/applications)創建一個新的 OAuthApp:
填寫上你自己的值:
-
首頁 URL:https://dex-k8s.qikqiak.com
- 認證回調 URL: https://dex-k8s.qikqiak.com/callback
要注意回調 URL 後面需要加上 callback,將生成的 ClientID和 Clientsecret記錄下來。
最後一定要記住需要對上面的兩個 URL:dex-login.qikqiak.com 和 dex-k8s.qikqiak.com 做 DNS 解析,由於 Dex 和 dex-k8s-authenticator 兩個應用我們都安裝在 kubernetes 集羣中,所以直接解析到 Ingress Controller 所在的節點即可。
安裝 Dex 和 dex-k8s-authenticator
爲了連接 Dex 應用,我們需要配置上 kubernetes 證書和私鑰信息,我這裏是通過 kubeadm 搭建的集羣,所以直接在 master 節點上直接獲取即可:
$ cat /etc/kubernetes/pki/ca.crt
-----BEGIN CERTIFICATE-----
......crt證書內容......
-----END CERTIFICATE-----
$ cat /etc/kubernetes/pki/ca.key
-----BEGIN RSA PRIVATE KEY-----
......key私鑰內容......
-----END RSA PRIVATE KEY-----
Clone dex-k8s-authenticator 代碼倉庫:
$ git clone [email protected]:mintel/dex-k8s-authenticator.git
cd dex-k8s-authenticator/
倉庫下面有 dex 和 dex-k8s-authenticator 和 Helm Chart 模板,分別創建對應的 values.yaml 文件即可進行安裝。
創建 dex 的 values 文件:(values-dex.yaml)
global:
deployEnv: prod
tls:
# 替換成你的kubernetes證書信息
certificate: |-
-----BEGIN CERTIFICATE-----
......crt證書內容......
-----END CERTIFICATE-----
# 替換成你的kubernetes私鑰信息
key: |-
-----BEGIN RSA PRIVATE KEY-----
......key私鑰內容......
-----END RSA PRIVATE KEY-----
ingress:
enabled: true
annotations:
kubernetes.io/ingress.class: nginx # nginx ingress
kubernetes.io/tls-acme: "true" # 使用cert manager進行自動化https
path: /
hosts:
- dex-k8s.qikqiak.com
tls:
- secretName: cert-auth-dex
hosts:
- dex-k8s.qikqiak.com
serviceAccount:
create: true
name: dex-auth-sa
config: |
issuer: https://dex-k8s.qikqiak.com/
storage:
type: sqlite3
config:
file: /var/dex.db
web:
http: 0.0.0.0:5556
frontend:
theme: "coreos"
issuer: "Example Co"
issuerUrl: "https://example.com"
logoUrl: https://example.com/images/logo-250x25.png
expiry:
signingKeys: "6h"
idTokens: "24h"
logger:
level: debug
format: json
oauth2:
responseTypes: ["code", "token", "id_token"]
skipApprovalScreen: true
connectors:
- type: github
id: github
name: GitHub
config:
clientID: $GITHUB_CLIENT_ID # 不用替換
clientSecret: $GITHUB_CLIENT_SECRET # 不用替換
redirectURI: https://dex-k8s.qikqiak.com/callback # github oauth callback url
orgs:
- name: max-k8s # github 組織
teams:
- team-red # github 組織下面的 team
staticClients:
- id: dex-k8s-authenticator
name: dex-k8s-authenticator
secret: generatedLongRandomPhrase
redirectURIs:
- https://login-k8s.qikqiak.com/callback/
envSecrets:
GITHUB_CLIENT_ID: "替換成你的github client id"
GITHUB_CLIENT_SECRET: "替換成你的github client secret"
然後創建 dex-k8s-authenticator 的 values 文件:(values-auth.yaml)
global:
deployEnv: prod
dexK8sAuthenticator:
clusters:
- name: k8s.example.com
short_description: "k8s cluster"
description: "Kubernetes cluster"
issuer: https://dex-k8s.qikqiak.com/
k8s_master_uri: https://<APIServer URL> # 替換成你kubernetes集羣apiserver地址
client_id: dex-k8s-authenticator
client_secret: generatedLongRandomPhrase
redirect_uri: https://login-k8s.qikqiak.com/callback/
k8s_ca_pem: |
-----BEGIN CERTIFICATE-----
......crt證書內容......
-----END CERTIFICATE-----
ingress:
enabled: true
annotations:
kubernetes.io/ingress.class: nginx
kubernetes.io/tls-acme: "true"
path: /
hosts:
- login-k8s.qikqiak.com
tls:
- secretName: cert-auth-login
hosts:
- login-k8s.qikqiak.com
Chart 模板的 values 文件準備好了,我們可以先將兩個應用的自動化 HTTPS 分別配置上,創建 SSL certificates:(https.yaml)
apiVersion: certmanager.k8s.io/v1alpha1
kind: ClusterIssuer
metadata:
name: le-clusterissuer
namespace: kube-system
spec:
acme:
server: https://acme-v02.api.letsencrypt.org/directory
email: [email protected]
privateKeySecretRef:
name: le-clusterissuer
http01: {}
---
apiVersion: certmanager.k8s.io/v1alpha1
kind: Certificate
metadata:
name: cert-auth-dex
namespace: kube-system
spec:
secretName: cert-auth-dex
dnsNames:
- dex-k8s.qikqiak.com
acme:
config:
- http01:
ingressClass: nginx
domains:
- dex-k8s.qikqiak.com
issuerRef:
name: le-clusterissuer
kind: ClusterIssuer
---
apiVersion: certmanager.k8s.io/v1alpha1
kind: Certificate
metadata:
name: cert-auth-login
namespace: kube-system
spec:
secretName: cert-auth-login
dnsNames:
- login-k8s.qikqiak.com
acme:
config:
- http01:
ingressClass: nginx
domains:
- login-k8s.qikqiak.com
issuerRef:
name: le-clusterissuer
kind: ClusterIssuer
當然前提是要安裝 Cert Manager,關於 Cert Manager 的安裝可以查看前面文章:Kubernetes Ingress 自動化 HTTPS,我們這裏不再展開了。
直接使用 kubectl 工具創建即可:
$ kubect create -f https.yaml
創建完成後可以通過 describe 命令查看運行狀況,查看到類似於 Certificateissued successfully的信息證明就已經配置成功了:
$ kubectl describe certificates cert-auth-dex -n kube-system
$ kubectl describe certificates cert-auth-login -n kube-system
然後在 dex-k8s-authenticator/ 根目錄下面直接通過 Helm 安裝:
$ helm install -n dex --namespace kube-system --values values-dex.yml charts/dex
$ helm install -n dex-auth --namespace kube-system --values values-auth.yml charts/dex-k8s-authenticator
安裝完成後通過下面的命令進行校驗(Dex 應該返回狀態碼400,dex-k8s-authenticator 應該返回狀態碼200):
$ curl -sI https://dex-k8s.qikqiak.com/callback | head -1
HTTP/2 400
$ curl -sI https://login-k8s.qikqiak.com/ | head -1
HTTP/2 200
RBAC 權限配置
上面的準備工作完成後,現在我們來爲 max-k8s 這個 group 下面的 team-red 進行權限控制,比如我們希望這個 team 下面的所有用戶都只有只讀權限,創建對應的 RBAC 配置文件:(read-auth.yaml)
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: cluster-read-all
rules:
-
apiGroups:
- ""
- apps
- autoscaling
- batch
- extensions
- policy
- rbac.authorization.k8s.io
- storage.k8s.io
resources:
- componentstatuses
- configmaps
- cronjobs
- daemonsets
- deployments
- events
- endpoints
- horizontalpodautoscalers
- ingress
- ingresses
- jobs
- limitranges
- namespaces
- nodes
- pods
- pods/log
- pods/exec
- persistentvolumes
- persistentvolumeclaims
- resourcequotas
- replicasets
- replicationcontrollers
- serviceaccounts
- services
- statefulsets
- storageclasses
- clusterroles
- roles
verbs:
- get
- watch
- list
- nonResourceURLs: ["*"]
verbs:
- get
- watch
- list
- apiGroups: [""]
resources: ["pods/exec"]
verbs: ["create"]
---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRoleBinding
metadata:
name: dex-cluster-auth
namespace: kube-system
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: cluster-read-all
subjects:
- kind: Group
name: "max-k8s:team-red"
如果對於 RBAC 權限控制這塊還不是很熟悉,同樣可以回頭去看看前面的文章:Kubernetes RBAC 詳解,使用 kubectl 直接創建:
$ kubectl create -f read-auth.yaml
APIServer 配置
現在我們相關的準備工作已經完成了,權限也配置上了,接下來是最重要的一步:爲 APIServer 提供 OIDC 相關的配置。關於 APIServer 中 OIDC 相關部分的配置可以查看官方文檔:https://kubernetes.io/docs/reference/access-authn-authz/authentication/由於我這裏的集羣是使用的 kubeadm 進行搭建的,所以直接去 master 節點上更改 APIserver 的靜態 Pod 配置即可:
$ cat /etc/kubernetes/manifests/kube-apiserver.yaml
......
spec:
containers:
- command:
- kube-apiserver
- --authorization-mode=Node,RBAC
- --oidc-client-id=dex-k8s-authenticator
- --oidc-groups-claim=groups
- --oidc-issuer-url=https://dex-k8s.qikqiak.com/
- --oidc-username-claim=email
......
然後將 kube-apiserver.yaml文件從 /etc/kubernetes/manifests/靜態 Pod 目錄下面移除,稍等一小會兒再移動回來,相當於強制重啓了。
這樣就完成了 KubeAPIServer 部分關於 OIDC 的配置。
測試
現在所有的工作都準備好了,接下來我們來測試下,前往登錄頁面(https://login-k8s.qikqiak.com)使用你的 GitHub 帳號(當然前提是得加入到上面我們的 max-k8s 組下面)進行登錄授權:
然後根據上面頁面中的信息進行 kubeconfig 信息配置,正常我們就可以訪問到 kubernetes 集羣了:
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
nginx-app-76b6449498-ffhgx 1/1 Running 0 28d
nginx-app-76b6449498-wzjq2 1/1 Running 0 28d
$ kubectl delete pod nginx-app-76b6449498-wzjq2
Error from server (Forbidden): pods "nginx-app-76b6449498-wzjq2" is forbidden: User "[email protected]" cannot delete resource "pods" in API group "" in the namespace "default"
我們可以看到在 GitHub 上面 max-k8s 這個組下面的 team-red 這個 Team 下面的用戶可以讀取我們的資源對象了,但是沒有寫相關的權限,證明我們的驗證成功了。
這樣以後要是有學生想要訪問我的集羣,只需要加入到我們的 GitHub 組裏面來就可以了,是不是很方便,顯然比手動去授權前進了一大步吧。當然除了通過 GitHub 去授權外,我們還可以使用其他的第三方用戶認證系統。
參考文檔
-
https://kubernetes.io/docs/reference/access-authn-authz/authentication/
-
https://kubernetes.io/docs/reference/access-authn-authz/authorization/
- https://www.ibm.com/developerworks/cn/cloud/library/cl-lo-openid-connect-kubernetes-authentication/index.html
k8s進階課程推薦:打造獨當一面的 Kubernetes 運維、開發工程師
掃描下面的二維碼(或微信搜索 k8s技術圈)關注我們的微信公衆帳號,在微信公衆帳號中回覆 加羣 即可加入到我們的 kubernetes 討論羣裏面共同學習。