基于k8s的微服务架构 (使用k8s部署微服务)

问题: 主要的政府机关 银行等都需要符合标准的安全规范,比如密码必须30天更换一次,比如开发团队是不能知道线上环境的库和server的用户名密码的。怎么办?

  1. 一种方法,就指专门的线上运维团队来干。但是这样又违背了devops的初衷。同时线上更换密码的工作还是很危险的。
  2. 采用自动化的方法来干。让所有的授权都来自于K8S 服务本身。这样就能做到授权的简单 干净。

k8s服务之间的调用代码,使用k8s部署微服务

如何做到这一点,我们首先要了解一下 K8s 的授权过程

当你去访问K8S 的时候:

curl -k $API_SERVER_URL/api/v1/namespaces
  1. 禁止您访问 API 端点(即状态码为 )。 403
  2. 您被标识为 ,并且不允许此标识列出命名空间。 system:anonymous

上面测试揭示了一些重要的工作机制: kube-apiserver

  • 首先,它标识请求的用户(您是谁),称为身份验证(或 AuthN)
  • 然后,它确定此用户允许哪些操作(您拥有哪些权限)(确定经过身份验证的用户具有哪些权限)是授权(或 AuthZ)。

k8s服务之间的调用代码,使用k8s部署微服务

我们可以重新评估上一个请求中发生的事情并注意到这一点。 curl

  1. 由于没有提供用户凭据,因此 Kubernetes 身份验证模块无法分配身份,因此它将请求标记为匿名。
  2. 根据 Kubernetes API 服务器的配置方式,可能还会收到 401 未授权 代码。
  3. Kubernetes 授权模块检查是否有权列出集群中的 命名空间 。由于它没有,它会返回一条错误消息。 system:anonymous403 Forbidden

假设标识有权访问命名空间资源,则会收到命名空间列表。

值得注意的是,我们从集群外部发出了请求,但此类请求也可能来自内部。

例如,kubelet 可能需要连接到 Kubernetes API 来报告其节点的状态

k8s服务之间的调用代码,使用k8s部署微服务

身份验证模块是整个系统的第一个看门人,使用静态令牌、证书或外部管理的标识对所有这些请求进行身份验证。

Kubernetes 具有一个身份验证模块,该模块具有几个值得注意的功能:

  1. 它支持 人类用户和程序用户
  2. 它支持外部用户(例如部署在集群 外部 的应用程序)和 内部用户 (例如由 Kubernetes 创建和管理的帐户)。
  3. 它支持 标准身份验证策略 ,例如静态令牌、持有者令牌、X509 证书、OIDC 等。
  4. 它同时支持 多种身份验证策略
  5. 您可以添加新的身份验证策略或逐步淘汰旧的 身份验证策略
  6. 您还可以允许 匿名访问 API。

Kubernetes API 区分内部和外部用户

Kubernetes API 服务器支持两种类型的 API 用户:内部和外部。

但为什么两者之间有这样的区别呢?

如果用户是集群的内部用户,我们需要为他们定义一个规范(即数据模型)。

当用户是外部用户时,这种规范已经存在于其他地方,比如其他的IDP。

我们可以将用户分为以下几类:

  1. Kubernetes 托管用户 :由 Kubernetes 集群本身创建并由 集群内应用使用的 用户帐户。
  2. 非 Kubernetes 托管用户:Kubernetes 集群外部的用户,例如:具有 群集管理员 提供的静态令牌或证书的用户。通过 外部身份提供商 (如 Keystone、Google 账户和 LDAP )进行身份验证的用户。

使用服务帐户管理 Kubernetes 内部身份

在 Kubernetes 中,内部用户被分配了称为 服务帐户 的身份。

这些标识由 创建并分配给应用程序。 kube-apiserver

当应用向 发出请求时,它可以通过共享链接到其服务帐户的签名令牌来验证其身份。 kube-apiserver

让我们检查服务账户定义:

kubectl create serviceaccount test
serviceaccount/test created

并使用以下方法检查资源:

kubectl get serviceaccount test -o yaml
apiVersion: v1
kind: ServiceAccount
metadata:
  name: test


那么 这个时候 ServiceAccount test 的secret 是什么呢?下面我们一步一步来解开整个谜题

让我们使用以下命令创建 pod:

nginx.yaml

apiVersion: v1
kind: Pod
metadata:
  name: nginx
spec:
  serviceAccount: test
  containers:
  - name: nginx
    image: nginx

我们进入docker

kubectl exec -ti nginx -- bash

从下面可以看出secret 在哪里:/var/run/secrets/kubernetes.io/serviceaccount

export APISERVER=https://kubernetes.default.svc
export SERVICEACCOUNT=/var/run/secrets/kubernetes.io/serviceaccount
export CACERT=${SERVICEACCOUNT}/ca.crt
export TOKEN=$(cat ${SERVICEACCOUNT}/token)
curl --cacert ${CACERT} --header "Authorization: Bearer ${TOKEN}" -X GET ${APISERVER}/api
{
  "kind": "APIVersions",
  "versions": [
    "v1"
  ],
  "serverAddressByClientCIDRs": [
    {
      "clientCIDR": "0.0.0.0/0",
      "serverAddress": "192.168.49.2:8443"
    }
  ]
}

我们来验证一下,看一下Pod

kubectl get pod nginx -o yaml
apiVersion: v1
kind: Pod
  name: nginx
spec:
  containers:
  - image: nginx
    volumeMounts:
    - mountPath: /var/run/secrets/kubernetes.io/serviceaccount
      name: kube-api-access-69mqr
      readOnly: true
  serviceAccount: test
  volumes:
  - name: kube-api-access-69mqr
    projected:
      defaultMode: 420
      sources:
      - serviceAccountToken:
          expirationSeconds: 3607
          path: token
      - configMap:
          items:
          - key: ca.crt
            path: ca.crt
          name: kube-root-ca.crt
      - downwardAPI:
          items:
          - fieldRef:
              apiVersion: v1
              fieldPath: metadata.namespace
            path: namespace

这里发生了很多事情,所以让我们解开定义。

  1. 声明了一个卷。 kube-api-access-69mqr
  2. 卷在 上以只读方式装载。 /var/run/secrets/kubernetes.io/serviceaccount

卷声明很有趣,因为它使用了该字段。 projected

投影卷是将多个现有卷合并为一个卷的卷。

k8s服务之间的调用代码,使用k8s部署微服务

在这种特殊情况下,预计体积是以下各项的组合:

  1. 在路径 上装载的卷。 serviceAccountTokentoken
  2. 一个卷。 configMap
  3. 卷装载在路径 上。 downwardAPInamespace

这些卷是什么?

服务帐户令牌卷是一个特殊卷,用于从当前 服务帐户 装载机密。

这用于使用正确的令牌填充文件。 /var/run/secrets/kubernetes.io/serviceaccount/token

配置映射卷是将配置映射中的所有密钥作为当前目录中的文件挂载的卷。

文件的内容是相应键的值(例如,如果键值为 ,则使用以下内容创建文件)。 replicas: 1replicas1

在这种情况下,ConfigMap 卷会挂载调用 Kubernetes API 所需的证书。 ca.crt

向下 API 卷是一个特殊的卷,它使用向下的 API 将有关 Pod 的信息公开给其容器。

在这种情况下,它用于将当前命名空间作为文件公开给容器。

你可以直接到 /var/run/secrets/kubernetes.io/serviceaccount/token 看token

你也可以用下面的命令generate 一个新的token

kubectl create token test
eyJhbGciOiJSUzI1NiIsImtpZCI6ImctMHJNO…

整个token 其实就是一个 JWT

这些是签名的 JWT 令牌。

若要检查它,可以复制字符串并将其粘贴到 jwt.io 网站上。

输出分为三个部分:

  1. 标头 描述令牌的签名方式。
  2. 有效负载 ― 令牌的实际数据。
  3. 签名 用于验证令牌是否未被修改。

k8s服务之间的调用代码,使用k8s部署微服务

下一次我们介绍 如何用K8S 给其他服务授权。