前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >使用Kubernetes身份在微服务之间进行身份验证

使用Kubernetes身份在微服务之间进行身份验证

作者头像
有点技术
发布2020-12-22 11:15:18
7.7K0
发布2020-12-22 11:15:18
举报
文章被收录于专栏:有点技术有点技术

使用Kubernetes身份在微服务之间进行身份验证

如果您的基础架构由相互交互的多个应用程序组成,则您可能会遇到保护服务之间的通信安全以防止未经身份验证的请求的问题。

想象一下,有两个应用程序:

•API•datastore

您可能希望datastore仅响应对API的请求,并拒绝来自其他任何地方的请求。

datastore将如何决定允许还是拒绝该请求?

一种流行的方法是请求身份令牌并将其传递给服务内的每个请求。

因此,与其直接向datastore发出请求,不如直接通过身份验证服务,检索令牌并使用该令牌对您对datastore的请求进行身份验证。

存在与令牌关联的特定上下文,该上下文允许datastore从API服务接受令牌并从其他地方拒绝令牌。

此上下文用于允许或拒绝该请求。

1.想象一下向API组件发出请求。

2.API向datastore进行身份验证的唯一方法是,如果它具有有效的令牌。API使用其凭据从授权服务器请求令牌。

1.API向datastore发出请求,并附加令牌作为有效身份的证明。

1.在回复请求之前,datastore会通过授权服务器验证令牌。

关于实现此身份验证机制,您有几种选择:

•您可以使用不会过期的静态令牌。在这种情况下,无需运行专用的身份验证服务器。•您可以设置一个OAuth服务器。•您可以推出身份验证和授权机制,例如相互TLS证书。

身份验证和授权服务器所需要做的就是:

1.验证请求者身份-请求者应该具有有效且可验证的身份。2.生成具有有限范围,有效性和所需audience的令牌。3.验证令牌-仅当令牌是所涉及的两个服务的合法令牌时,才允许服务到服务的通信。

允许您实施身份验证和授权基础结构的专用软件示例包括Keycloak或Dex等工具。

使用Keycloack时,首先需要:

1.使用您的电子邮件和密码登录-您的身份已通过验证。2.为您的用户创建了一个有效的会话。该会话可能描述您属于哪些组。3.每个请求都经过验证,无效时将要求您重新登录。

基础架构中的两个应用程序也是如此。

1.后端组件使用其API密钥和密钥向Keycloack发出请求,以生成会话令牌。2.后端使用会话令牌向第二个应用程序发出请求。3.第二个应用程序从请求中检索令牌,并使用Keycloak对其进行验证。4.如果令牌有效,它将回复该请求。

您可能没有注意到,但是Kubernetes提供了与ServiceAccount,角色和RoleBindings一起实现身份验证和授权的原语。

Kubernetes作为身份验证和授权服务器

在Kubernetes中,您可以使用ServiceAccount分配身份[1] 。

用户和Pod可以使用这些身份作为对API进行身份验证和发出请求的机制。

然后,将ServiceAccount链接到授予对资源的访问权限的角色。

例如,如果某个角色授予创建和删除Pod的权限,则您将无法修改Secrets或创建ConfigMap。

您可以使用ServiceAccount作为一种机制来验证集群中应用程序之间的请求吗?

如果Kubernetes API可用作身份验证和授权服务器怎么办?

让我们尝试一下。

创建集群

您将需要访问启用了 ServiceAccount卷投影功能[2] 的Kubernetes集群。

如果您不知道什么是ServiceAccount卷,请不要担心-您将在本文后面的内容中进一步了解它。

ServiceAccount卷投影功能要求Kubernetes API服务器以特定的API标志运行。

对不同托管Kubernetes提供商的支持可能会有所不同。

但是,您可以使用以下方法在minikube中启用卷投影功能来启动本地集群:

代码语言:javascript
复制
minikube start \  --extra-config=apiserver.service-account-signing-key-file=/var/lib/minikube/certs/sa.key \  --extra-config=apiserver.service-account-key-file=/var/lib/minikube/certs/sa.pub \  --extra-config=apiserver.service-account-issuer=kubernetes/serviceaccount \  --extra-config=apiserver.service-account-api-audiences=api

您还应该克隆该 示例存储库[3] ,因为它包含本文中将引用的演示源代码。

现在,您将部署两项服务:

•您会将这些服务称为API服务和datastore。•它们使用Go编程语言编写,并通过HTTP进行通信。•每个服务都在其名称空间中运行,并使用专用的ServiceAccount标识。•datastore仅在调用者具有有效身份时才成功回复请求,否则它会拒绝并显示错误。

部署API组件

API服务是侦听端口8080的无头Web应用程序。

当您向它发出请求时,API组件:

1.向datastore发出其ServiceAccount标识的HTTP GET请求。2.转发响应。

您可以使用以下方法在群集中部署应用程序并暴露服务:

代码语言:javascript
复制
kubectl apply -f service_accounts/api/deployment.yamlnamespace/api createdserviceaccount/api createddeployment.apps/app createdservice/app created

您可以使用以下方法检索API服务的URL:

代码语言:javascript
复制
minikube --namespace api service app --urlhttp://192.168.99.101:31541

如果您向该应用发出请求,您将获得成功的答复吗?

让我们尝试一下:

代码语言:javascript
复制
curl http://192.168.99.101:31541curl: (7) Failed to connect to 192.168.99.101 port 31541: Connection refused

由于您尚未部署datastore,因此将出现此错误。

保持终端打开。

打开一个新的终端以执行下一组步骤。

部署datastore

datastore服务是侦听端口8081的另一个无头Web应用程序。

当客户提出任何请求时,datastore:

1.在请求标头中查找令牌。如果没有,则返回HTTP 401错误响应。2.使用Kubernetes API检查令牌的有效性。如果无效,它将以HTTP 403响应进行回复。3.最后,当令牌有效时,它将回复原始请求。

您可以使用以下方法创建datastore:

代码语言:javascript
复制
kubectl apply -f service_accounts/data-store/deployment.yamlnamespace/data-store createdserviceaccount/data-store createdclusterrolebinding.rbac.authorization.k8s.io/role-tokenreview-binding createddeployment.apps/app createdservice/app created

现在,使用curl再次向API服务发出请求:

代码语言:javascript
复制
curl http://192.168.99.101:31541Hello from data store. You have been authenticated

datastore服务成功验证了令牌并回复到API。

API将请求转发给您。

如果直接向datastore发送请求怎么办?

使用以下方法检索datastore的URL:

代码语言:javascript
复制
minikube --namespace data-store service app --urlhttp://192.168.64.28:31690

让我们用它curl来请求:

代码语言:javascript
复制
curl http://192.168.64.28:31690X-Client-Id not supplied

这没用。

但是您可以提供一个虚拟X-Client-Id标题

代码语言:javascript
复制
curl -H 'X-Client-Id: dummy' http://192.168.64.28:31690Invalid token

优秀!

这没用!

您使用Kubernetes和ServiceAccount保护了datastore免受未经授权的访问。

只有拥有有效的令牌,您才能对此请求。

但是,所有这些工作如何进行?让我们找出答案。

Under the hood

ServiceAccount是一种将Kubernetes工作负载与身份相关联的方法。

您可以将ServiceAccount与角色和RoleBinding结合使用,以定义集群中哪些资源或哪些人可以访问哪些资源。

例如,当您想将“读取机密”仅限制为群集中的管理员用户时,可以使用ServiceAccount来进行。

1.ServiceAccount是身份。身份既可以分配给用户,也可以分配给Pod。

2.角色是链接到名称空间的权限列表。ClusterRole是群集范围内可用权限的列表

1.身份没有任何权限,除非您将其链接到角色。您可以使用ClusterRoleBindings将身份链接到ClusterRole。

1.您可以使用RoleBindings将身份链接到角色。

不过,ServiceAccount不仅适用于用户。

您可以验证人员以及集群中的应用程序。

如果您希望您的应用程序列出集群中所有可用的Pod,则需要创建一个与对Pod API的只读访问权限相关联的ServiceAccount。

之前部署两个应用程序时,还创建了两个ServiceAccount:

代码语言:javascript
复制
kubectl get serviceaccount --namespace apiNAME      SECRETS   AGEapi       1         4m5sdefault   1         4m5skubectl get serviceaccount --namespace data-storeNAME           SECRETS   AGEdefault        1         6m4sdata-store   1         6m4s

这些ServiceAccount是与应用程序关联的身份,但它们并未定义授予的权限。

为此,您可能需要列出Role和ClusterRoles:

代码语言:javascript
复制
kubectl get rolebindings,clusterrolebindings \  --all-namespaces \  -o custom-columns='KIND:kind,ROLE:metadata.name,SERVICE_ACCOUNTS:subjects[?(@.kind=="ServiceAccount")].name'KIND                 ROLE                       SERVICE_ACCOUNTSRoleBinding          kube-proxy                 <none>ClusterRoleBinding   cluster-admin              <none>ClusterRoleBinding   kubeadm:get-nodes          <none>ClusterRoleBinding   role-tokenreview-binding   data-store

上面的命令使用kubectl自定义列过滤的输出kubectl get。

该表显示了什么RoleBinding链接到角色(以及什么ClusterRoleBinding链接到ClusterRole)。

具有任何角色的唯一组件是datastore。

API没有角色或角色绑定。

为何没有角色和RoleBinding的ServiceAccount又如何?

API应用具有一个空的ServiceAccount,该帐户没有任何权限。

但是,您可以使用该ServiceAccount身份来验证对Kubernetes API的请求(但不能创建,更新,删除等资源)。

那么datastore呢?

它具有什么样的访问权限?

让我们回顾一下ClusterRoleBinding:

代码语言:javascript
复制
kubectl describe clusterrolebinding role-tokenreview-bindingName:         role-tokenreview-bindingLabels:       <none>Annotations:  <none>Role:  Kind:  ClusterRole  Name:  system:auth-delegatorSubjects:  Kind            Name        Namespace  ----            ----        ---------  ServiceAccount  data-store  data-store

从上面的输出中,您可以知道ClusterRoleBinding将datastore ServiceAccount链接到system:auth-delegatorClusterRole。

哪些权限授予ClusterRole?

让我们找出:

代码语言:javascript
复制
kubectl describe clusterrole system:auth-delegatorName:         system:auth-delegatorLabels:       kubernetes.io/bootstrapping=rbac-defaultsAnnotations:  rbac.authorization.kubernetes.io/autoupdate: truePolicyRule:  Resources                                  Non-Resource URLs  Resource Names  Verbs  ---------                                  -----------------  --------------  -----  tokenreviews.authentication.k8s.io         []                 []              [create]  subjectaccessreviews.authorization.k8s.io  []                 []              [create]

该system:auth-delegatorClusterRole有权限调用tokenreview API。

那是什么样的许可?

可以将kubectl与can-i子命令和模拟--as标志一起使用以测试权限:

代码语言:javascript
复制
kubectl auth can-i create deployments --as=data-store --namespace data-storenokubectl auth can-i list pods --as=data-store --namespace data-storenokubectl auth can-i delete services --as=data-store --namespace data-storeno

您可以继续查询所有Kubernetes资源,但是ServiceAccount只有一个权限。

代码语言:javascript
复制
kubectl auth can-i create tokenreviews --as=sa-test-1yes

什么是TokenReview?

向Kubernetes API发出请求

Kubernetes API验证ServiceAccount身份。

特别是,有一个特定的组件负责验证和拒绝它们:Token Review API

tokenreview API接受令牌并返回它们是否有效-是的,就这么简单。

让我们根据令牌查看API手动验证API组件的身份。

它的令牌评论API,所以你可能需要一个令牌。

什么令牌,但是?

每次创建ServiceAccount时,Kubernetes都会创建一个secret。

机密持有ServiceAccount的令牌,您可以使用该令牌来调用Kubernetes API。

这是应该根据tokenreview API进行验证的令牌。

因此,让我们使用以下方法检索APIServiceAccount的令牌:

代码语言:javascript
复制
kubectl --namespace api describe serviceaccount apiName:                apiMountable secrets:   api-token-lxcb4Tokens:              api-token-lxcb4

然后,要检查Secret对象,可以发出以下命令:

代码语言:javascript
复制
kubectl --namespace api describe secret api-token-lxcb4Name:         api-token-lxcb4Type:  kubernetes.io/service-account-tokenData====ca.crt:     1111 bytesnamespace:  3 bytestoken:      eyJhbGciOiJSUzI1NiIsImtpZCI6…

token在数据对象是表示JSON网络令牌有效载荷base64编码对象。

现在是时候验证令牌了。

要验证令牌的有效性,您需要创建TokenReview资源:

代码语言:javascript
复制
kind: TokenReviewapiVersion: authentication.k8s.io/v1metadata:  name: testspec:  token: eyJhbGciOiJS…

您可以使用以下方法验证请求:

代码语言:javascript
复制
kubectl apply -o yaml -f token.yaml

请注意-o yaml显示kubectl apply命令输出的标志。

发出命令时,响应应如下所示:

代码语言:javascript
复制
apiVersion: authentication.k8s.io/v1kind: TokenReviewmetadata:  name: testspec:  token: eyJhbGciOiJS…status:  audiences:  - api  authenticated: true  user:    groups:    - system:serviceaccounts    - system:serviceaccounts:api    - system:authenticated    uid: 7df3a132-c9fe-48d1-9fa5-b654c3156977    username: system:serviceaccount:api:api

响应中的关键信息位于具有以下字段的状态对象中:

•Authenticated:该字段设置为true,表示令牌已成功验证。•在用户对象中,您可以找到以下属性:•与Pod-使用的ServiceAccount相对应的用户名system:serviceaccount:test:sa-test-1。•当前用户的系统用户标识的uid。•组包括用户所属的组。•目标对象包含令牌旨在使用的目标对象列表。在这种情况下,只有api才是有效的audience群体。太好了,您刚刚验证了ServiceAccount令牌!

你懂的:

•令牌有效。•调用者的身份(APIServiceAccount)。•请求者者所属的组。

由于您可以验证和验证任何令牌,因此可以利用datastore组件中的机制对请求进行身份验证和授权!

让我们看一下如何使用Kubernetes Go客户端在应用程序中包含上述逻辑。

实现服务

以下是这两种服务与Kubernetes API相互交互的方式:

1.在启动时,API组件读取ServiceAccount令牌并将其保留在内存中。2.API组件调用将令牌作为HTTP标头(即)传递的datastoreX-Client-Id。3.datastore收到请求后,会从X-Client-Id标头中读取令牌,然后向令牌审阅API发出请求以检查其有效性。4.如果对响应进行了身份验证,则datastore组件将以成功消息进行答复,否则为401错误。

下图表示上述调用流程:

•API组件已分配了ServiceAccount令牌。

•当您向API发出请求时,令牌将在所有后续请求中传递。

•datastore将从请求中检索令牌。

•datastore使用令牌查看API验证身份。

首先,让我们看一下API服务的实现。

您可以在文件中找到应用程序代码service_accounts/api/main.go

ServiceAccount令牌会自动安装在其中/var/run/secrets/kubernetes.io/serviceaccount/token,您可以通过以下方式读取其值:

代码语言:javascript
复制
func readToken() {  b, err := ioutil.ReadFile("/var/run/secrets/kubernetes.io/serviceaccount/token")  if err != nil {    panic(err)  }  serviceToken = string(b)}

然后,将服务令牌传递到X-Client-IdHTTP标头中对Secret store服务的调用:

代码语言:javascript
复制
func handleIndex(w http.ResponseWriter, r *http.Request) {…client := &http.Client{}req, err := http.NewRequest("GET", serviceConnstring, nil)if err != nil {  panic(err)}req.Header.Add("X-Client-Id", serviceToken)resp, err := client.Do(req)…

一旦收到来自Secret Store的回复,然后将其作为响应发送回:

代码语言:javascript
复制
  …if err != nil {  http.Error(w, err.Error(), http.StatusInternalServerError)  return} else {  defer resp.Body.Close()  body, _ := ioutil.ReadAll(resp.Body)  io.WriteString(w, string(body))}

以下YAML清单用于部署API服务:

代码语言:javascript
复制
deployment.yamlapiVersion: v1kind: Namespacemetadata:  name: api---apiVersion: v1kind: ServiceAccountmetadata:  name: api  namespace: api---apiVersion: apps/v1kind: Deploymentmetadata:  name: app  namespace: apispec:  replicas: 1  selector:    matchLabels:      app: api  template:    metadata:      labels:        app: api    spec:      serviceAccount: api      containers:      - name: app        image: amitsaha/k8s-sa-volume-demo-api:sa-1        env:        - name: LISTEN_ADDR          value: ":8080"        - name: DATA_STORE_CONNSTRING          value: "http://app.data-store.svc.cluster.local"        ports:        - containerPort: 8080---apiVersion: v1kind: Servicemetadata:  name: app  namespace: apispec:  type: NodePort  selector:    app: api  ports:    - port: 8080      targetPort: 8080

您将注意到,除了具有与之关联的ServiceAccount之外,上面的部署清单没有什么特别的。

让我们进入datastore服务。

您可以在中找到完整的应用程序service_accounts/data-store/main.go

datastore服务执行两项关键操作:

1.它X-Client-Id从传入的请求中检索标头的值。2.然后,它调用Kubernetes令牌查看API来检查令牌是否有效。

步骤(1)由以下代码执行:

代码语言:javascript
复制
clientId := r.Header.Get("X-Client-Id")if len(clientId) == 0 {  http.Error(w, "X-Client-Id not supplied", http.StatusUnauthorized)  return}

然后,使用Kubernetes Go客户端执行步骤(2)。

首先,我们创建一个ClientSet对象:

代码语言:javascript
复制
config, err := rest.InClusterConfig()clientset, err := kubernetes.NewForConfig(config)

该InClusterConfig()功能自动读取Pod的ServiceAccount令牌,因此您不必手动指定路径。

然后,构造一个TokenReview对象,该对象在Token字段中指定要验证的令牌:

代码语言:javascript
复制
tr := authv1.TokenReview{  Spec: authv1.TokenReviewSpec{      Token: clientId,  },}

最后,您可以使用以下命令创建TokenReview请求:

代码语言:javascript
复制
result, err := clientset.AuthenticationV1().TokenReviews().Create(ctx, &tr, metav1.CreateOptions{})

以下YAML清单将创建datastore服务所需的各种资源:

代码语言:javascript
复制
apiVersion: v1kind: Namespacemetadata:  name: data-store---apiVersion: v1kind: ServiceAccountmetadata:  name: data-store  namespace: data-store---apiVersion: rbac.authorization.k8s.io/v1kind: ClusterRoleBindingmetadata:  name: role-tokenreview-bindingroleRef:  apiGroup: rbac.authorization.k8s.io  kind: ClusterRole  name: system:auth-delegatorsubjects:- kind: ServiceAccount  name: data-store  namespace: data-store---apiVersion: apps/v1kind: Deploymentmetadata:  name: app  namespace: data-storespec:  replicas: 1  selector:    matchLabels:      app: data-store  template:    metadata:      labels:        app: data-store    spec:      serviceAccount: data-store      containers:      - name: app        image: amitsaha/k8s-sa-volume-demo-ss:sa-1        env:        - name: LISTEN_ADDR          value: ":8081"        ports:        - containerPort: 8081---apiVersion: v1kind: Servicemetadata:  name: app  namespace: data-storespec:  selector:    app: data-store  ports:    - protocol: TCP      port: 80      targetPort: 8081

与API服务相比,datastore服务需要创建ClusterRoleBinding资源,该资源将data-storeServiceAccount与system:auth-delegatorClusterRole关联。

返回到您在其中部署了datastore服务的终端会话,并检查日志:

代码语言:javascript
复制
kubectl --namepsace data-store logs <data-store-pod-id>2020/08/21 09:09:10 v1.TokenReviewStatus{Authenticated:true,User:v1.UserInfo{Username:"system:serviceaccount:data-store:data-store",UID:"24ae6734-9ca1-44d8-af93-98b6e6f8da17", Groups:[]string{"system:serviceaccounts","system:serviceaccounts:api", "system:authenticated"}, Extra:map[string]v1.ExtraValue(nil)},Audiences:[]string{"api"}, Error:""}

输出是您之前看到的JSON响应的Go结构版本。

因此,您将看到API组件如何读取ServiceAccount令牌并将其传递到datastore作为身份验证的一种方式。

datastore服务检索令牌并使用Kubernetes API对其进行检查。

有效时,datastore组件允许处理来自API服务的请求。

该实现效果很好,但是存在三个缺点:

每个serviceaccent一个secret

创建ServiceAccount时,Kubernetes会创建一个带有令牌的配套Secret对象。

您可以使用令牌通过Kubernetes API进行身份验证。

在此示例中,您可以检查ServiceAccount并使用以下命令找到令牌:

代码语言:javascript
复制
kubectl --namespace api describe serviceaccount apiName:                apiNamespace:           apiMountable secrets:   api-token-ttr8qTokens:              api-token-ttr8q

匹配命名空间中的Secret对象:

代码语言:javascript
复制
kubectl get secrets --namespace apiNAME                  TYPEapi-token-ttr8q       kubernetes.io/service-account-tokendefault-token-vppc9   kubernetes.io/service-account-token

但是,任何可以读取名称空间中的机密的工作负载也可以读取同一名称空间中的ServiceAccount令牌。

换句话说,您可以让任何其他Pod使用相同的ServiceAccount来针对Kubernetes API进行身份验证-有效地冒充其他人。

不幸的是,没有机制可以限制对命名空间中Secrets子集的访问。

该应用程序可以访问所有这些访问权限,或者没有访问权。

您可以为每个应用程序创建一个名称空间,并在其中存储一个ServiceAccount,但这通常会显得过分。

长期有效的服务账户令牌

与ServiceAccount关联的令牌是长期的,不会过期。

换句话说,一旦您可以访问其中之一,就可以永久使用它(或者直到管理员删除与令牌关联的密钥)。

您可以通过手动删除和重新分配ServiceAccount来手动旋转身份。

如果听起来需要做很多工作,那是因为确实如此。

没有对绑定token的audience

作为群集管理员,您不能将令牌与特定的audience相关联。

有权访问ServiceAccount令牌的任何人都可以使用Kubernetes API进行身份验证,并有权与集群中运行的任何其他服务进行通信。

目标服务没有任何方法可以验证与它一起提供的令牌是否完全是针对自己的。

例如,想象一下买一张从伦敦到纽约的机票。

如果您从英国航空公司购买机票,则无法使用该机票登上维珍航空的航班。

您的机票绑定到特定的观众(英国航空公司)。

但是对于Kubernetes,同一票证(令牌)可以用于任何航空公司(观众)。

您可以通过实施诸如双向TLS之类的解决方案,或将基于JWT的解决方案与中央授权服务器配合使用来解决这两个挑战。

但是,在Kubernetes中,您可以使用ServiceAccount令牌卷投影功能来创建有时限且针对特定audience的ServiceAccount令牌,这些令牌不会在群集存储中持久存在。

Kubernetes API服务器充当中央授权服务器,您不必担心令牌到期。

此功能在Kubernetes 1.12中引入,并在1.13中得到了进一步改进,并为特定于工作负载的ServiceAccount提供了更安全的替代方法。

这将在即将推出的Kubernetes 1.20版本中提升为GA功能。

在本文的下一部分中,您将重新实现相同的代码,以使用ServiceAccount令牌卷投影对应用进行身份验证。

清理两个命名空间是一个好主意:

代码语言:javascript
复制
kubectl delete namespace apinamespace "api" deletedkubectl delete namespace data-storenamespace "data-store" deleted

使用ServiceAccount令牌卷投影的服务间验证

通过ServiceAccount令牌卷投影(ProjectedServiceAccountToken)可用于工作负载的ServiceAccount令牌是受时间限制,受audience约束的,并且不与secret对象关联。

如果删除了Pod或删除了ServiceAccount,则这些令牌将无效,从而可以防止任何误用。

一个serviceAccountToken卷投影是一个projected卷类型。

当将此卷类型添加到Pod时,ServiceAccount令牌将安装在文件系统上—与安装ServiceAccount令牌的方式相同。

虽然有区别。

Kubelet将在令牌即将到期时自动旋转令牌。

另外,您可以配置希望此令牌可用的路径。

让我们看看如何修改API组件以包括“ServiceAccount令牌卷投影”。

API组件

您可以使用以下内容读取通过批量投影安装的ServiceAccount令牌:

代码语言:javascript
复制
b, err := ioutil.ReadFile("/var/run/secrets/tokens/api-token")serviceToken = string(b)

请注意,ServiceAccount令牌的路径与前一种情况不同(即以前是/var/run/secrets/kubernetes.io/serviceaccount/token)。

由于ServiceAccount令牌的卷投影功能依赖于kubelet定期刷新的令牌,因此建议每5分钟在应用程序中重新读取一次令牌。

您可以通过Go中的代码来完成此操作,如下所示:

代码语言:javascript
复制
ticker := time.NewTicker(300 * time.Second)done := make(chan bool)go func() {    for {        select {      case <-done:          return      case <-ticker.C:          readToken()      }    }}()

该readToken()函数读取文件,/var/run/secrets/tokens/api-token并将全局变量serviceToken设置为令牌值。

如果您不熟悉Go的代码,可以将代码视为后台线程,该线程定期运行功能。

您可以在中找到整个应用程序代码service_accounts_volume_projection/api/main.go。

现在,让我们部署此服务。

您将在部署清单(service_accounts_volume_projection/api/deployment.yaml)中使用该镜像:

代码语言:javascript
复制
deployment.yamlapiVersion: v1kind: Namespacemetadata:  name: api---apiVersion: v1kind: ServiceAccountmetadata:  name: api  namespace: api---apiVersion: apps/v1kind: Deploymentmetadata:  name: app  namespace: apispec:  replicas: 1  selector:    matchLabels:      app: api  template:    metadata:      labels:        app: api    spec:      serviceAccount: api      volumes:        - name: api-token          projected:            sources:            - serviceAccountToken:                path: api-token                expirationSeconds: 600                audience: data-store      containers:      - name: app        image: amitsaha/k8s-sa-volume-demo-api:sa-2        env:        - name: LISTEN_ADDR          value: ":8080"        - name: DATA_STORE_CONNSTRING          value: "http://app.data-store.svc.cluster.local"        ports:        - containerPort: 8080        volumeMounts:          - mountPath: /var/run/secrets/tokens            name: api-token---apiVersion: v1kind: Servicemetadata:  name: app  namespace: apispec:  type: NodePort  selector:    app: api  ports:    - port: 8080      targetPort: 8080

名为api-token的projected类型卷将根据serviceAccountToken被创建。

该卷定义了三个附加属性:

1.在path其中令牌将可配置的volume内。2.该audience字段指定令牌的目标audience(如果未指定,则默认为api)。3.该expirationSeconds指示标记有效多久-最小为600秒或10分钟。

请注意,该audience字段如何指定该ServiceAccount令牌仅允许与标识为的服务进行通信data-store。

如果您data-store在Secret store组件中忽略作为audience,则该API将无法与其进行对话-不是它的audience!

您可以使用以下方法创建新的deployment:

代码语言:javascript
复制
kubectl apply -f service_accounts_volume_projection/api/deployment.yamlnamespace/api createdserviceaccount/api createddeployment.apps/app createdservice/app created

通过以下方式检索API服务的URL:

代码语言:javascript
复制
minikube --namespace api service app --urlhttp://192.168.99.101:31541

您可以通过以下方式发出请求:

代码语言:javascript
复制
curl http://192.168.99.101:31541curl: (7) Failed to connect to http://192.168.99.101 port 31541: Connection refused

这是预期的,因为尚未部署datastore。

保持终端打开。

接下来,让我们修改和部署datastore服务。

datastore

现在,datastore的tokenreview有效负载如下:

代码语言:javascript
复制
tr := authv1.TokenReview{  pec: authv1.TokenReviewSpec{    Token:  clientId,    Audiences:  []string{"data-store"},  },}

现在,在TokenReview对象中,datastoredata-store作为访问者显式传递。

如果令牌不包括data-store在访问者中,则tokenreview API将不会授权该请求。

换句话说,datastore服务可以断言调用方的身份,并验证传入的请求令牌是否应用于datastore服务。

您可以在中找到整个应用程序代码service_accounts_volume_projection/data-store/main.go。

接下来,让我们部署此服务。

您可以使用以下方法创建deployment:

代码语言:javascript
复制
kubectl apply -f service_accounts_volume_projection/data-store/deployment.yamlnamespace/data-store createdserviceaccount/data-store createdclusterrolebinding.rbac.authorization.k8s.io/role-tokenreview-binding createddeployment.apps/app createdservice/app created

让我们检查服务是否正常运行:

代码语言:javascript
复制
kubectl --namespace data-store describe service appName:           appNamespace:      data-storeLabels:         <none>Selector:       app=data-storeType:           ClusterIPIP:             10.106.239.243Port:           <unset>  80/TCPTargetPort:     8081/TCPEndpoints:      172.18.0.5:8081

Endpoints上面输出中的值告诉我们应用程序现在已启动并正在运行。

现在,使用curl再次向API服务发出请求:

代码语言:javascript
复制
curl http://192.168.99.101:31541Hello from data store. You have been authenticated

您应该使用以下方法检查secret存储的日志:

代码语言:javascript
复制
kubectl --namespace data-store logs <pod id>(v1.TokenReviewStatus) &TokenReviewStatus{Authenticated:true,User:UserInfo{Username:system:serviceaccount:api:api,UID:ec7c304f-9722-4d1b-9f67-d3ce32cd8d4c,Groups:[system:serviceaccounts system:serviceaccounts:api system:authenticated],Extra:map[string]ExtraValue{authentication.kubernetes.io/pod-name:[app-65d954658c-dbbr5],authentication.kubernetes.io/pod-uid: [1b37a3f4-54f1-419c-b435-affce3f4a0f3],},},Error:,Audiences:[data-store],}

在切换到API服务的日志时,应该看到以下几行说明了何时从文件系统中重新读取ServiceAccount令牌:

代码语言:javascript
复制
2020/08/26 05:03:43 Refreshing service account token2020/08/26 05:13:42 Refreshing service account token2020/08/26 05:18:42 Refreshing service account token

概要

ServiceAccount令牌卷投影可让您将非全局,有时间限制和受audience绑定的服务令牌与Kubernetes工作负载相关联。

在本文中,您看到了一个在服务之间使用ServiceAccount卷投影进行身份验证的示例,以及如何使用它更好地替代默认的ServiceAccount令牌。

诸如Linkerd和Istio之类的Kubernetes本地化软件正在对其内部通信进行适配,而诸如GKE和AWS EKS之类的托管Kubernetes服务提供商正在使用这种投影卷类型来实现更强大的Pod身份系统。

微信

引用链接

[1] 使用ServiceAccount分配身份: https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/ [2] ServiceAccount卷投影功能: https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/#service-account-token-volume-projection [3] 示例存储库: https://github.com/amitsaha/kubernetes-sa-volume-demo

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-12-10,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 有点技术 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Kubernetes作为身份验证和授权服务器
  • 创建集群
  • 部署API组件
  • 部署datastore
  • Under the hood
  • 向Kubernetes API发出请求
  • 实现服务
  • 每个serviceaccent一个secret
  • 长期有效的服务账户令牌
  • 使用ServiceAccount令牌卷投影的服务间验证
    • API组件
      • datastore
      • 概要
      相关产品与服务
      容器服务
      腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档