利用 Cert-Manager 为Webhook Service 颁发证书

4,660 阅读4分钟

根据K8S文档描述, Kubernetes API Server 通过HTTPS POST访问Webhook Server, 也就是说Webhook Server 必须要监听在HTTPS协议上.

要使用HTTPS协议, 有3个东西是必须的: ca, key, cert.
其中ca 是指ca 证书, key 是指服务器使用使用的私钥, cert 指公钥, 一般也称为服务器证书.
API-Server 在访问Webhook server 的时候, 要指定ca证书
Webhook Server在启动的时候, 要加载key和cert.

社区推荐使用Cert-Manager来为webhook 提供证书管理服务.
本文不讨论Cert-Manager的工作原理, 而是从使用者的角度来介绍如何利用Cert-Manager为Webhook配置证书.

cert-manager is a native Kubernetes certificate management controller. It can help with issuing certificates from a variety of sources, such as Let’s Encrypt, HashiCorp Vault, Venafi, a simple signing key pair, or self signed.

Cert-Manager 的基本概念

要使用cert-manager, 需要先了解这几个概念: Issuers, Certificate, ca injector.

Issuers

Issuers 是cert-manager 定义的k8s resource, 相当于证书颁发机构, 用于生成证书.
Issuers 又分为 Issuer 和 ClusterIssuer:

Issuer 是 namespace level 的 issuer, 可以为其所在的namespace 提供证书.

apiVersion: cert-manager.io/v1
kind: Issuer
metadata:
  ...
spec:
  ...

ClusterIssuer 是 cluster level 的issuer, 可以为集群内所有namespace 提供证书.

apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  ...
spec:
  ...

Certificate

Certificate 是 cert-manager 在 k8s 中定义的一种资源, 属于 namespace level.

cert-manager 会根据certificate的定义结合issuer来创建真正的证书对(cert-pair)

apiVersion: cert-manager.io/v1alpha2
kind: Certificate
metadata:
  name: webhook-cert
  namespace: {{ .Release.Namespace }}
spec:
  dnsNames:
  - webhook.{{ .Release.Namespace }}.svc
  - webhook.{{ .Release.Namespace }}.svc.cluster.local
  issuerRef:
    kind: Issuer
    name: selfsigned-issuer
  secretName: webhook-server-cert

CA Injector

CA Injector 是cert-manager的一个controller. 它可以将caBundle 字段写入到这三个K8S Resource中:

  • ValidatingWebhookConfiguration
  • MutatingWebhookConfiguration
  • CustomResourceDefinition

要使用CA Injector, 需要在webhook的yaml中添加 annotation: cert-manager.io/inject-ca-from. 告诉CA Injector, 要将哪一个证书写到cabundle中.

例如:

apiVersion: admissionregistration.k8s.io/v1beta1
kind: ValidatingWebhookConfiguration
metadata:
  ...
annotations:
  cert-manager.io/inject-ca-from: example1/webhook1-certificate
webhooks:
  ...

这个例子表示将使用namespace example1下的 secret webhook-certificate 的ca 填充到cabundle中.

试一试

1. 部署Cert-Manager

kubectl apply --validate=false -f https://github.com/jetstack/cert-manager/releases/download/v1.0.3/cert-manager.yaml

2. 创建 Issuer

通常来讲, 为webhook-server 颁发证书使用自签名证书即可, 因为除了K8S API Server, 没有其他外部服务会去访问webhook.

issuer.yaml:

apiVersion: cert-manager.io/v1
kind: Issuer
metadata:
  name: selfsigned-issuer
  namespace: default
spec:
  selfSigned: {}

创建

$ kubectl apply -f issuer.yaml

$ kubectl get issuers selfsigned-issuer -o wide
NAME                READY   STATUS   AGE
selfsigned-issuer   True             2m7s

3. 创建Certificate

现在我们要创建certificate, 一个certificate最基本的要包含dnsNames 和 issuerRef.
dnsNames 表示这个证书签发给哪个域名.
issuerRef 表示使用哪个issuer来签发这个证书.
最后还需要secretName, 表示证书存放在哪个secret里面.

cert.yaml

apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
  name: luna-cert
  namespace: default
spec:
  dnsNames:
  - luna-webhook.default.svc
  - luna-webhook.default.svc.cluster.local
  issuerRef:
    kind: Issuer
    name: selfsigned-issuer
  secretName: luna-cert

创建证书

$ kubectl apply -f cert.yaml

$ k get certificate luna-cert
NAME        READY   SECRET      AGE
luna-cert   True    luna-cert   1m

4. 创建Webhook

webhook.yaml

apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingWebhookConfiguration
metadata:
  annotations:
    cert-manager.io/inject-ca-from: default/luna-cert
  name: luna-validating-webhook-configuration
webhooks:
- admissionReviewVersions:
  - v1beta1
  clientConfig:
    service:
      name: luna-webhook
      namespace: default
      path: /luna-validate-webhook
      port: 443
  failurePolicy: Fail
  matchPolicy: Equivalent
  name: vservice
  rules:
  - apiGroups:
    - ""
    apiVersions:
    - v1
    operations:
    - CREATE
    - UPDATE
    resources:
    - services
    scope: '*'
  sideEffects: None
  timeoutSeconds: 10

创建

kubectl apply -f webhook.yaml

现在再看刚刚创建的webhook, 发现caBundle已经被填充进去了.

apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingWebhookConfiguration
metadata:
  ...
annotations:
  cert-manager.io/inject-ca-from: default/luna-cert
webhooks:
- admissionReviewVersions:
  - v1beta1
  clientConfig:
    caBundle: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUMrakNDQWVLZ0F3SUJBZ0lRSjFwc3ZsdmZsNlNabUhvWFI3SUVBekFOQmdrcWhraUc5dzBCQVFzRkFEQUEKTUI0WERUSXdNVEF5TWpFeU1ESXdOMW9YRFRJeE1ERXlNREV5TURJd04xb3dBRENDQVNJd0RRWUpLb1pJaHZjTgpBUUVCQlFBRGdnRVBBRENDQVFvQ2dnRUJBS3M2czAxR1FRa3R6VGZUQXRBTkV3RnJ1RS95WEVINmxQaVhwTGoyCnIzQjBQZkFLem1wZDdYd2QrbUxaNnA1aytDdEFKc293ZU5lRUNDd3prZE1Ha3p0cE1zK1huZ1d1dTVsajJZVUwKaW81Tm5TMjVYaHkxYlZOb0Y0SENuMGlrMWxLTXZBZnBEMXlKeDBkalY4TzcrdkMwL2NkN25BanMzbFcrVnVCcwpUT2tlY1g4elJ2YVBnd1pGRUd3SjR2MmRITGVsZjBvbkdRU25yRWZvMEE5am9DaGFGWkdzcEUrTEwwdm5mOWhXCkYzVVVLaFIrd25PdnZOajJYa2dzaktPYmRtUVpEaENBbDJxekw2cGNYZ2xPc21hTjVTMEJaYm5SSWRIZERBU1gKVGN1Nk42QzJjcUlHNnA3OW5ZeXZMajFaM0diTFdnODl0M3hSRGhxUkZleDdVaDBDQXdFQUFhTndNRzR3RGdZRApWUjBQQVFIL0JBUURBZ1dnTUF3R0ExVWRFd0VCL3dRQ01BQXdUZ1lEVlIwUkFRSC9CRVF3UW9JWWJIVnVZUzEzClpXSm9iMjlyTG1SbFptRjFiSFF1YzNaamdpWnNkVzVoTFhkbFltaHZiMnN1WkdWbVlYVnNkQzV6ZG1NdVkyeDEKYzNSbGNpNXNiMk5oYkRBTkJna3Foa2lHOXcwQkFRc0ZBQU9DQVFFQW8wOS9QWGR2OCtYV0RMd1k5S2tTRjYyNApySGpoQkJpL0RMWExLTGFMa1NlUEpuSCtwcGQ2cmt4ejJudkpKWnVBTzhKYmlrR3o0ZkFsUkhqSmFxR3oxV1JhCkQ2ellXZ0NmWHQ3RVlJejJXZm50OVpNbnBWRU9wcEUrUnJ1QURBcnVsRGlJTk5Xc1dtOFluK3lWc3gvbkF5MVYKRk9HbEJ3aE1FNEdHLzZlNERFd2hFalpWR3FOMVlxcFFWOUJCQXhrZVhBcHMzTWMwS1EzTDlJMHlUZzdrdHdNYQp1eVdaU0t0SDZtMTFoNE1ueFBXcHJHTlNTTExwMytNcXlvMnI1Q25Rd0J3QmR4Rmg4NWFCaWhyOVd4K2NxejZkCjFrKzdxSEc2SEFVQjVCVGtRZ0hvWnpkR2VJN3pldXFsSG8wa2lZMlRNdkIvSEVSd21WQ3BsNXNsTXB1azJRPT0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo=

总结:

上面我展示了: 利用Cert-Manager自动为Webhook颁发证书.一个证书其实包含3方面内容, 可以查看Certificate 的 secret.

$ kubectl describe secrets luna-cert
Name:         luna-cert
Namespace:    default
Labels:       <none>
Annotations:  cert-manager.io/alt-names: luna-webhook.default.svc,luna-webhook.default.svc.cluster.local
              cert-manager.io/certificate-name: luna-cert
              cert-manager.io/common-name:
              cert-manager.io/ip-sans:
              cert-manager.io/issuer-group:
              cert-manager.io/issuer-kind: Issuer
              cert-manager.io/issuer-name: selfsigned-issuer
              cert-manager.io/uri-sans:

Type:  kubernetes.io/tls

Data
====
ca.crt:   1094 bytes
tls.crt:  1094 bytes
tls.key:  1675 bytes

这里展示的是Cert-Manager将ca.crt 填充到webhook的cabundle中.
另外两个tls.crt, tls.key需要挂载到webhook所在的pod中. 如果你用的是kubebuilder, 目录应该是 /tmp/k8s-webhook-server/serving-certs

luna-deployment.yaml
  template:
    spec:
      containers:
      - name: luna
        image: harbor.inner.galaxy.ksyun.com/galaxy/luna-controller:0.1.0
        imagePullPolicy: IfNotPresent
        ports:
        - containerPort: 8443
          name: webhook
          protocol: TCP
        volumeMounts:
        - mountPath: /tmp/k8s-webhook-server/serving-certs
          name: cert
          readOnly: true
      volumes:
      - name: cert
        secret:
          defaultMode: 420
          secretName: luna-cert

参考

cert-manager.io/docs/