为 MySQL 客户端开启 TLS

    1. 为 TiDB Server 颁发一套 Server 端证书,为 MySQL Client 颁发一套 Client 端证书。并创建两个 Secret 对象,Secret 名字分别为:${cluster_name}-tidb-server-secret${cluster_name}-tidb-client-secret,分别包含前面创建的两套证书;

      注意

      创建的 Secret 对象必须符合上述命名规范,否则将导致 TiDB 集群部署失败。

    2. 部署集群,设置 .spec.tidb.tlsClient.enabled 属性为 true

      • 如需跳过作为 MySQL 客户端的内部组件(如 TidbInitializer、Dashboard、Backup、Restore)的 TLS 认证,你可以给集群对应的 TidbCluster 加上 tidb.tidb.pingcap.com/skip-tls-when-connect-tidb="true" 的 annotation。
      • 如需关闭 TiDB 服务端对客户端 CA 证书的认证,你可以设置 .spec.tidb.tlsClient.disableClientAuthn 属性为 true,即在配置 TiDB 服务端启用安全连接 中不设置 ssl-ca 参数。
      • 如需跳过作为 MySQL 客户端的内部组件(如 TidbInitializer、Dashboard、Backup、Restore)的 CA 证书认证,你可以设置 .spec.tidb.tlsClient.skipInternalClientCA 属性为 true

      为 MySQL 客户端开启 TLS - 图2注意

      已部署的集群 .spec.tidb.tlsClient.enabled 属性从 false 改为 true,将导致 TiDB Pod 滚动重启。

    3. 配置 MySQL 客户端使用加密连接。

    其中,颁发证书的方式有多种,本文档提供两种方式,用户也可以根据需要为 TiDB 集群颁发证书,这两种方式分别为:

    • 使用 cfssl 系统颁发证书;
    • 使用 cert-manager 系统颁发证书;

    当需要更新已有 TLS 证书时,可参考。

    1. 首先下载 cfssl 软件并初始化证书颁发机构:

    2. ca-config.json 配置文件中配置 CA 选项:

      1. {
      2. "signing": {
      3. "default": {
      4. "expiry": "8760h"
      5. },
      6. "profiles": {
      7. "server": {
      8. "expiry": "8760h",
      9. "usages": [
      10. "signing",
      11. "key encipherment",
      12. "server auth"
      13. ]
      14. },
      15. "client": {
      16. "expiry": "8760h",
      17. "usages": [
      18. "signing",
      19. "key encipherment",
      20. "client auth"
      21. ]
      22. }
      23. }
      24. }
      25. }
    3. 您还可以修改 ca-csr.json 证书签名请求 (CSR):

      1. {
      2. "CN": "TiDB Server",
      3. "CA": {
      4. "expiry": "87600h"
      5. },
      6. "key": {
      7. "algo": "rsa",
      8. "size": 2048
      9. },
      10. "names": [
      11. {
      12. "C": "US",
      13. "L": "CA",
      14. "O": "PingCAP",
      15. "ST": "Beijing",
      16. "OU": "TiDB"
      17. }
      18. ]
      19. }
    4. 使用定义的选项生成 CA:

      1. cfssl gencert -initca ca-csr.json | cfssljson -bare ca -
    5. 生成 Server 端证书。

      首先生成默认的 server.json 文件:

      1. cfssl print-defaults csr > server.json

      然后编辑这个文件,修改 CNhosts 属性:

      1. ...
      2. "CN": "TiDB Server",
      3. "hosts": [
      4. "127.0.0.1",
      5. "::1",
      6. "${cluster_name}-tidb",
      7. "${cluster_name}-tidb.${namespace}",
      8. "${cluster_name}-tidb.${namespace}.svc",
      9. "*.${cluster_name}-tidb",
      10. "*.${cluster_name}-tidb.${namespace}",
      11. "*.${cluster_name}-tidb.${namespace}.svc",
      12. "*.${cluster_name}-tidb-peer",
      13. "*.${cluster_name}-tidb-peer.${namespace}",
      14. "*.${cluster_name}-tidb-peer.${namespace}.svc"
      15. ],
      16. ...

      其中 ${cluster_name} 为集群的名字,${namespace} 为 TiDB 集群部署的命名空间,用户也可以添加自定义 hosts

      最后生成 Server 端证书:

      1. cfssl gencert -ca=ca.pem -ca-key=ca-key.pem -config=ca-config.json -profile=server server.json | cfssljson -bare server
    6. 生成 Client 端证书。

      首先生成默认的 client.json 文件:

      1. cfssl print-defaults csr > client.json

      然后编辑这个文件,修改 CNhosts 属性,hosts 可以留空:

      1. ...
      2. "CN": "TiDB Client",
      3. "hosts": [],
      4. ...

      最后生成 Client 端证书:

    7. 到这里假设你已经按照上述文档把两套证书都创建好了。通过下面的命令为 TiDB 集群创建 Secret 对象:

      1. kubectl create secret generic ${cluster_name}-tidb-server-secret --namespace=${namespace} --from-file=tls.crt=server.pem --from-file=tls.key=server-key.pem --from-file=ca.crt=ca.pem
      2. kubectl create secret generic ${cluster_name}-tidb-client-secret --namespace=${namespace} --from-file=tls.crt=client.pem --from-file=tls.key=client-key.pem --from-file=ca.crt=ca.pem

      这样就给 Server/Client 端证书分别创建了:

      • 一个 Secret 供 TiDB Server 启动时加载使用;
      • 另一个 Secret 供 MySQL 客户端连接 TiDB 集群时候使用。

    用户可以生成多套 Client 端证书,并且至少要生成一套 Client 证书供 TiDB Operator 内部组件访问 TiDB Server(目前有 TidbInitializer 会访问 TiDB Server 来设置密码或者一些初始化操作)。

    使用 cert-manager 颁发证书

    1. 安装 cert-manager。

      请参考官网安装:。

    2. 创建一个 Issuer 用于给 TiDB 集群颁发证书。

      为了配置 cert-manager 颁发证书,必须先创建 Issuer 资源。

      首先创建一个目录保存 cert-manager 创建证书所需文件:

      1. mkdir -p cert-manager
      2. cd cert-manager

      然后创建一个 tidb-server-issuer.yaml 文件,输入以下内容:

      1. apiVersion: cert-manager.io/v1
      2. kind: Issuer
      3. metadata:
      4. name: ${cluster_name}-selfsigned-ca-issuer
      5. namespace: ${namespace}
      6. spec:
      7. selfSigned: {}
      8. ---
      9. apiVersion: cert-manager.io/v1
      10. kind: Certificate
      11. metadata:
      12. name: ${cluster_name}-ca
      13. namespace: ${namespace}
      14. spec:
      15. secretName: ${cluster_name}-ca-secret
      16. commonName: "TiDB CA"
      17. isCA: true
      18. renewBefore: 720h # 30d
      19. issuerRef:
      20. kind: Issuer
      21. ---
      22. apiVersion: cert-manager.io/v1
      23. kind: Issuer
      24. metadata:
      25. name: ${cluster_name}-tidb-issuer
      26. namespace: ${namespace}
      27. spec:
      28. ca:
      29. secretName: ${cluster_name}-ca-secret

      上面的文件创建三个对象:

      • 一个 SelfSigned 类型的 Issuer 对象(用于生成 CA 类型 Issuer 所需要的 CA 证书);
      • 一个 Certificate 对象,isCa 属性设置为 true
      • 一个可以用于颁发 TiDB Server TLS 证书的 Issuer。

      最后执行下面的命令进行创建:

      1. kubectl apply -f tidb-server-issuer.yaml
    3. 创建 Server 端证书。

      cert-manager 中,Certificate 资源表示证书接口,该证书将由上面创建的 Issuer 颁发并保持更新。

      首先来创建 Server 端证书,创建一个 tidb-server-cert.yaml 文件,并输入以下内容:

      1. apiVersion: cert-manager.io/v1
      2. kind: Certificate
      3. metadata:
      4. name: ${cluster_name}-tidb-server-secret
      5. namespace: ${namespace}
      6. spec:
      7. secretName: ${cluster_name}-tidb-server-secret
      8. duration: 8760h # 365d
      9. renewBefore: 360h # 15d
      10. subject:
      11. organizations:
      12. - PingCAP
      13. commonName: "TiDB Server"
      14. usages:
      15. - server auth
      16. dnsNames:
      17. - "${cluster_name}-tidb"
      18. - "${cluster_name}-tidb.${namespace}"
      19. - "${cluster_name}-tidb.${namespace}.svc"
      20. - "*.${cluster_name}-tidb"
      21. - "*.${cluster_name}-tidb.${namespace}"
      22. - "*.${cluster_name}-tidb.${namespace}.svc"
      23. - "*.${cluster_name}-tidb-peer"
      24. - "*.${cluster_name}-tidb-peer.${namespace}"
      25. - "*.${cluster_name}-tidb-peer.${namespace}.svc"
      26. ipAddresses:
      27. - 127.0.0.1
      28. - ::1
      29. issuerRef:
      30. name: ${cluster_name}-tidb-issuer
      31. kind: Issuer
      32. group: cert-manager.io

      其中 ${cluster_name} 为集群的名字:

      • spec.secretName 请设置为 ${cluster_name}-tidb-server-secret
      • usages 请添加上 server auth
      • dnsNames 需要填写这 6 个 DNS,根据需要可以填写其他 DNS:
        • ${cluster_name}-tidb
        • ${cluster_name}-tidb.${namespace}
        • ${cluster_name}-tidb.${namespace}.svc
        • *.${cluster_name}-tidb
        • *.${cluster_name}-tidb.${namespace}
        • *.${cluster_name}-tidb.${namespace}.svc
        • *.${cluster_name}-tidb-peer
        • *.${cluster_name}-tidb-peer.${namespace}
        • *.${cluster_name}-tidb-peer.${namespace}.svc
      • ipAddresses 需要填写这两个 IP ,根据需要可以填写其他 IP:
        • 127.0.0.1
        • ::1
      • issuerRef 请填写上面创建的 Issuer;
      • 其他属性请参考 cert-manager API

      通过执行下面的命令来创建证书:

      1. kubectl apply -f tidb-server-cert.yaml

      创建这个对象以后,cert-manager 会生成一个名字为 ${cluster_name}-tidb-server-secret 的 Secret 对象供 TiDB Server 使用。

    4. 创建 Client 端证书。

      创建一个 tidb-client-cert.yaml 文件,并输入以下内容:

      1. apiVersion: cert-manager.io/v1
      2. kind: Certificate
      3. metadata:
      4. name: ${cluster_name}-tidb-client-secret
      5. namespace: ${namespace}
      6. spec:
      7. secretName: ${cluster_name}-tidb-client-secret
      8. duration: 8760h # 365d
      9. renewBefore: 360h # 15d
      10. subject:
      11. organizations:
      12. - PingCAP
      13. commonName: "TiDB Client"
      14. usages:
      15. - client auth
      16. issuerRef:
      17. name: ${cluster_name}-tidb-issuer
      18. kind: Issuer
      19. group: cert-manager.io

      其中 ${cluster_name} 为集群的名字:

      • spec.secretName 请设置为 ${cluster_name}-tidb-client-secret
      • usages 请添加上 client auth
      • dnsNamesipAddresses 不需要填写;
      • issuerRef 请填写上面创建的 Issuer;
      • 其他属性请参考 。

      通过执行下面的命令来创建证书:

      1. kubectl apply -f tidb-client-cert.yaml

      创建这个对象以后,cert-manager 会生成一个名字为 ${cluster_name}-tidb-client-secret 的 Secret 对象供 TiDB Client 使用。

    5. TiDB Operator 集群内部有 4 个组件需要请求 TiDB Server,当开启 TLS 验证后,这些组件可以使用证书来请求 TiDB Server,每个组件都可以使用单独的证书。这些组件有:

      • TidbInitializer
      • PD Dashboard
      • Backup
      • Restore

      如需要使用 TiDB Lightning 恢复 Kubernetes 上的集群数据,则也可以为其中的 TiDB Lightning 组件生成 Client 端证书。

      下面就来生成这些组件的 Client 证书。

      1. 创建一个 tidb-components-client-cert.yaml 文件,并输入以下内容:

        其中 ${cluster_name} 为集群的名字:

        • spec.secretName 请设置为 ${cluster_name}-${component}-client-secret
        • usages 请添加上 client auth
        • dnsNamesipAddresses 不需要填写;
        • issuerRef 请填写上面创建的 Issuer;
        • 其他属性请参考 。

        如需要为 TiDB Lignting 组件生成 Client 端证书,则可以使用以下内容并通过在 TiDB Lightning 的 Helm Chart values.yaml 中设置 tlsCluster.tlsClientSecretName${cluster_name}-lightning-client-secret

        1. apiVersion: cert-manager.io/v1
        2. kind: Certificate
        3. metadata:
        4. name: ${cluster_name}-lightning-client-secret
        5. namespace: ${namespace}
        6. spec:
        7. secretName: ${cluster_name}-lightning-client-secret
        8. duration: 8760h # 365d
        9. subject:
        10. organizations:
        11. - PingCAP
        12. commonName: "Lightning client"
        13. usages:
        14. issuerRef:
        15. name: ${cluster_name}-tidb-issuer
        16. kind: Issuer
        17. group: cert-manager.io
      2. 通过执行下面的命令来创建证书:

        1. kubectl apply -f tidb-components-client-cert.yaml
      3. 创建这些对象以后,cert-manager 会生成 4 个 Secret 对象供上面四个组件使用。

      注意

      TiDB Server 的 TLS 兼容 MySQL 协议。当证书内容发生改变后,需要管理员手动执行 SQL 语句 alter instance reload tls 进行刷新。

    接下来将会创建一个 TiDB 集群,并且执行以下步骤:

    • 开启 MySQL 客户端 TLS;
    • 对集群进行初始化(这里创建了一个数据库 app);
    • 创建一个 Backup 对象对集群进行备份;
    • 创建一个 Restore 对象对进群进行恢复;
    • TidbInitializer,PD Dashboard,Backup 以及 Restore 分别使用单独的 Client 证书(用 tlsClientSecretName 指定)。
    1. 创建三个 .yaml 文件:

      • tidb-cluster.yaml:

        1. apiVersion: pingcap.com/v1alpha1
        2. kind: TidbCluster
        3. metadata:
        4. name: ${cluster_name}
        5. namespace: ${namespace}
        6. spec:
        7. version: v5.4.0
        8. timezone: UTC
        9. pvReclaimPolicy: Retain
        10. pd:
        11. baseImage: pingcap/pd
        12. maxFailoverCount: 0
        13. replicas: 1
        14. requests:
        15. storage: "10Gi"
        16. config: {}
        17. tlsClientSecretName: ${cluster_name}-pd-dashboard-client-secret
        18. tikv:
        19. baseImage: pingcap/tikv
        20. maxFailoverCount: 0
        21. replicas: 1
        22. requests:
        23. storage: "100Gi"
        24. config: {}
        25. tidb:
        26. baseImage: pingcap/tidb
        27. maxFailoverCount: 0
        28. replicas: 1
        29. service:
        30. type: ClusterIP
        31. config: {}
        32. tlsClient:
        33. enabled: true
        34. ---
        35. apiVersion: pingcap.com/v1alpha1
        36. kind: TidbInitializer
        37. metadata:
        38. name: ${cluster_name}-init
        39. namespace: ${namespace}
        40. spec:
        41. image: tnir/mysqlclient
        42. cluster:
        43. namespace: ${namespace}
        44. name: ${cluster_name}
        45. initSql: |-
        46. create database app;
        47. tlsClientSecretName: ${cluster_name}-tidb-initializer-client-secret
      • backup.yaml:

        1. apiVersion: pingcap.com/v1alpha1
        2. kind: Backup
        3. metadata:
        4. name: ${cluster_name}-backup
        5. namespace: ${namespace}
        6. spec:
        7. backupType: full
        8. br:
        9. cluster: ${cluster_name}
        10. clusterNamespace: ${namespace}
        11. sendCredToTikv: true
        12. from:
        13. host: ${host}
        14. secretName: ${tidb_secret}
        15. port: 4000
        16. user: root
        17. tlsClientSecretName: ${cluster_name}-backup-client-secret
        18. s3:
        19. provider: aws
        20. region: ${my_region}
        21. secretName: ${s3_secret}
        22. bucket: ${my_bucket}
        23. prefix: ${my_folder}
      • restore.yaml:

        1. apiVersion: pingcap.com/v1alpha1
        2. kind: Restore
        3. metadata:
        4. name: ${cluster_name}-restore
        5. namespace: ${namespace}
        6. spec:
        7. backupType: full
        8. br:
        9. cluster: ${cluster_name}
        10. clusterNamespace: ${namespace}
        11. sendCredToTikv: true
        12. to:
        13. host: ${host}
        14. secretName: ${tidb_secret}
        15. port: 4000
        16. user: root
        17. tlsClientSecretName: ${cluster_name}-restore-client-secret
        18. s3:
        19. provider: aws
        20. region: ${my_region}
        21. secretName: ${s3_secret}
        22. bucket: ${my_bucket}
        23. prefix: ${my_folder}

      其中 ${cluster_name} 为集群的名字,${namespace} 为 TiDB 集群部署的命名空间。通过设置 spec.tidb.tlsClient.enabled 属性为 true 来开启 MySQL 客户端 TLS。

    2. 部署 TiDB 集群:

      1. kubectl apply -f tidb-cluster.yaml
    3. 集群备份:

      1. kubectl apply -f backup.yaml
    4. 集群恢复:

      1. kubectl apply -f restore.yaml

    可以根据官网文档提示,使用上面创建的 Client 证书,通过下面的方法连接 TiDB 集群:

    获取 Client 证书的方式并连接 TiDB Server 的方法是:

    1. mysql --comments -uroot -p -P 4000 -h ${tidb_host} --ssl-cert=client-tls.crt --ssl-key=client-tls.key --ssl-ca=client-ca.crt

    为 MySQL 客户端开启 TLS - 图4注意

    最后请参考 来验证是否正确开启了 TLS。