安全开发-Rancher平台搭建

一、前言

1.1 背景

随着容器化和云原生技术的普及,Kubernetes 已经成为事实上的容器编排标准。但在实际落地过程中,很多团队会面临几个现实问题:

  • 标准 Kubernetes 组件多、部署复杂,学习和运维成本高;
  • 多集群、多环境管理困难,缺少统一的控制平面;
  • 内部服务之间缺少可信的 TLS 证书体系,HTTPS 和 mTLS 难以规模化落地;
  • 入口网关、证书管理、权限控制等安全能力分散,难以形成统一的 DevSecOps 流程。

本文要解决的,就是如何用一套轻量、可复现、适合内网/边缘/测试环境的方式,把 Kubernetes 底座、私有 CA、入口网关和多集群管理平台串起来。核心思路是:

  • 用 k3s 作为轻量级 Kubernetes 底座;
  • 用 cert-manager + step-ca 构建私有证书管理体系;
  • 用 Traefik 作为统一入口网关;
  • 用 Rancher 作为多集群管理和权限控制中枢;
  • 最后把业务节点接入 Rancher,形成管理集群与业务集群分离的结构。

1.2 平台目标

搭建完成后的平台具备以下能力:

  1. 轻量 Kubernetes 集群:基于 k3s,单节点即可运行,资源占用低,适合开发、测试、边缘和小型生产环境。
  2. 统一证书管理:支持自签 CA 和 step-ca 私有 CA 两种方案,能够自动签发、续期证书,并为 Ingress 提供默认 TLS 证书。
  3. 统一入口网关:通过 Traefik 暴露内部服务,所有 Web 服务统一走 HTTPS,支持通配符证书。
  4. 多集群管理:通过 Rancher 统一管理本地集群和下游业务集群,支持节点注册、权限控制、应用商店等。
  5. 业务集群接入:业务节点可以独立加入 Rancher,形成管理平面与业务平面分离的部署结构。

1.3 整体架构

本文搭建的平台整体分为四层:

层级组件作用
基础设施层k3s轻量级 Kubernetes 集群,承载所有工作负载
证书层cert-manager、step-ca、step-issuer私有 CA、证书签发、自动续期
入口层TraefikIngress Controller,统一 HTTPS 入口
管理层Rancher多集群管理、权限控制、应用商店
业务层下游 k3s 节点运行业务工作负载,由 Rancher 纳管

整体流程是:

  1. 先搭建管理节点 k3s;
  2. 在管理节点上部署 cert-manager 和 step-ca,建立私有证书体系;
  3. 配置 Traefik 默认证书,实现全站 HTTPS;
  4. 部署 Rancher,并通过私有 CA 证书暴露 Web 界面;
  5. 在 Rancher 中创建业务集群,并将业务节点注册进集群;
  6. 业务集群同样接入 step-ca,实现证书体系统一。

二、核心组件介绍

在正式动手之前,先把平台会用到的几个核心组件讲清楚,理解它们各自的定位,后续搭建时才能明白“为什么需要这个组件“。

2.4 什么是 k3s

k3s 是由 Rancher Labs(现 SUSE)开发的轻量级 Kubernetes 发行版,专为资源受限的环境设计。它将完整的 Kubernetes 功能打包到一个小于 100MB 的二进制文件中,极大降低了 Kubernetes 的部署和运维门槛。

k3s 的核心特点:

  • 轻量级:单二进制文件,内存占用低(约 512MB 即可运行)
  • 易于安装:一条命令即可完成部署,启动时间短
  • 高度兼容:完全符合 CNCF 认证的 Kubernetes 标准
  • 内置组件:自带 containerd、CoreDNS、Traefik Ingress、Local Path Provisioner 等
  • 适用场景:边缘计算、IoT 设备、CI/CD 环境、开发测试、小型生产环境

为什么叫 k3s?因为 Kubernetes 通常缩写为 K8s(K + 8 个字母 + s),而 k3s 是它的“轻量版“,大约是 K8s 一半的大小,所以用 3 代替了 8。

2.5 什么是 Rancher

Rancher 是供采用容器的团队使用的完整软件堆栈。它解决了管理多个 Kubernetes 集群的运营和安全挑战,并为 DevOps 团队提供用于运行容器化工作负载的集成工具。

用更简单的话说:

  • Kubernetes 像一群“养在不同圈里的牛“(很多服务器/集群/应用)
  • Rancher 就像“牧场主的管理平台“,让你在一个网页界面里:
    • 查看所有集群的状态
    • 创建/删除应用
    • 管理权限(谁能操作什么)
    • 进行升级、配置、监控入口等操作

Rancher 的核心能力:

  • 多集群管理:统一管理分布在不同基础设施(本地、云端、边缘)上的 Kubernetes 集群
  • 统一身份认证:支持 LDAP、AD、GitHub 等多种认证方式,RBAC 细粒度权限控制
  • 应用商店:内置 Helm 应用商店,一键部署常用应用
  • 安全策略:Pod 安全策略、网络策略、秘密管理等
  • 监控日志:集成 Prometheus、Grafana、EFK 等可观测性工具

2.6 什么是 Traefik

Traefik 是一个现代化的 HTTP 反向代理和负载均衡器,专为微服务和容器化环境设计。它是 k3s 默认内置的 Ingress Controller,负责把集群外部的流量路由到集群内部的服务。

Traefik 的核心特点:

  • 自动服务发现:原生对接 Kubernetes Ingress、IngressRoute、Gateway API 等资源,服务变更后自动更新路由,无需手动 reload
  • 动态配置:支持通过 CRD(如 IngressRoute、Middleware、TLSStore)动态调整路由、中间件和 TLS 配置
  • 自动 HTTPS:内置 ACME 支持,可自动申请和续期 Let’s Encrypt 证书,也支持挂载自定义证书
  • 中间件丰富:限流、重试、熔断、认证、重定向、压缩等开箱即用
  • 可观测性:内置 Prometheus 指标、访问日志、Tracing 支持
  • 轻量部署:单个二进制文件,启动快,资源占用低,与 k3s 天然契合

在本文中的作用:

Traefik 是整个平台的统一入口网关。Rancher、step-ca、Gitea 等所有 Web 服务都通过 Ingress 暴露,由 Traefik 统一监听 80/443 端口并根据域名转发。我们会在第五章为它配置默认 TLS 证书,这样所有 Ingress 都能自动使用我们签发的私有证书,实现全站 HTTPS。

2.7 什么是 cert-manager

cert-manager 是 Kubernetes 生态中最流行的证书管理工具,由 Jetstack 开发并捐赠给 CNCF。它能够自动化证书的签发、续期和管理。

cert-manager 的核心功能:

  • 支持多种证书签发源:Let’s Encrypt、Vault、Venafi、自签证书、私有 CA 等
  • 自动续期:证书到期前自动重新签发
  • 与 Ingress 集成:通过 annotation 自动为 Ingress 资源签发证书
  • CRD 化管理:Issuer、ClusterIssuer、Certificate 等自定义资源

2.8 什么是 step-ca

step-ca(Smallstep Certificates)是一个开源的私有证书颁发机构(CA)工具,由 Smallstep 公司开发。它可以作为企业内部的私有 CA,为服务间通信(mTLS)、开发环境、IoT 设备等签发证书。

step-ca 的特点:

  • 开源轻量:基于 Go 语言开发,部署简单
  • 支持多种 provisioner:JWK、ACME、X.509、OIDC 等
  • 证书生命周期管理:签发、续期、吊销
  • 与 cert-manager 集成:通过 step-issuer 插件实现 Kubernetes 证书自动化

三、k3s 轻量级 Kubernetes 集群部署

k3s 是整个平台的底座,我们将在其上部署 Rancher、cert-manager 等所有组件。本节将从零开始完成 k3s 的安装与基础配置。

3.1 环境准备与代理设置

由于国内网络环境的特殊性,直接从 GitHub 下载 k3s 可能会比较慢。我们先配置代理加速下载。

➜  ~ export ALL_PROXY=socks5://127.0.0.1:1080
➜  ~ export NO_PROXY=localhost,127.0.0.1,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16,.svc,.cluster.local,.internal

环境变量说明:

变量作用示例值说明
ALL_PROXY设置全局代理,所有协议的网络请求都走该代理socks5://127.0.0.1:1080 表示本地 SOCKS5 代理
NO_PROXY设置不走代理的地址列表,用于排除内网和集群内部通信包含 localhost、私有网段、K8s 内部域名等

注意:NO_PROXY 中务必包含 Kubernetes 内部域名后缀 .svc、.cluster.local 以及你自定义的内网域名,否则集群内部服务间通信可能会被错误地路由到代理,导致连接失败。

3.2 安装 k3s

k3s 官方提供了一键安装脚本,执行即可完成部署。

➜  ~ curl -sfL https://get.k3s.io | sh -
[INFO]  Finding release for channel stable
[INFO]  Using v1.36.4+k3s1 as release
[INFO]  Downloading hash https://github.com/k3s-io/k3s/releases/download/v1.36.4%2Bk3s1/sha256sum-amd64.txt
[INFO]  Downloading binary https://github.com/k3s-io/k3s/releases/download/v1.36.4%2Bk3s1/k3s
[INFO]  Verifying binary download
[INFO]  Installing k3s to /usr/local/bin/k3s
[INFO]  Skipping installation of SELinux RPM
[INFO]  Creating /usr/local/bin/kubectl symlink to k3s
[INFO]  Creating /usr/local/bin/crictl symlink to k3s
[INFO]  Creating /usr/local/bin/ctr symlink to k3s
[INFO]  Creating killall script /usr/local/bin/k3s-killall.sh
[INFO]  Creating uninstall script /usr/local/bin/k3s-uninstall.sh
[INFO]  env: Creating environment file /etc/systemd/system/k3s.service.env
[INFO]  systemd: Creating service file /etc/systemd/system/k3s.service
[INFO]  systemd: Enabling k3s unit
Created symlink /etc/systemd/system/multi-user.target.wants/k3s.service → /etc/systemd/system/k3s.service.
[INFO]  systemd: Starting k3s

从输出可以看到,安装脚本做了以下工作:

  1. 查找稳定版(stable channel)的最新版本(v1.36.4+k3s1)
  2. 下载并校验二进制文件
  3. 将 k3s 安装到 /usr/local/bin/k3s
  4. 创建 kubectl、crictl、ctr 的软链接(k3s 内置了这些工具)
  5. 创建 killall 和 uninstall 脚本,方便后续管理
  6. 创建 systemd 服务并设置开机自启
  7. 启动 k3s 服务

3.3 验证服务状态

安装完成后,使用 systemctl 检查 k3s 服务是否正常运行。

➜  ~ sudo systemctl status k3s
● k3s.service - Lightweight Kubernetes
     Loaded: loaded (/etc/systemd/system/k3s.service; enabled; preset: disabled)
     Active: active (running) since Mon 2026-09-07 15:25:39 CST; 10min ago
 Invocation: 99095155af9d409e89fa2a896144c1d2
       Docs: https://k3s.io
    Process: 731854 ExecStartPre=/sbin/modprobe br_netfilter (code=exited, status=0/SUCCESS)
    Process: 731855 ExecStartPre=/sbin/modprobe overlay (code=exited, status=0/SUCCESS)
   Main PID: 731857 (k3s-server)
      Tasks: 90
     Memory: 1.1G (peak: 1.5G)
        CPU: 1min 7.383s
     CGroup: /system.slice/k3s.service
             ├─731857 "/usr/local/bin/k3s server"
             ├─731882 "containerd "
             ├─732845 /var/lib/rancher/k3s/data/5a9973ddf4c7ec074f657c06287e0e6a07a24ecafd6d326827f70ef1e95bdd2d/bin/containerd-shim-runc-v2 -namespace k8s.io -id 56abeed741a5c42ccff0aca25f4d>
             ├─732863 /var/lib/rancher/k3s/data/5a9973ddf4c7ec074f657c06287e0e6a07a24ecafd6d326827f70ef1e95bdd2d/bin/containerd-shim-runc-v2 -namespace k8s.io -id f456ac1b99ad155643207305ec80>
             ├─732902 /var/lib/rancher/k3s/data/5a9973ddf4c7ec074f657c06287e0e6a07a24ecafd6d326827f70ef1e95bdd2d/bin/containerd-shim-runc-v2 -namespace k8s.io -id ef0c3f22d2b5e0c871226f3c6d21>
             ├─733729 /var/lib/rancher/k3s/data/5a9973ddf4c7ec074f657c06287e0e6a07a24ecafd6d326827f70ef1e95bdd2d/bin/containerd-shim-runc-v2 -namespace k8s.io -id 4d6ae7b54e0dcdd0017e6443dd28>
             └─733818 /var/lib/rancher/k3s/data/5a9973ddf4c7ec074f657c06287e0e6a07a24ecafd6d326827f70ef1e95bdd2d/bin/containerd-shim-runc-v2 -namespace k8s.io -id 2a6ed6a4dfaff98ca662084bb5f7>

9月 07 15:26:42 nuc k3s[731857]: I0907 15:26:42.214293  731857 resource_quota_monitor.go:229] "QuotaMonitor created object count evaluator" resource="contentitems.hub.traefik.io"
9月 07 15:26:42 nuc k3s[731857]: I0907 15:26:42.214821  731857 shared_informer.go:402] "Waiting for caches to sync"
9月 07 15:26:43 nuc k3s[731857]: I0907 15:26:43.396087  731857 shared_informer.go:402] "Waiting for caches to sync"
9月 07 15:26:43 nuc k3s[731857]: I0907 15:26:43.415361  731857 shared_informer.go:409] "Caches are synced"
9月 07 15:26:44 nuc k3s[731857]: I0907 15:26:44.497106  731857 shared_informer.go:409] "Caches are synced"
9月 07 15:26:45 nuc k3s[731857]: time="2026-09-07T15:26:45+08:00" level=info msg="Event occurred" apiVersion=v1 fieldPath= kind=Service logger=service-lb-controller message="Updated LoadBalan>
9月 07 15:35:37 nuc k3s[731857]: time="2026-09-07T15:35:37+08:00" level=info msg="COMPACT compactRev=1425 targetCompactRev=2088 currentRev=3088"
9月 07 15:35:37 nuc k3s[731857]: time="2026-09-07T15:35:37+08:00" level=info msg="COMPACT deleted 616 rows from 663 revisions in 60.925125ms - compacted to 2088/3088"
9月 07 15:35:37 nuc k3s[731857]: time="2026-09-07T15:35:37+08:00" level=info msg="COMPACT compacted from 1425 to 2088 in 1 transactions over 61ms"
9月 07 15:35:37 nuc k3s[731857]: I0907 15:35:37.853628  731857 cidrallocator.go:278] updated ClusterIP allocator for Service CIDR 10.43.0.0/16

从输出可以看到几个关键信息:

  • 服务状态为 active (running),已正常运行 10 分钟
  • k3s 内置了 containerd 作为容器运行时(不需要额外安装 Docker)
  • 启动时自动加载了 br_netfilter 和 overlay 内核模块(Kubernetes 必需)
  • 内存占用约 1.1GB,充分体现了 k3s 的轻量特性

3.4 安装 Helm

Helm 是 Kubernetes 的包管理器,类似 Linux 的 apt/yum。我们后续部署 Rancher、cert-manager 等组件都会通过 Helm Chart 来完成。

Arch Linux 系统安装方式:

➜  ~ sudo pacman -S helm
正在解析依赖关系...
正在查找软件包冲突...

软件包 (1) helm-4.2.2-1

下载大小:      18.51 MiB
全部安装大小:  78.48 MiB

:: 进行安装吗? [Y/n]
:: 正在获取软件包......
 helm-4.2.2-1-x86_64                                                                     18.5 MiB  6.27 MiB/s 00:03 [#####################################################################] 100%
(1/1) 正在检查密钥环里的密钥                                                                                        [#####################################################################] 100%
(1/1) 正在检查软件包完整性                                                                                          [#####################################################################] 100%
(1/1) 正在加载软件包文件                                                                                            [#####################################################################] 100%
(1/1) 正在检查文件冲突                                                                                              [#####################################################################] 100%
(1/1) 正在检查可用存储空间                                                                                          [#####################################################################] 100%
:: 正在处理软件包的变化...
(1/1) 正在安装 helm                                                                                                 [#####################################################################] 100%
:: 正在运行事务后钩子函数...
(1/1) Arming ConditionNeedsUpdate...

Ubuntu/Debian 系统安装方式:

sudo apt-get install curl gpg apt-transport-https --yes
curl -fsSL https://packages.buildkite.com/helm-linux/helm-debian/gpgkey | gpg --dearmor | sudo tee /usr/share/keyrings/helm.gpg > /dev/null
echo "deb [signed-by=/usr/share/keyrings/helm.gpg] https://packages.buildkite.com/helm-linux/helm-debian/any/ any main" | sudo tee /etc/apt/sources.list.d/helm-stable-debian.list
sudo apt-get update
sudo apt-get install helm

也可以直接从 Helm 官方 GitHub Release 页面 下载对应平台的二进制文件,解压后放到 PATH 目录即可。

3.5 配置 kubeconfig

k3s 安装完成后,默认会将 kubeconfig 文件写入 /etc/rancher/k3s/k3s.yaml,但该文件权限为 root 所有。为了让普通用户能使用 kubectl,需要进行以下配置。

首先,修改 k3s 配置,让 kubeconfig 文件具有可读权限:

➜  ~ echo 'write-kubeconfig-mode: "0644"' | sudo tee /etc/rancher/k3s/config.yaml
write-kubeconfig-mode: "0644"
➜  ~ sudo systemctl restart k3s

然后将 kubeconfig 复制到用户目录:

➜  ~ mkdir -p ~/.kube
➜  ~ sudo cp /etc/rancher/k3s/k3s.yaml ~/.kube/config
➜  ~ chmod 600 ~/.kube/config
chmod: 更改 '/home/kali-team/.kube/config' 的权限: 不允许的操作
➜  ~ id kali-team
uid=1000(kali-team) gid=984(users) 组=984(users),998(wheel),969(docker),967(libvirt)
➜  ~ sudo chown kali-team:users ~/.kube/config
➜  ~ chmod 600 ~/.kube/config
➜  ~ sudo mv /etc/rancher/k3s/k3s.yaml /etc/rancher/k3s/k3s.yaml.bak
➜  ~ sudo ln -s /home/ubuntu/.kube/config /etc/rancher/k3s/k3s.yaml
➜  ~ kubectl get nodes
NAME   STATUS   ROLES           AGE   VERSION
nuc    Ready    control-plane   78m   v1.36.4+k3s1
➜  ~ sudo chmod 644 /etc/rancher/k3s/config.yaml.d/50-rancher.yaml

关键步骤说明:

  1. 创建 ~/.kube 目录,这是 kubectl 默认查找 kubeconfig 的位置
  2. 将 k3s 生成的配置文件复制到用户目录
  3. 修改文件所有者为当前用户(因为是 sudo 复制的,所有者为 root)
  4. 设置文件权限为 600(仅所有者可读写,符合 kubeconfig 安全规范)
  5. 将原文件备份并创建软链接,保持 k3s 默认路径的兼容性

看到 kubectl get nodes 返回节点状态为 Ready,说明集群已经正常运行。

3.6 配置镜像加速器

在国内环境中,直接从 Docker Hub、gcr.io 等国外镜像仓库拉取镜像可能会很慢或失败。k3s 支持通过 registries.yaml 配置镜像代理(镜像重写)。

创建配置文件 /etc/rancher/k3s/registries.yaml:

mirrors:
  docker.io:
    endpoint:
      - "https://docker.m.daocloud.io"
  quay.io:
    endpoint:
      - "https://quay.m.daocloud.io"
  gcr.io:
    endpoint:
      - "https://gcr.m.daocloud.io"
  ghcr.io:
    endpoint:
      - "https://ghcr.m.daocloud.io"
  registry.k8s.io:
    endpoint:
      - "https://k8s.m.daocloud.io"

配置说明:

镜像仓库代理地址用途
docker.iodocker.m.daocloud.ioDocker Hub,最常用的镜像仓库
quay.ioquay.m.daocloud.ioRed Hat 旗下的镜像仓库
gcr.iogcr.m.daocloud.ioGoogle Container Registry
ghcr.ioghcr.m.daocloud.ioGitHub Container Registry
registry.k8s.iok8s.m.daocloud.ioKubernetes 官方镜像仓库

配置完成后重启 k3s 使配置生效:

sudo systemctl restart k3s

验证所有系统 Pod 是否正常运行:

➜  ~ kubectl get pods -A
NAMESPACE     NAME                                      READY   STATUS      RESTARTS        AGE
kube-system   coredns-54996dc9b4-tsz6g                  1/1     Running     0               67m
kube-system   helm-install-traefik-c25nh                0/1     Completed   1 (2m51s ago)   67m
kube-system   helm-install-traefik-crd-lx65s            0/1     Completed   0               67m
kube-system   local-path-provisioner-77b9867795-5r8bw   1/1     Running     0               67m
kube-system   metrics-server-6dc596dfb8-tm782           1/1     Running     0               67m
kube-system   svclb-traefik-1ea9c1dc-sfk7z              2/2     Running     0               2m48s
kube-system   traefik-59b7647586-8m94p                  1/1     Running     0               2m48s
➜  ~ kubectl get nodes
NAME   STATUS   ROLES           AGE   VERSION
nuc    Ready    control-plane   67m   v1.36.4+k3s1
➜  ~

k3s 默认内置了以下核心组件:

  • CoreDNS:集群 DNS 服务,负责服务发现
  • Traefik:Ingress Controller,负责 HTTP/HTTPS 入口路由
  • Metrics Server:资源指标采集,为 HPA 和 kubectl top 提供数据
  • Local Path Provisioner:本地存储供应者,提供基于主机路径的 PV

3.7 设置系统代理(可选)

如果你的环境需要通过代理访问外网(比如下载镜像、拉取 Helm Chart 等),可以为 k3s 服务和 containerd 配置系统级代理。

注意:安装完成后建议取消代理,避免业务请求也走代理导致各种莫名其妙的网络问题。

➜  ~ sudo mkdir -p /etc/systemd/system/k3s.service.d/
➜  ~ sudo vim /etc/systemd/system/k3s.service.d/http-proxy.conf
➜  ~ sudo vim /etc/systemd/system/k3s.service.env
➜  ~ sudo mkdir -p /etc/systemd/system/containerd.service.d/
➜  ~ sudo vim /etc/systemd/system/containerd.service.d/http-proxy.conf
➜  ~ sudo systemctl daemon-reload
➜  ~ sudo systemctl restart k3s
➜  ~

http-proxy.conf 配置内容:

[Service]
Environment="HTTP_PROXY=socks5://127.0.0.1:1080"
Environment="HTTPS_PROXY=socks5://127.0.0.1:1080"
Environment="NO_PROXY=localhost,127.0.0.1,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16,.svc,.cluster.local,.internal"

k3s.service.env 配置内容:

HTTPS_PROXY='socks5://127.0.0.1:1080'

配置方式说明:

systemd 的 drop-in 配置目录(.d/)允许你在不修改原始 service 文件的情况下扩展或覆盖服务配置。这是 systemd 推荐的服务自定义方式,升级时不会丢失配置。

文件作用
k3s.service.d/http-proxy.conf为 k3s 服务进程设置代理环境变量
k3s.service.envk3s 读取的环境变量文件,同样可以设置代理
containerd.service.d/http-proxy.conf为 containerd 容器运行时设置代理(影响镜像拉取)

配置完成后需要执行 systemctl daemon-reload 让 systemd 重新加载配置,然后重启 k3s 服务。


四、证书管理体系建设

在 DevSecOps 平台中,证书是安全通信的基石。无论是服务间的 mTLS、Web 界面的 HTTPS 访问,还是 API 调用的身份认证,都离不开 TLS 证书。我们将使用 cert-manager 作为统一的证书管理工具,并提供两种 CA 方案供选择。

本章推荐阅读顺序:

  1. 4.1 安装 cert-manager:两种方案共用的基础设施,必须先装。
  2. 4.2 方案一:内置自签证书:最简方案,适合快速搭建开发测试环境。
  3. 4.3 方案二:step-ca 私有 CA:功能更完整,适合生产环境。

两个方案二选一即可,后续第五章的 Traefik 默认证书配置会根据你选择的方案提供对应的配置项。

4.1 安装 cert-manager

首先添加 Jetstack 的 Helm 仓库并安装 cert-manager。

➜  ~ helm repo add jetstack https://charts.jetstack.io
"jetstack" has been added to your repositories
➜  ~ helm repo update
Hang tight while we grab the latest from your chart repositories...
...Successfully got an update from the "rancher-stable" chart repository
...Successfully got an update from the "jetstack" chart repository
Update Complete. ⎈Happy Helming!⎈
➜  ~ helm upgrade --install cert-manager jetstack/cert-manager \
  --namespace cert-manager \
  --create-namespace \
  --set crds.enabled=true
Release "cert-manager" does not exist. Installing it now.
NAME: cert-manager
LAST DEPLOYED: Mon Sep  7 15:48:42 2026
NAMESPACE: cert-manager
STATUS: deployed
REVISION: 1
DESCRIPTION: Install complete
TEST SUITE: None
NOTES:
cert-manager v1.21.1 has been deployed successfully!

In order to begin issuing certificates, you will need to set up a ClusterIssuer
or Issuer resource (for example, by creating a 'letsencrypt-staging' issuer).

More information on the different types of issuers and how to configure them
can be found in our documentation:

https://cert-manager.io/docs/configuration/

For information on how to configure cert-manager to automatically provision
Certificates for Ingress resources, take a look at the `ingress-shim`
documentation:

https://cert-manager.io/docs/usage/ingress/

For information on how to configure cert-manager to automatically provision
Certificates for Gateway API resources, take a look at the `gateway resource`
documentation:

https://cert-manager.io/docs/usage/gateway/
➜  ~ kubectl -n cert-manager get po
NAME                                       READY   STATUS    RESTARTS   AGE
cert-manager-689c4c5575-j56vb              1/1     Running   0          40s
cert-manager-cainjector-6fbb9c8cd6-qw2wc   1/1     Running   0          40s
cert-manager-webhook-646c95c5ff-wp5nq      1/1     Running   0          40s

helm upgrade –install 命令参数说明:

参数含义
upgrade --install如果 release 不存在则安装,存在则升级(幂等操作,推荐使用)
cert-managerRelease 名称,即本次部署的实例名
jetstack/cert-managerChart 来源,格式为 仓库名/Chart名
--namespace cert-manager指定部署到的命名空间
--create-namespace如果命名空间不存在则自动创建
--set crds.enabled=true设置 Chart 值,启用 CRD(自定义资源定义)的安装

cert-manager 部署了三个核心组件:

  • cert-manager:核心控制器,监听 Certificate 等 CRD 资源,处理证书签发和续期
  • cert-manager-cainjector:CA 注入器,将 CA 证书注入到 webhook 和 API 服务的配置中
  • cert-manager-webhook:Webhook 服务,提供准入控制和动态准入插件功能

4.2 方案一:内置自签证书

这是最简单的方案,使用 cert-manager 内置的 SelfSigned Issuer 创建根 CA,再基于根 CA 签发业务证书。适合快速搭建开发测试环境。

步骤 1:创建 SelfSigned ClusterIssuer

SelfSigned Issuer 使用自签名的方式签发证书,通常用于创建根 CA 证书。

# 1. 创建 SelfSigned ClusterIssuer
➜  ~ cat <<EOF | kubectl apply -f -
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: selfsigned-issuer
spec:
  selfSigned: {}
EOF
clusterissuer.cert-manager.io/selfsigned-issuer created
➜  ~

ClusterIssuer vs Issuer 的区别:

资源类型作用范围适用场景
ClusterIssuer集群级,所有命名空间都可以引用集群统一的证书签发源,如公共 CA、全局私有 CA
Issuer命名空间级,只能被同一命名空间的 Certificate 引用各团队/项目独立的证书签发策略

步骤 2:创建根 CA 证书

使用 SelfSigned Issuer 签发一张自签名的根 CA 证书。

# 2. 创建根 CA 证书
➜  ~ cat <<EOF | kubectl apply -f -
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
  name: kali-team-root-ca
  namespace: cert-manager
spec:
  commonName: kali-team-root-ca
  isCA: true
  duration: 87600h
  renewBefore: 720h
  issuerRef:
    kind: ClusterIssuer
    name: selfsigned-issuer
  privateKey:
    algorithm: ECDSA
    size: 256
  secretName: kali-team-root-ca
EOF
certificate.cert-manager.io/kali-team-root-ca created
➜  ~

Certificate 资源关键字段说明:

字段含义示例值说明
commonName证书通用名称(CN)根 CA 的标识名
isCA: true标记这是一张 CA 证书,可用于签发其他证书根 CA 必须设置为 true
duration证书有效期87600h = 10 年
renewBefore到期前多久自动续期720h = 30 天
issuerRef引用的签发者指定使用哪个 Issuer/ClusterIssuer 签发
privateKey.algorithm私钥算法ECDSA 比 RSA 更轻量高效
privateKey.size私钥长度ECDSA-256 提供相当于 RSA-3072 的安全强度
secretName证书存储的 Secret 名称签发后证书和私钥会存入该 Secret

步骤 3:等待 CA 证书就绪

使用 kubectl wait 命令等待证书签发完成。

# 3. 等待 CA 证书 Ready
➜  ~ kubectl wait --for=condition=Ready certificate/kali-team-root-ca -n cert-manager --timeout=60s
certificate.cert-manager.io/kali-team-root-ca condition met
➜  ~

kubectl wait 命令参数说明:

参数含义
--for=condition=Ready等待资源的 Ready 条件变为 true
certificate/kali-team-root-ca资源类型和名称,格式为 类型/名称
-n cert-manager指定命名空间
--timeout=60s超时时间,超过后命令返回失败

步骤 4:创建基于 CA 的 ClusterIssuer

有了根 CA 证书后,创建一个基于该 CA 的 ClusterIssuer,用于后续签发业务证书。

# 4. 创建基于 CA 的 ClusterIssuer
➜  ~ cat <<EOF | kubectl apply -f -
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: kali-team-ca
spec:
  ca:
    secretName: kali-team-root-ca
EOF
clusterissuer.cert-manager.io/kali-team-ca created
➜  ~

这个 ClusterIssuer 会从 kali-team-root-ca Secret 中读取根 CA 证书和私钥,然后用它来签发业务证书。

步骤 5:创建 Traefik 默认证书

为 Traefik Ingress Controller 创建一张通配符证书,作为所有 Ingress 的默认 TLS 证书。

# 5. 直接创建 Traefik 默认证书 Secret
➜  ~ cat <<EOF | kubectl apply -f -
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
  name: internal-default-cert
  namespace: kube-system
spec:
  secretName: internal-default-cert
  commonName: kali-team
  dnsNames:
    - "*.kali-team.internal"
    - "*.*.kali-team.internal"
    - "kali-team.internal"
  duration: 87600h
  renewBefore: 720h
  issuerRef:
    kind: ClusterIssuer
    name: kali-team-ca
    group: cert-manager.io
  privateKey:
    algorithm: RSA
    size: 2048
EOF
certificate.cert-manager.io/internal-default-cert created
➜  ~

通配符证书说明:

DNS 名称覆盖范围
kali-team.internal根域名本身
*.kali-team.internal一级子域名,如 rancher.kali-team.internal
*.*.kali-team.internal二级子域名,如 app.dev.kali-team.internal

注意:证书存储在 kube-system 命名空间,因为 Traefik 默认部署在该命名空间下,只能读取同命名空间的 Secret。

步骤 6:验证证书签发结果

➜  ~ kubectl -n kube-system get certificate
NAME                    READY   SECRET                  AGE
internal-default-cert   True    internal-default-cert   41s
➜  ~
# 6. 等待证书签发
➜  ~ kubectl wait --for=condition=Ready certificate/internal-default-cert -n kube-system --timeout=120s
certificate.cert-manager.io/internal-default-cert condition met
➜  ~
# 7. 验证 Secret 已创建
➜  ~ kubectl get secret internal-default-cert -n kube-system
NAME                    TYPE                DATA   AGE
internal-default-cert   kubernetes.io/tls   3      5m18s
➜  ~

Secret 类型为 kubernetes.io/tls,包含 3 个数据项:tls.crt(证书链)、tls.key(私钥)和 ca.crt(CA 证书)。

步骤 7:导出根 CA 证书并导入系统信任库

为了让操作系统信任我们签发的证书(不会出现浏览器警告),需要将根 CA 证书导入系统信任库。

➜  ~ kubectl -n cert-manager get secret kali-team-root-ca \
  -o jsonpath='{.data.ca\.crt}' | base64 -d > kali-team-root-ca.crt
➜  ~ cat kali-team-root-ca.crt
-----BEGIN CERTIFICATE-----
...
-----END CERTIFICATE-----
➜  ~

导入到系统信任库(Arch Linux 系统):

➜  ~ sudo cp kali-team-root-ca.crt /etc/ca-certificates/trust-source/anchors/nuc.crt
➜  ~ sudo trust extract-compat

验证证书信息:

➜  ~ kubectl -n kube-system get secret internal-default-cert \
  -o jsonpath='{.data.ca\.crt}' | base64 -d | openssl x509 -noout -subject -issuer -dates
subject=CN=kali-team-root-ca
issuer=CN=kali-team-root-ca
notBefore=Sep  7 08:14:02 2026 GMT
notAfter=Sep  4 08:14:02 2036 GMT
➜  ~

从输出可以看到,根 CA 证书是自签名的(subject 和 issuer 相同),有效期为 10 年。

最后查看所有 ClusterIssuer 状态:

➜  ~ kubectl get clusterissuer
NAME                READY   AGE
kali-team-ca        True    13m
selfsigned-issuer   True    16m
➜  ~

两个 ClusterIssuer 都已就绪,分别是:

  • selfsigned-issuer:自签名签发者,仅用于创建根 CA
  • kali-team-ca:基于根 CA 的签发者,用于签发业务证书

4.3 方案二:step-ca 私有 CA

如果你需要更完善的私有 CA 功能(证书吊销、细粒度权限、ACME 协议支持等),可以选择 step-ca 方案。step-ca 是 Smallstep 推出的开源私有 CA 解决方案,功能更强大,适合生产环境使用。

注意:方案一和方案二二选一即可。如果选择方案二,需要确保 cert-manager 已经安装。

首先确认 cert-manager 运行状态:

➜  ~ kubectl get pods -n cert-manager
NAME                                       READY   STATUS    RESTARTS   AGE
cert-manager-689c4c5575-j56vb              1/1     Running   0          2d2h
cert-manager-cainjector-6fbb9c8cd6-qw2wc   1/1     Running   0          2d2h
cert-manager-webhook-646c95c5ff-wp5nq      1/1     Running   0          2d2h
➜  ~ kubectl get crd | grep cert-manager
certificaterequests.cert-manager.io                               2026-09-07T07:48:42Z
certificates.cert-manager.io                                      2026-09-07T07:48:42Z
challenges.acme.cert-manager.io                                   2026-09-07T07:48:42Z
clusterissuers.cert-manager.io                                    2026-09-07T07:48:42Z
issuers.cert-manager.io                                           2026-09-07T07:48:42Z
orders.acme.cert-manager.io                                       2026-09-07T07:48:42Z

可以看到 cert-manager 安装后自动注册了 6 个 CRD,覆盖了证书请求、证书、挑战、集群签发者、签发者和订单等资源类型。

接下来添加 Smallstep 的 Helm 仓库:

➜  ~ helm repo add smallstep https://smallstep.github.io/helm-charts
"smallstep" has been added to your repositories
➜  ~ helm repo update
Hang tight while we grab the latest from your chart repositories...
...Successfully got an update from the "smallstep" chart repository
...Successfully got an update from the "rancher-stable" chart repository
...Successfully got an update from the "jetstack" chart repository
Update Complete. ⎈Happy Helming!⎈
➜  ~

4.3.1 安装 step-issuer

step-issuer 是 cert-manager 的外部签发者插件,它让 cert-manager 能够通过 step-ca 来签发证书。

➜  ~ helm upgrade --install step-issuer \
  smallstep/step-issuer \
  -n smallstep \
  --create-namespace
Release "step-issuer" does not exist. Installing it now.
NAME: step-issuer
LAST DEPLOYED: Wed Sep  9 18:22:16 2026
NAMESPACE: smallstep
STATUS: deployed
REVISION: 1
DESCRIPTION: Install complete
TEST SUITE: None
NOTES:
⚙️   Thanks for installing step-issuer.

step-issuer is ideal for issuing certificates
from your own private Certificate Authority (CA).

To start issuing certificates, you will need:

👉 A cert-manager installation
👉 A step-ca Certificate Authority (CA) or a Smallstep Certificate Manager authority
👉 A StepIssuer resource that links step-issuer to your CA

To continue, follow the instructions here:

https://u.step.sm/step-issuer

验证 step-issuer 运行状态:

➜  ~ kubectl get pods -n smallstep
NAME                           READY   STATUS    RESTARTS   AGE
step-issuer-6567b4fff4-f96vj   1/1     Running   0          61m
➜  ~

4.3.2 安装 step-certificates

step-certificates 是 step-ca 的核心服务,负责 PKI 初始化和证书签发。

➜  ~ helm upgrade --install step-certificates \
  smallstep/step-certificates \
  -n smallstep \
  --create-namespace
Release "step-certificates" does not exist. Installing it now.
NAME: step-certificates
LAST DEPLOYED: Wed Sep  9 21:08:30 2026
NAMESPACE: smallstep
STATUS: deployed
REVISION: 1
DESCRIPTION: Install complete
NOTES:
Thanks for installing Step CA.

1. Get the PKI and Provisioner secrets running these commands:
   kubectl get -n smallstep -o jsonpath='{.data.password}' secret/step-certificates-ca-password | base64 --decode
   kubectl get -n smallstep -o jsonpath='{.data.password}' secret/step-certificates-provisioner-password | base64 --decode
2. Get the CA URL and the root certificate fingerprint running this command:
   kubectl -n smallstep logs job.batch/step-certificates

3. Delete the configuration job running this command:
   kubectl -n smallstep delete job.batch/step-certificates
➜  ~

查看初始化 Job 的日志,获取 CA 信息:

➜  ~ kubectl -n step-ca logs job.batch/step-certificates
Welcome to Step Certificates configuration.

Configuring kubctl with service account...
Cluster "cfc" set.
User "bootstrap" set.
Context "cfc" created.
Switched to context "cfc".

Checking cluster permissions...
Checking for permission to create configmaps in step-ca namespace: yes
Checking for permission to create secrets in step-ca namespace: yes

Initializating the CA...

Generating root certificate... done!
Generating intermediate certificate... done!

✔ Root certificate: /home/step/certs/root_ca.crt
✔ Root private key: /home/step/secrets/root_ca_key
✔ Root fingerprint: f5d6518046eea544ee1e6b1eef4a6b4566e33a86a2a6cef1fed581b4cb734930
✔ Intermediate certificate: /home/step/certs/intermediate_ca.crt
✔ Intermediate private key: /home/step/secrets/intermediate_ca_key
✔ Database folder: /home/step/db
✔ Default configuration: /home/step/config/defaults.json
✔ Certificate Authority configuration: /home/step/config/ca.json

Your PKI is ready to go. To generate certificates for individual services see 'step help ca'.
FEEDBACK 😍 🍻
  The step utility is not instrumented for usage statistics. It does not phone
  home. But your feedback is extremely valuable. Any information you can provide
  regarding how you're using `step` helps. Please send us a sentence or two,
  good or bad at [email protected] or join GitHub Discussions
  https://github.com/smallstep/certificates/discussions and our Discord
  https://u.step.sm/discord.

Creating configmaps and secrets in step-ca namespace ...
configmap/step-certificates-config replaced
configmap/step-certificates-certs replaced
configmap/step-certificates-secrets replaced
secret/step-certificates-ca-password replaced
secret/step-certificates-provisioner-password replaced
configmap/step-certificates-config labeled
configmap/step-certificates-certs labeled

从初始化日志可以看到 step-ca 自动创建了完整的 PKI 体系:

  • 根证书(Root CA):顶层信任锚,通常离线保存
  • 中间证书(Intermediate CA):实际用于签发业务证书的 CA,根证书只用来签中间证书
  • 配置文件:ca.json 是 CA 的主配置文件
  • 相关密钥和密码:存储在 Kubernetes Secret 中

初始化完成后输出的关键信息:

Step Certificates installed!

CA URL: https://step-certificates.step-ca.svc.cluster.local
CA Fingerprint: f5d6518046eea544ee1e6b1eef4a6b4566e33a86a2a6cef1fed581b4cb734930

4.3.3 添加 cert-manager Provisioner

step-ca 使用 Provisioner(签发者配置)来控制谁可以申请证书。我们需要为 cert-manager 创建一个专门的 JWK 类型 Provisioner。

进入 step-certificates Pod 进行配置:

➜  ~ kubectl exec -it -n smallstep step-certificates-0 -- sh
Defaulted container "step-certificates" out of: step-certificates, step-certificates-init (init)
~ $
~ $ cp config/ca.json /tmp/ca.json
~ $ step ca provisioner add cert-manager --type JWK --create --ca-config /tmp/ca.json
Please enter a password to encrypt the provisioner private key? [leave empty and we'll generate one]:
✔ Password: f7zl9rRbrBzujPSXT71k4Yd1TsLrBQyB
✔ CA Configuration: /tmp/ca.json

Success! Your `step-ca` config has been updated. To pick up the new configuration SIGHUP (kill -1 <pid>) or restart the step-ca process.
~ $

因为 config 目录下的 ca.json 是通过 ConfigMap 挂载的只读文件,所以先复制一份到临时目录进行修改,然后再将修改后的配置更新到 ConfigMap 中,最后重启 step-certificates 以加载新配置。

Provisioner 类型说明:

类型用途适用场景
JWK使用 JSON Web Key 对证书签名请求进行认证服务间调用、自动化工具(如 cert-manager)
ACME支持 ACME 协议(类似 Let’s Encrypt)需要自动证书管理的 HTTP 服务
OIDC使用 OpenID Connect 身份认证用户自助申请证书
X5C使用 X.509 客户端证书认证已有 PKI 体系的集成

查看 Provisioner 列表:

~ $ step ca provisioner list

从 ConfigMap 中验证 cert-manager Provisioner 是否已正确加载:

➜  ~ kubectl -n smallstep get configmap step-certificates-config \
  -o jsonpath="{.data['ca\.json']}" \
  | jq -r '.authority.provisioners[] | select(.name=="cert-manager") | {name,type,kid:.key.kid}'
{
  "name": "cert-manager",
  "type": "JWK",
  "kid": "3Fckyy_09lJ1hoJ-7dHIwLbmUffrzz42SIvifmrEjOw"
}

kid(Key ID) 是 JWK 中的密钥标识符,用于在多个密钥中标识具体使用哪一个。后续配置 StepClusterIssuer 时需要用到这个值。

4.3.4 创建密码 Secret

将 Provisioner 的私钥密码保存为 Kubernetes Secret,供 step-issuer 使用:

➜  ~ kubectl -n smallstep create secret generic step-certificates-cert-manager-password \
  --from-literal=password='f7zl9rRbrBzujPSXT71k4Yd1TsLrBQyB'
secret/step-certificates-cert-manager-password created
➜  ~ kubectl get secret -n smallstep step-certificates-cert-manager-password
NAME                                      TYPE     DATA   AGE
step-certificates-cert-manager-password   Opaque   1      11s
➜  ~
➜  ~ CA_ROOT_B64=$(kubectl -n smallstep get configmap step-certificates-certs \
  -o jsonpath="{.data['root_ca\.crt']}" | base64 -w 0)
➜  ~ echo ${CA_ROOT_B64}
LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJ3ekNDQVdtZ0F3SUJBZ0lRWmFMY3MwellLcENaWHI3UU01aHQyVEFLQmdncWhrak9QUVFEQWpCQU1Sb3cKR0FZRFZRUUtFeEZUZEdWd0lFTmxjblJwWm1sallYUmxjekVpTUNBR0ExVUVBeE1aVTNSbGNDQkRaWEowYVdacApZMkYwWlhNZ1VtOXZkQ0JEUVRBZUZ3MHlOakE1TURreE16QTRNekZhRncwek5qQTVNRFl4TXpBNE16RmFNRUF4CkdqQVlCZ05WQkFvVEVWTjBaWEFnUTJWeWRHbG1hV05oZEdWek1TSXdJQVlEVlFRREV4bFRkR1Z3SUVObGNuUnAKWm1sallYUmxjeUJTYjI5MElFTkJNRmt3RXdZSEtvWkl6ajBDQVFZSUtvWkl6ajBEQVFjRFFnQUV5UEdveGw4bApPd0FEMHhUSDBQQWVPNjBtU3lyRG9SOUh6UGdreWV0L0NSMTE4alJRNlZzWS9McHBrQW1uRTcwWXY2UHEyQ0gyCmFHVitFcXJZVjZxWldLTkZNRU13RGdZRFZSMFBBUUgvQkFRREFnRUdNQklHQTFVZEV3RUIvd1FJTUFZQkFmOEMKQVFFd0hRWURWUjBPQkJZRUZHTDVPc2FMWkFQenpuam5FazZFazNlMkU1eHFNQW9HQ0NxR1NNNDlCQU1DQTBnQQpNRVVDSVFDSHFscEJPbWRublJJZUpiWEdHUUU2UzVYS0NXaG5zSFNTa2dZL0dmMndjd0lnSTZJOGwrYUcyaitOCkRHWDh6Z0JNdTF2Ym5wRCtGcXVvbEhoL25hU2RicE09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K

kubectl create secret 命令参数说明:

参数含义
create secret generic创建普通类型的 Secret
step-certificates-cert-manager-passwordSecret 名称
--from-literal=password='...'从字面量创建键值对,key 为 password

4.3.5 创建 StepClusterIssuer

在创建 StepClusterIssuer 之前,需要先通过 Ingress 将 step-ca 服务暴露为 ca.kali-team.internal 域名,以便其他集群也能访问。

创建 StepClusterIssuer 资源,链接 cert-manager 和 step-ca:

➜  ~ cat <<EOF | kubectl apply -f -
apiVersion: certmanager.step.sm/v1beta1
kind: StepClusterIssuer
metadata:
  name: smallstep
spec:
  caBundle: >-
    LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJ3ekNDQVdtZ0F3SUJBZ0lRWmFMY3MwellLcENaWHI3UU01aHQyVEFLQmdncWhrak9QUVFEQWpCQU1Sb3cKR0FZRFZRUUtFeEZUZEdWd0lFTmxjblJwWm1sallYUmxjekVpTUNBR0ExVUVBeE1aVTNSbGNDQkRaWEowYVdacApZMkYwWlhNZ1VtOXZkQ0JEUVRBZUZ3MHlOakE1TURreE16QTRNekZhRncwek5qQTVNRFl4TXpBNE16RmFNRUF4CkdqQVlCZ05WQkFvVEVWTjBaWEFnUTJWeWRHbG1hV05oZEdWek1TSXdJQVlEVlFRREV4bFRkR1Z3SUVObGNuUnAKWm1sallYUmxjeUJTYjI5MElFTkJNRmt3RXdZSEtvWkl6ajBDQVFZSUtvWkl6ajBEQVFjRFFnQUV5UEdveGw4bApPd0FEMHhUSDBQQWVPNjBtU3lyRG9SOUh6UGdreWV0L0NSMTE4alJRNlZzWS9McHBrQW1uRTcwWXY2UHEyQ0gyCmFHVitFcXJZVjZxWldLTkZNRU13RGdZRFZSMFBBUUgvQkFRREFnRUdNQklHQTFVZEV3RUIvd1FJTUFZQkFmOEMKQVFFd0hRWURWUjBPQkJZRUZHTDVPc2FMWkFQenpuam5FazZFazNlMkU1eHFNQW9HQ0NxR1NNNDlCQU1DQTBnQQpNRVVDSVFDSHFscEJPbWRublJJZUpiWEdHUUU2UzVYS0NXaG5zSFNTa2dZL0dmMndjd0lnSTZJOGwrYUcyaitOCkRHWDh6Z0JNdTF2Ym5wRCtGcXVvbEhoL25hU2RicE09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K
  provisioner:
    kid: 3Fckyy_09lJ1hoJ-7dHIwLbmUffrzz42SIvifmrEjOw
    name: cert-manager
    passwordRef:
      key: password
      name: step-certificates-cert-manager-password
      namespace: smallstep
  url: https://ca.kali-team.internal
EOF

StepClusterIssuer 关键字段说明:

字段含义
caBundlestep-ca 的根 CA 证书(Base64 编码),用于验证 step-ca 服务的 TLS 证书
provisioner.kidJWK Provisioner 的密钥 ID
provisioner.nameProvisioner 名称
provisioner.passwordRef引用存储 Provisioner 密码的 Secret
urlstep-ca 服务的访问地址

验证 step-issuer 和 StepClusterIssuer 状态:

➜  ~ kubectl get pods -A | grep step-issuer
smallstep                   step-issuer-6c65fd4d77-hpvsd                          1/1     Running             0                107m
➜  ~
➜  ~ kubectl get stepclusterissuer smallstep
NAME        AGE
smallstep   9m26s
➜  ~

4.3.6 签发业务证书

使用 StepClusterIssuer 签发一张通配符证书到 kube-system 命名空间(Traefik 所在的命名空间)。

注意:证书需要签发到 Traefik 所在的命名空间,因为 Kubernetes 的 Secret 不能跨命名空间读取。如果 Rancher(cattle-system 命名空间)也需要使用证书,需要单独签发一份到对应命名空间。

➜  ~ cat <<EOF | kubectl apply -f -
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
  name: kali-team-internal
  namespace: kube-system
spec:
  secretName: kali-team-internal
  duration: 2160h
  renewBefore: 360h
  subject:
    organizations:
      - kali-team
  dnsNames:
    - kali-team.internal
    - "*.kali-team.internal"
  issuerRef:
    name: smallstep
    kind: StepClusterIssuer
    group: certmanager.step.sm
EOF
certificate.cert-manager.io/kali-team-internal created
➜  ~

Certificate 字段说明:

字段值说明
duration2160h证书有效期,90 天
renewBefore360h到期前 15 天自动续期
issuerRef.kindStepClusterIssuer引用的签发者类型为 StepClusterIssuer
issuerRef.groupcertmanager.step.sm外部签发者的 API 组

验证证书签发结果:

➜  ~ kubectl get certificate -n kube-system
NAME                 READY   SECRET                   AGE
kali-team-internal   True    kali-team-internal       22s
➜  ~

使用 openssl 查看证书详情:

➜  ~ kubectl get secret kali-team-internal \
  -n kube-system \
  -o jsonpath='{.data.tls\.crt}' \
  | base64 -d \
  | openssl x509 -noout \
      -subject \
      -issuer \
      -dates \
      -ext subjectAltName
subject=CN=kali-team.internal
issuer=O=Step Certificates, CN=Step Certificates Intermediate CA
notBefore=Sep  9 14:50:34 2026 GMT
notAfter=Dec  8 14:51:34 2026 GMT
X509v3 Subject Alternative Name:
    DNS:kali-team.internal, DNS:*.kali-team.internal
➜  ~

从输出可以看到:

  • 证书由 Step Certificates Intermediate CA 签发(中间 CA)
  • 有效期约 90 天
  • 包含两个 SAN(主题备用名称):根域名和一级通配符域名

4.3.7 配置 DNS 和 CA 证书 SAN

为了让业务节点能够通过 ca.kali-team.internal 访问 step-ca 服务,需要确保 DNS 解析正常。先用 busybox 测试集群内 DNS 解析:

➜  ~ kubectl rollout restart deployment/coredns -n kube-system
deployment.apps/coredns restarted
➜  ~ kubectl run nslookup-test --image=busybox --rm -it --restart=Never -- nslookup ca.kali-team.internal
Server:         10.43.0.10
Address:        10.43.0.10:53

Name:   ca.kali-team.internal
Address: 10.111.13.99

All commands and output from this session will be recorded in container logs, including credentials and sensitive information passed through the command prompt.
If you don't see a command prompt, try pressing enter.
warning: couldn't attach to pod/nslookup-test, falling back to streaming logs: Internal error occurred: Internal error occurred: error attaching to container: container is in CONTAINER_EXITED state
Server:         10.43.0.10
Address:  10.43.0.10:53

Name:   ca.kali-team.internal
Address: 10.111.13.99

pod "nslookup-test" deleted from default namespace
➜  ~

DNS 解析正常。接下来需要确保 step-ca 服务的证书中包含 ca.kali-team.internal 这个 SAN,否则客户端会报证书域名不匹配错误。

修改 ConfigMap 中的 step-certificates-config,在 dnsNames 中添加外部域名:

"dnsNames": [
    "step-certificates.smallstep.svc.cluster.local",
    "ca.kali-team.internal",
    "127.0.0.1"
]

dnsNames 字段说明:

DNS 名称用途
step-certificates.smallstep.svc.cluster.local集群内部服务发现地址
ca.kali-team.internal外部访问的域名(通过 Ingress)
127.0.0.1本地回环地址,用于 Pod 内部访问

修改完成后重启 step-certificates StatefulSet:

➜  ~ kubectl rollout restart statefulset step-certificates -n smallstep
statefulset.apps/step-certificates restarted
➜  ~

五、Traefik 入口网关配置

Traefik 是 k3s 内置的 Ingress Controller,负责将外部流量路由到集群内部的服务。为了让所有通过 HTTPS 访问的服务都能使用我们签发的证书,需要配置 Traefik 的默认 TLS 证书。

5.1 配置默认证书

k3s 通过 HelmChart 来管理 Traefik 的部署。我们可以使用 HelmChartConfig 来自定义 Traefik 的配置,而不需要直接修改 HelmChart。

方案 A:使用内置自签证书

如果你选择了方案一(内置自签证书),使用以下配置:

# 使用 HelmChartConfig 配置(正确的 Traefik 3.x 语法)
➜  ~ cat <<EOF | kubectl apply -f -
apiVersion: helm.cattle.io/v1
kind: HelmChartConfig
metadata:
  name: traefik
  namespace: kube-system
spec:
  valuesContent: |
    globalArguments: []
    additionalArguments:
      - "--serverstransport.insecureskipverify=true"
    tlsOptions:
      default:
        minVersion: VersionTLS12
    tlsStore:
      default:
        defaultCertificate:
          secretName: internal-default-cert
EOF
helmchartconfig.helm.cattle.io/traefik created
➜  ~

方案 B:使用 step-ca 签发的证书

如果你选择了方案二(step-ca 私有 CA),使用以下配置:

# 使用 HelmChartConfig 配置(正确的 Traefik 3.x 语法)
➜  ~ cat <<EOF | kubectl apply -f -
apiVersion: helm.cattle.io/v1
kind: HelmChartConfig
metadata:
  name: traefik
  namespace: kube-system
spec:
  valuesContent: |
    globalArguments: []
    additionalArguments:
      - "--serverstransport.insecureskipverify=true"
    tlsOptions:
      default:
        minVersion: VersionTLS12
    tlsStore:
      default:
        defaultCertificate:
          secretName: kali-team-internal
EOF
helmchartconfig.helm.cattle.io/traefik created
➜  ~

配置项说明:

配置项含义
globalArguments: []清空全局参数,避免 Traefik 2.x 兼容参数在 3.x 中产生警告
additionalArguments额外的命令行参数
--serverstransport.insecureskipverify=true后端服务传输跳过证书验证(在内部自签证书场景下使用)
tlsOptions.default.minVersion默认 TLS 最低版本,设置为 TLS 1.2 以保证安全性
tlsStore.default.defaultCertificate.secretName默认证书的 Secret 名称,当 Ingress 未指定 TLS 证书时使用

HelmChartConfig 是什么? 它是 k3s 特有的功能,允许用户通过自定义资源的方式覆盖内置 Helm Chart 的 values 配置,而不需要手动重新安装 Chart。k3s 的 Helm Controller 会自动检测并应用这些配置。

重启 Traefik 使配置生效:

# 或者使用 rollout restart
➜  ~ kubectl rollout restart deployment traefik -n kube-system
deployment.apps/traefik restarted
➜  ~ kubectl get pods -n kube-system -l app.kubernetes.io/name=traefik
NAME                       READY   STATUS    RESTARTS   AGE
traefik-5f4d7646b8-pql94   1/1     Running   0          28s
➜  ~

你也可以直接通过 Traefik 的 TLSStore CRD 来配置默认证书:

➜  ~ cat <<'EOF' | kubectl apply -f -
apiVersion: traefik.io/v1alpha1
kind: TLSStore
metadata:
  name: default
  namespace: kube-system
spec:
  defaultCertificate:
    secretName: kali-team-internal
EOF
tlsstore.traefik.io/default created

TLSStore 是 Traefik 提供的自定义资源,用于管理 TLS 存储。名为 default 的 TLSStore 会被所有 IngressRoute 作为默认证书来源使用。


六、Rancher 多集群管理平台

Rancher 是整个 DevSecOps 平台的管理中枢,它提供统一的 Web 界面来管理多个 Kubernetes 集群、用户权限、应用商店等。

6.1 安装 Rancher

首先添加 Rancher 官方 Helm 仓库:

➜  ~ helm repo add rancher-stable https://releases.rancher.com/server-charts/stable
"rancher-stable" has been added to your repositories
➜  ~ kubectl create namespace cattle-system
namespace/cattle-system created
➜  ~ helm repo update
Hang tight while we grab the latest from your chart repositories...
...Successfully got an update from the "rancher-stable" chart repository
Update Complete. ⎈Happy Helming!⎈

使用 step-ca 签发的证书安装 Rancher:

➜  ~ kubectl -n cattle-system get ingress rancher \
  -o jsonpath='{.spec.tls[0].secretName}{"\n"}'
tls-rancher-ingress
➜  ~ helm upgrade rancher rancher-stable/rancher \
  -n cattle-system \
  --set hostname=rancher.kali-team.internal \
  --set replicas=1 \
  --set privateCA=true \
  --set ingress.tls.source=secret \
  --set ingress.tls.secretName=kali-team-internal
Release "rancher" has been upgraded. Happy Helming!
NAME: rancher
LAST DEPLOYED: Mon Sep  7 17:39:58 2026
NAMESPACE: cattle-system
STATUS: deployed
REVISION: 5
DESCRIPTION: Upgrade complete
TEST SUITE: None
NOTES:
Rancher Server has been upgraded. Rancher may take several minutes to fully initialize.

Please standby while Certificates are being issued, Containers are started and the Ingress rule comes up.

Check out our docs at https://rancher.com/docs/

## First Time Login

If you provided your own bootstrap password during installation, browse to https://rancher.kali-team.internal to get started.
If this is the first time you installed Rancher, get started by running this command and clicking the URL it generates:

echo https://rancher.kali-team.internal/dashboard/?setup=$(kubectl get secret --namespace cattle-system bootstrap-secret -o go-template='{{.data.bootstrapPassword|base64decode}}')

Rancher Helm 参数说明:

参数值含义
hostnamerancher.kali-team.internalRancher 访问域名,Ingress 会使用此域名配置路由
replicas1Rancher 副本数,单节点环境设为 1,生产环境建议设为 3 实现高可用
privateCAtrue标记使用私有 CA 签发的证书,Rancher 会做相应的配置调整
ingress.tls.sourcesecretTLS 证书来源类型,secret 表示使用已有的 Secret
ingress.tls.secretNamekali-team-internalTLS 证书所在的 Secret 名称

验证 Ingress 的 TLS 配置是否生效:

➜  ~ kubectl -n cattle-system get ingress rancher \
  -o jsonpath='{.spec.tls[0].secretName}{"\n"}'
kali-team-internal
➜  ~

可以看到 TLS 配置已经从默认的 tls-rancher-ingress 变为我们指定的 kali-team-internal。

6.2 首次登录与初始化

Rancher 安装完成后,第一次登录需要使用 bootstrap 密码来设置管理员密码。

获取 bootstrap 密码:

➜  ~ kubectl get secret --namespace cattle-system bootstrap-secret -o go-template='{{.data.bootstrapPassword|base64decode}}{{"\n"}}'
d729kxkj8mdxkc6ztr4pnxl5tprrd9cfnhthtshhjhz8s4mkj8cs49
➜  ~

在浏览器中访问 https://rancher.kali-team.internal,使用上面的 bootstrap 密码登录,然后设置新的管理员密码。

Rancher 首次登录界面

图 1:Rancher 首次登录界面

6.3 导入 Step CA 根证书

由于我们使用了私有 CA 签发的证书,需要将 Step CA 的根证书导入到 Rancher 中,这样 Rancher 才能信任由该 CA 签发的所有证书(比如接入新集群时的 TLS 通信)。

首先从 step-ca 导出根 CA 证书:

➜  ~ kubectl -n smallstep get configmap step-certificates-certs \
  -o jsonpath='{.data.root_ca\.crt}' > kali-team-root-ca.crt
➜  ~ cat kali-team-root-ca.crt
-----BEGIN CERTIFICATE-----
...
-----END CERTIFICATE-----

将根证书导入操作系统信任库(Arch Linux):

➜  ~ sudo cp kali-team-root-ca.crt /etc/ca-certificates/trust-source/anchors/nuc.crt
➜  ~ sudo trust extract-compat
➜  ~ curl https://ca.kali-team.internal/health
{"status":"ok"}
➜  ~

使用 curl 测试 ca 服务,如果没有证书错误说明系统已经信任该证书。返回 {"status":"ok"} 表示 step-ca 服务运行正常。

将根证书导入 Rancher:

➜  ~ cp kali-team-root-ca.crt /tmp/root_ca.crt
➜  ~ kubectl -n cattle-system create secret generic tls-ca \
  --from-file=cacerts.pem=/tmp/root_ca.crt \
  --dry-run=client -o yaml \
  | kubectl apply -f -
secret/tls-ca created
➜  ~ kubectl -n cattle-system get secret tls-ca
NAME     TYPE     DATA   AGE
tls-ca   Opaque   1      8s
➜  ~ kubectl -n cattle-system get secret tls-ca \
  -o jsonpath='{.data.cacerts\.pem}' \
  | base64 -d \
  | openssl x509 -noout -subject -issuer
subject=O=Step Certificates, CN=Step Certificates Root CA
issuer=O=Step Certificates, CN=Step Certificates Root CA
➜  ~ kubectl -n cattle-system rollout restart deployment rancher

命令说明:

  • tls-ca 是 Rancher 约定的 Secret 名称,用于存储私有 CA 根证书
  • cacerts.pem 是约定的 key 名称,Rancher 会从这个 key 中读取 CA 证书
  • 使用 --dry-run=client -o yaml | kubectl apply -f - 是一种幂等操作技巧:如果 Secret 不存在则创建,存在则更新
  • 最后重启 Rancher Deployment 让配置生效

为什么要导入根证书? Rancher 在管理下游集群时,需要与集群的 Kubernetes API 进行 TLS 通信。如果下游集群使用私有 CA 签发的证书,Rancher 必须信任该 CA,否则会因为证书验证失败而无法连接。


七、业务集群节点搭建

管理平台(Rancher + 管理节点 k3s)搭建完成后,接下来需要搭建业务集群。通常不会将业务系统部署在与管理平台同一台服务器上,而是使用独立的节点。

7.1 创建新集群

登录 Rancher 管理界面,在 Cluster Management 中创建新的集群。

建议配置:管理平台集群(local 集群)可以配置 hide-local-cluster: true 来隐藏,让界面更整洁,专注于业务集群管理。

Rancher 创建集群

图 2:Rancher 创建集群

在创建集群时,可以配置镜像仓库重写(Registry Mirror),根据需要填写。这里只重写 Docker Hub 的镜像:

Rancher 集群镜像仓库重写配置

图 3:集群创建向导中配置镜像仓库重写,加速国内镜像拉取

点击创建后,Rancher 会生成一个节点注册命令,用于在新节点上执行以加入集群:

Rancher 集群注册命令

图 4:Rancher 生成的节点注册命令,包含 token 和角色配置

7.2 节点注册与加入

在新的业务节点(Ubuntu 系统)上,先导入 Step CA 的根证书,确保节点信任私有 CA 签发的证书:

ubuntu@nuc-node:~$ ls
kali-team-root-ca.crt
ubuntu@nuc-node:~$ sudo cp kali-team-root-ca.crt \
  /usr/local/share/ca-certificates/kali-team-root-ca.crt
ubuntu@nuc-node:~$ sudo update-ca-certificates
Updating certificates in /etc/ssl/certs...
rehash: warning: skipping ca-certificates.crt,it does not contain exactly one certificate or CRL
1 added, 0 removed; done.
Running hooks in /etc/ca-certificates/update.d...
done.
ubuntu@nuc-node:~$

Ubuntu 系统证书导入说明:

命令/路径作用
/usr/local/share/ca-certificates/Ubuntu 系统自定义 CA 证书存放目录
sudo update-ca-certificates更新系统证书信任库,扫描目录中的证书并添加到信任链

验证 Rancher 证书是否被信任:

ubuntu@nuc-node:~$ curl https://rancher.kali-team.internal/cacerts
-----BEGIN CERTIFICATE-----
...
-----END CERTIFICATE-----
ubuntu@nuc-node:~$

curl 能正常获取证书而不报 SSL 错误,说明系统已经信任了我们的私有 CA。

执行 Rancher 提供的注册命令,将节点加入集群:

ubuntu@nuc-node:~$ curl -fL https://rancher.kali-team.internal/system-agent-install.sh | sudo  sh -s - --server https://rancher.kali-team.internal --label 'cattle.io/os=linux' --token z8592ctfvdw24czgpxgg9m5fbvhljdghppz9kh4vj225rbbgmwqdr9 --etcd --controlplane --worker
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100 34452    0 34452    0     0   563k      0 --:--:-- --:--:-- --:--:--  570k
[INFO]  Label: cattle.io/os=linux
[INFO]  Role requested: etcd
[INFO]  Role requested: controlplane
[INFO]  Role requested: worker
[INFO]  CA strict verification is set to true
[INFO]  Using default agent configuration directory /etc/rancher/agent
[INFO]  Using default agent var directory /var/lib/rancher/agent
[INFO]  Successfully downloaded CA certificate
[INFO]  Value from https://rancher.kali-team.internal/cacerts is an x509 certificate
[INFO]  Successfully tested Rancher connection
[INFO]  Downloading rancher-system-agent binary from https://rancher.kali-team.internal/assets/rancher-system-agent-amd64
[INFO]  Successfully downloaded the rancher-system-agent binary.
[INFO]  Downloading rancher-system-agent-uninstall.sh script from https://rancher.kali-team.internal/assets/system-agent-uninstall.sh
[INFO]  Successfully downloaded the rancher-system-agent-uninstall.sh script.
[INFO]  Generating Cattle ID
INFO[0000] Rancher System Agent version v0.15.1 (529229529671ac2b1908eff7f56097daaef3417b) - Connection Info Validation
INFO[0000] Validating remote configuration
INFO[0000] Checking connection info file: /var/lib/rancher/agent/rancher2_connection_info.json.tmp
INFO[0000] Connection info file exists
INFO[0000] Connection info file is valid JSON
INFO[0000] Connection info has required kubeConfig field
INFO[0000] Connection info has namespace: fleet-default
INFO[0000] Connection info has secretName: custom-dd2fe218e55b-machine-plan
INFO[0000] Connection info validation successful
[INFO]  Successfully downloaded and validated Rancher connection information
[INFO]  systemd: Creating service file
[INFO]  Creating environment file /etc/systemd/system/rancher-system-agent.env
[INFO]  Enabling rancher-system-agent.service
Created symlink /etc/systemd/system/multi-user.target.wants/rancher-system-agent.service → /etc/systemd/system/rancher-system-agent.service.
[INFO]  Starting/restarting rancher-system-agent.service
ubuntu@nuc-node:~$

注册命令参数说明:

参数含义
--server https://rancher.kali-team.internalRancher Server 的地址
--label 'cattle.io/os=linux'为节点添加标签,用于标识操作系统类型
--token z8592ctfvdw...集群注册令牌,用于身份验证
--etcd节点承担 etcd 角色(存储集群数据)
--controlplane节点承担控制平面角色(API Server、Controller 等)
--worker节点承担工作节点角色(运行业务 Pod)

注册脚本会下载并安装 rancher-system-agent,这是 Rancher 用来管理下游节点的 Agent 服务。它通过 Rancher Server 获取节点配置,然后按照配置安装 k3s/RKE2 等 Kubernetes 发行版。

安装过程中,Agent 会自动下载 k3s 二进制、配置 systemd 服务、加入集群。等待几分钟后,节点就会出现在 Rancher 的集群节点列表中。

7.3 配置 kubeconfig

节点加入集群后,同样需要配置本地用户的 kubeconfig,以便使用 kubectl 命令。

ubuntu@nuc-node:~$ echo 'write-kubeconfig-mode: "0644"' | sudo tee /etc/rancher/k3s/config.yaml
write-kubeconfig-mode: "0644"
ubuntu@nuc-node:~$ mkdir -p ~/.kube
ubuntu@nuc-node:~$ sudo cp /etc/rancher/k3s/k3s.yaml ~/.kube/config
ubuntu@nuc-node:~$ sudo chown ubuntu: ~/.kube/config
ubuntu@nuc-node:~$ id
uid=1000(ubuntu) gid=1000(ubuntu) groups=1000(ubuntu),4(adm),24(cdrom),27(sudo),30(dip),105(lxd)
ubuntu@nuc-node:~$ sudo chown ubuntu:ubuntu ~/.kube/config
ubuntu@nuc-node:~$ chmod 600 ~/.kube/config
ubuntu@nuc-node:~$ sudo mv /etc/rancher/k3s/k3s.yaml /etc/rancher/k3s/k3s.yaml.bak
ubuntu@nuc-node:~$ sudo ln -s /home/ubuntu/.kube/config /etc/rancher/k3s/k3s.yaml
ubuntu@nuc-node:~$ kubectl get nodes
NAME       STATUS   ROLES                       AGE    VERSION
nuc-node   Ready    control-plane,etcd,worker   150m   v1.36.4+k3s1
ubuntu@nuc-node:~$ sudo chmod 644 /etc/rancher/k3s/config.yaml.d/50-rancher.yaml
ubuntu@nuc-node:~$

验证节点状态:

ubuntu@nuc-node:~$ kubectl get nodes
NAME       STATUS   ROLES                       AGE    VERSION
nuc-node   Ready    control-plane,etcd,worker   150m   v1.36.4+k3s1
ubuntu@nuc-node:~$

节点状态为 Ready,角色同时包含 control-plane、etcd 和 worker,说明这是一个单节点集群(所有角色都在同一个节点上)。

7.4 DNS 解析配置

由于 *.kali-team.internal 是我们自定义的内网域名,没有公共 DNS 解析记录。有两种解决方案:

  1. 在公司内网 DNS 服务器上添加 A 记录(推荐生产环境使用)
  2. 在 CoreDNS 中配置重写规则(适合测试/开发环境)

这里我们使用 CoreDNS 的 NodeHosts 功能来配置,类似于修改 hosts 文件。

编辑 CoreDNS ConfigMap:

ubuntu@nuc-node:~$ k3s kubectl -n kube-system edit configmap coredns
configmap/coredns edited

在 NodeHosts 部分添加域名解析记录,格式与 /etc/hosts 文件相同:IP地址 域名。

修改完成后重启 CoreDNS:

ubuntu@nuc-node:~$ k3s kubectl -n kube-system rollout restart deployment coredns
deployment.apps/coredns restarted

业务集群节点上线后界面

图 5:业务集群节点上线后界面

为什么不用 Ingress 的 externalIP 或者 LoadBalancer? 在私有云/内网环境中,通常没有云厂商提供的 LoadBalancer,k3s 默认使用 ServiceLB(基于 DaemonSet 的 hostPort 实现)。通过 CoreDNS 配置自定义域名解析,可以让集群内部的服务也能通过域名访问到 Traefik Ingress。

  • 或者使用自定义DNS
ubuntu@nuc-node:~$ cat <<'EOF' | kubectl apply -f - && kubectl -n kube-system rollout restart deployment coredns
apiVersion: v1
kind: ConfigMap
metadata:
  name: coredns-custom
  namespace: kube-system
data:
  kali-team.server: |
    kali-team.internal:53 {
        errors
        hosts {
            10.111.13.105 hub.kali-team.internal
            fallthrough
        }
        forward . /etc/resolv.conf
    }
EOF
configmap/coredns-custom created
deployment.apps/coredns restarted
ubuntu@nuc-node:~$

7.5 业务节点证书配置

业务集群也需要配置证书管理。由于 step-ca 服务部署在管理平台集群上,业务节点只需要安装 cert-manager 和 step-issuer,然后通过外部域名连接到 step-ca 即可。

步骤 1:安装 cert-manager

ubuntu@nuc-node:~$ helm repo add jetstack https://charts.jetstack.io
"jetstack" has been added to your repositories
ubuntu@nuc-node:~$ helm upgrade --install cert-manager jetstack/cert-manager \
  --namespace cert-manager \
  --create-namespace \
  --set crds.enabled=true
Release "cert-manager" does not exist. Installing it now.
NAME: cert-manager
LAST DEPLOYED: Thu Sep 10 11:10:49 2026
NAMESPACE: cert-manager
STATUS: deployed
REVISION: 1
DESCRIPTION: Install complete
TEST SUITE: None
NOTES:
cert-manager v1.21.1 has been deployed successfully!

In order to begin issuing certificates, you will need to set up a ClusterIssuer
or Issuer resource (for example, by creating a 'letsencrypt-staging' issuer).

More information on the different types of issuers and how to configure them
can be found in our documentation:

https://cert-manager.io/docs/configuration/

For information on how to configure cert-manager to automatically provision
Certificates for Ingress resources, take a look at the `ingress-shim`
documentation:

https://cert-manager.io/docs/usage/ingress/

For information on how to configure cert-manager to automatically provision
Certificates for Gateway API resources, take a look at the `gateway resource`
documentation:

https://cert-manager.io/docs/usage/gateway/
ubuntu@nuc-node:~$ kubectl -n cert-manager get po
NAME                                       READY   STATUS    RESTARTS   AGE
cert-manager-689c4c5575-dznnx              1/1     Running   0          2m2s
cert-manager-cainjector-6fbb9c8cd6-7wkd5   1/1     Running   0          2m2s
cert-manager-webhook-646c95c5ff-g55h5      1/1     Running   0          2m2s
ubuntu@nuc-node:~$

步骤 2:安装 step-issuer

ubuntu@nuc-node:~$ helm repo add smallstep  https://smallstep.github.io/helm-charts
"smallstep" already exists with the same configuration, skipping
ubuntu@nuc-node:~$
ubuntu@nuc-node:~$ helm repo update
Hang tight while we grab the latest from your chart repositories...
...Successfully got an update from the "jetstack" chart repository
...Successfully got an update from the "smallstep" chart repository
Update Complete. ⎈Happy Helming!⎈
ubuntu@nuc-node:~$
ubuntu@nuc-node:~$ helm install \
   step-issuer smallstep/step-issuer \
   --namespace smallstep \
   --create-namespace
NAME: step-issuer
LAST DEPLOYED: Sat Sep 12 14:11:53 2026
NAMESPACE: smallstep
STATUS: deployed
REVISION: 1
DESCRIPTION: Install complete
TEST SUITE: None
NOTES:
⚙️  Thanks for installing step-issuer.

step-issuer is ideal for issuing certificates
from your own private Certificate Authority (CA).

To start issuing certificates, you will need:

👉 A cert-manager installation
👉 A step-ca Certificate Authority (CA) or a Smallstep Certificate Manager authority
👉 A StepIssuer resource that links step-issuer to your CA

To continue, follow the instructions here:

https://u.step.sm/step-issuer
ubuntu@nuc-node:~$

验证 step-issuer 运行状态:

ubuntu@nuc-node:~$ kubectl get pods -n smallstep
NAME                           READY   STATUS    RESTARTS   AGE
step-issuer-6c65fd4d77-jggxr   1/1     Running   0          21h
ubuntu@nuc-node:~$

步骤 3:创建 Provisioner 密码 Secret

ubuntu@nuc-node:~$ kubectl -n smallstep create secret generic step-certificates-cert-manager-password \
  --from-literal=password='f7zl9rRbrBzujPSXT71k4Yd1TsLrBQyB'
secret/step-certificates-cert-manager-password created
ubuntu@nuc-node:~$

步骤 4:创建 StepClusterIssuer

注意:这里的 url 必须使用外部暴露的域名 https://ca.kali-team.internal,因为 svc.cluster.local 是管理平台集群内部的地址,业务集群的节点访问不到。

ubuntu@nuc-node:~$ cat <<EOF | kubectl apply -f -
apiVersion: certmanager.step.sm/v1beta1
kind: StepClusterIssuer
metadata:
  name: smallstep
spec:
  caBundle: >-
    LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJ3ekNDQVdtZ0F3SUJBZ0lRWmFMY3MwellLcENaWHI3UU01aHQyVEFLQmdncWhrak9QUVFEQWpCQU1Sb3cKR0FZRFZRUUtFeEZUZEdWd0lFTmxjblJwWm1sallYUmxjekVpTUNBR0ExVUVBeE1aVTNSbGNDQkRaWEowYVdacApZMkYwWlhNZ1VtOXZkQ0JEUVRBZUZ3MHlOakE1TURreE16QTRNekZhRncwek5qQTVNRFl4TXpBNE16RmFNRUF4CkdqQVlCZ05WQkFvVEVWTjBaWEFnUTJWeWRHbG1hV05oZEdWek1TSXdJQVlEVlFRREV4bFRkR1Z3SUVObGNuUnAKWm1sallYUmxjeUJTYjI5MElFTkJNRmt3RXdZSEtvWkl6ajBDQVFZSUtvWkl6ajBEQVFjRFFnQUV5UEdveGw4bApPd0FEMHhUSDBQQWVPNjBtU3lyRG9SOUh6UGdreWV0L0NSMTE4alJRNlZzWS9McHBrQW1uRTcwWXY2UHEyQ0gyCmFHVitFcXJZVjZxWldLTkZNRU13RGdZRFZSMFBBUUgvQkFRREFnRUdNQklHQTFVZEV3RUIvd1FJTUFZQkFmOEMKQVFFd0hRWURWUjBPQkJZRUZHTDVPc2FMWkFQenpuam5FazZFazNlMkU1eHFNQW9HQ0NxR1NNNDlCQU1DQTBnQQpNRVVDSVFDSHFscEJPbWRublJJZUpiWEdHUUU2UzVYS0NXaG5zSFNTa2dZL0dmMndjd0lnSTZJOGwrYUcyaitOCkRHWDh6Z0JNdTF2Ym5wRCtGcXVvbEhoL25hU2RicE09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K
  provisioner:
    kid: 3Fckyy_09lJ1hoJ-7dHIwLbmUffrzz42SIvifmrEjOw
    name: cert-manager
    passwordRef:
      key: password
      name: step-certificates-cert-manager-password
      namespace: smallstep
  url: https://ca.kali-team.internal
EOF
stepclusterissuer.certmanager.step.sm/smallstep created
ubuntu@nuc-node:~$

验证 StepClusterIssuer 和 step-issuer 状态:

ubuntu@nuc-node:~$ kubectl get certificate -n cattle-system
No resources found in cattle-system namespace.
ubuntu@nuc-node:~$ kubectl get pods -A | grep step-issuer
smallstep             step-issuer-6c65fd4d77-rfq9t                 1/1     Running     0             10m
ubuntu@nuc-node:~$ kubectl get stepclusterissuer smallstep
NAME        AGE
smallstep   112s
ubuntu@nuc-node:~$

步骤 5:签发 Rancher 命名空间证书

为 cattle-system 命名空间(Rancher 所在命名空间)签发通配符证书:

ubuntu@nuc-node:~$ cat <<EOF | kubectl apply -f -
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
  name: kali-team-internal
  namespace: cattle-system
spec:
  secretName: kali-team-internal
  duration: 2160h
  renewBefore: 360h
  subject:
    organizations:
      - kali-team
  dnsNames:
    - kali-team.internal
    - "*.kali-team.internal"
  issuerRef:
    name: smallstep
    kind: StepClusterIssuer
    group: certmanager.step.sm
EOF
certificate.cert-manager.io/kali-team-internal created
ubuntu@nuc-node:~$

验证证书签发:

ubuntu@nuc-node:~$ kubectl get certificate -n cattle-system
NAME                 READY   SECRET               AGE
kali-team-internal   True    kali-team-internal   55s
ubuntu@nuc-node:~$

步骤 6:配置 Traefik 默认证书

先确认 Traefik 的 TLSStore CRD 是否已存在:

ubuntu@nuc-node:~$ kubectl get crd tlsstores.traefik.io
NAME                   CREATED AT
tlsstores.traefik.io   2026-09-12T11:58:57Z
ubuntu@nuc-node:~$

设置全局默认证书:

ubuntu@nuc-node:~$ kubectl get tlsstore -A
NAMESPACE     NAME      AGE
kube-system   default   86s
ubuntu@nuc-node:~$ kubectl get jobs -n kube-system | grep helm-install-traefik
helm-install-traefik       Complete   1/1           5s         4m16s
helm-install-traefik-crd   Complete   1/1           7s         12h
ubuntu@nuc-node:~$

如果 kube-system 命名空间中存在名为 default 的 TLSStore,说明全局默认证书配置已经持久化生效。Traefik 重启后仍然会保留此配置。

业务集群节点证书 图 6:业务集群节点证书


十、参考资料