通過 GitHub 和 Dex 訪問 Kubernetes 集羣

我們知道可以通過 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 進行認證授權的流程圖:

通過 GitHub 和 Dex 訪問 Kubernetes 集羣

  1. 用戶通過訪問 dex-k8s-authenticator 應用進行登錄請求(dex-login.qikqiak.com)

  2. dex-k8s-authenticator 應用跳轉請求到 Dex(dex-k8s.qikqiak.com)

  3. Dex 跳轉到 GitHub 授權頁面

  4. GitHub 將響應信息回傳給 Dex

  5. Dex 轉發響應信息給 dex-k8s-authenticator

  6. 用戶通過 GitHub 獲得 ID Token

  7. dex-k8s-authenticator 添加 ID Token 到 kubeconfig

  8. kubectl 傳遞 ID Token 到 KubeAPIServer

  9. KubeAPIServer 返回響應結果給 kubectl

  10. 用戶從 kubectl 獲取相關信息。

準備工作

首先,我們當然需要有一個可用的 kubernetes 集羣,由於我們這裏會通過 Helm 來進行應用安裝,所以需要提前準備好 Helm 相關環境,可以查看前面的文章瞭解更多關於 Helm 的信息。

然後,由於要爲很多用戶進行授權,所以我們這裏需要在 GitHub 上面創建一個組織,我們直接對這個組織下面的一個團隊進行授權即可,我們這裏的組織名稱爲:max-k8s,團隊名稱爲:team-red,當然我們也可以對組織下的所有用戶進行授權,但是通過團隊來進行授權顯然更加靈活。前往 GitHub 組織設置頁面(https://github.com/organizations/max-k8s/settings/applications)創建一個新的 OAuthApp:

通過 GitHub 和 Dex 訪問 Kubernetes 集羣

填寫上你自己的值:

要注意回調 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 組下面)進行登錄授權:

通過 GitHub 和 Dex 訪問 Kubernetes 集羣

通過 GitHub 和 Dex 訪問 Kubernetes 集羣

通過 GitHub 和 Dex 訪問 Kubernetes 集羣

然後根據上面頁面中的信息進行 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 去授權外,我們還可以使用其他的第三方用戶認證系統。

參考文檔

k8s進階課程推薦:打造獨當一面的 Kubernetes 運維、開發工程師

掃描下面的二維碼(或微信搜索 k8s技術圈)關注我們的微信公衆帳號,在微信公衆帳號中回覆 加羣 即可加入到我們的 kubernetes 討論羣裏面共同學習。

通過 GitHub 和 Dex 訪問 Kubernetes 集羣

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章