cert-manager
- jetstack/cert-manager 是什么?
- 自颁发 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 支持的 Provider 非常少 - 和 LEGO 相比
- ACMEDNS
- Akamai
- AzureDNS
- CloudFlare
- DigitalOcean
- Google CloudDNS
- RFC-2136
- Route53
- Webhook
- DNS01 支持的 Provider 非常少 - 和 LEGO 相比
caution
- 尽量不要创建相同证书 - 如果需要可考虑同步
- DNS01 才支持泛域名证书 - 最简单是使用 ACMEDNS
Ingress
annotation | desc |
---|---|
cert-manager.io/issuer | Issuser |
cert-manager.io/cluster-issuer | ClusterIssuer |
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" 用于区分 |
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
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
# ingress 生成的 certificate
spec:
dnsNames:
- web.example.com
issuerRef:
group: cert-manager.io
kind: ClusterIssuer
name: letsencrypt
secretName: web-cert
usages:
- digital signature
- key encipherment
安装
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
# 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: my-cloudflare-[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
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
dnsNames:
- domain.tld
- '*.domain.tld'
# 不能添加这个域名 - 已经被上面覆盖
# - sub.domain.tld
- '*.sub.domain.tld'
证书跨空间
- 配置 ingress 设置默认 tls secret, 然后之后的 ingress 不配置 secret
- 修改较大,不建议
- 同步
目前无法修改 secret annotations - #977- 可以使用预先存在的 secret - 然后配合 kubed 使用
- 在来源上定义,同步到目标
- 可以使用预先存在的 secret - 然后配合 kubed 使用
- emberstack/kubernetes-reflector
- 可替代 kubed - 支持证书 secret 同步
- 先定义目标再定义来源
kubed
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
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
直接支持证书
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 外部账号绑定
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:malformed
Certificate will be issued with an empty Issuer DN, which contravenes RFC 5280 and could break some strict clients
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: citus-cert
spec:
# 添加这个
commonName: what.ever
Could not determine authoritative nameservers for
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
dig TXT _acme-challenge.dev.wener.me
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