通过 GitHub OAuth 和 Dex 访问 Kubernetes 集群

通过 GitHub OAuth 和 Dex 访问 Kubernetes 集群

标签: kubernetes   github   dex   kubectl   rbac  

我们知道可以通过 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 进行认证授权的流程图:

k8s 认证流程
k8s 认证流程
  1. 用户通过访问 dex-k8s-authenticator 应用进行登录请求(login-k8s.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)创建一个新的OAuth App

github organization settings
github organization settings

填写上你自己的值:

要注意回调 URL 后面需要加上callback,将生成的Client IDClient secret记录下来。

最后一定要记住需要对上面的两个 URL:login-k8s.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 git@github.com: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: icnych@gmail.com
    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 命令查看运行状况,查看到类似于Certificate issued 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 组下面)进行登录授权:

k8s auth login page
k8s auth login page
k8s github auth
k8s github auth
k8s dex kubeconfig page
k8s dex kubeconfig page

然后根据上面页面中的信息进行 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 "ych_1024@163.com" cannot delete resource "pods" in API group "" in the namespace "default"

我们可以看到在 GitHub 上面 max-k8s 这个组下面的 team-red 这个 Team 下面的用户可以读取我们的资源对象了,但是没有写相关的权限,证明我们的验证成功了。

这样以后要是有学生想要访问我的集群,只需要加入到我们的 GitHub 组里面来就可以了,是不是很方便,显然比手动去授权前进了一大步吧。当然除了通过 GitHub 去授权外,我们还可以使用其他的第三方用户认证系统。

kubernetes进阶课程

参考文档

微信公众号

扫描下面的二维码关注我们的微信公众帐号,在微信公众帐号中回复◉加群◉即可加入到我们的 kubernetes 讨论群里面共同学习。

wechat-account-qrcode

「真诚赞赏,手留余香」

阳明

请我喝杯咖啡?

使用微信扫描二维码完成支付

相关文章