使用k8s给你的服务签发证书

55次阅读

共计 2742 个字符,预计需要花费 7 分钟才能阅读完成。

使用场景

当我们需要进行服务端认证,甚至双向认证时,我们需要生成密钥对和服务信息,并使用 ca 对公钥和服务信息进行批准签发,生成一个证书。

我们简单描述下单向认证和双向认证的场景流程

在单向认证场景中:

  • 服务端会将自己的证书和公钥告知客户端
  • 客户端向 CA 查询该证书的合法性,确认合法后会记录服务端公钥
  • 客户端会与服务端明文通信确认加密方式,
  • 客户端确认加密方式后,会生成随机码作为对称加密密钥,以服务端的公钥对对称加密密钥进行加密,告知服务端,
  • 服务端以自己的私钥解密得到对称加密密钥,
  • 之后,客户端与服务端之间使用对称加密密钥进行加密通信。

在双向认证的场景中:

  • 服务端会将自己的证书和公钥告知客户端
  • 客户端向 CA 查询该证书的合法性,确认合法后会记录服务端公钥
  • 客户端会将自己的证书和公钥发给服务端,
  • 服务端发现客户端的证书也可以通过 CA 认证,则服务端会记录客户端的公钥
  • 然后客户端会与服务端明文通信确认加密方式,
  • 但服务端会用客户端的公钥将加密方式进行加密
  • 客户端使用自己的私钥解密得到加密方式,会生成随机码作为对称加密密钥,以服务端的公钥对对称加密密钥进行加密,告知服务端,
  • 服务端以自己的私钥解密得到对称加密密钥,
  • 之后,客户端与服务端之间使用对称加密密钥进行加密通信。

那么,我们如果要开放自己的 https 服务,或者给 kubelet 创建可用的客户端证书,就需要:

  • 生成密钥对
  • 生成使用方的信息
  • 使用 ca 对使用方的公钥和其他信息进行审核,签发,生成一个使用方证书。

这里使用方可以是客户端(kubelet)或服务端(比如一个我们自己开发的 webhook server)

手动签发证书

k8s 集群部署时会自动生成一个 CA(证书认证机构),当然这个 CA 是我们自动生成的,并不具有任何合法性。k8s 还提供了一套 api,用于对用户自主创建的证书进行认证签发。

准备

  • 安装 k8s 集群
  • 安装 cfssl 工具,从这里下载 cfssl 和 cfssljson

创建你的证书

执行下面的命令,生成 server.csr 和 server-key.pem。

cat <<EOF | cfssl genkey - | cfssljson -bare server
{
  "hosts": [
    "my-svc.my-namespace.svc.cluster.local",
    "my-pod.my-namespace.pod.cluster.local",
    "192.0.2.24",
    "10.0.34.2"
  ],
  "CN": "my-pod.my-namespace.pod.cluster.local",
  "key": {
    "algo": "ecdsa",
    "size": 256
  }
}
EOF

这里你可以修改文件里的内容,主要是:

  • hosts。服务地址,你可以填入 service 的域名,service 的 clusterIP,podIP 等等
  • CN。对于 SSL 证书,一般为网站域名;而对于代码签名证书则为申请单位名称;而对于客户端证书则为证书申请者的姓名
  • key。加密算法和长度。一般有 ecdsa 算法和 rsa 算法,rsa算法的 size 一般是 2048 或 1024

这一步生成的 server-key.pem 是服务端的私钥,而 server.csr 则含有公钥、组织信息、个人信息(域名)。

创建一个 CSR 资源

执行如下脚本:

cat <<EOF | kubectl apply -f -
apiVersion: certificates.k8s.io/v1beta1
kind: CertificateSigningRequest
metadata:
  name: my-svc.my-namespace
spec:
  request: $(cat server.csr | base64 | tr -d '\n')
  usages:
  - digital signature
  - key encipherment
  - server auth
EOF

在 k8s 集群中创建一个 csr 资源。注意要将第一步中创建的 server.csr 内容进行 base64 编码,去掉换行后填入 spec.request 中。spec.usages中填入我们对证书的要求,包括数字签名、密钥加密、服务器验证。一般填这三个就够了。

之后我们通过kubectl describe csr my-svc.my-namespace 可以看到:

Name:                   my-svc.my-namespace
Labels:                 <none>
Annotations:            <none>
CreationTimestamp:      Tue, 21 Mar 2017 07:03:51 -0700
Requesting User:        yourname@example.com
Status:                 Pending
Subject:
        Common Name:    my-svc.my-namespace.svc.cluster.local
        Serial Number:
Subject Alternative Names:
        DNS Names:      my-svc.my-namespace.svc.cluster.local
        IP Addresses:   192.0.2.24
                        10.0.34.2
Events: <none>

认证 csr

注意到,csr 的 status 是 pending,说明还没有被 CA 认证。在 k8s 集群中,如果是 node 上 kubelet 创建的 CSR,kube-controller-manager 会自动进行认证,而我们手动创建的证书,需要进行手动认证:
kubectl certificate approve

也可以拒绝:kubectl certificate deny

之后我们再检查 csr, 发现已经是 approved 了:

kubectl get csr
NAME                  AGE       REQUESTOR               CONDITION
my-svc.my-namespace   10m       yourname@example.com    Approved,Issued

我们可以通过

kubectl get csr my-svc.my-namespace -o jsonpath='{.status.certificate}' | base64 --decode > server.crt

命令,得到 server 的证书。之后你就可以使用 server.crt 和 server-key.pem 作为你的服务的 https 认证

流程总结

  1. 编写一个 json 文件,描述 server 的信息,包括域名(或 IP),CN,加密方式
  2. 执行 cfssl 命令生成 server 的密钥,和认证请求文件 server.csr
  3. 将 server.csr 内容编码,在 k8s 中创建一个 server 的 CSR 资源
  4. 手动对该 CSR 资源进行认证签发
  5. 将 k8s 生成的 server.crt 即服务端证书拷贝下来。
  6. server.crt 和 server-key.pem 即 server 的 https 服务配置

参考:https://kubernetes.io/docs/ta…

正文完
 0