乔克
乔克
Published on 2024-11-15 / 29 Visits
0
0

kubernetes使用cert-manager签发证书

集群信息

kubernetes: 1.19.16
Linux kernel: 3.10.0
helm: 3.5.4

安装 Cert-Manager

(1)安装 CRD
kubectl apply -f https://github.com/jetstack/cert-manager/releases/download/v1.6.1/cert-manager.crds.yaml

安装完成后可以看到如下 CRD 信息:

# kubectl get crd | grep cert
certificaterequests.cert-manager.io           2021-12-13T04:26:04Z
certificates.cert-manager.io                  2021-12-13T04:26:04Z
challenges.acme.cert-manager.io               2021-12-13T04:26:04Z
clusterissuers.cert-manager.io                2021-12-13T04:26:05Z
issuers.cert-manager.io                       2021-12-13T04:26:05Z
orders.acme.cert-manager.io                   2021-12-13T04:26:05Z
(2)安装 cert-manager
# 添加helm repo
helm repo add jetstack https://charts.jetstack.io
helm repo update

# 安装cert-manager
helm install \
  cert-manager jetstack/cert-manager \
  --namespace cert-manager \
  --create-namespace \
  --version v1.6.1 \

待安装完成后,可以看到如下 Pod 信息。

# kubectl get po -n cert-manager 
NAME                                       READY   STATUS      RESTARTS   AGE
cert-manager-57d89b9548-cq78f              1/1     Running     0          4h3m
cert-manager-cainjector-5bcf77b697-hmjj2   1/1     Running     0          4h3m
cert-manager-startupapicheck-hn5g5         0/1     Completed   4          4h3m
cert-manager-webhook-8687fc66d4-ppllm      1/1     Running     0          4h3m

待所以 Pod 变成 Running 过后,cert-manager 安装完成。

创建 ClusterIssuer

cert-manager 提供了 IssuerClusterIssuer 两种类型的签发机构,Issuer 只能用来签发自己所在命名空间下的证书,ClusterIssuer 可以签发任意命名空间下的证书。

ACME 验证证书的方式有 http01dns01 两种,这里会分别进行测试。

http01

1、创建测试 ClusterIssuer

创建测试证书主要是为了验证证书是否能正常生成,而生产环境对接口的调用有限制【1】,避免不可用,建议先生成测试证书。

# cat letsencrypt-staging.yaml

apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: letsencrypt-staging
spec:
  acme:
    # 务必将此处替换为你自己的邮箱, 否则会配置失败。当证书快过期时 Let's Encrypt 会与你联系
    email: gavin.tech@qq.com
    server: https://acme-staging-v02.api.letsencrypt.org/directory
    privateKeySecretRef:
      # 将用来存储 Private Key 的 Secret 资源
      name: letsencrypt-staging
    # Add a single challenge solver, HTTP01 using nginx
    solvers:
    - http01:
        ingress:
          class: nginx
2、创建正式 ClusterIssuer
# cat letsencrypt-prod.yaml

apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: letsencrypt-prod
spec:
  acme:
    server: https://acme-v02.api.letsencrypt.org/directory
    email:  gavin.tech@qq.com
    privateKeySecretRef:
      name: letsencrypt-prod
    solvers:
      - http01:
          ingress:
            class: nginx
3、手动生成证书
apiVersion: cert-manager.io/v1 
kind: Certificate
metadata:
  name: coolops-tls
spec:
  secretName: coolops-tls
  duration: 2160h # 90d
  renewBefore: 360h # 15d
  isCA: false
  dnsNames:
  - "coolops.cn"
  - "*.coolops.cn"
  issuerRef:
    name: letsencrypt-staging
    kind: ClusterIssuer
    group: cert-manager.io

创建过后,通过以下命令查看状态

# kubectl get certificate
NAME                  READY   SECRET               AGE
coolops-tls   True    coolops-tls   122m

待测试证书生成无误后,就可以生成生产证书了,如下定义 YAML。

apiVersion: cert-manager.io/v1 
kind: Certificate
metadata:
  name: coolops-tls-prod
spec:
  secretName: coolops-tls-prod
  duration: 2160h # 90d
  renewBefore: 360h # 15d
  isCA: false
  dnsNames:
  - "coolops.cn"
  - "*.coolops.cn"
  issuerRef:
    name: letsencrypt-prod
    kind: ClusterIssuer
    group: cert-manager.io
4、自动生成证书

自动生成证书通过在 ingress 中添加 annotaions 实现,如下。

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: test-coolops
  annotations:
    # 务必添加以下两个注解, 指定 ingress 类型及使用哪个 cluster-issuer
    kubernetes.io/ingress.class: "nginx"
    kubernetes.io/tls-acme: "true"
    cert-manager.io/cluster-issuer:"letsencrypt-staging" # 这里先用测试环境的证书测通后,就可以替换成正式服证书

    # 如果你使用 issuer, 使用以下注解 
    # cert-manager.io/issuer: "letsencrypt-staging"
spec:
  tls:
  - hosts:
    - test-nginx.coolops.cn                # TLS 域名 - 这里仅支持单域名,下面会讲通配符的域名配置
    secretName: test-nginx-coolops-tls   # 用于存储证书的 Secret 对象名字,可以是任意名称,cert-manager会自动生成对应名称的证书名称
  rules:
  - host: test-nginx.coolops.cn
    http:
      paths:
      - path: /
        backend:
          serviceName: nginx
          servicePort: 80

dns01

这里使用阿里 DNS 进行校验。其他 webhook 可以到这里【2】进行查看。

1、部署 wehbook

这里使用的是 https://github.com/pragkent/alidns-webhook

由于改项目很多地方没有及时更新,对于我这个版本的 apiVersion 是不一样的,所以更改后如下:

---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: alidns-webhook
  namespace: cert-manager
  labels:
    app: alidns-webhook

---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: alidns-webhook
  namespace: cert-manager
  labels:
    app: alidns-webhook
rules:
  - apiGroups:
      - ''
    resources:
      - 'secrets'
      - 'configmaps'
    verbs:
      - 'get'

---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: alidns-webhook
  namespace: cert-manager
  labels:
    app: alidns-webhook
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: Role
  name: alidns-webhook
subjects:
  - apiGroup: ""
    kind: ServiceAccount
    name: alidns-webhook
    namespace: cert-manager

---
# Grant the webhook permission to read the ConfigMap containing the Kubernetes
# apiserver's requestheader-ca-certificate.
# This ConfigMap is automatically created by the Kubernetes apiserver.
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: alidns-webhook:webhook-authentication-reader
  namespace: kube-system
  labels:
    app: alidns-webhook
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: Role
  name: extension-apiserver-authentication-reader
subjects:
  - apiGroup: ""
    kind: ServiceAccount
    name: alidns-webhook
    namespace: cert-manager
---
# apiserver gets the auth-delegator role to delegate auth decisions to
# the core apiserver
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: alidns-webhook:auth-delegator
  namespace: cert-manager
  labels:
    app: alidns-webhook
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: system:auth-delegator
subjects:
  - apiGroup: ""
    kind: ServiceAccount
    name: alidns-webhook
    namespace: cert-manager
---
# Grant cert-manager permission to validate using our apiserver
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: alidns-webhook:domain-solver
  labels:
    app: alidns-webhook
rules:
  - apiGroups:
      - acme.yourcompany.com
    resources:
      - '*'
    verbs:
      - 'create'
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: alidns-webhook:domain-solver
  labels:
    app: alidns-webhook
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: alidns-webhook:domain-solver
subjects:
  - apiGroup: ""
    kind: ServiceAccount
    name: cert-manager
    namespace: cert-manager

---
# Source: alidns-webhook/templates/service.yaml
apiVersion: v1
kind: Service
metadata:
  name: alidns-webhook
  namespace: cert-manager
  labels:
    app: alidns-webhook
spec:
  type: ClusterIP
  ports:
    - port: 443
      targetPort: https
      protocol: TCP
      name: https
  selector:
    app: alidns-webhook

---
# Source: alidns-webhook/templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: alidns-webhook
  namespace: cert-manager
  labels:
    app: alidns-webhook
spec:
  replicas:
  selector:
    matchLabels:
      app: alidns-webhook
  template:
    metadata:
      labels:
        app: alidns-webhook
    spec:
      serviceAccountName: alidns-webhook
      containers:
        - name: alidns-webhook
          image: pragkent/alidns-webhook:0.1.0
          imagePullPolicy: IfNotPresent
          args:
            - --tls-cert-file=/tls/tls.crt
            - --tls-private-key-file=/tls/tls.key
          env:
            - name: GROUP_NAME
              value: "acme.yourcompany.com"
          ports:
            - name: https
              containerPort: 443
              protocol: TCP
          livenessProbe:
            httpGet:
              scheme: HTTPS
              path: /healthz
              port: https
          readinessProbe:
            httpGet:
              scheme: HTTPS
              path: /healthz
              port: https
          volumeMounts:
            - name: certs
              mountPath: /tls
              readOnly: true
          resources:
            {}

      volumes:
        - name: certs
          secret:
            secretName: alidns-webhook-webhook-tls

---
apiVersion: apiregistration.k8s.io/v1
kind: APIService
metadata:
  name: v1alpha1.acme.yourcompany.com
  labels:
    app: alidns-webhook
  annotations:
    cert-manager.io/inject-ca-from: "cert-manager/alidns-webhook-webhook-tls"
spec:
  group: acme.yourcompany.com
  groupPriorityMinimum: 1000
  versionPriority: 15
  service:
    name: alidns-webhook
    namespace: cert-manager
  version: v1alpha1

---
# Create a selfsigned Issuer, in order to create a root CA certificate for
# signing webhook serving certificates
apiVersion: cert-manager.io/v1
kind: Issuer
metadata:
  name: alidns-webhook-selfsign
  namespace: cert-manager
  labels:
    app: alidns-webhook
spec:
  selfSigned: {}

---

# Generate a CA Certificate used to sign certificates for the webhook
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
  name: alidns-webhook-ca
  namespace: cert-manager
  labels:
    app: alidns-webhook
spec:
  secretName: alidns-webhook-ca
  duration: 43800h # 5y
  issuerRef:
    name: alidns-webhook-selfsign
  commonName: "ca.alidns-webhook.cert-manager"
  isCA: true

---

# Create an Issuer that uses the above generated CA certificate to issue certs
apiVersion: cert-manager.io/v1
kind: Issuer
metadata:
  name: alidns-webhook-ca
  namespace: cert-manager
  labels:
    app: alidns-webhook
spec:
  ca:
    secretName: alidns-webhook-ca

---

# Finally, generate a serving certificate for the webhook to use
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
  name: alidns-webhook-webhook-tls
  namespace: cert-manager
  labels:
    app: alidns-webhook
spec:
  secretName: alidns-webhook-webhook-tls
  duration: 8760h # 1y
  issuerRef:
    name: alidns-webhook-ca
  dnsNames:
  - alidns-webhook
  - alidns-webhook.cert-manager
  - alidns-webhook.cert-manager.svc
  - alidns-webhook.cert-manager.svc.cluster.local

部署完成后,可以看到如下 Pod 信息。

# kubectl get po -n cert-manager 
NAME                                       READY   STATUS      RESTARTS   AGE
alidns-webhook-6cdb855fdb-lw7x4            1/1     Running     0          175m
cert-manager-57d89b9548-cq78f              1/1     Running     0          4h35m
cert-manager-cainjector-5bcf77b697-hmjj2   1/1     Running     0          4h35m
cert-manager-startupapicheck-hn5g5         0/1     Completed   4          4h35m
cert-manager-webhook-8687fc66d4-ppllm      1/1     Running     0          4h35m
2、在阿里云创建 secret key id 和 secret

建议创建一个 RAM 账户,然后授权 AliyunDNSFullAccess 权限,再创建 Accesskey。

3、创建 Secret
# cat aliyun-secret.yaml 

apiVersion: v1
kind: Secret
metadata:
  name: alidns-secret
  namespace: cert-manager
data:
  access-key: TFRBSTV0UUUzxxxxxxxxxxxxxxL 
  secret-key: NmtWQ1Mycmx0OWxxxxxxxxxxxxxxxx
4、创建测试 ClusterIssuer
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: letsencrypt-staging-dns
spec:
  acme:
    email: coolops@163.com
    server: https://acme-staging-v02.api.letsencrypt.org/directory 
    privateKeySecretRef:
      name: letsencrypt-stagging-account-key
    solvers:
    - dns01:
        webhook:
          groupName: acme.yourcompany.com
          solverName: alidns
          config:
            region: ""
            accessKeySecretRef:
              name: alidns-secret
              key: access-key
            secretKeySecretRef:
              name: alidns-secret
              key: secret-key
5、创建生产 ClusterIssuer
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: letsencrypt-prod-dns
spec:
  acme:
    email: coolops@163.com
    server: https://acme-v02.api.letsencrypt.org/directory 
    privateKeySecretRef:
      name: letsencrypt-prod-account-key
    solvers:
    - dns01:
        webhook:
          groupName: acme.yourcompany.com
          solverName: alidns
          config:
            region: ""
            accessKeySecretRef:
              name: alidns-secret
              key: access-key
            secretKeySecretRef:
              name: alidns-secret
              key: secret-key
6、手动生成证书
apiVersion: cert-manager.io/v1 
kind: Certificate
metadata:
  name: coolops-tls 
spec:
  secretName: coolops-tls 
  duration: 2160h # 90d
  renewBefore: 360h # 15d
  isCA: false
  dnsNames:
  - "coolops.cn"
  - "*.coolops.cn"
  issuerRef:
    name: letsencrypt-staging-dns
    kind: ClusterIssuer
    group: cert-manager.io

带测试证书生成无误后,可以替换为生产证书。

7、ingress 中使用证书
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: test-coolops
  annotations:
    # 务必添加以下两个注解, 指定 ingress 类型及使用哪个 cluster-issuer
    kubernetes.io/ingress.class: "nginx"
    cert-manager.io/cluster-issuer:"letsencrypt-staging-dns" # 这里先用测试环境的证书测通后,就可以替换成正式服证书
spec:
  tls:
  - hosts:
    - "*.coolops.cn"                     # 如果填写单域名就只会生产单域名的证书,如果是通配符请填写"*.example.com", 注意:如果填写example.com只会生成www.example.com一个域名。
    secretName: coolops-tls   # 测试的证书,填写刚刚创建Certificate的名称,注意更换环境时证书也要一起更换,这里并不会像单域名一样自动生成
  rules:
  - host: www.coolops.cn
    http:
      paths:
      - path: /
        backend:
          serviceName: nginx
          servicePort: 80

链接

【1】https://letsencrypt.org/zh-cn/docs/staging-environment/
【2】https://cert-manager.io/docs/configuration/acme/dns01/
【3】https://github.com/PowerDos/k8s-cret-manager-aliyun-webhook-demo


Comment