跳至正文
Wener Notes

Cert Manager

约 6 分钟阅读
  • 是什么?
    • 自颁发 CA 证书管理
    • ACME 自动证书申请
    • 外部证书管理集成
  • crds - 自定义资源
    • issuers.cert-manager.io
    • orders.acme.cert-manager.io
    • certificaterequests.cert-manager.io
    • certificates.cert-manager.io
    • challenges.acme.cert-manager.io
    • clusterissuers.cert-manager.io
  • 配置
  • Ingress
  • 注意
  • 尽量不要创建相同证书 - 如果需要可考虑同步
  • DNS01 才支持泛域名证书 - 最简单是使用 ACMEDNS

Ingress

annotationdesc
cert-manager.io/issuerIssuser
cert-manager.io/cluster-issuerClusterIssuer
cert-manager.io/issuer-kind外部 Issuers
cert-manager.io/issuer-group外部 Issuers
kubernetes.io/tls-acme: "true"如果安装时设置了 ingressShim.defaultIssuer 则会使用默认 Issuser
acme.cert-manager.io/http01-ingress-class用于 resolve 的 Ingress
acme.cert-manager.io/http01-edit-in-place:创建新的 Ingress 配置还是修改现有的 Ingress,会设置 "cert-manager.io/issue-temporary-certificate": "true" 用于区分
yaml
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: kuard
  annotations:
    kubernetes.io/ingress.class: 'nginx'
    # 可直接使用指定的 issuser
    cert-manager.io/issuer: 'letsencrypt-staging'

spec:
  tls:
    - hosts:
        - example.example.com
      secretName: quickstart-example-tls
  rules:
    - host: example.example.com
      http:
        paths:
          - path: /
            backend:
              serviceName: kuard
              servicePort: 80

Certificate

yaml
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
  name: example-com
  namespace: sandbox
spec:
  # Secret names are always required.
  secretName: example-com-tls
  duration: 2160h # 90d
  renewBefore: 360h # 15d
  subject:
    organizations:
      - jetstack
  # The use of the common name field has been deprecated since 2000 and is
  # discouraged from being used.
  commonName: example.com
  isCA: false
  privateKey:
    algorithm: RSA
    encoding: PKCS1
    size: 2048
  usages:
    - server auth
    - client auth
  # At least one of a DNS Name, URI, or IP address is required.
  dnsNames:
    - example.com
    - www.example.com
  uris:
    - spiffe://cluster.local/ns/sandbox/sa/example
  ipAddresses:
    - 192.168.0.5
  # Issuer references are always required.
  issuerRef:
    name: ca-issuer
    # We can reference ClusterIssuers by changing the kind here.
    # The default value is Issuer (i.e. a locally namespaced Issuer)
    kind: Issuer
    # This is optional since cert-manager will default to this value however
    # if you are using an external issuer, change this to that issuer group.
    group: cert-manager.io
yaml
# ingress 生成的 certificate
spec:
  dnsNames:
    - web.example.com
  issuerRef:
    group: cert-manager.io
    kind: ClusterIssuer
    name: letsencrypt
  secretName: web-cert
  usages:
    - digital signature
    - key encipherment

安装

bash
ver=$(curl -Ls https://api.github.com/repos/jetstack/cert-manager/releases/latest | jq -r .tag_name)
# 安装自定义资源
curl -sfLO https://github.com/jetstack/cert-manager/releases/download/$ver/cert-manager.crds.yaml
kubectl apply -f cert-manager.crds.yaml

# 创建 NS
kubectl create namespace cert-manager

# Helm 安装
helm repo add jetstack https://charts.jetstack.io
helm repo update

helm install \
  cert-manager jetstack/cert-manager \
  --namespace cert-manager \
  --version $ver

# Helm 安装 - 通过镜像
helm repo add wener https://charts.wener.tech
helm repo update

cat << YAML > cert-manager.values.yaml
image:
  repository: registry.cn-hongkong.aliyuncs.com/cmi/jetstack_cert-manager-controller
webhook:
  image:
    repository: registry.cn-hongkong.aliyuncs.com/cmi/jetstack_cert-manager-webhook
cainjector:
  image:
    repository: registry.cn-hongkong.aliyuncs.com/cmi/jetstack_cert-manager-cainjector
installCRDs: true
# 版本相关
extraArgs:
  - --acme-http01-solver-image=registry.cn-hongkong.aliyuncs.com/cmi/jetstack_cert-manager-acmesolver:$ver
YAML

helm install \
  cert-manager wener/cert-manager \
  --namespace cert-manager --create-namespace \
  --version $ver -f cert-manager.values.yaml

# 查看安装状态
kubectl -n cert-manager rollout status deploy/cert-manager
# 验证安装
kubectl -n cert-manager get deploy

ACME

yaml
# Cloudflare 接口 Token 的 Secret
apiVersion: v1
kind: Secret
metadata:
  name: cloudflare-api-token-secret
type: Opaque
stringData:
  api-token: <API Token>

---
# Cloudflare 接口 Key 的 Secret
apiVersion: v1
kind: Secret
metadata:
  name: cloudflare-api-key-secret
type: Opaque
stringData:
  api-key: <API Key>

---
# letsencrypt staging 环境
apiVersion: cert-manager.io/v1alpha2
kind: ClusterIssuer
metadata:
  name: letsencrypt-staging
  namespace: default
spec:
  acme:
    server: https://acme-staging-v02.api.letsencrypt.org/directory
    # 生产地址
    # server: https://acme-v02.api.letsencrypt.org/directory
    # 账号
    email: [email protected]
    # 存储 ACME 账号私钥的 secret 名字
    privateKeySecretRef:
      name: letsencrypt-staging

    # DNS-01
    solvers:
      # selector 为空匹配所有域名
      - selector: {}
        dns01:
          clouddns:
            # The ID of the GCP project
            # reference: https://docs.cert-manager.io/en/latest/tasks/issuers/setup-acme/dns01/google.html
            project: $PROJECT_ID
            # This is the secret used to access the service account
            serviceAccountSecretRef:
              name: clouddns-dns01-solver-svc-acct
              key: key.json

      # 为 foo.com 使用该 provider
      # 还可以使用 matchLabels 和 dnsZones
      - selector:
          dnsNames:
            - foo.com
        dns01:
          cloudflare:
            email: [email protected]
            # 需要先创建 secret
            # kubectl create secret generic cloudflare-api-key-secret
            # CF 支持 API Token 和 API Key
            apiKeySecretRef:
              name: cloudflare-api-key-secret
              key: api-key

cmctl

  • cmctl = kubectl cert-manager
bash
brew install cmctl

kubectl get certificate -A
kubectl get certificaterequest -A

# 触发 renew
cmctl renew --namespace=app --all
# 证书状态
cmctl status certificate my-certificate -n my-namespace

FAQ

account credentials not found for domain

如果是 dns, 可能是域名不匹配.

例如 申请 sub.domain.tld. 需要配置的是子域名, 不会自动匹配泛域名, 例如配置过 _acme_changlle.domain.tld 也不会生效

Error creating new order :: Domain name "sub.domain.tld" is redundant with a wildcard domain in the same request

yaml
dnsNames:
  - domain.tld
  - '*.domain.tld'
  # 不能添加这个域名 - 已经被上面覆盖
  # - sub.domain.tld
  - '*.sub.domain.tld'

证书跨空间

  1. 配置 ingress 设置默认 tls secret, 然后之后的 ingress 不配置 secret
  • 修改较大,不建议
  1. 同步

kubed

yaml
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
  name: sandbox
  namespace: cert-manager
spec:
  secretName: sandbox-tls
  commonName: sandbox
  issuerRef:
    name: sandbox-ca
    kind: Issuer
    group: cert-manager.io
  secretTemplate:
    annotations:
      kubed.appscode.com/sync: 'cert-manager-tls=sandbox' # Sync certificate to matching namespaces

reflector

yaml
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
  name: source
  namespace: cert-manager
spec:
  secretName: source-tls
  commonName: source
  issuerRef:
    name: source-ca
    kind: Issuer
    group: cert-manager.io
  secretTemplate:
    annotations:
      reflector.v1.k8s.emberstack.com/reflection-allowed: "true"
      reflector.v1.k8s.emberstack.com/reflection-allowed-namespaces: "dev,staging,prod"  # Control destination namespaces
      reflector.v1.k8s.emberstack.com/reflection-auto-enabled: "true" # Auto create reflection for matching namespaces
      reflector.v1.k8s.emberstack.com/reflection-allowed-namespaces: "dev,staging,prod" # Control auto-reflection namespaces

直接支持证书

yaml
apiVersion: cert-manager.io/v1alpha1
kind: Certificate
metadata:
  name: default-cert
  annotations:
    reflector.v1.k8s.emberstack.com/secret-reflection-allowed: 'true'
    reflector.v1.k8s.emberstack.com/secret-reflection-allowed-namespaces: 'namespace-1,namespace-2,namespace-[0-9]*'
spec:
  secretName: certificate-secret

The request must include a value for the "externalAccountBinding" field

  • zarossl 需要 EAB 外部账号绑定
yaml
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: zerossl-prod
spec:
  acme:
    externalAccountBinding:
      keyAlgorithm: HS256
      keyID: XXX
      keySecretRef:
        key: secret
        name: zerossl-eabsecret
    preferredChain: ''
    privateKeySecretRef:
      name: zerossl-prod
    server: https://acme.zerossl.com/v2/DV90
    solvers:
      - http01:
          ingress:
            class: traefik
---
apiVersion: v1
kind: Secret
metadata:
  name: zerossl-eabsecret
  # 注意 ns
  namespace: cert-manager
type: Opaque
# stringData:
#   secret: XXX
data:
  secret: XXX

Failed to retrieve Order resource: 404 urn:ietf:params:acme:error

Certificate will be issued with an empty Issuer DN, which contravenes RFC 5280 and could break some strict clients

yaml
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
  name: citus-cert
spec:
  # 添加这个
  commonName: what.ever

Could not determine authoritative nameservers for

yaml
extraArgs:
  - --dns01-recursive-nameservers-only
  #- --dns01-recursive-nameservers=8.8.8.8:53,1.1.1.1:53
  - --dns01-recursive-nameservers=114.114.114.114:53,223.5.5.5:53

DNS record for "dev.wener.me" not yet propagated

bash
dig TXT _acme-challenge.dev.wener.me
yaml
apiVersion: acme.cert-manager.io/v1
kind: Challenge
metadata:
spec:
  dnsName: dev.wener.me
  issuerRef:
    group: cert-manager.io
    kind: Issuer
    name: letsencrypt
  # TXT 值
  key: KEY

关联信息

反向链接、本文链接的其他页面和外部资料。

反向链接

  • K8S Awesome
    笔记 · cert-manager

    cncf landscape · awesome-cloud-native · Service Mesh / Cloud Native · GoogleCloudPlatform/scion · Google Cloud Platform 相关的 Go 项目。

参考资料

GitHub4 条
其他外链4 条
最近更新commit 40ad671编辑