Kubernetes (K8S) 中Traefik自动申请证书

2023-10-17 15:25:53 浏览数 (3)

Kubernetes (K8S) 中Traefik自动申请证书

王先森2023-08-292023-08-29

Traefik自动申请证书

Traefik实现自动申请HTTPS证书要使用Let’s Encrypt自动生成证书,需要使用ACME。需要在静态配置中定义 “证书解析器”,Traefik负责从ACME服务器中检索证书。

然后,每个 “路由器 “被配置为启用TLS,并通过tls.certresolver配置选项与一个证书解析器关联。

Traefik的ACME验证方式主要有以下三种:

  • tlsChallenge
  • httpChallenge
  • dnsChallenge

如果使用tlsChallenge,则要求Let’s Encrypt到 Traefik 443 端口必须是可达的。如果使用httpChallenge,则要求Let’s Encrypt到 Traefik 80端口必须是可达的。如果使用dnsChallenge,则需要对应的providers。

部署cert-manager

借助 Kubernetes,我们获得了一个强大且可扩展的平台来解决许多复杂的场景。cert-manager是一个功能强大的解决方案,可以帮助我们自动化和管理与 TLS 证书相关的几乎所有内容。它提供了一套针对各种场景的自定义资源定义(CRD),并与原生IngressGateway资源很好地集成。

cert-manager 在 Kubernetes 机密中存储和缓存证书和私钥,使它们高度可用,可供入口控制器(如 Traefik Proxy)或应用程序进一步使用。

注意:默认情况下,cert-manager 不会自动清除机密,从而允许它重新附加到已颁发的证书并避免颁发新证书。当您需要创建和删除大量资源并且不希望受到速率限制时,这变得非常方便。

cert-manager 可以与各种来源交互来颁发证书,包括 Let's EncryptHashiCorpVault 以及私有 PKI。对于AWS 私有证书颁发机构、Google Cloud 证书颁发机构服务或Cloudflare Origin CA 等不受支持的情况,外部颁发者允许您扩展证书管理器功能。

先决条件

要学习本教程,您需要具备以下条件:

  • Kubernetes 集群 >= v1.20
  • Let’s Encrypt 的公共托管 DNS 域 — 出于本文的目的,我将使用 腾讯云
  • Traefik 2.10,您可以通过这篇文章安装部署 Kubernetes部署升级Traefik2.6

您可以使用以下命令安装 cert-manager 1.12:

代码语言:javascript复制
kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.12.3/cert-manager.yaml

提供 Web 端口的服务。在本教程中,我将使用 whoami 作为示例

代码语言:javascript复制
cat > whoami.yml <<EOF 
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: 1
  selector:
    matchLabels:
      app: whoami
  template:
    metadata:
      labels:
        app: whoami
    spec:
      containers:
        - name: whoami
          image: containous/whoami
          ports:
            - name: web
              containerPort: 80
EOF

Traefik 代理与 cert-manager 和 Let’s Encrypt

让我们探索如何结合 Kubernetes 入口控制器(如 Traefik Proxy 和 cert-manager)来保护 Web 应用程序的安全。Let’s Encrypt 提供多种质询类型来验证域名的控制权。根据您的要求,您可以选择HTTP-01您的服务何时可供公共访问或DNS-01专用端点。

使用 Let Encrypt 时请注意速率限制。为了避免出现令人不快的意外,建议使用 Let’s Encrypt暂存环境:

代码语言:javascript复制
#测试(staging):https://acme-staging-v02.api.letsencrypt.org/directory
#生产(production):https://acme-v02.api.letsencrypt.org/directory

自动化 HTTPS

Let’s Encrypt 使用 ACME 协议来校验域名是否真的属于你,校验成功后就可以自动颁发免费证书,证书有效期只有 90 天,在到期前需要再校验一次来实现续期,而 cert-manager 是可以自动续期的,所以事实上并不用担心证书过期的问题。目前主要有 HTTP 和 DNS 两种校验方式。

HTTP-01 校验

HTTP-01 的校验是通过给你域名指向的 HTTP 服务增加一个临时 location,:在校验的时候 Let’s Encrypt 会发送http 请求到 http://<YOUR_DOMAIN>/.well-known/acme-challenge/<TOKEN>,其中 YOUR DOMAIN 就是被校验的域名,TOKEN 是 ert-manager 生成的一个路径,它通过修改 Ingress 规则来增加这个临时校验路径并指向提供TOKEN 的服务。Let’s Encrypt 会对比 TOKEN 是否符合预期,校验成功后就会颁发证书了,不过这种方法不支持泛域名证书。

使用 HTTP 校验这种方式,首先需要将域名解析配置好,也就是需要保证 ACME 服务端可以正常访闯到你的 HTTP服务。这里我们以上面的 whoami应用为例,我们已经将 whoami.boysec.cn 域名做好了正确的解析。

这里必须要绑定公网IP才可以。

方法一

由于 Let’s Encrypt 的生产环境有着严格的接口调用限制,所以一般我们需要先在 staging 环境测试通过后,再切换到生产环境。首先我们创建一个default范围测试环境和生产环境使用的 HTTP-1 校验方式的证书颁发机构:

测试环境生成环境

代码语言:javascript复制
cat <<EOF >staging-http.yml 
apiVersion: cert-manager.io/v1
kind: Issuer  # 选择整个集群(ClusterIssuer)还是单个namespace
metadata:
 name: staging-http01
spec:
 acme:
   email: wangxiansen@boysec.cn  # 用于 ACME 注册的邮箱
   # ACME 服务端地址
   server: https://acme-staging-v02.api.letsencrypt.org/directory
   privateKeySecretRef: 
     # 用于存放 ACME 帐号 private key 的 secret
     name: boysec-staging-http01
   solvers:
     - http01:  # ACME HTTP-01 类型
         ingress:
           class: traefik # 指定ingress的名称
EOF
代码语言:javascript复制
cat <<EOF >production-http.yml 
apiVersion: cert-manager.io/v1
kind: Issuer  # 选择整个集群(ClusterIssuer)还是单个namespace
metadata:
 name: production-http01
spec:
 acme:
   email: wangxiansen@boysec.cn  # 用于 ACME 注册的邮箱
   # ACME 服务端地址
   server: https://acme-v02.api.letsencrypt.org/directory
   privateKeySecretRef: 
     # 用于存放 ACME 帐号 private key 的 secret
     name: boysec-http01
   solvers:
     - http01:  # ACME HTTP-01 类型
         ingress:
           class: traefik # 指定ingress的名称
EOF

创建完成后可以看到两个 issuers 对象:

代码语言:javascript复制
kubectl get issuers

有了 Issuer/ClusterIssuer 证书颁发机构,接下来我们就可以生成免费证书了,cert-manager 给我们提供了 Certificate 这个用于生成证书的自定义资源对象,不过这个对象需要在一个具体的命名空间下使用,证书最终会在这个命名空间下以 Secret 的资源对象存储。我们这里是要结合 traefik 一起使用,实际上我们只需要修改 Ingress 对象,添加上 cert-manager 的相关注解即可,不需要手动创建 Certificate 对象了,修改上面的 whoami应用的 Ingress 资源对象,如下所示:

代码语言:javascript复制
cat << EOF >ing.yml 
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: whoami
  annotations:
    cert-manager.io/issuer: "staging-http01"
spec:
  tls:
    - hosts:
        - whoami.boysec.cn
      secretName: whoami-tls
  rules:
    - host: whoami.boysec.cn
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: whoami
                port:
                  name: web
EOF

验证

代码语言:javascript复制
$ kubectl get issuer -o wide
NAME                READY   STATUS                                                 AGE
production-http01   True    The ACME account was registered with the ACME server   18m
staging-http01      True    The ACME account was registered with the ACME server   19m
$ kubectl get certificateRequest -o wide
NAME                            APPROVED   DENIED   READY   ISSUER            STATUS
tls-whoami-ingress-http-fdw2x   True                True    le-example-http   Certificate fetched from issuer successfully

$ kubectl get certificates
NAME                      READY   SECRET                    ISSUER            STATUS
tls-whoami-ingress-http   True    tls-whoami-ingress-http   le-example-http   Certificate is up to date and has not expired

方法二(推荐)

通过修改traefik ConfigMap文件

代码语言:javascript复制
entryPoints:
      web:
        address: ":80"          ## 配置 80 端口,并设置入口名称为 web
      websecure:
        address: ":443"         # 配置443端口,并设置入口名称为 websecure
    certificatesResolvers:
      wangxiansen-test: # 可以换成你喜欢的名字
        acme:
          caServer: https://acme-staging-v02.api.letsencrypt.org/directory # ACME 服务端地址
          email: wangxiansen@boysec.cn # 届时你申请签发证书的邮箱
          storage: acme-staging-web.json           # 存储位置
          httpChallenge:
            entryPoint: web
      wangxiansen: # 可以换成你喜欢的名字
        acme:
          email: wangxiansen@boysec.cn # 届时你申请签发证书的邮箱
          storage: acme-web.json           # 存储位置
          httpChallenge:
            entryPoint: web

创建ingressroute

代码语言:javascript复制
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
  name: whoamiauto-tls-http
spec:
  entryPoints:
  - web
  routes:
  - match: Host(`whoami.boysec.cn`)
    kind: Rule
    services:
      - name: whoami
        port: 80
  tls:
    certResolver: wangxiansen-test

打开浏览器就会发现申请测试的证书了,然后修改certResolverwangxiansen

DNS-01 校验

DNS-01 的校验是通过 DNS 提供商的 API 拿到你的 DNS 控制权限, 在 Let’s Encrypt 为 cert-manager 提供 TOKEN 后,cert-manager 将创建从该 TOKEN 和你的帐户密钥派生的 TXT 记录,并将该记录放在 _acme-challenge.。然后 Let’s Encrypt 将向 DNS 系统查询该记录,如果找到匹配项,就可以颁发证书,这种方法是支持泛域名证书的。

方法一

测试环境生成环境

代码语言:javascript复制
cat <<EOF >staging-dns.yml 
apiVersion: cert-manager.io/v1
kind: Issuer
metadata:
 name: boysec-staging-dns
 namespace: whoami
spec:
 acme:
   email: user@example.com
   # We use the staging server here for testing to avoid hitting
   server: https://acme-staging-v02.api.letsencrypt.org/directory
   privateKeySecretRef:
     # if not existing, it will register a new account and stores it
     name: boysec-staging-key
   solvers:
     - dns01:
         cloudflare:
           apiTokenSecretRef:
             name: cloudflare-api-token-secret
             key: api-token

EOF
代码语言:javascript复制
cat <<EOF >production-dns.yml 
apiVersion: cert-manager.io/v1
kind: Issuer
metadata:
 name: boysec-dns
 namespace: whoami
spec:
 acme:
   email: user@example.com
   # We use the staging server here for testing to avoid hitting
   server: https://acme-v02.api.letsencrypt.org/directory
   privateKeySecretRef:
     # if not existing, it will register a new account and stores it
     name: boysec-key
   solvers:
     - dns01:
         cloudflare:
           apiTokenSecretRef:
             name: cloudflare-api-token-secret
             key: api-token

EOF

方法二(推荐)

通过修改traefik ConfigMap文件

代码语言:javascript复制
entryPoints:
      web:
        address: ":80"          ## 配置 80 端口,并设置入口名称为 web
      websecure:
        address: ":443"         # 配置443端口,并设置入口名称为 websecure
      certificatesResolvers:
        tencent-test: # 可以换成你喜欢的名字
          acme:
            email: wangxiansen@boysec.cn # 届时你申请签发证书的邮箱
            storage: acme-staging-dns.json 
            caServer: https://acme-staging-v02.api.letsencrypt.org/directory
            dnsChallenge:
              provider: tencentcloud # 腾讯云的编码
              delayBeforeCheck: 0
              resolvers:         
                - "119.29.29.29:53" # 腾讯云的 DNS 地址
        tencent: # 可以换成你喜欢的名字
          acme:
            email: wangxiansen@boysec.cn # 届时你申请签发证书的邮箱
            storage: acme-dns.json
            dnsChallenge:
              provider: tencentcloud # 腾讯云的编码
              delayBeforeCheck: 0
              resolvers:         
                - "119.29.29.29:53" # 腾讯云的 DNS 地址

然后创建密钥

代码语言:javascript复制
kubectl create secret generic tencent-token --from-literal=TENCENTCLOUD_SECRET_ID=AKIDoSadfsafdsfasdfsdfLBj23 --from-literal=TENCENTCLOUD_SECRET_KEY=lFTP4afafasdfsfdfsgasfgfMK1Ra7NM -n kube-system

$ kubectl edit -n kube-system pods traefik-v2-6996759d46-d9czt 
    spec:
      serviceAccountName: traefik-ingress-controller
      terminationGracePeriodSeconds: 1
      containers:
        - name: traefik-v2
          image: traefik:v2.10
          args:
            - --configfile=/config/traefik.yaml
          envFrom:          # 添加环境变量
            - secretRef:
                name: tencent-token

创建ingressroute

代码语言:javascript复制
cat << EOF > auto-tls-dns.yml
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
  name: whoami-auto-tls-dns
spec:
  entryPoints:
    - websecure
  routes:
    - match: Host(`who.boysec.cn`)
      kind: Rule
      services:
        - name: whoami
          port: 80
  tls:
    certResolver: tencent-test
    domains:
    - main: boysec.cn        
      sans:
      - '*.boysec.cn'        # 匹配所有boysec.cn下的二级域名
EOF

打开可以发现测试证书已下发,可以替换生成环境证书将certResolver替换为生产环境证书

再次访问就会发现证书是受信任的了

由于写文章测试过多被域名重复申请限制,所以先截图测试环境图,后期补上生成环境图

0 人点赞