跳到主要内容

2.Traefik-ACME自签发证书

ACME

Traefik 通过扩展 CRD 的方式来扩展 Ingress 的功能,除了默认的用 Secret 的方式可以支持应用的 HTTPS 之外,还支持自动生成 HTTPS 证书。

部署 whoami 服务

比如现在我们有一个如下所示的** whoami** 应用:

apiVersion: v1
kind: Service
metadata:
name: whoami
spec:
ports:
- protocol: TCP
name: web
port: 80
selector:
app: whoami
---
kind: Deployment
apiVersion: apps/v1
metadata:
name: whoami
labels:
app: whoami
spec:
replicas: 2
selector:
matchLabels:
app: whoami
template:
metadata:
labels:
app: whoami
spec:
containers:
- name: whoami
image: registry.cn-beijing.aliyuncs.com/xxk8s/whoami
ports:
- name: web
containerPort: 80

应用配置

root@master01:/k8s-traefik# kubectl apply -f whoim.yaml -n test-pod
service/whoami created
deployment.apps/whoami created
root@master01:/k8s-traefik#
root@master01:/k8s-traefik# kubectl get pod -n test-pod
NAME READY STATUS RESTARTS AGE
mysql-0 1/1 Running 0 42h
whoami-84b5557bd8-5mlph 1/1 Running 0 4s
whoami-84b5557bd8-8rsdl 1/1 Running 0 4s

定义一个 IngressRoute 对象:

apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
name: ingressroute-demo
spec:
entryPoints:
- web
routes:
- match: Host(`who.xinn.com`) && PathPrefix(`/notls`)
kind: Rule
services:
- name: whoami
port: 80
root@master01:/k8s-traefik# kubectl apply -f whoami-traefik.yaml -n test-pod
ingressroute.traefik.containo.us/ingressroute-demo created

通过 entryPoints 指定了我们这个应用的入口点是 web,也就是通过 32080 端口访问,然后访问的规则就是要匹配 who.xinn.com 这个域名,并且具有 /notls 的路径前缀的请求才会被 whoami 这个 Service 所匹配。
我们可以直接创建上面的几个资源对象,然后对域名做对应的解析后,就可以访问应用了:

2b215164fd04

在 IngressRoute 对象中我们定义了一些匹配规则,这些规则在 Traefik 中有如下定义方式:
4e593ec95bc8

使用自签名证书

如果我们需要用 HTTPS 来访问我们这个应用的话,就需要监听 websecure 这个入口点,也就是通过 443 端口来访问,同样用 HTTPS 访问应用必然就需要证书,这里我们用 openssl 来创建一个自签名的证书:

$ openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout tls.key -out tls.crt -subj "/CN=who.xinn.com"

然后通过 Secret 对象来引用证书文件:

# 要注意证书文件名称必须是 tls.crt 和 tls.key
root@master01:/k8s-traefik# kubectl create secret tls who-tls --cert=tls.crt --key=tls.key -n test-pod
secret/who-tls created

这个时候我们就可以创建一个 HTTPS 访问应用的 IngressRoute 对象了:

apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
name: ingressroute-tls-demo
spec:
entryPoints:
- websecure
routes:
- match: Host(`who.xinn.com`) && PathPrefix(`/tls`)
kind: Rule
services:
- name: whoami
port: 80
tls:
secretName: who-tls
root@master01:/k8s-traefik# vim whoami-traefik-tls.yaml
root@master01:/k8s-traefik# kubectl apply -f whoami-traefik-tls.yaml -n test-pod
ingressroute.traefik.containo.us/ingressroute-tls-demo created

创建完成后就可以通过 HTTPS 来访问应用了,由于我们是自签名的证书,所以证书是不受信任的:

9d297cda970f

基于 ACME 自签发证书

除了手动提供证书的方式之外 Traefik 同样也支持使用 Let’s Encrypt自动生成证书,要使用 Let’s Encrypt 来进行自动化 HTTPS,就需要首先开启 ACME,开启 ACME 需要通过静态配置的方式,也就是说可以通过环境变量、启动参数等方式来提供。
ACME 有多种校验方式 tlsChallengehttpChallengednsChallenge 三种验证方式,之前更常用的是 http 这种验证方式,关于这几种验证方式的使用可以查看文档:https://www.qikqiak.com/traefik-book/https/acme/ 了解他们之间的区别。要使用 tls 校验方式的话需要保证 Traefik 的 443 端口是可达的,dns 校验方式可以生成通配符的证书,只需要配置上 DNS 解析服务商的 API 访问密钥即可校验。我们这里用 DNS 校验的方式来为大家说明如何配置 ACME。
我们可以重新修改 Helm 安装的 values 配置文件,添加如下所示的定制参数:

# ci/deployment-prod.yaml
# 设置 Traefik 的 ACME 自动证书管理功能
additionalArguments:
# 使用 dns 验证方式
- --certificatesResolvers.ali.acme.dnsChallenge.provider=alidns
# 使用阿里云 DNS 验证域名所有权
# 先使用staging环境进行验证,验证成功后再使用移除下面一行的配置
# - --certificatesResolvers.ali.acme.caServer=https://acme-staging-v02.api.letsencrypt.org/directory
# 邮箱配置
- --certificatesResolvers.ali.acme.email=562188771@qq.com
# 保存 ACME 证书的位置
- --certificatesResolvers.ali.acme.storage=/data/acme.json

envFrom:
- secretRef:
name: traefik-alidns-secret
# ALICLOUD_ACCESS_KEY
# ALICLOUD_SECRET_KEY
# ALICLOUD_REGION_ID

#persistence:
# enabled: true # 开启持久化
# accessMode: ReadWriteOnce
# size: 128Mi
# path: /data

persistence:
enabled: true
name: data
# existingClaim: ""
accessMode: ReadWriteOnce
size: 128Mi
storageClass: "rook-cephfs"
# volumeName: ""
path: /data
annotations: {}



# 由于上面持久化了ACME的数据,需要重新配置下面的安全上下文
securityContext:
readOnlyRootFilesystem: false
runAsGroup: 0
runAsUser: 0
runAsNonRoot: false
kubectl create secret generic traefik-alidns-secret -n kube-system \
--from-literal=KEY=xxxxxxxxxxxxxx \
--from-literal=SECRET_KEY=xxxxxxxxxxxxxx \
--from-literal=REGION_ID=cn-beijing

这样我们可以通过设置 --certificatesresolvers.ali.acme.dnschallenge.provider=alidns 参数来指定指定阿里云的 DNS 校验,要使用阿里云的 DNS 校验我们还需要配置 3 个环境变量:ALICLOUD_ACCESS_KEYALICLOUD_SECRET_KEYALICLOUD_REGION_ID,分别对应我们平时开发阿里云应用的时候的密钥,可以登录阿里云后台 https://ram.console.aliyun.com/manage/ak 获取,由于这是比较私密的信息,所以我们用 Secret 对象来创建:

$ kubectl create secret generic traefik-alidns-secret --from-literal=ALICLOUD_ACCESS_KEY=<aliyun ak> --from-literal=ALICLOUD_SECRET_KEY=<aliyun sk> --from-literal=ALICLOUD_REGION_ID=cn-beijing -n kube-system

创建完成后将这个 Secret 通过环境变量配置到 Traefik 的应用中,还有一个值得注意的是验证通过的证书我们这里存到 /data/acme.json 文件中,我们一定要将这个文件持久化,否则每次 Traefik 重建后就需要重新认证,而 Let’s Encrypt 本身校验次数是有限制的。
所以我们在 values 中重新开启了数据持久化,不过开启过后需要我们提供一个可用的 PV 存储,由于我们将 Traefik 固定到 master1 节点上的,所以我们可以创建一个 hostpath 类型的 PV(后面会详细讲解):

$ cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: PersistentVolume
metadata:
name: traefik
spec:
accessModes:
- ReadWriteOnce
capacity:
storage: 128Mi
hostPath:
path: /data/k8s/traefik
EOF
root@master01:/k8s-traefik# kubectl get pvc -n kube-system
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
traefik Bound pvc-856bc7d6-4d51-4904-b200-57c63cc1d38e 128Mi RWO rook-cephfs 3m10s
root@master01:/k8s-traefik/traefik# helm upgrade --install traefik ./ -f ./xin-traefik-valuse.yaml --namespace kube-system
Release "traefik" has been upgraded. Happy Helming!
NAME: traefik
LAST DEPLOYED: Wed Aug 28 16:52:29 2024
NAMESPACE: kube-system
STATUS: deployed
REVISION: 4
TEST SUITE: None
NOTES:
Traefik Proxy v2.10.4 has been deployed successfully on kube-system namespace !

🚨 When enabling persistence for certificates, permissions on acme.json can be
lost when Traefik restarts. You can ensure correct permissions with an
initContainer. See https://github.com/traefik/traefik-helm-chart/issues/396 for
more info. 🚨

使用如下所示的命令更新 Traefik:

$ helm upgrade --install traefik ./traefik -f ./traefik/ci/deployment-prod.yaml --namespace kube-system

更新完成后现在我们来修改上面我们的 whoami 应用:

apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
name: ingressroute-tls-demo
spec:
entryPoints:
- websecure
routes:
- match: Host(`who.qikqiak.com`) && PathPrefix(`/tls`)
kind: Rule
services:
- name: whoami
port: 80
tls:
certResolver: ali
domains:
- main: '*.qikqiak.com'

在 Traefik 的配置中,使用 certResolver 指定了一个 ACME 证书解析器。这个解析器负责与 ACME 服务器(如 Let's Encrypt)进行交互,以自动获取和续订证书。
certResolver: ali指定使用名为 ali 的证书解析器。
通过指定 .xinn.cc,获取一个通配符证书,这个证书将适用于 xinn.cc 下的所有子域名。
在 Traefik 的 ACME 配置中,指定了证书存储的路径。通常,Traefik 会将多个域名的证书自动写入到同一个 acme.json 文件中,它存储了由 ACME 证书解析器(certResolver)管理的 SSL/TLS 证书以及相关的密钥和元数据。

  • 首次获取: 当一个新的域名请求到达 Traefik,并且需要使用 TLS 加密时,Traefik 会检查 acme.json 文件中是否已经存在对应的证书。如果没有,Traefik 会使用 ACME 协议与指定的 ACME 服务器通信,自动申请一个新的证书。
  • 续订证书: Traefik 会自动检查已经存储的证书的有效期,并在证书接近过期时自动与 ACME 服务器通信来续订证书。续订后的证书也会存储在 acme.json 文件中。
root@master01:/k8s-traefik# kubectl apply -f whoami-traefik-tls-ali.yaml -n test-pod
ingressroute.traefik.containo.us/ingressroute-tls-demo created

其他的都不变,只需要将 tls 部分改成我们定义的 ali 这个证书解析器,如果我们想要生成一个通配符的域名证书的话可以定义 domains 参数来指定,然后更新 IngressRoute 对象,这个时候我们再去用 HTTPS 访问我们的应用(当然需要将域名在阿里云 DNS 上做解析):

85a9ae64b2bd

我们可以看到访问应用已经是受浏览器信任的证书了,查看证书我们还可以发现该证书是一个通配符的证书。