目录

KubernetesIngress自动化https

随着 HTTPS 不断普及,大多数网站开始由 HTTP 升级到 HTTPS。使用 HTTPS 需要向权威机构申请证书,并且需要付出一定的成本,如果需求数量多,则开支也相对增加。cert-manager 是 Kubernetes 上的全能证书管理工具,支持利用 cert-manager 基于 ACME 协议与 Let’s Encrypt 签发免费证书并为证书自动续期,实现永久免费使用证书。

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

cert-manager 工作原理

cert-manager 部署到 Kubernetes 集群后会查阅其所支持的自定义资源 CRD,可通过创建 CRD 资源来指示 cert-manager 签发证书并为证书自动续期。如下图所示: https://tc.ctq6.cn/tc/f4e57b54c56515446c86ba05e7bc8f6c.svg

  • Issuer/ClusterIssuer:用于指示 cert-manager 签发证书的方式,本文主要讲解签发免费证书的 ACME 方式

    说明:

    Issuer 与 ClusterIssuer 之间的区别是:Issuer 只能用来签发自身所在 namespace 下的证书,ClusterIssuer 可以签发任意 namespace 下的证书。

  • Certificate:用于向 cert-manager 传递域名证书的信息、签发证书所需要的配置,以及对 Issuer/ClusterIssuer 的引用。

免费证书签发原理

Let’s Encrypt 利用 ACME 协议校验域名的归属,校验成功后可以自动颁发免费证书。免费证书有效期只有90天,需在到期前再校验一次实现续期。使用 cert-manager 可以自动续期,即实现永久使用免费证书。校验域名归属的两种方式分别是 HTTP-01 和 DNS-01,校验原理详情可参见 Let’s Encrypt 的运作方式。

HTTP-01 校验原理

HTTP-01 的校验原理是给域名指向的 HTTP 服务增加一个临时 location。此方法仅适用于给使用 Ingress 暴露流量的服务颁发证书,并且不支持泛域名证书。 例如,Let’s Encrypt 会发送 HTTP 请求到 http://<YOUR_DOMAIN>/.well-known/acme-challenge/<TOKEN>。``YOUR_DOMAIN 是被校验的域名。TOKEN 是 ACME 协议客户端负责放置的文件,在此处 ACME 客户端即 cert-manager,通过修改或创建 Ingress 规则来增加临时校验路径并指向提供 TOKEN 的服务。Let’s Encrypt 会对比 TOKEN 是否符合预期,校验成功后就会颁发证书。

DNS-01 校验原理

DNS-01 的校验原理是利用 DNS 提供商的 API Key 拿到用户 DNS 控制权限。此方法不需要使用 Ingress,并且支持泛域名证书。 在 Let’s Encrypt 为 ACME 客户端提供令牌后,ACME 客户端 \(cert-manager\) 将创建从该令牌和帐户密钥派生的 TXT 记录,并将该记录放在 _acme-challenge.<YOUR_DOMAIN>。Let’s Encrypt 将向 DNS 系统查询该记录,找到匹配项即可颁发证书。

校验方式对比

  • HTTP-01 校验方式的优点是配置简单通用,不同 DNS 提供商均可使用相同的配置方法。缺点是需要依赖 Ingress,若仅适用于服务支持 Ingress 暴露流量,不支持泛域名证书。

  • DNS-01 校验方式的优点是不依赖 Ingress,并支持泛域名。缺点是不同 DNS 提供商的配置方式不同,DNS 提供商过多而 cert-manager 的 Issuer 不能全部支持。部分可以通过部署实现 cert-manager 的 Webhook 服务来扩展 Issuer 进行支持。例如 DNSPod 和 阿里 DNS,详情请参见 Webhook 列表。

操作步骤

安装 cert-manager

通常使用 yaml 方式一键安装 cert-manager 到集群,可参考官网文档 Installing with regular manifests。 cert-manager 官方使用的镜像在 quay.io 进行拉取。也可以执行以下命令,使用同步到国内 CCR 的镜像一键安装。

我们这里用来管理 SSL/TLS 证书的组件是Cert manager,它对于每一个 ingress endpoint 将会自动创建一个新的证书,当 certificates 过期时还能自动更新,除此之外,Cert manager 也可以和其它的 providers 一起工作,例如 HashiCorp Vault。为了方便我们这里使用Helm来部署即可。

首先需要通过helm 中央仓库获取最新的cert-manager Chart,这里选择bitnami,首先导出values.yaml配置文件

1
2
3
helm repo add jetstack https://charts.jetstack.io
helm show values jetstack/cert-manager/cert-manager > values.yaml
helm upgrade --install cert-manager jetstack/cert-manager --set installCRDs=true --create-namespace -n cert-manager --debug

创建证书签发服务(使用http)

Cert manager安装后,接下来需要定义上面的letsencrypt-prod这个cluster issuer,这里使用上面的clusterissuers.certmanager.k8s.io这个CRD来定义:(cluster-issuer.yaml)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: letsencrypt
spec:
  acme:
    # You must replace this email address with your own.
    # Let's Encrypt will use this to contact you about expiring
    # certificates, and issues related to your account.
    email: user@example.com
    server: https://acme-staging-v02.api.letsencrypt.org/directory
    privateKeySecretRef:
      # Secret resource that will be used to store the account's private key.
      name: letsencrypt
    # Add a single challenge solver, HTTP01 using nginx
    solvers:
    - http01:
        ingress:
          class: traefik-cert-manager

然后直接创建这个ClusterIssuer资源:

1
kubectl create -f cluster-issuer.yaml

注意如果使用http绑定clss时,需要配置traefik2.0 的ingress.class

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
        - --global.checknewversion
        - --global.sendanonymoususage
        - --entryPoints.web.address=:80
        - --entryPoints.admin.address=:8080
        - --entryPoints.websecure.address=:443
        - --providers.kubernetesIngress.ingressClass=traefik-cert-manager
        - --api.dashboard=true
        - --ping=true
        - --providers.kubernetescrd
        - --providers.kubernetesingress

创建证书签发服务(使用webhook)

由于目前阿里云不在cert-manager官方支持的webhook列表中,即需要安装第三方webhook,详情请参见 Webhook 列表。

目前采用的webhook为DNS ACME webhook,这是 Cert-Manager 与阿里云 DNS(又名 AliDNS)一起使用的 webhook 实现。有关 webhook 的更多详细信息,请参阅 cert-manager 的文档:https://cert-manager.io/docs/concepts/webhook/

安装阿里云 DNS ACME webhook

使用helm部署阿里云dns acme webhook

1
2
3
helm repo add cert-manager-alidns-webhook https://devmachine-fr.github.io/cert-manager-alidns-webhook
helm repo update
helm upgrade --install alidns-webhook cert-manager-alidns-webhook/alidns-webhook -n cert-manager --debug 

创建持有阿里云凭证的secret:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
cat << 'EOF' | kubectl apply -f -
apiVersion: v1
kind: Secret
metadata:
  name: alidns-secret
  namepace: cert-manager
data:
  access-token: your-base64-encoded-access-token
  secret-key: your-base64-encoded-secret-key
EOF

Cert manager安装后,接下来需要定义上面的letsencrypt-prod这个cluster issuer,这里使用上面的clusterissuers.certmanager.k8s.io这个CRD来定义:(cluster-issuer.yaml)

 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
cat << EOF |kubectl apply -f -
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: letsencrypt
  namespace: cert-manager
spec:
  acme:
    email: panqinglong@300.cn
    server: https://acme-v02.api.letsencrypt.org/directory
    privateKeySecretRef:
      name: letsencrypt
    solvers:
    - dns01:
        webhook:
            config:
              accessTokenSecretRef:
                key: access-token
                name: alidns-secrets
              regionId: cn-beijing
              secretKeySecretRef:
                key: secret-key
                name: alidns-secrets
            groupName: zhimember.com
            solverName: alidns-solver
      selector:
        dnsNames:
        - 'zhimember.com'
        - '*.zhimmeber.com'
EOF

然后直接创建这个ClusterIssuer资源:

1
kubectl create -f cluster-issuer.yaml

使用 ClusterIssuer 创建一个认证, yaml配置文件如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
cat << EOF |kubectl apply -f -
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
  name: traefik-tls
  namespace: kube-system
spec:
  secretName: traefik-tls
  commonName: zhimember.com
  dnsNames:
  - zhimember.com
  - "*.zhimember.com"
  issuerRef:
    name: letsencrypt
    kind: ClusterIssuer
EOF

测试

上面我们已经安装了Cert manager,定义了ClusterIssuer,接下来我们来配置 HTTPS 去访问我们的 Kubernetes Dashboard 的服务,Dashboard 的部署我们这里就不多说了,直接添加一个 Ingress 资源对象即可:(traefik-ingress.yaml)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22

cat << EOF|kubectl apply -f -
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: traefik-ui
  namespace: kube-system
  annotations:
spec:
  tls:
  - hosts:
    - traefikui.zhimember.com
    secretName: traefik-tls
  rules:
  - host: traefikui.zhimember.com
    http:
      paths:
      - path: '/'
        backend:
          serviceName: traefik
          servicePort: 8080
EOF

由于我们目前使用的kubernetes ingress为traefik2.0 版本以上,使用定义路由的方式已经变为CRD模式了,所以添加的IngressRoute如下所示:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
cat << 'EOF'|kubectl apply -f -
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
  name: traefik-ui
  namespace: kube-system
spec:
  entryPoints:
  - websecure  # 注意这里是websecure这个entryPoint,监控443端口
  routes:
  - match: Host(`traefikui.zhimemner.com`) && PathPrefix(`/`)
    kind: Rule
    services:
    - name: traefik
      port: 8080
  tls:
    secretName: traefik-tls  # 使用我们配置的  kusui-tls 这个secret
EOF

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

1
kubectl create -f traefik-ingress.yaml

当然如果需要在公网中进行访问,我们还需要将我们这里的域名解析到 Ingress Controller 所在的任意一个节点,或者在本地的/etc/hosts中加上映射也是可以的。