使用 Pulumi 部署 cert-manager 创建 K8S 自签名证书并信任证书

2024年4月29日  l101782 minutes

在搭建本地 Kubernetus 集群后,由于环境在内网,做不了域名验证,无法使用 Let’s Encrypt 颁发和自动更新证书,然而很多应用要求必须启用 HTTPS,只能用自签名 CA 证书,并由此 CA 继续颁发其他证书。

所以我们准备了以下工具,开始搭建。

  • Pulumi : 当前非常流行的 IaC 工具,值得一试。
  • cert-manager : 云原生证书管理,用于自动管理和颁发各种发行来源的 TLS 证书。它将确保证书有效并定期更新,并尝试在到期前的适当时间更新证书。

核心步骤和相关代码如下,更多源码请参考我们的 GitHub 项目 xlabs-ops

使用 Pulumi 安装 cert-manager,生成自签名 CA 证书,根据自签名 CA 证书生成 cert-manager ClusterIssuer,都在如下代码了。

import * as pulumi from "@pulumi/pulumi";
import * as kubernetes from "@pulumi/kubernetes";
import * as tls from "@pulumi/tls";

// 部署 cert-manager Helm chart
const certManagerRelease = new kubernetes.helm.v3.Release("cert-manager", {
  name: "cert-manager",
  chart: "cert-manager",
  version: "1.14.5",
  namespace: "cert-manager",
  createNamespace: true,
  timeout: 600,
  repositoryOpts: {
    repo: "https://charts.jetstack.io"
  },
  values: {
    installCRDs: true
  }
});

// 生成一个 CA private key
const caPrivateKey = new tls.PrivateKey("caPrivateKey", {
  algorithm: "RSA"
});

// 生成一个 自签名 CA 证书
const caCert = new tls.SelfSignedCert("caCert", {
  // keyAlgorithm: "RSA",
  privateKeyPem: caPrivateKey.privateKeyPem,
  isCaCertificate: true,
  validityPeriodHours: 87600, // 10 year
  allowedUses: ["cert_signing", "crl_signing"],
  subject: {
    commonName: "your.domain.com",
    organization: "Xlabs Club"
  }
});

// 生成一个带有 CA crt 和 key 的 Kubernetes Secret
const caSecret = new kubernetes.core.v1.Secret("caSecret", {
  metadata: {
    name: "selfsigned-cert-manager-ca",
    namespace: "cert-manager"
  },
  type: "Opaque",
  stringData: {
    "tls.crt": caCert.certPem,
    "tls.key": caPrivateKey.privateKeyPem
  }
});

// 创建一个自签名的 ClusterIssuer 给 ingress 用
const clusterIssuer = new kubernetes.apiextensions.CustomResource(
  "selfsigned-issuer",
  {
    apiVersion: "cert-manager.io/v1",
    kind: "ClusterIssuer",
    metadata: {
      name: "selfsigned-issuer",
      // 注意 ClusterIssuer 和 caSecret 放在同一个 namespace,不写 namespace 时 ClusterIssuer 找不到 caSecret
      namespace: "cert-manager"
    },
    spec: {
      ca: {
        secretName: caSecret.metadata.name
      }
    }
  },
  { dependsOn: certManagerRelease }
);

export const certManagerVersion = certManagerRelease.version;
export const clusterIssuerName = clusterIssuer.metadata.name;

// Export CA 证书,便于客户端导入信任证书
export const caCertificatePem = caCert.certPem;

以上执行 pulumi up 后,我们就得到了一个自签名的 CA 证书、一个可用于为 ingress 自动签发 TLS 的 ClusterIssuer。

在 K8S ingress 上,增加以下 annotations 即可自动生成 TLS 证书,注意名字 selfsigned-issuer 与上面创建的名字保持一致。

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  annotations:
    cert-manager.io/cluster-issuer: selfsigned-issuer

另外提供一个小插曲,实际上 cert-manager 的 ClusterIssuer 不需要指定 namespace,但是在创建的时候发现不写 namespace,caSecret 创建在 default namespace 后,ClusterIssuer 找不到 caSecret,所以为他们两个都特别指定了 namespace。

让 Edge/Chrome 信任自签名证书

以上生成的自签名证书在浏览器访问时,会有红色提示不安全,被禁止访问,所以需要将我们的 CA 证书导入本机并选择信任。

# 导出上面步骤生产的 CA 证书
pulumi stack output caCertificatePem --show-secrets > ca.crt.pem

Mac 用户,通过以下命令行,或打开 Keychain Access 应用程序手动导入并在 info trust 中选择 always trust。

sudo security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain ca.crt.pem

对于 Ubuntu 用户,可通过以下命令导入。某些 Ubuntu Desktop 增加信任证书后,Edge 仍然提示证书无效,在 Edge 地址栏输入 edge://settings/privacy/manageCertificates 重新导入了一次就解决了。


mv ca.crt.pem ca.crt
sudo apt-get install -y ca-certificates
sudo cp ca.crt /usr/local/share/ca-certificates/my-local-ca.crt
sudo update-ca-certificates

Windows 用户,双击 ca.crt 安装证书到“受信任的根证书颁发机构”。