使用cert-manager实现Ingress https

什么是https

超文本传输协议HTTP协议被用于在Web浏览器和网站服务器之间传递信息,HTTP协议以明文方式发送内容,不提供任何方式的数据加密,如果攻击者截取了Web浏览器和网站服务器之间的传输报文,就可以直接读懂其中的信息,因此,HTTP协议不适合传输一些敏感信息,比如:信用卡号、密码等支付信息。

为了解决HTTP协议的这一缺陷,需要使用另一种协议:安全套接字层超文本传输协议HTTPS,为了数据传输的安全,HTTPS在HTTP的基础上加入了SSL协议,SSL依靠证书来验证服务器的身份,并为浏览器和服务器之间的通信加密。http和https使用的是完全不同的连接方式,用的端口也不一样,前者是80,后者是443。

什么是cert-manager

cert-manager 是一个云原生证书管理开源项目,用于在 Kubernetes 集群中提供 HTTPS 证书并自动续期,支持 Let’s Encrypt, HashiCorp Vault 这些免费证书的签发。在Kubernetes集群中,我们可以通过 Kubernetes Ingress 和 Let’s Encrypt 实现外部服务的自动化 HTTPS。

在Kubernetes集群中使用 HTTPS 协议,需要一个证书管理器、一个证书自动签发服务,主要通过 Ingress 来发布 HTTPS 服务,因此需要Ingress Controller并进行配置,启用 HTTPS 及其路由。
image

环境依赖

  • 本文使用 Helm 安装,所以请确保 Helm 已安装,且版本最好>2.10
  • 集群必须已经装有 Ingress Controller
  • 外部客户端配置hosts,IP 指向 Ingress Controller 对外暴露的地址(如果IP是公网地址并做了域名解析,则无需配置)

部署cert-manager

使用helm安装cert-manager

安装 CustomResourceDefinition资源

1
kubectl apply -f https://raw.githubusercontent.com/jetstack/cert-manager/release-0.7/deploy/manifests/00-crds.yaml

创建cert-manager namespace

1
kubectl create namespace cert-manager

标记cert-Manager命名空间以禁用资源验证

1
kubectl label namespace cert-manager certmanager.k8s.io/disable-validation=true

添加 Jetstack Helm repository

1
helm repo add jetstack https://charts.jetstack.io

更新本地Helm chart repository

1
helm repo update

使用Helm chart安装cert-manager

1
2
3
4
5
helm install \
--name cert-manager \
--namespace cert-manager \
--version v0.7.0 \
jetstack/cert-manager

查看cert-manager部署结果

1
2
3
4
5
# kubectl get pods --namespace cert-manager
NAME READY STATUS RESTARTS AGE
cert-manager-5658b7db79-824lt 1/1 Running 0 11h
cert-manager-cainjector-768fd47f68-ls6zh 1/1 Running 0 11h
cert-manager-webhook-5b4bc6b547-8qk2v 1/1 Running 0 11h

创建ClusterIssuer

我们需要先创建一个签发机构,cert-manager 给我们提供了 Issuer 和 ClusterIssuer 这两种用于创建签发机构的自定义资源对象,Issuer 只能用来签发自己所在 namespace 下的证书,ClusterIssuer 可以签发任意 namespace 下的证书,这里以 ClusterIssuer 为例创建一个签发机构:

1
2
3
4
5
6
7
8
9
10
11
12
# cat issuer.yaml 
apiVersion: certmanager.k8s.io/v1alpha1
kind: ClusterIssuer
metadata:
name: letsencrypt-prod
spec:
acme:
server: https://acme-v02.api.letsencrypt.org/directory
email: xxxx@126.com
privateKeySecretRef:
name: letsencrypt-prod
http01: {}

说明:

  • metadata.name 是我们创建的签发机构的名称,后面我们创建证书的时候会引用它
  • spec.acme.email 是你自己的邮箱,证书快过期的时候会有邮件提醒,不过 cert-manager 会利用 acme 协议自动给我们重新颁发证书来续期
  • spec.acme.server 是 acme 协议的服务端,我们这里用 Let’s Encrypt,这个地址就写死成这样就行
  • spec.acme.privateKeySecretRef 指示此签发机构的私钥将要存储到哪个 Secret 对象中,名称不重要
  • spec.acme.http01 这里指示签发机构使用 HTTP-01 的方式进行 acme 协议 (还可以用 DNS 方式,acme 协议的目的是证明这台机器和域名都是属于你的,然后才准许给你颁发证书)

部署

1
# kubectl apply -f issuer.yaml

查看clusterissuer创建结果

1
2
3
# kubectl get clusterissuer
NAME AGE
letsencrypt-prod 11m

创建Certificate

有了签发机构,接下来我们就可以生成免费证书了,cert-manager 给我们提供了 Certificate 这个用于生成证书的自定义资源对象,它必须局限在某一个 namespace 下,证书最终会在这个 namespace 下以 Secret 的资源对象存储,创建一个 Certificate 对象:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# cat cert.yaml 
apiVersion: certmanager.k8s.io/v1alpha1
kind: Certificate
metadata:
name: dashboard-imroc-io
namespace: istio-system
spec:
secretName: dashboard-imroc-io
issuerRef:
name: letsencrypt-prod
kind: ClusterIssuer
dnsNames:
- istio.kiali.com
acme:
config:
- http01:
ingressClass: traefik
domains:
- istio.kiali.com

说明:

  • spec.secretName 指示证书最终存到哪个 Secret 中
  • spec.issuerRef.kind 值为 ClusterIssuer 说明签发机构不在本 namespace 下,而是在全局
  • spec.issuerRef.name 我们创建的签发机构的名称 (ClusterIssuer.metadata.name)
  • spec.dnsNames 指示该证书的可以用于哪些域名
  • spec.acme.config.http01.ingressClass 使用 HTTP-01 方式校验该域名和机器时,cert-manager 会尝试创建Ingress 对象来实现该校验,如果指定该值,会给创建的 Ingress 加上 kubernetes.io/ingress.class 这个 annotation,如果我们的 Ingress Controller 是 traefik Ingress Controller,指定这个字段可以让创建的 Ingress 被 traefik Ingress Controller 处理。
  • spec.acme.config.http01.domains 指示该证书的可以用于哪些域名

执行部署命令

1
# kubectl apply -f cert.yaml

查看certificate创建结果

1
2
3
# kubectl get certificate -n istio-system
NAME AGE
dashboard-imroc-io 54s

执行上述步骤,如有问题,可使用如下命令排查原因

1
2
# kubectl describe -n istio-system certificate dashboard-imroc-io
# kubectl describe clusterissuer letsencrypt-prod

查看生成的secret 结果

1
2
# kubectl get secret -n istio-system | grep dashboard-imroc-io
dashboard-imroc-io kubernetes.io/tls 3 2m32s

测试Ingress使用https

创建一个nginx

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
# cat test-nginx.yaml 
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: my-nginx
spec:
replicas: 1
template:
metadata:
labels:
run: my-nginx
spec:
containers:
- name: my-nginx
image: nginx
ports:
- containerPort: 443
---
apiVersion: v1
kind: Service
metadata:
name: my-nginx
labels:
app: my-nginx
spec:
ports:
- port: 443
protocol: TCP
name: https
selector:
run: my-nginx
---
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: my-nginx
annotations:
kubernetes.io/ingress.class: "traefik"
kubernetes.io/tls-acme: "true"
certmanager.k8s.io/cluster-issuer: "letsencrypt-prod"
spec:
rules:
- host: test.nginx.com
http:
paths:
- backend:
serviceName: my-nginx
servicePort: 443
path: /
tls:
- secretName: dashboard-imroc-io
hosts:
- test.nginx.com

需要注意的是上面我们添加的两个annotations非常重要,这个将告诉 Cert Manager 去生成证书,然后由于我们这里要使用 HTTPS,所以我们需要添加一个 tls 证书,而证书就是通过k8sui-tls这个 Secret 对象来提供的,要注意的是这个 Secret 对象并不是我们手动创建的,而是 Cert Manager 自动创建的证书对应的对应。然后直接创建这个资源对象即可:

1
# kubectl apply -f test-nginx.yaml

创建完成后隔一会儿我们可以看到会多出现一个随机名称的 Ingress 对象,这个 Ingress 对象就是用来专门验证证书的:

1
2
3
# kubectl get ingress -n istio-system
NAME HOSTS ADDRESS PORTS AGE
cm-acme-http-solver-z562f test.nginx.com 80 62s

我们可以通过 Traefik 的 Dashboard 可以观察到这一变化,验证成功后,这个 Ingress 对象也自动删除了:

这个时候我们可以去describe下我们的 Ingress 对象,查看有无报错

1
# kubectl describe ingress my-nginx

同样我们可以去查看 Cert manager 的 Pod 日志信息:

1
# kubectl logs -f cert-manager-5658b7db79-824lt --namespace cert-manager

最后,我们来打开浏览器使用https访问服务

到这里我们就完成了使用Let’s Encrypt实现Kubernetes Ingress自动化 HTTPS。

参考资料
项目地址:https://github.com/jetstack/cert-manager
文档地址:https://cert-manager.readthedocs.io

0%