4

通过 GitHub OAuth 和 Dex 访问 Kubernetes 集群

 2 years ago
source link: https://www.qikqiak.com/post/k8s-auth-via-oidc/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

我们知道可以通过 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 settingsgithub 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 [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 命令查看运行状况,查看到类似于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 pagek8s auth login page k8s github authk8s github auth k8s dex kubeconfig pagek8s 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 "[email protected]" cannot delete resource "pods" in API group "" in the namespace "default"

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

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

Kubernetes进阶训练营

微信公众号

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

wechat-account-qrcode

「真诚赞赏,手留余香」


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK