APISIX Ingress 如何使用 cert-Manager 管理证书

作者张晋涛,API7.ai 云原生技术专家

cert-manager 解决了什么问题 

cert-manager[1] 是由 JETSTACK[2] 在 2017 年开源,并在 2020 年 11 月正式捐赠给了 CNCF 成为 sandbox 级别的项目。在 2022 年 10 月正式成为 CNCF incubating 级别的项目。

cert-manager 主要用来自动化的管理 Kubernetes 和 OpenShift 中的 x509 证书身份,它使证书和证书签发结构成为了 Kubernetes 上第一类支持的资源(通过 CRD 实现),并且允许开发者可以很简单的为应用程序申请证书,以便提升应用访问的安全性。

那么我们来看看在 cert-manager 出现之前,在 Kubernetes 中如何管理证书呢?

Kubernetes 中的证书如何管理

在 Kubernetes 中存储数据主要有以下两种原生的方式:

  • ConfigMap

  • Secret

不过 ConfigMap 中所有的信息都是明文的,存储一些相对普通的配置信息还可以,但对于证书这类比较私密的信息,就不那么适用了。

Kubernetes 在设计的时候就推荐使用 Secret 来存储证书等相关信息,并且还为此提供了默认支持。我们可以很简单的通过 kubectl create secret tls 来存储证书的信息。比如:

➜  ~ kubectl create secret
 tls moelove-tls --cert=./cert.pem --key=./cert-key.pem
secret/moelove-tls created
➜  ~ kubectl get secret moelove-tls -oyaml
apiVersion: v1
data:
  tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUNFekNDQWJtZ0F3SUJBZ0lVVHhCTC9aQkdpOEJCOUFVN2JRWi9jK3c2L1Rzd0NnWUlLb1pJemowRUF3SXcKVFRFTE1Ba0dBMVVFQmhNQ1EwNHhFREFPQmdOVkJBY1RCMEpsYVdwcGJtY3hGVEFUQmdOVkJBb1RERTF2WlV4dgpkbVVnU1U1R1R6RVZNQk1HQTFVRUF4TU1iVzlsYkc5MlpTNXBibVp2TUI0WERUSXlNVEF4T1RBM01UY3dNRm9YCkRUSXpNVEF4T1RBM01UY3dNRm93VFRFTE1Ba0dBMVVFQmhNQ1EwNHhFREFPQmdOVkJBY1RCMEpsYVdwcGJtY3gKRlRBVEJnTlZCQW9UREUxdlpVeHZkbVVnU1U1R1R6RVZNQk1HQTFVRUF4TU1iVzlsYkc5MlpTNXBibVp2TUZrdwpFd1lIS29aSXpqMENBUVlJS29aSXpqMERBUWNEUWdBRVVTcEFjNGE1UXQwQ0NVa2hGSGY3WnZvR1FReVVPUUxSClJhZG0rSUUrV1ZkOThyWkc5NFpob08ybDZSWkY2MnVPN3FpZ2VsaUJwY0FGQ3FzWU9HNnVLcU4zTUhVd0RnWUQKVlIwUEFRSC9CQVFEQWdXZ01CMEdBMVVkSlFRV01CUUdDQ3NHQVFVRkJ3TUJCZ2dyQmdFRkJRY0RBakFNQmdOVgpIUk1CQWY4RUFqQUFNQjBHQTFVZERnUVdCQlFnS01icnBUb3k4NVcvRy9hMGZtYzlDMUJRbURBWEJnTlZIUkVFCkVEQU9nZ3h0YjJWc2IzWmxMbWx1Wm04d0NnWUlLb1pJemowRUF3SURTQUF3UlFJZ1EzTzhJZ0N2MlRkNUhhV00KcE1LWmRCLzNXdEMreERlSVdPbER6L2hCdzE0Q0lRRExQNG0weFpmSkJvRGc5cERocThGdHN5VDdVZVhVdlZGQQpsS0tReFZNOXFBPT0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo=
  tls.key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSUsyZjZHQlNZQ0R4eVoycnB2bVZ1YW5MNDhxeW9SK1NiWmxiQzNqSUZybzhvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFVVNwQWM0YTVRdDBDQ1VraEZIZjdadm9HUVF5VU9RTFJSYWRtK0lFK1dWZDk4clpHOTRaaApvTzJsNlJaRjYydU83cWlnZWxpQnBjQUZDcXNZT0c2dUtnPT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo=
kind: Secret
metadata:
  creationTimestamp: "2022-10-19T07:24:26Z"
  name: moelove-tls
  namespace: default
  resourceVersion: "2103326"
  uid: 14f86514-a1d1-4d99-b000-9ed8b5189d56
type: kubernetes.io/tls

通过上述命令,在 Kubernetes 中创建了一个名为 moelove-tls 的 secret 资源,并且它是 kubernetes.io/tls 类型的。

应用程序想要使用的时候,直接引用该资源即可从中获取相应的证书信息。多数情况下,我们会将其用于 Ingress 的场景中。比如:

➜  ~ kubectl create ingress moelove-ing --rule="moelove.info/=moelove:8080,tls=moelove-tls"
ingress.networking.k8s.io/moelove-ing created
➜  ~ kubectl get ing moelove-ing -oyaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  creationTimestamp: "2022-10-19T07:32:43Z"
  generation: 1
  name: moelove-ing
  namespace: default
  resourceVersion: "2104268"
  uid: b90f09f7-8036-4b9f-9744-a247141ea8da
spec:
  rules:
  - host: moelove.info
    http:
      paths:
      - backend:
          service:
            name: moelove
            port:
              number: 8080
        path: /
        pathType: Exact
  tls:
  - hosts:
    - moelove.info
    secretName: moelove-tls
status:
  loadBalancer: {}

通过上述命令,创建了一个名为 moelove-ing 的 Ingress 资源,并声明其域名为 moelove.info , 且使用 moelove-tls 为此域名添加了证书保护。对应的 Ingress controller 组件获取到此 Ingress 资源后,便可自动的将证书配置给此域名使用,进而提升网站的安全性。

遇到了哪些问题

我们来看看在此过程中遇到了哪些问题呢?

证书签发过程繁琐

上述内容中我并没有演示如何进行证书的签发,如果你对此感兴趣可以查看 OpenSSL 的文档[3] 。在证书的签发过程中,需要理解很多概念。而且签发过程都发生在 Kubernetes 集群之外,不能很好的通过“声明式”配置的方式来了解具体发生了什么事情。尤其是证书可以有多种不同的加密算法,各种不同的配置等。

所以如果使用默认的方式,只能最后将生成的证书和密钥存储在 Kubernetes 的 Secrets 中。

证书续签/重签繁琐

我们都知道证书是有过期时间的,在证书过期或者被吊销之前,必须准备好新的证书,并且保证其过期时间要晚于原证书的过期时间。

在 Kubernetes Secrets 中存储的证书,从这方面考虑的话有些不足:

  • 不存在自动化的过期时间检查

也就是说,你可以在 Kubernetes 中存储任意的证书,无论该证书是否已经过期。

  • 不存在无效数据的检查

也就是说,如果存储在 Kubernetes Secrets 中的数据是损坏的,或者是无效的,那么在 Kubernetes 中也不会有什么特殊的处理。

安全性不足

事实上,存储在 Kubernetes  Secretes 中的证书和密钥信息,仅仅是进行了 base64 的编码,任何人只要拿到了此数据,均可对其进行 base64 的解码,进而获取到其中的真实数据。比如:

➜  ~ kubectl get secrets moelove-tls -o jsonpath='{ .data.tls\.crt }' |base64 -d
-----BEGIN CERTIFICATE-----
MIICEzCCAbmgAwIBAgIUTxBL/ZBGi8BB9AU7bQZ/c+w6/TswCgYIKoZIzj0EAwIw
TTELMAkGA1UEBhMCQ04xEDAOBgNVBAcTB0JlaWppbmcxFTATBgNVBAoTDE1vZUxv
dmUgSU5GTzEVMBMGA1UEAxMMbW9lbG92ZS5pbmZvMB4XDTIyMTAxOTA3MTcwMFoX
DTIzMTAxOTA3MTcwMFowTTELMAkGA1UEBhMCQ04xEDAOBgNVBAcTB0JlaWppbmcx
FTATBgNVBAoTDE1vZUxvdmUgSU5GTzEVMBMGA1UEAxMMbW9lbG92ZS5pbmZvMFkw
EwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEUSpAc4a5Qt0CCUkhFHf7ZvoGQQyUOQLR
Radm+IE+WVd98rZG94ZhoO2l6RZF62uO7qigeliBpcAFCqsYOG6uKqN3MHUwDgYD
VR0PAQH/BAQDAgWgMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjAMBgNV
HRMBAf8EAjAAMB0GA1UdDgQWBBQgKMbrpToy85W/G/a0fmc9C1BQmDAXBgNVHREE
EDAOggxtb2Vsb3ZlLmluZm8wCgYIKoZIzj0EAwIDSAAwRQIgQ3O8IgCv2Td5HaWM
pMKZdB/3WtC+xDeIWOlDz/hBw14CIQDLP4m0xZfJBoDg9pDhq8FtsyT7UeXUvVFA
lKKQxVM9qA==
-----END CERTIFICATE-----

通过以上命令便直接拿到了证书相关的原始数据。

另一方面,当我们要更新证书和密钥数据的时候,并不存在二次确认的过程,可以直接进行更新。这对于大多数场景下都是不符合安全策略的。

接下来我们看看 cert-manager 如何解决这些问题。

cert-manager 如何解决

自动签发

cert-manager 通过 CRD 的方式进行开发和扩展,其添加和实现了 IssuersClusterIssuers 资源,代表证书的签发机构(CA)。

并且支持了多种内置类型,以及可以很方便的与外部组件进行集成,比如:

  • SelfSigned:自签证书

  • CA:提供 CA 进行签发

  • Vault:使用 Vault 进行签发

  • Venafi:使用 Venafi 进行签发

  • External:使用一些外部的组件进行签发,比如:

    - kms-issuer[4] :使用 AWS KMS 签发

    - google-cas-issuer[5] :使用 Google CAS 进行签发

    - [origin-ca-issuer][6]:使用 Cloudflare Origin CA[7] 进行签发

  • ACME(Automated Certificate Management Environment ):自动化进行证书签发

通过这些组件可以非常方便的进行证书的签发。后续内容中将会以 Vault 为例进行具体介绍。

自动续签/重签

cert-manager 中我们可以非常方便的通过 cmctl CTL 手动的进行证书 renew 操作,同时 cert-manager 也会自动的检查证书的有效期和证书的完整性。

如果出现证书过期,或者证书数据不完整,都可以自动触发证书的重新签发,节约了人力成本和维护成本。

安全性保障

cert-manager 中通过 CRD 的方式增加了 signers 资源,允许对证书请求进行确认,进行 Approved 或者 Denied 。只有 Approve 后,才会真正生效,并签发证书。这样可以更加的安全。

APISIX Ingress 如何与 cert-manager 集成

安装部署

Apache APISIX Ingress 是一个 Kubernetes Ingress controller,可以支持通过 Ingress,自定义资源,以及 Gateway API 等方式进行代理规则的配置。

接下来演示如何将 APISIX Ingress 与 cert-manager 集成,为代理的域名添加 TLS 证书,提升安全性。

同时,我们使用 Vault 进行证书的签发。

部署 APISIX Ingress controller

部署 APISIX Ingress 很简单,仅需要执行如下步骤即可:

tao@moelove:~$ helm repo add apisix https://charts.apiseven.com
tao@moelove:~$ helm repo add bitnami https://charts.bitnami.com/bitnami
tao@moelove:~$ helm repo update
tao@moelove:~$ helm install apisix apisix/apisix --set gateway.tls.enabled=true --set gateway.type=NodePort   --set ingress-controller.enabled=true   --set ingress-controller.config.apisix.serviceNamespace=apisix   --namespace apisix   --create-namespace   --set ingress-controller.config.apisix.serviceName=apisix-admin --set ingress-controller.config.ingressPublishService="apisix/apisix-gateway"
NAME: apisix
LAST DEPLOYED: Wed Oct 19 21:33:37 2022
NAMESPACE: apisix
STATUS: deployed
REVISION: 1
TEST SUITE: None
NOTES:
1. Get the application URL by running these commands:
  export NODE_PORT=$(kubectl get --namespace apisix -o jsonpath="{.spec.ports[0].nodePort}" services apisix-gateway)
  export NODE_IP=$(kubectl get nodes --namespace apisix -o jsonpath="{.items[0].status.addresses[0].address}")
  echo http://$NODE_IP:$NODE_PORT

待所有的 Pod 处于 Running 状态便表示已经部署成功。

tao@moelove:~$ kubectl -n apisix get pods  
NAME                                         READY   STATUS    RESTARTS   AGE
apisix-777c9fdd67-rf8zs                      1/1     Running   0          6m48s
apisix-etcd-0                                1/1     Running   0          6m48s
apisix-etcd-1                                1/1     Running   0          6m48s
apisix-etcd-2                                1/1     Running   0          6m48s
apisix-ingress-controller-568544b554-k7nd4   1/1     Running   0          6m48s

部署 Vault

部署 Vault 的时候,也可以使用 Helm 。这里我增加了一个 --set "server.dev.enabled=true" 配置项,这样在部署后无需进额外操作便可直接使用了。(注意这个配置不要用到生产环境)

tao@moelove:~$ helm repo add hashicorp https://helm.releases.hashicorp.com
tao@moelove:~$ helm install vault hashicorp/vault --set "injector.enabled=false" --set "server.dev.enabled=true"
NAME: vault
LAST DEPLOYED: Wed Oct 19 21:53:50 2022
NAMESPACE: default
STATUS: deployed
REVISION: 1
NOTES:
Thank you for installing HashiCorp Vault!


Now that you have deployed Vault, you should look over the docs on using
Vault with Kubernetes available here:


https://www.vaultproject.io/docs/


Your release is named vault. To learn more about the release, try:


  $ helm status vault
  $ helm get manifest vault

在部署完成后,当 Pod 处于 Running 状态时,说明已经部署完成。

tao@moelove:~$ kubectl get pods  
NAME      READY   STATUS    RESTARTS   AGE
vault-0   1/1     Running   0          29s
tao@moelove:~$ kubectl get svc
NAME             TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)             AGE
kubernetes       ClusterIP   10.96.0.1      <none>        443/TCP             84m
vault            ClusterIP   10.96.190.88   <none>        8200/TCP,8201/TCP   4m14s
vault-internal   ClusterIP   None           <none>        8200/TCP,8201/TCP   4m14s

接下来进入 Vault 内进行操作,此处开启了 pki 的能力,并且配置了对应的 policy。

tao@moelove:~$ kubectl  exec -it vault-0 -- sh
/ $ vault secrets enable pki
Success! Enabled the pki secrets engine at: pki/
/ $ vault write pki/root/generate/internal common_name=moelove.info ttl=8760h
Key              Value
---              -----
certificate      -----BEGIN CERTIFICATE-----
MIIDODCCAiCgAwIBAgIUds5uMJV9rOkwFEt6Xof5T2SVFccwDQYJKoZIhvcNAQEL
...
VM4DRVgDkqY9JdHU
-----END CERTIFICATE-----
expiration       1668983612
issuer_id        8df13015-7c70-df9a-7bb7-9b3b4afe7f82
issuer_name      n/a
issuing_ca       -----BEGIN CERTIFICATE-----
MIIDODCCAiCgAwIBAgIUds5uMJV9rOkwFEt6Xof5T2SVFccwDQYJKoZIhvcNAQEL
...
VM4DRVgDkqY9JdHU
-----END CERTIFICATE-----
key_id           c9fcfcb0-3548-a9a7-e706-30510592c797
key_name         n/a
serial_number    76:ce:6e:30:95:7d:ac:e9:30:14:4b:7a:5e:87:f9:4f:64:95:15:c7
/ $
/ $ vault write pki/config/urls issuing_certificates="http://vault.default:8200/v1/pki/ca" crl_distribution_points="http://vault.default:8200/v1/pki/crl"
Success! Data written to: pki/config/urls
/ $ vault write pki/roles/moelove-dot-info allowed_domains=moelove.info allow_subdomains=true max_ttl=72h
Success! Data written to: pki/roles/moelove-dot-info
/ $
/ $ vault policy write pki - <<EOF
> path "pki*"                        { capabilities = ["read", "list"] }
> path "pki/sign/moelove-dot-info"    { capabilities = ["create", "update"] }
> path "pki/issue/moelove-dot-info"   { capabilities = ["create"] }
> EOF
Success! Uploaded policy: pki

接下来,配置 Kubernetes 认证:

/ $ vault auth enable kubernetes
Success! Enabled kubernetes auth method at: kubernetes/
/ $ vault write auth/kubernetes/config kubernetes_host="https://$KUBERNETES_PORT_443_TCP_ADDR:443"
Success! Data written to: auth/kubernetes/config
/ $ vault write auth/kubernetes/role/issuer  bound_service_account_names=issuer bound_service_account_namespaces=default policies=pki ttl=20m
Success! Data written to: auth/kubernetes/role/issuer

完成上述操作后,下面部署 cert-manager。

部署 cert-manager

现在可以通过 Helm 安装 cert-manager 了,安装的过程也比较简单。

tao@moelove:~$ helm repo add jetstack https://charts.jetstack.io
tao@moelove:~$ helm repo update jetstack
Hang tight while we grab the latest from your chart repositories...
...Successfully got an update from the "jetstack" chart repository
Update Complete. ⎈Happy Helming!⎈
tao@moelove:~$ kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.10.0/cert-manager.crds.yaml
customresourcedefinition.apiextensions.k8s.io/clusterissuers.cert-manager.io created
customresourcedefinition.apiextensions.k8s.io/challenges.acme.cert-manager.io created
customresourcedefinition.apiextensions.k8s.io/certificaterequests.cert-manager.io created
customresourcedefinition.apiextensions.k8s.io/issuers.cert-manager.io created
customresourcedefinition.apiextensions.k8s.io/certificates.cert-manager.io created
customresourcedefinition.apiextensions.k8s.io/orders.acme.cert-manager.io created
tao@moelove:~$ helm install \
>   cert-manager jetstack/cert-manager \
>   --namespace cert-manager \
>   --create-namespace \
>   --version v1.10.0


xNAME: cert-manager
LAST DEPLOYED: Wed Oct 19 22:51:06 2022
NAMESPACE: cert-manager
STATUS: deployed
REVISION: 1
TEST SUITE: None
NOTES:
cert-manager v1.10.0 has been deployed successfully!


In order to begin issuing certificates, you will need to set up a ClusterIssuer
or Issuer resource (for example, by creating a 'letsencrypt-staging' issuer).


More information on the different types of issuers and how to configure them
can be found in our documentation:


https://cert-manager.io/docs/configuration/


For information on how to configure cert-manager to automatically provision
Certificates for Ingress resources, take a look at the `ingress-shim`
documentation:


https://cert-manager.io/docs/usage/ingress/

接下来检查 Pod 的状态:

tao@moelove:~$ kubectl -n cert-manager get pods
NAME                                       READY   STATUS    RESTARTS   AGE
cert-manager-69b456d85c-znpq4              1/1     Running   0          117s
cert-manager-cainjector-5f44d58c4b-wcd27   1/1     Running   0          117s
cert-manager-webhook-566bd88f7b-7rptf      1/1     Running   0          1

接下来便可以开始配置和验证了。

如何配置及验证

配置和签发证书
tao@moelove:~$ kubectl create serviceaccount issuer
serviceaccount/issuer created
tao@moelove:~$ kubectl get secret
NAME                          TYPE                 DATA   AGE
sh.helm.release.v1.vault.v1   helm.sh/release.v1   1      36m
tao@moelove:~$ vim issuer-secret.yaml
tao@moelove:~$ cat issuer-secret.yaml
apiVersion: v1
kind: Secret
metadata:
  name: issuer-token-moelove
  annotations:
    kubernetes.io/service-account.name: issuer
type: kubernetes.io/service-account-token
tao@moelove:~$ kubectl apply -f issuer-secret.yaml
secret/issuer-token-moelove created
tao@moelove:~$ kubectl get sa,secret
NAME                     SECRETS   AGE
serviceaccount/default   0         118m
serviceaccount/issuer    0         2m11s
serviceaccount/vault     0         38m


NAME                                 TYPE                                  DATA   AGE
secret/issuer-token-moelove          kubernetes.io/service-account-token   3      35s
secret/sh.helm.release.v1.vault.v1   helm.sh/release.v1                    1      38m

创建 Issuer

通过此配置将使用 Vault 作为证书签发机构,通过引用在 Vault 中配置的 role 和 secret 等,进行自动的签发。

tao@moelove:~$ cat vault-issuer.yaml
apiVersion: cert-manager.io/v1
kind: Issuer
metadata:
  name: vault-issuer
  namespace: default
spec:
  vault:
    server: http://vault.default
    path: pki/sign/moelove-dot-info
    auth:
      kubernetes:
        mountPath: /v1/auth/kubernetes
        role: moelove-dot-info
        secretRef:
          name: issuer-token-moelove
          key: token
tao@moelove:~$ kubectl apply -f vault-issuer.yaml
issuer.cert-manager.io/vault-issuer created

创建证书

通过此处的配置即可自动的签发证书,并在后续使用时候可以通过 moelove-info-tls 进行引用。

tao@moelove:~$ cat moelove-dot-info-cert.yaml
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
  name: moelove-info
  namespace: default
spec:
  secretName: moelove-info-tls
  issuerRef:
    name: vault-issuer
  commonName: www.moelove.info
  dnsNames:
  - www.moelove.info
tao@moelove:~$ kubectl apply -f moelove-dot-info-cert.yaml
certificate.cert-manager.io/moelove-info created
验证

接下来通过代理一个 HTTPBIN 的服务进行验证。

首先创建一个 HTTPBIN 的应用程序,并创建相应的 Service。

kubectl run httpbin --image kennethreitz/httpbin
kubectl expose pod httpbin --port=80

然后定义如下资源进行代理和引用证书:

# Define ApisixTls Objects
apiVersion: apisix.apache.org/v2
kind: ApisixTls
metadata:
  name: moelove
spec:
  hosts:
  - moelove.info
  secret:
    name: moelove-info-tls


---
# Define the route to access the backend
apiVersion: apisix.apache.org/v2
kind: ApisixRoute
metadata:
  name: moelove
spec:
  http:
  - name: httpbin
    match:
      paths:
      - /*
      hosts:
      - moelove.info
    backends:
    - serviceName: httpbin
      servicePort: 80

将这些资源应用到集群中即可。然后通过 kubectl port-forward 转发 APISIX 的 443 端口到本地后, 进行测试访问:

$ ~ kubectl port-forward -n ingress-apisix svc/apisix-gateway 8443:443 &
$ ~ curl -sk https://moelove.info:8443/ip --resolve 'moelove.info:8443:127.0.0.1'
{
  "origin": "172.17.18.1"
}

可以看到,已经正确的为 moelove.info 这个域名配置了 HTTPS 证书,并且通过 APISIX Ingress 为其配置了代理。

总结

本文中介绍了 Kubernetes 中证书的默认存储方式,以及这种方式存在的一些痛点。cert-manager 的出现比较好的解决了这些问题,逐步成为了 Kubernetes 生态中证书签发/管理领域中的事实标准。并且其可以和 Vault 等工具进行集成,更加的安全。

Apache APISIX Ingress 项目致力于打造更好用的 Ingress controller,所以很早就添加了完善的 cert-manager 集成能力。本篇中也通过理论加实践的方式为读者介绍了如何在 Apache APISIX Ingress 中配合使用 cert-manager 通过 Vault 签发的证书,并为应用程序提供 HTTPS 代理。希望对读者能有所帮助。

[1] https://cert-manager.io/

[2] https://www.jetstack.io/

[3] https://www.openssl.org/docs/faq.html

[4] https://github.com/Skyscanner/kms-issuer

[5] https://github.com/jetstack/google-cas-issuer

[6] https://github.com/cloudflare/origin-ca-issuer

[7] https://developers.cloudflare.com/ssl/origin-configuration/origin-ca

118fad0a24e3637dbcc6217e640b475567.gif

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值