
Teleport RFD 5 深度解读独立 kubernetes_service 与多集群 Kubernetes 接入设计【免费下载链接】teleportThe easiest, and most secure way to access and protect all of your infrastructure.项目地址: https://gitcode.com/gh_mirrors/tel/teleport本篇文章基于 Teleport 开源仓库中的 RFD 5Kubernetes (k8s) service enhancements 展开系统梳理 Teleport 如何将 Kubernetes 集成从 Proxy 服务中剥离为独立的kubernetes_service实现单 Teleport 集群对接多个 k8s 集群、支持防火墙后的反向隧道接入、基于标签的细粒度 RBAC以及tsh kube命令族与 kubectl exec 认证插件等完整能力。读完本文你将掌握teleport.yaml中kubernetes_service的完整配置语义、多集群注册与路由原理以及三个典型部署场景的可运行配置样例。背景与动机为什么要把 k8s 集成从 Proxy 中分离在 RFD 5 之前Teleport 的 Kubernetes 集成是以Proxy 服务的可选插件addon形式实现的即所有能力都挂在proxy_service.kubernetes配置段下。这一设计存在多个结构性限制RFD 5 在 Why 一节中明确列出了它们单集群限制一个 Teleport 集群只能对接一个 k8s 集群要支持多个 k8s 集群只能借助 Trusted Cluster可信集群机制而这在运维上要复杂得多。网络障碍场景受限位于防火墙、NAT 等网络障碍之后的 k8s 集群同样被迫依赖 Trusted Cluster 才能接入增加了部署拓扑的复杂度。职责耦合k8s 集成与 Proxy 的逻辑耦合在一起违背了关注点分离separation of concerns原则。糟糕的过期体验用户证书过期后kubectl只会抛出一堆晦涩难懂的 TLS 错误用户无法直观得知需要重新登录。可发现性差没有任何简单途径判断一个 Teleport 集群背后是否挂载了 k8s 集群。审计盲区审计日志会忽略所有非交互式non-interactivek8s 请求。RFD 5 正是针对这些问题提出的一整套增强方案其状态在文档头部的 front matter 中标记为implemented已实现说明这些设计不仅停留在纸面而且已经落地到当前仓库的代码与配置体系中。兼容性承诺新旧两种集成方式并行RFD 5 在 Backwards compatibility 一节中做出明确承诺旧的 k8s 集成即proxy_service.kubernetes段继续受支持且行为不变未迁移的用户不会被破坏新的kubernetes_service会优先于旧的 Proxy 集成被使用且所有新特性只会在新服务上添加。这一双轨制策略意味着如果你在现有环境中已有proxy_service.kubernetes配置升级后无需立刻改动但后续若要使用多集群、标签 RBAC 等新能力则应迁移到独立的kubernetes_service。服务心智模型Teleport 各服务的职责划分RFD 5 用一个简洁的心智模型mental model来统一指导设计它把 Teleport 的服务划分为三类角色服务角色说明auth_service集群指挥中心负责状态管理、凭据签发与授权判定proxy_service无状态路由器堡垒机/网关唯一需要对外公网暴露的 Teleport 服务负责接收外部连接ssh_service/application_service/kubernetes_service无状态节点代表单台主机 / Web 应用 / k8s 集群向auth_service注册直连或经 proxy 反向隧道只处理经由proxy_service转发来的连接需要特别说明的是文档用星号标注了一个细节application_service和kubernetes_service出于资源优化考量可以代表多个应用 / 多个 k8s 集群但无论代表多少资源它们始终是客户端到达目标之前的最后一跳final hop。在这一模型下kubernetes_service与ssh_service的地位完全对等这也为后续的注册、心跳、RBAC 等机制复用通用服务框架打下了基础。kubernetes_service 配置定义从嵌套到平级新旧格式对比新引入的kubernetes_service会完整继承旧proxy_service.kubernetes段的全部配置与行为。旧格式如下# Old format: proxy_service: kubernetes: enabled: yes public_addr: [k8s.example.com:3026] listen_addr: 0.0.0.0:3026 kubeconfig_file: /secrets/kubeconfig而新格式将其提升为顶层配置段# New format: kubernetes_service: enabled: yes public_addr: [k8s.example.com:3026] listen_addr: 0.0.0.0:3026 kubeconfig_file: /secrets/kubeconfig在保留原有字段的基础上kubernetes_service还新增了两个字段kube_cluster_namek8s 集群的名称labels用于 RBAC 的静态标签。同时public_addr和listen_addr两个字段变为可选。当二者都未设置时服务将通过 Proxy 反向隧道 接入集群。这一配置结构在当前仓库的 lib/config/fileconf.go 中有精确的代码对应——Kube结构体即kubernetes_service的解析模型它内嵌了通用的Service配置提供enabled等公共字段并声明了PublicAddr apiutils.Stringspublic_addr公开地址KubeconfigFile stringkubeconfig_fileKubeClusterName stringkube_cluster_name默认回退到 Teleport 集群名StaticLabels map[string]stringlabelsRBAC 静态标签DynamicLabels []CommandLabelcommands动态标签ResourceMatchers []ResourceMatcherresourceskube_cluster 资源匹配器同一文件中的KubeProxy结构体lib/config/fileconf.go则对应旧式proxy_service.kubernetes段其中的ClusterName字段注释明确指出If set, this proxy will handle kubernetes requests for the cluster即旧模式下一旦设置集群名该 Proxy 就要负责处理对应 k8s 集群的请求——这正是旧模型单集群耦合的根源。通用服务特性kubernetes_service实现了所有 Teleport 通用服务的特性直连 Auth 服务器或通过 Proxy 隧道连接使用 join token 注册并拥有专属的 role 角色通过 heartbeat心跳宣告自身的在线状态接入审计日志体系。公共端点kube_listen_addr 与代理暴露kubernetes_service不直接对外服务客户端请求客户端必须经由proxy_service连接。因此若要在 Proxy 上暴露 k8s 监听端口需设置kube_listen_addr配置项proxy_service: enabled: yes public_addr: example.com kube_listen_addr: 0.0.0.0:3026RFD 5 特别注明这与旧格式完全等价proxy_service: enabled: yes public_addr: example.com kubernetes: enabled: yes listen_addr: 0.0.0.0:3026在源码层面KubeAddr stringkube_listen_addr被定义为启用 Kubernetes 端点而无需本地 k8s 集群的简写lib/config/fileconf.go其旁还有KubePublicAddrkube_public_addr用于声明该端点的公网地址。可见 listener 在 Proxy、数据面在独立服务 的职责划分在配置解析层面即被固化。多集群支持两种连接方式一个 Teleport 集群可以挂载多个 k8s 集群具体连接方式分为两类。方式一服务运行在 k8s 集群内部推荐这是 RFD 5 推荐的用法。运行在 k8s 内部的kubernetes_service不持有 kubeconfig而是使用 Pod 的 service account 凭据向所在集群的 k8s API 完成认证。集群名称通过kubernetes_service.kube_cluster_name字段在teleport.yaml中显式指定——文档明确指出没有可移植的方式从环境变量猜测 k8s 集群名因此必须显式配置。方式二服务运行在 k8s 集群外部此时需提供kubeconfig_file在kubernetes_service段中旧模式下则对应proxy_service.kubeconfig。文件中所有k8s API context 都会被解析并注册。文档给出了一个包含两个 context 的示例 kubeconfigapiVersion: v1 kind: Config clusters: - cluster: server: https://a.example.com name: a-cluster - cluster: server: https://b.example.com name: b-cluster contexts: - context: cluster: a-cluster user: a-creds name: a - context: cluster: b-cluster user: b-creds name: b current-context: a users: - name: a-creds user: client-key-data: ... client-certificate-data: ... - name: b-creds user: client-key-data: ... client-certificate-data: ...这里有一个容易踩坑的命名细节上面有两个 k8s contextProxy 会以contexts段中的名字即a和b来指代它们而不是clusters段中的a-cluster、b-cluster。原因是clusters段中的条目本身不指定使用哪组凭据且同一个 cluster 可能绑定多组凭据只有 context 才完整定义了集群 用户的组合。因此tsh kube ls输出的集群名对应的是 context 名。集群元数据与心跳让客户端看见所有 k8s 集群为了让客户端感知到所有可用的 k8s 集群注册后的 k8s 服务会通过心跳heartbeats宣告自己承载的 k8s 集群。RFD 5 设计在ServerSpecV2中新增kube_clusters字段存放集群名列表。随后Auth 服务器和 Proxy 都可以被查询返回所有 Proxy 宣告的 k8s 集群的并集union。tsh正是利用这个查询来向用户展示全部可用集群即tsh kube clusters/tsh kube ls。在实现层面当前仓库 api/types/kubernetes.go 中的KubeClusters类型即这一模型的数据结构它提供了Find(name)按名查找、ToMap()按名建索引、Len/Less/Swap排序接口等辅助方法并配套了DeduplicateKubeClusters去重函数与 kubernetes_test.go 对应的排序、去重测试。此外同文件中的ValidateKubeClusterName使用正则^[a-zA-Z0-9._-]$校验集群名合法性api/types/kubernetes.go说明集群名只允许字母、数字、点、下划线和连字符。路由机制把目标集群编进 TLS 证书客户端知道有哪些集群之后还需要能在集群间切换。由于 Teleport 无法控制真实的 k8s 客户端kubectl唯一的传输通道就是用户 TLS 证书。RFD 5 的设计是为 k8s 访问签发的 TLS 证书中会把 k8s 集群名作为Subject的一个扩展写入方式与既有的kube_users、kube_groups嵌入方式一致。这与当前RouteToCluster字段用于将流量路由到 Trusted Cluster 的机制类似。Proxy 收到 k8s 请求后的处理流程如下文档原文流程认证请求校验证书从证书扩展中解析目标 k8s 集群名先查本地的kubernetes_service若命中直接转发请求并返回若未命中则从 Auth 服务器拉取全部 k8s 集群列表若目标集群在该列表中将请求转发到合适地址——目标地址可以是某个kubernetes_service也可以是另一个与kubernetes_service建有隧道的 Proxy若映射中仍找不到目标集群则返回错误。这一先本地、后全局、最后报错的三段式查找逻辑保证了多集群环境下路由的高效性与最终一致性。从当前仓库的 Proxy 侧实现看lib/srv/alpnproxy/proxy.go 中提供了shouldRouteToKubeService(sni)判断函数说明在现代 Teleport 中 k8s 流量还会结合 SNI/ALPN 协议升级proxy peering完成分流属于该 RFD 路由设计的后续演进。Service 反向隧道让防火墙后的集群也能接入为支持处于防火墙之后的 k8s 集群RFD 5 复用了 Trusted Cluster 与节点 IoT 模式中已有的反向隧道reverse tunnel逻辑允许同一集群内的 k8s 服务向 Proxy 建立隧道。从用户视角看其配置方式与 IoT 节点完全一致只需在teleport.yaml的auth_servers字段中填写公共 Proxy 地址。当通过反向隧道连接时kubernetes_service默认不在任何本地端口监听——除非显式设置了listen_addr。teleport: auth_servers: proxy.example.com:3080 # kubernetes_service 通过该地址经 proxy 反向隧道接入 kubernetes_service: enabled: yes kube_cluster_name: third.example.com # 注意此场景不需要 public_addr / listen_addr这一设计让位于 NAT/防火墙后的集群不再需要独立的 Trusted Cluster 编排大幅降低了多集群接入的运维复杂度。CLI 变更tsh kube 命令族与 kubectl exec 插件tsh kube 命令tsh kube系列命令用于查询已注册的集群并切换 kubeconfig 的当前 context。RFD 5 给出了完整的使用演示$ tsh login --proxyproxy.example.com --userawly ... # 列出所有已注册集群 $ tsh kube ls Cluster Name Status ------------- ------ a.k8s.example.com online b.k8s.example.com online c.k8s.example.com online # 登录后kubeconfig 默认指向第一个集群按字母序 $ kubectl config current-context awlya.k8s.example.com # 所有集群都会被填充为 context $ kubectl config get-contexts CURRENT NAME CLUSTER AUTHINFO * awlya.k8s.example.com proxy.example.com awlya.k8s.example.com awlyb.k8s.example.com proxy.example.com awlyb.k8s.example.com awlyc.k8s.example.com proxy.example.com awlyc.k8s.example.com # 在集群之间切换 $ tsh kube login c.k8s.example.com # 或者 $ kubectl config use-context awlyc.k8s.example.com # 查看当前集群 $ kubectl config current-context awlyc.k8s.example.com从当前仓库的 CLI 实现看tool/tsh/common/kube.go 中完整实现了这一命令族newKubeCommand组织命令树其中kubeLoginCommand对应tsh kube login见 tool/tsh/common/kube.go负责切换目标集群并支持selectorsOrWildcard、checkClusterSelection等选择逻辑kubeCredentialsCommand则对应下文要讲的 exec 凭据插件。kubectl exec 认证插件kubectl 的 exec authn plugin 机制允许动态为kubectl签发凭据bearer token 或客户端 key/cert而无需把凭据静态写入 kubeconfig。tsh在以tsh kube credentials --kube-clusterfoo方式执行时foo为已注册的 k8s 集群名实现该 exec 插件规范。其行为要点tsh在返回凭据前会先把证书缓存到磁盘若客户端证书已过期或缺失tsh会回退到登录提示login prompt重新认证。对应的实现即 tool/tsh/common/kube.go 中的kubeCredentialsCommand其issueCert方法负责签发证书writeKeyResponse/writeByteResponse/writeResponse等方法按 exec 插件规范输出不同格式keyring 对象或原始 PEM 证书的响应。这样kubectl与 Teleport 之间便形成了按需取证、过期重登的闭环彻底规避了 RFD 5 背景部分提到的过期证书导致晦涩 TLS 错误问题。RBAC基于标签的 k8s 集群访问控制由于 Auth 服务器现在能感知全部 k8s 集群管理员便可限制用户对特定集群的访问让整个组织只需维护一个 Teleport 集群即可统一管控所有 k8s 访问同时遵循最小权限原则。访问控制与节点nodes一样基于标签。标签在teleport.yaml中声明teleport: auth_servers: [auth.example.com] kubernetes_service: enabled: yes kube_cluster_name: a.k8s.example.com labels: env: prod commands: - name: kube_service_hostname command: [hostname] period: 1h上例同时展示了静态标签labels与动态标签commands按周期执行命令生成标签值的写法。动态标签在 lib/config/fileconf.go 中对应DynamicLabels []CommandLabel字段。随后通过角色role进行限制例如下面这个dev角色只允许访问env: prod的集群kind: role version: v3 metadata: name: dev spec: allow: kubernetes_groups: - system:masters kubernetes_labels: - env: prod - region: us-west1 - cluster_name: ^us.*\.example\.com$ # cluster_name 标签由系统自动生成注意示例中的注释cluster_name标签由系统自动生成因此可以在kubernetes_labels里用正则如^us.*\.example\.com$按集群名做前缀/模式匹配。这与kubernetes_groups映射到 k8s RBAC 的 group如system:masters一起构成了集群级 集群内 RBAC的双层授权体系。审计日志从只记交互到全量记录旧的 k8s 集成只在审计日志中记录交互式会话如kubectl exec、kubectl port-forward。新的kubernetes_service将记录全部 k8s API 请求——这一行为在通过proxy_service.kubernetes使用 k8s 时同样适用。这意味着诸如kubectl get pods等非交互式请求也会进入审计日志满足更严格的合规审计需求。完整配置示例三个典型部署场景RFD 5 提供了三个可运行级别的部署场景以下完整保留并补充说明。场景 1Proxy 与 k8s 服务混合部署 增量接入第一步Proxy 运行在 k8s 集群内部teleport-root.yamlauth_service: cluster_name: example.com public_addr: auth.example.com:3025 proxy_service: public_addr: proxy.example.com:3080 kube_listen_addr: 0.0.0.0:3026 kubernetes_service: enabled: yes$ tsh kube ls Cluster Name Status ------------- ------ example.com online注意这里的kubernetes_service没有显式设置kube_cluster_name因此它默认取 Teleport 的cluster_name即example.com。第二步向既有 Auth 服务器接入一个新的 k8s 集群teleport-other.yamlteleport: auth_servers: auth.example.com:3025 kubernetes_service: enabled: yes kube_cluster_name: other.example.com # 注意需要 public_addr/listen_addrproxy_service 才能连到该 kubernetes_service public_addr: other.example.com:3026 listen_addr: 0.0.0.0:3026$ tsh kube ls Cluster Name Status ------------- ------ example.com online other.example.com online第三步第三个集群改为经 Proxy 的反向隧道接入无需开放任何端口teleport-third.yamlteleport: auth_servers: proxy.example.com:3080 kubernetes_service: enabled: yes kube_cluster_name: third.example.com # 注意不需要 public_addr/listen_addr因为该 kubernetes_service # 通过上面的 auth_servers 地址经 proxy_service 反向隧道接入$ tsh kube ls Cluster Name Status ------------- ------ example.com online other.example.com online third.example.com online场景 2单个中心 Teleport 实例对接多个 k8s 集群teleport.yaml中使用kubeconfig_file一次注册文件内的所有 contextproxy_service: public_addr: proxy.example.com:3080 kube_listen_addr: 0.0.0.0:3026 kubernetes_service: enabled: yes kubeconfig_file: kubeconfig配套的kubeconfig文件注意此时两个 context 共享同一组凭据shared-credsapiVersion: v1 kind: Config clusters: - cluster: server: https://a.example.com name: a-cluster - cluster: server: https://b.example.com name: b-cluster contexts: - context: cluster: a-cluster user: shared-creds name: a - context: cluster: b-cluster user: shared-creds name: b current-context: a users: - name: shared-creds user: client-key-data: ... client-certificate-data: ...$ tsh kube ls Cluster Name Status ------------- ------ a online b online输出印证了前文以 context 名为准的规则显示的是a、b而非a-cluster、b-cluster。场景 3中心 Proxy 作网关多个 k8s 集群反向隧道接入Proxy 侧teleport-proxy.yamlproxy_service: public_addr: proxy.example.com:3080 kube_listen_addr: 0.0.0.0:3026集群 A 侧teleport-a.yamlteleport: auth_servers: proxy.example.com:3080 kubernetes_service: enabled: yes kube_cluster_name: a.example.com集群 B 侧teleport-b.yamlteleport: auth_servers: proxy.example.com:3080 kubernetes_service: enabled: yes kube_cluster_name: b.example.com$ tsh kube ls Cluster Name Status ------------- ------ a.example.com online b.example.com online从设计到实现RFD 5 在仓库中的落地点RFD 5 的状态为implemented其核心设计均可与当前仓库代码相互印证配置模型lib/config/fileconf.go 中的Kube结构体定义了kubernetes_service的全部字段kube_cluster_name、labels、commands、resources等KubeProxy结构体保留旧格式兼容配置解析测试见 lib/config/configuration_test.go。集群数据模型api/types/kubernetes.go 中的KubeClusters类型及配套排序、去重、名称校验正则^[a-zA-Z0-9._-]$方法是心跳宣告 并集查询的底层支撑对应测试见 api/types/kubernetes_test.go。tsh CLItool/tsh/common/kube.go 实现了tsh kube ls、tsh kube loginkubeLoginCommand与tsh kube credentialskubeCredentialsCommandkubectl exec 插件三个核心命令。Proxy 路由lib/srv/alpnproxy/proxy.go 中的shouldRouteToKubeService展示了现代版本中结合 SNI/ALPN 将请求分流到 kube 服务的演进实现。综上RFD 5 奠定了 Teleport Kubernetes 接入的现代架构基石独立的kubernetes_service让 k8s 接入与 Proxy 解耦并支持多集群证书扩展与标签 RBAC 让谁能访问哪个集群可声明、可审计反向隧道让防火墙后的集群零端口接入tsh kube命令族与 exec 插件则让终端用户的体验从晦涩 TLS 报错变为即点即用。无论你计划把 Teleport 作为多集群的唯一入口、为隔离网络内的集群建立安全网关还是为团队实施基于标签的最小权限管控本文中的配置样例与源码落点都可以作为直接参考。【免费下载链接】teleportThe easiest, and most secure way to access and protect all of your infrastructure.项目地址: https://gitcode.com/gh_mirrors/tel/teleport创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考