Enable TLS for the MySQL Client

This document describes how to enable TLS for MySQL client of the TiDB cluster on Kubernetes. Starting from TiDB Operator v1.1, TLS for the MySQL client of the TiDB cluster on Kubernetes is supported.

To enable TLS for the MySQL client, perform the following steps:

  1. Issue two sets of certificates: a set of server-side certificates for TiDB server, and a set of client-side certificates for MySQL client. Create two Secret objects, ${cluster_name}-tidb-server-secret and ${cluster_name}-tidb-client-secret, respectively including these two sets of certificates.

    Enable TLS for the MySQL Client - 图1Note

    The Secret objects you created must follow the above naming convention. Otherwise, the deployment of the TiDB cluster will fail.

    Certificates can be issued in multiple methods. This document describes two methods. You can choose either of them to issue certificates for the TiDB cluster:

  2. Deploy the cluster, and set .spec.tidb.tlsClient.enabled to true.

  3. Configure the MySQL client to use an encrypted connection.

If you need to renew the existing TLS certificate, refer to Renew and Replace the TLS Certificate.

Issue two sets of certificates for the TiDB cluster

This section describes how to issue certificates for the TiDB cluster using two methods: cfssl and cert-manager.

Using cfssl

  1. Download cfssl and initialize the certificate issuer:

    1. mkdir -p ~/bin
    2. curl -s -L -o ~/bin/cfssl https://pkg.cfssl.org/R1.2/cfssl_linux-amd64
    3. curl -s -L -o ~/bin/cfssljson https://pkg.cfssl.org/R1.2/cfssljson_linux-amd64
    4. chmod +x ~/bin/{cfssl,cfssljson}
    5. export PATH=$PATH:~/bin
    6. mkdir -p cfssl
    7. cd cfssl
    8. cfssl print-defaults config > ca-config.json
    9. cfssl print-defaults csr > ca-csr.json
  2. Configure the client auth (CA) option in ca-config.json:

    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. Change the certificate signing request (CSR) of ca-csr.json:

    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. Generate CA by the configured option:

    1. cfssl gencert -initca ca-csr.json | cfssljson -bare ca -
  5. Generate the server-side certificate:

    First, create the default server.json file:

    1. cfssl print-defaults csr > server.json

    Then, edit this file to change the CN, hosts attributes:

    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} is the name of the cluster. ${namespace} is the namespace in which the TiDB cluster is deployed. You can also add your customized hosts.

    Finally, generate the server-side certificate:

    1. cfssl gencert -ca=ca.pem -ca-key=ca-key.pem -config=ca-config.json -profile=server server.json | cfssljson -bare server
  6. Generate the client-side certificate:

    First, create the default client.json file:

    1. cfssl print-defaults csr > client.json

    Then, edit this file to change the CN, hosts attributes. You can leave the hosts empty:

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

    Finally, generate the client-side certificate:

    1. cfssl gencert -ca=ca.pem -ca-key=ca-key.pem -config=ca-config.json -profile=client client.json | cfssljson -bare client
  7. Create the Kubernetes Secret object.

    If you have already generated two sets of certificates as described in the above steps, create the Secret object for the TiDB cluster by the following command:

    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

    You have created two Secret objects for the server-side and client-side certificates:

    • The TiDB server loads one Secret object when it starts
    • The MySQL client uses another Secret object when it connects to the TiDB cluster

You can generate multiple sets of client-side certificates. At least one set of client-side certificates is needed for the internal components of TiDB Operator to access the TiDB server. Currently, TidbInitializer accesses the TiDB server to set the password or perform initialization.

Using cert-manager

  1. Install cert-manager.

    Refer to cert-manager installation in Kubernetes.

  2. Create an Issuer to issue certificates for the TiDB cluster.

    To configure cert-manager, create the Issuer resources.

    First, create a directory which saves the files that cert-manager needs to create certificates:

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

    Then, create a tidb-server-issuer.yaml file with the following content:

    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. duration: 87600h # 10yrs
    19. renewBefore: 720h # 30d
    20. issuerRef:
    21. name: ${cluster_name}-selfsigned-ca-issuer
    22. kind: Issuer
    23. ---
    24. apiVersion: cert-manager.io/v1
    25. kind: Issuer
    26. metadata:
    27. name: ${cluster_name}-tidb-issuer
    28. namespace: ${namespace}
    29. spec:
    30. ca:
    31. secretName: ${cluster_name}-ca-secret

    This .yaml file creates three objects:

    • An Issuer object of SelfSigned class, used to generate the CA certificate needed by Issuer of CA class
    • A Certificate object, whose isCa is set to true
    • An Issuer, used to issue TLS certificates for the TiDB server

    Finally, execute the following command to create an Issuer:

    1. kubectl apply -f tidb-server-issuer.yaml
  3. Generate the server-side certificate.

    In cert-manager, the Certificate resource represents the certificate interface. This certificate is issued and updated by the Issuer created in Step 2.

    First, create a tidb-server-cert.yaml file with the following content:

    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} is the name of the cluster. Configure the items as follows:

    • Set spec.secretName to ${cluster_name}-tidb-server-secret
    • Add server auth in usages
    • Add the following 6 DNSs in dnsNames. You can also add other DNSs according to your needs:
      • ${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
    • Add the following 2 IPs in ipAddresses. You can also add other IPs according to your needs:
      • 127.0.0.1
      • ::1
    • Add the Issuer created above in the issuerRef
    • For other attributes, refer to cert-manager API.

    Execute the following command to generate the certificate:

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

    After the object is created, cert-manager generates a ${cluster_name}-tidb-server-secret Secret object to be used by the TiDB server.

  4. Generate the client-side certificate:

    Create a tidb-client-cert.yaml file with the following content:

    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} is the name of the cluster. Configure the items as follows:

    • Set spec.secretName to ${cluster_name}-tidb-client-secret
    • Add client auth in usages
    • dnsNames and ipAddresses are not required
    • Add the Issuer created above in the issuerRef
    • For other attributes, refer to cert-manager API

    Execute the following command to generate the certificate:

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

    After the object is created, cert-manager generates a ${cluster_name}-tidb-client-secret Secret object to be used by the TiDB client.

  5. Create multiple sets of client-side certificates (optional).

    Four components in the TiDB Operator cluster need to request the TiDB server. When TLS is enabled, these components can use certificates to request the TiDB server, each with a separate certificate. The four components are listed as follows:

    • TidbInitializer
    • PD Dashboard
    • Backup
    • Restore

    If you need to restore data using TiDB Lightning, you need to generate a server-side certificate for the TiDB Lightning component.

    To create certificates for these components, take the following steps:

    1. Create a tidb-components-client-cert.yaml file with the following content:

      1. apiVersion: cert-manager.io/v1
      2. kind: Certificate
      3. metadata:
      4. name: ${cluster_name}-tidb-initializer-client-secret
      5. namespace: ${namespace}
      6. spec:
      7. secretName: ${cluster_name}-tidb-initializer-client-secret
      8. duration: 8760h # 365d
      9. renewBefore: 360h # 15d
      10. subject:
      11. organizations:
      12. - PingCAP
      13. commonName: "TiDB Initializer client"
      14. usages:
      15. - client auth
      16. issuerRef:
      17. name: ${cluster_name}-tidb-issuer
      18. kind: Issuer
      19. group: cert-manager.io
      20. ---
      21. apiVersion: cert-manager.io/v1
      22. kind: Certificate
      23. metadata:
      24. name: ${cluster_name}-pd-dashboard-client-secret
      25. namespace: ${namespace}
      26. spec:
      27. secretName: ${cluster_name}-pd-dashboard-client-secret
      28. duration: 8760h # 365d
      29. renewBefore: 360h # 15d
      30. subject:
      31. organizations:
      32. - PingCAP
      33. commonName: "PD Dashboard client"
      34. usages:
      35. - client auth
      36. issuerRef:
      37. name: ${cluster_name}-tidb-issuer
      38. kind: Issuer
      39. group: cert-manager.io
      40. ---
      41. apiVersion: cert-manager.io/v1
      42. kind: Certificate
      43. metadata:
      44. name: ${cluster_name}-backup-client-secret
      45. namespace: ${namespace}
      46. spec:
      47. secretName: ${cluster_name}-backup-client-secret
      48. duration: 8760h # 365d
      49. renewBefore: 360h # 15d
      50. subject:
      51. organizations:
      52. - PingCAP
      53. commonName: "Backup client"
      54. usages:
      55. - client auth
      56. issuerRef:
      57. name: ${cluster_name}-tidb-issuer
      58. kind: Issuer
      59. group: cert-manager.io
      60. ---
      61. apiVersion: cert-manager.io/v1
      62. kind: Certificate
      63. metadata:
      64. name: ${cluster_name}-restore-client-secret
      65. namespace: ${namespace}
      66. spec:
      67. secretName: ${cluster_name}-restore-client-secret
      68. duration: 8760h # 365d
      69. renewBefore: 360h # 15d
      70. subject:
      71. organizations:
      72. - PingCAP
      73. commonName: "Restore client"
      74. usages:
      75. - client auth
      76. issuerRef:
      77. name: ${cluster_name}-tidb-issuer
      78. kind: Issuer
      79. group: cert-manager.io

      In the .yaml file above, ${cluster_name} is the name of the cluster. Configure the items as follows:

      • Set the value of spec.secretName to ${cluster_name}-${component}-client-secret.
      • Add client auth in usages.
      • dnsNames and ipAddresses are not required.
      • Add the Issuer created above in the issuerRef.
      • For other attributes, refer to cert-manager API.

      To generate a client-side certificate for TiDB Lightning, use the following content and set tlsCluster.tlsClientSecretName to ${cluster_name}-lightning-client-secret in TiDB Lightning’s values.yaml file.

      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. renewBefore: 360h # 15d
      10. subject:
      11. organizations:
      12. - PingCAP
      13. commonName: "Lightning client"
      14. usages:
      15. - client auth
      16. issuerRef:
      17. name: ${cluster_name}-tidb-issuer
      18. kind: Issuer
      19. group: cert-manager.io
    2. Create the certificate by running the following command:

      1. kubectl apply -f tidb-components-client-cert.yaml
    3. After creating these objects, cert-manager will generate four secret objects for the four components.

    Enable TLS for the MySQL Client - 图2Note

    TiDB server’s TLS is compatible with the MySQL protocol. When the certificate content is changed, the administrator needs to manually execute the SQL statement alter instance reload tls to refresh the content.

Deploy the TiDB cluster

In this step, you create a TiDB cluster and perform the following operations:

  • Enable TLS for the MySQL client
  • Initialize the cluster (an app database is created for demonstration)
  • Create a Backup object to back up the cluster
  • Create a Restore object to restore the cluster
  • Use separate client-side certificates for TidbInitializer, PD Dashboard, Backup, and Restore (specified by tlsClientSecretName)
  1. Create three .yaml files:

    • tidb-cluster.yaml file:

      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}

    In the above file, ${cluster_name} is the name of the cluster, and ${namespace} is the namespace in which the TiDB cluster is deployed. To enable TLS for the MySQL client, set spec.tidb.tlsClient.enabled to true.

  2. Deploy the TiDB cluster:

    1. kubectl apply -f tidb-cluster.yaml
  3. Back up the cluster:

    1. kubectl apply -f backup.yaml
  4. Restore the cluster:

    1. kubectl apply -f restore.yaml

Configure the MySQL client to use an encrypted connection

To connect the MySQL client with the TiDB cluster, use the client-side certificate created above and take the following methods. For details, refer to Configure the MySQL client to use encrypted connections.

Execute the following command to acquire the client-side certificate and connect to the TiDB server:

  1. kubectl get secret -n ${namespace} ${cluster_name}-tidb-client-secret -ojsonpath='{.data.tls\.crt}' | base64 --decode > client-tls.crt
  2. kubectl get secret -n ${namespace} ${cluster_name}-tidb-client-secret -ojsonpath='{.data.tls\.key}' | base64 --decode > client-tls.key
  3. kubectl get secret -n ${namespace} ${cluster_name}-tidb-client-secret -ojsonpath='{.data.ca\.crt}' | base64 --decode > client-ca.crt
  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

Enable TLS for the MySQL Client - 图3Note

The default authentication plugin of MySQL 8.0 is updated from mysql_native_password to caching_sha2_password. Therefore, if you use MySQL client from MySQL 8.0 to access the TiDB service (TiDB version < v4.0.7), and if the user account has a password, you need to explicitly specify the --default-auth=mysql_native_password parameter.

Finally, to verify whether TLS is successfully enabled, refer to checking the current connection.