Skip to main content

Command Palette

Search for a command to run...

用 SPIRE 为 Pod 提供身份

Updated
4 min read

开始之前

SPIFFE 是一个认证框架,能为多种节点和工作负载类型提供证实能力,解决“我是我”的问题,前面文章演示过用 SPIRE 给类 Unix 进程提供身份的方法,今天这篇就试试给 Pod 提供身份。

这次实验会在前面的基础之上,在 Kubernetes 集群之外运行独立的 SPIRE Server,在集群中用 Pod 的形式运行 SPIRE Agent 作为节点,最后在其它 Pod 中访问 SPIRE Agent,获取 SVID。本文所涉及的对象关系如下图所示:

开始之前,需要做一些准备:

  • 有一个 Kubernetes 集群,Kind 或者 Minikube 也都是可以完成测试的。

  • SPIRE 1.5.x 的二进制文件,可以从 https://spiffe.io/downloads/ 下载

  • 构建镜像所需的基础镜像和 Podman/Docker 等工具。

Kubernetes 相关插件

这里要用到 SPIRE 的三个插件:

Kubernetes Node Attestor:用于证实 Node 身份,需要分别在 Server 和 Agent 两侧进行配置。目前可以选择 k8s_sat 或者 k8s_psat 两种插件,两侧的插件选择应保持一致,分别用于 ServiceAccount Token 和新版本 Kubernetes 中新增的 Projected ServiceAccount Token,本文选择的是 k8s_sat

Projected Token 具有更好的安全性,延伸阅读:https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/

Kubernetes Bundle:Trust Bundle 是数字证书的集合,在 Kubernetes 中往往需要使用 Configmap 来存储和共享,所以一个直接的想法就是通过 spire-server bundle show 命令来获取证书集合,并生成 Configmap。但是这个插件可以方便地通过 Kubernetes API 来自动维护证书集合到 Configmap 的转换过程,并自动完成轮转工作。

Kubernetes Workload Attestor:用于证实 Workload 身份,只需要在 Agent 中配置即可。

配置和启动 SPIRE Server

简单粗暴上配置:

server {
...
    }
}

plugins {
    DataStore "sql" {
...
    }

KeyManager "disk" {
...
}

    Notifier "k8sbundle" {
        plugin_data {
            kube_config_file_path = "/home/dustise/.kube/config"
        }
    }

    NodeAttestor "k8s_sat" {
        plugin_data {
            clusters = {
                "kindcluster" = {
                    service_account_allow_list = ["spire:spire-agent"]
                    use_token_review_api_validation = true

上面的 SPIRE Server 配置中,省略了通用部分,具体内容可以参考前面一篇文章,重点看一下两节 Kubernetes 相关配置。

k8sbundle 的作用就是把 Trust Bundle 内容保存到 Configmap 里面,因此是需要和 API Server 打交道的,这里给他直接配置了一个 KubeConfig 文件,访问方式还有其他的配置内容,可以参考官方文档。要注意的是,这里使用的 KubeConfig 文件所包含的账号是 Cluster Admin 权限,如果使用其他的账号,需要具备对 Configmap 进行 create 和 patch 操作的授权。

k8s_sat 一节中,clusters 字段是一个 Map,其中可以对接多个 Kubernetes 集群,这里我们填充了三个字段:

  • service_account_allow_list:允许 Agent 注册时使用的 Service Account。

  • use_token_review_api_validation:使用 TokenReview API 对 Serivce Account Token 进行验证,除此之外,还可以使用证书进行认证。

  • kube_config_file:和 API Server 进行沟通的凭据。

和 Bundle 类似,这里同样需要具备一定的权限来完成 SPIRE Server 的工作,

  • Configmap 的 patch、get、list

  • tokenreviews 的 create

创建好配置文件之后,可以先在目标集群中创建 spire 命名空间。使用 spire-server -config=[config file path] 命令启动服务器。稍后会在集群中看到新建的 Configmap。

更多配置信息可以参考官方文档

Server 启动成功后,可以提前为工作负载创建 Node 和 Entry:

spire-server entry create -socketPath=socks/spire-server.sock \
    -spiffeID spiffe://spiffe.dom/clusters/kindcluster \
    -selector k8s_sat:cluster:kindcluster -node

spire-server entry create -socketPath=socks/spire-server.sock \
    -spiffeID spiffe://spiffe.dom/ns/default/sa/default \
    -parentID spiffe://spiffe.dom/ns/spire/sa/spire-agent \
    -selector k8s:ns:default \
    -selector k8s:sa:default

首先用 k8s_sat:cluster:kindcluster 创建了一个在 spiffe.dom 中的 Node 条目,它的 SPIFFE ID 是 spiffe://spiffe.dom/clusters/kindcluster

接下来以 Node 条目为上级,使用 k8s:ns:default + k8s:sa:default 的 Selector,创建一个 SPIFFE ID spiffe://spiffe.dom/ns/default/sa/default,代表在 default 命名空间中用 default Service Account 身份运行的 Pod。

创建 Agent

在运行 Agent 之前,首先要制作一个镜像,这里偷懒的使用现成二进制进行构建:

FROM busybox:1.35.0-glibc
RUN mkdir -p /spire/bin
COPY spire-agent /spire/bin
CMD ["/spire/bin/spire-agent", "-config=/spire/conf/k8s-agent.conf"]

这里要创建一个 Agent 的工作负载,为了让 Agent 能够通过进程号查询工作负载的 Pod 信息,并对工作负载提供 Workload API,需要满足几个条件:

  • Agent 需要有授权访问 Kubernetes 的特定资源

  • 共享 Socket 文件,让 Workload 可以访问 Agent 提供的 Workload API

  • 能够识别调用 Workload API 的进程的 Pod 信息,从而生成 Selector

综合以上考虑,我们需要设计这样的 Workload:

  • 用主机卷的方式在每个节点上暴露 Socket

  • 能够访问 Trust Bundle 所在的 Configmap

  • Agent 和 Workload 共享 IPC 空间,便于通过进程号识别身份

  • Agent 所使用的 Service Account 需要具备和 API Server/Kubelet 通信查询信息的能力。

因此产生如下的 YAML 片段:

    spec:
      hostPID: true
      hostNetwork: true
      serviceAccountName: spire-agent
...
      containers:
        - name: spire-agent
          image: gcr.io/spiffe-io/spire-agent:1.5.0
          args: ["-config", "/run/spire/config/agent.conf"]
          volumeMounts:
            - name: spire-config
              mountPath: /run/spire/config
              readOnly: true
            - name: spire-bundle
              mountPath: /run/spire/bundle
            - name: spire-agent-socket
              mountPath: /run/spire/sockets
              readOnly: false        
      volumes:
        - name: spire-config
          configMap:
            name: spire-agent
        - name: spire-bundle
          configMap:
            name: spire-bundle
        - name: spire-agent-socket
          hostPath:
            path: /run/spire/sockets
            type: DirectoryOrCreate
...

这段 YAML 有几个要点:

  • 使用了符合 SPIRE Server 配置中要求的 ServiceAccount

  • HostPID 共享主机 PID 空间

  • HostNetwork 共享主机网络空间

  • 加载 Trust Bundle 所在的 Configmap

  • 加载一个主机卷用于输出 Socket 文件

  • 用一个 Configmap 保存配置文件并加载

Agent 的配置文件如下:

agent {
  data_dir = "/run/spire"
  log_level = "DEBUG"
  server_address = "10.211.55.5"
  server_port = "8081"
  socket_path = "/run/spire/sockets/agent.sock"
  trust_bundle_path = "/run/spire/bundle/bundle.crt"
  trust_domain = "spiffe.dom"
}

plugins {
  NodeAttestor "k8s_sat" {
    plugin_data {
      cluster = "kindcluster"
    }
  }

  KeyManager "memory" {
...
  }

  WorkloadAttestor "k8s" {
    plugin_data {
      skip_kubelet_verification = true
    }
  }
}

Agent 配置相对来说稍显复杂:

  • server_addressserver_pod,用于访问前面启动的 SPIRE SERVER

  • trust_bundle_path 引用 Configmap 的加载路径即可

  • trust_domain 需要保持和 SPIRE Server 定义一致

  • k8s_satcluster 字段中,集群名称需要和 SPIRE Server 的 Map 中的定义匹配

  • skip_kubelet_verification:跳过对 Kubelet 证书的检查

Agent 使用的 Service Account 也需要进行 RBAC 授权,需要能够对 podnode 以及 node/proxy 进行 get 操作。

先后把配置 Configmap、RBAC 以及 Daemonset 等资源提交之后,会看到 Agent Pod 启动。

启动客户端

任意启动一个客户端程序,为模仿接入 Workload API 的实现,其中还是需要使用 SPIRE Agent 的二进制。客户端应该使用 Agent 的 Socket 访问 Wokrload API,同时为了表明身份,同样需要用 HostPID 供 Agent 识别,因此运行如下工作负载:

...
      hostPID: true
      hostNetwork: true
...
      containers:
        - name: client
          image: gcr.io/spiffe-io/spire-agent:1.2.3
          command: ["sleep"]
          args: ["1000000000"]
          volumeMounts:
            - name: spire-agent-socket
              mountPath: /run/spire/sockets
              readOnly: true
      volumes:
        - name: spire-agent-socket
          hostPath:
            path: /run/spire/sockets
            type: Directory

Pod 在 default 命名空间启动之后,进入 Shell 使用 spire-agent api fetch 命令,就能成功的获取 SVID 了:

$ bin/spire-agent api  fetch -socketPath=/run/spire/sockets/agent.sock
Received 1 svid after 83.772792ms

SPIFFE ID:              spiffe://spiffe.dom/shutup
SVID Valid After:       2022-11-24 17:02:03 +0000 UTC
SVID Valid Until:       2022-11-24 17:04:13 +0000 UTC
CA #1 Valid After:      2022-11-23 14:57:51 +0000 UTC

To be continued

现在我们就用一个非常笨拙的方法,把 Kubernetes 的工作负载识别能力接入到了 SPIRE Server 里面了。事实上接入 Kubernetes 还有别的部署和使用方式,例如使用 CRD、在集群内运行 SPIRE Server、使用 Envoy 等接入 Workload API 等。官网文档中对这些案例都有较为详细的指导。

结合前面对于 Ghostunel 等的介绍,不难看出,打通虚拟机和 Kubernetes 工作负载身份是可行的,而根据联邦一文的描述,这个体系还可以和 OIDC 等进行互通,进一步扩大 SPIFFE SVID 的版图。

More from this blog

龙虾恐慌:AIOps 又要改名了?

ChatGPT 开始,把 AI 拉近到普罗大众的面前,让无数人感受到 AI 的亲民魅力。而龙虾,则把大模型驱动的自动化能力,突然间变得水灵灵、活泼泼地走进千家万户。它不只是“风口上的猪”,而是风口本身。热度高到让 Mac mini 一度断货,不知道这在不在库克的预料之内。 每代人都有每代人的鸡蛋,春节期间,我就领了我的鸡蛋。翻出古老的 MacBook Air M1,充值各种大模型。当然了,这个工具

Mar 9, 20261 min read

再见 2025

我猜不少人以为这个号废了吧?并没有,只是今年变化有点大,一直有种抄起键盘,无从说起的感觉,所以一直偷懒到今天,2025 的最后一天。 今年是我的第四个本命年,去年末一期播客里,大内说本命年不是灾年,是变化年,有危也有机。可是讲真啊,只看到危,没看到机。 各种因缘际会,从鹅厂跳槽到前东家,已经接近四年,第一个合同期已经进入尾声。除了前两年还在云原生领域嗷嗷叫,后两年基本都是些鸡零狗碎的东西了,用老东家的术语说是——偏离主航道,可谓是前景暗淡了。 一旦确定要滚蛋,反倒心思轻松起来,每天骑着我的小红车...

Jan 5, 20261 min read

辅助编程?dora 说:我知道你很急可是请你别急

从 OpenGPT 把大模型的火烧旺了之后,这三年来,相信很多组织或摩拳擦掌、或躬身入局,希望借助聪明能干的大模型,或想偿还技术宅,或想降本增效,或想弯道超车。一时间,沉寂许久的 AIxx 又活过来了,LLM Ops、Vibe Coding、中医大模型、GPT 算命等等,全都老树发新芽,焕发了勃勃生机。那么视角拉回从业者最关注的饭碗相关的领域之一——AI 辅助开发,产生了什么触动,应该如何拥抱呢? DORA 的年度报告中给出了很有意思的结论——强者恒强。 执行摘要部分总结了几个有趣的点: 问题...

Oct 6, 20251 min read

[译]dora:ai 辅助软件开发状态报告

执行摘要 在 2025 年,科技领导者面临的核心问题已不再是“是否要采用 AI”,而是“如何实现其价值”。 DORA 的研究基于超过 100 小时的定性访谈和来自全球近 5,000 名技术专业人士的问卷调查。研究揭示了一个关键事实:AI 在软件开发中的主要角色是“放大器”。它会放大高效能组织的优势,也会凸显组织的缺陷。 关键结论:AI 是放大器 AI 投资的最大回报并非来自工具本身,而是来自组织底层系统的战略性建设: 高质量的内部平台 清晰的工作流 团队的协同能力 缺少这些基础,AI ...

Oct 2, 202514 min read

僭越了,有人在用 Rust 写 Kubernetes

一个新语言问世,最爱做的事情之一,就是重写存量软件了。 云原生喝酒 SIG 重点扶持项目——rk8s(https://github.com/rk8s-dev/rk8s) 也可以归在这个范畴里,只不过这个项目重写的东西比较大,是 Kubernetes。 从 2025 年 1 月第一个 Commit 开始,到现在有了 200 多次 Commit,十几万行代码。当然距离 Kubernetes 的几百万行代码还差得远——老马就是喜欢整这种大无畏项目。 另外该项目也是国内第一个脱离 Cargo 转向使用 ...

Sep 27, 20253 min read

【伪】架构师

342 posts