Configuring TLS for MySQL Instances

Page last updated:

This topic describes how to configure TLS for a MySQL instance.

Overview

Tanzu MySQL for Kubernetes is configured to require an encrypted connection, and client connections cannot fall back to use an unencrypted connection. By default, the MySQL server uses a self-signed certificate. Clients can connect to the MySQL server, but they cannot verify the connection.

To verify the connection to the MySQL server, the MySQL instance can be configured with a TLS certificate and private key.

To configure a MySQL instance for TLS, you must first create a TLS Secret in the same namespace as the MySQL instance. There are several ways to create the Secret. This topic describes two methods:

After creating the secret, you add the name of the secret to your copy of the mysql.yaml file, and configure the instance with the updated file, as described in Configure MySQL for TLS below.

For general information about TLS secrets, see the Kubernetes documentation.

Prerequisites

Before you configure TLS for a MySQL Instance, you must have:

  • Access and permissions to the MySQL instance.

  • The Kubernetes Command Line Interface (kubectl) installed: For more information, see the Kubernetes documentation.

Create the TLS Secret Manually

This procedure describes how to create the TLS Secret using kubectl. To create the TLS Secret using cert-manager instead, see Create TLS Secret with cert-manager below.

  1. Generate a certificate and private key using a certificate manager, such as OpenSSL, certstrap, or Let’s Encrypt.

    When creating the certificate, supply the server hostname for the subject alternative names (SANs). The server hostname is the DNS name that you use when connecting your app to the MySQL instance:

    • If your apps are deployed in the same Kubernetes cluster as your instance:
      The hostname is INSTANCE-NAME.DEVELOPMENT-NAMESPACE, for example, mysql-sample.default.
    • If your app is deployed outside the Kubernetes cluster and you have configured the MySQL spec.serviceType as LoadBalancer:
      The hostname is the external DNS name of the load balancer. See Access the MySQL Server from an External IP Address in Accessing MySQL Instances.
  2. Create the TLS Secret by running:

    kubectl -n DEVELOPMENT-NAMESPACE create secret generic TLS-SECRET-NAME \
      --type kubernetes.io/tls \
      --from-file=tls.crt=PATH-TO-CERTIFICATE \
      --from-file=tls.key=PATH-TO-PRIVATE-KEY \
      --from-file=ca.crt=PATH-TO-CERTIFICATE-AUTHORITY
    

    Where:

    • DEVELOPMENT-NAMESPACE is the namespace for the MySQL instance.
    • TLS-SECRET-NAME is the name you choose for the TLS Secret.
    • PATH-TO-CERTIFICATE is the file path to the certificate created in the step above.
    • PATH-TO-PRIVATE-KEY is the file path to the private key created in the step above.
    • PATH-TO-CERTIFICATE-AUTHORITY is the file path to the certificate authority that signed the certificate.

    For example:

      $ kubectl -n my-namespace create secret generic mysql-tls-secret \
          --type kubernetes.io/tls \
          --from-file=tls.crt=/path/server.crt \
          --from-file=tls.key=/path/server.key \
          --from-file=ca.crt=/path/server_ca.crt
    

Create TLS Secret with cert-manager

This procedure describes how to create the TLS Secret using cert-manager.

To create the TLS Secret through the kubectl instead, see Create the TLS Secret Manually above.

Prerequisite: Before you install cert-manager, you must have the Helm v3 command-line tool installed. For information about installing Helm, see the Helm documentation.

  1. Install cert-manager on your Kubernetes cluster by running these commands:

    kubectl create namespace cert-manager
    helm repo add jetstack https://charts.jetstack.io
    helm repo update
    helm install cert-manager jetstack/cert-manager \
    --namespace cert-manager --version v1.0.2 --set installCRDs=true
    
  2. Use cert-manager to create either a cluster-wide ClusterIssuer resource or a namespace-local Issuer resource in the same namespace as your MySQL instance.

    For information about the Issuer types, see the cert-manager documentation.

    Note: Some Issuer types, for example ACME, do not produce a TLS Secret that includes the ca.crt key.

  3. Choose and record a name for the TLS secret name, for example, mysql-tls-secret. Use this name as the spec.secretName when you create the Certificate resource below.

  4. Use the cert-manager ClusterIssuer or Issuer from step 2 above to create a Certificate resource in the same namespace as the MySQL instance:

    1. Specify your TLS secret name from step 3 in the spec.secretName parameter of your request YAML.
    2. Specify the name of your ClusterIssuer or Issuer in thespec.issuerRef.name parameter of your request YAML.
    3. Supply server hostname for the subject alternative names (SANs) using the spec.dnsNames and spec.ipAddresses parameters in your request YAML.
    4. For spec.dnsNames and spec.ipAddresses specify the DNS names or IP addresses that you use when connecting your app to the MySQL instance.
      • For apps deployed within the same Kubernetes cluster as your instance, specify MYSQL-INSTANCE-NAME.MYSQL-INSTANCE-NAMESPACE for spec.dnsNames. (You may optionally fully-qualify that hostname as MYSQL-INSTANCE-NAME.MYSQL-INSTANCE-NAMESPACE.svc.CLUSTER_DOMAIN. Kubernetes defaults to the value cluster.local for CLUSTER_DOMAIN, e.g. mysql-sample.default.svc.cluster.local.)
      • For apps deployed outside of the Kubernetes cluster, if you have configured the MySQL instance’s serviceType as LoadBalancer, then spec.ipAddresses is the external IP address of the load balancer. That address is visible by running: kubectl get service INSTANCE-NAME -o jsonpath={.status.loadBalancer.ingress[].ip}.
        See the docs for Accessing the MySQL Server from an External IP Address.
      • Include any additional DNS names, IP addresses, URIs or subject alternative names you may have configured to access your MySQL instance and which may be used by a client desiring a TLS connection to the instance. Certificate Resources.

    For information about creating cert-manager certificates, see the cert-manager documentation.

    For information about troubleshooting certificate creation, see the cert-manager documentation.

  5. Verify that the TLS secret created has the ca.crt key by running:

    kubectl -n DEVELOPMENT-NAMESPACE get secret TLS-SECRET-NAME -o jsonpath="{.data['ca\.crt']}"
    

    Where:

    • DEVELOPMENT-NAMESPACE is the namespace for the MySQL instance.
    • TLS-SECRET-NAME is the name you chose for the TLS Secret.

    For example:

    $ kubectl -n my-namespace get secret mysql-tls-secret -o jsonpath="{.data['ca\.crt']}"  

Configure MySQL for TLS

To configure TLS for the MySQL instance:

  1. In your copy of the mysql.yaml for the MySQL instance, specify spec.tls.secret.name as the name of the TLS Secret created in the namespace.

  2. Create or update the MySQL instance by running:

    kubectl apply -f FILENAME -n DEVELOPMENT-NAMESPACE
    

    Where:

    • FILENAME is the name of the configuration file for your MySQL resource.
    • DEVELOPMENT-NAMESPACE is the namespace for the MySQL instance.

Connect to a Load Balanced MySQL Instance over TLS

This section shows how to connect to a MySQL Instance configured with a load balancing service (spec.serviceType.LoadBalancer) from an off-cluster machine running the mysql CLI client.

To allow the client connection which verifies the MySQL server certificate, give the client the certificate of the CA which signed the MySQL TLS certificate. The client uses this CA to authenticate the MySQL server-provided TLS certificate.

To connect mysql client to the MySQL instance over TLS:

  1. Obtain the certificate of the CA which signed the MySQL Server’s TLS certificate.

    The CA certificate is stored in the TLS secrets ca.crt field, base64 encoded. You can save that certificate out to a local file ca.crt by running:

    kubectl -n DEVELOPMENT-NAMESPACE get secret TLS-SECRET-NAME -o jsonpath="{.data['ca\.crt']}" | base64 -d > ca.crt
    

    For example:

    $ kubectl -n my-namespace get secret mysql-tls-secret -o jsonpath="{.data['ca\.crt']}" | base64 -d > ca.crt
    
  2. Create a MySQL username and password to test your connection with. You need to connect as a non-root user because MySQL instances prohibit root connection from remote machines.

    For an example of creating a database, user, and password for connection testing, see Connecting Apps to MySQL Instances.

  3. Obtain the IP address that your MySQL Instance is listening on for connections. Load balanced instances expose their IP via their status.loadBalancer.ingress property:

    kubectl get service INSTANCE-NAME -o jsonpath={.status.loadBalancer.ingress[].ip}
    
  4. Connect your local mysql client to your MySQL instance, providing:

    • USERNAME : the username you created above. (example: bn_wordpress)
    • PASSWORD : the password for the user you created above. (example: hunter2)
    • IP_ADDRESS: the external IP address from the previous step
    • CA_CERTPATH: the pathname to the certificate file created in step 2 above (example: ./ca.crt)
    mysql -u USERNAME -pPASSWORD -h IP_ADDRESS --ssl-mode=VERIFY_CA --ssl-ca=CA_CERTPATH
    

    For example:

    $ mysql -u bn_wordpress -phunter2 -h 192.168.64.200 --ssl-mode=VERIFY_CA --ssl-ca=./ca.crt 
    mysql: [Warning] Using a password on the command line interface can be insecure. Welcome to the MySQL monitor. Commands end with ; or \g. Your MySQL connection id is 7 ... Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
    mysql>