通过 GitHub 和 Dex 访问 Kubernetes 集群
作者 阳明
来源 | k8s技术圈(ID:kube100)
我们知道可以通过 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 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 命令查看运行状况,查看到类似于 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 "ych_1024@163.com" cannot delete resource "pods" in API group "" in the namespace "default"
我们可以看到在 GitHub 上面 max-k8s 这个组下面的 team-red 这个 Team 下面的用户可以读取我们的资源对象了,但是没有写相关的权限,证明我们的验证成功了。
这样以后要是有学生想要访问我的集群,只需要加入到我们的 GitHub 组里面来就可以了,是不是很方便,显然比手动去授权前进了一大步吧。当然除了通过 GitHub 去授权外,我们还可以使用其他的第三方用户认证系统。
参考文档
https://github.com/dexidp/dex
https://github.com/mintel/dex-k8s-authenticator
https://kubernetes.io/docs/reference/access-authn-authz/authentication/
https://kubernetes.io/docs/reference/access-authn-authz/authorization/
https://medium.com/preply-engineering/k8s-auth-a81f59d4dff6
https://www.ibm.com/developerworks/cn/cloud/library/cl-lo-openid-connect-kubernetes-authentication/index.html