Another important aspect of security is securing and authenticating the ports used to access the database. The most common way to do this is by enabling TLS/SSL to encrypt data and authenticate the servers using user-created certificates. The process for creating the private keystore and truststore in Java is described in the section on "Configuring TLS/SSL on the VoltDB Server" in the Using VoltDB guide. This process is the same whether you are running the cluster directly on servers or in Kubernetes.
The one difference when enabling TLS/SSL for the cluster in Kubernetes is that if you want the Operator to verify the
authenticity of the cluster's certificate, you must also configure the operator with an appropriate truststore, in PEM
format. If not, you must set the cluster.clusterSpec.ssl.insecure
property to
true.
The easiest way to allow verification with a PEM format truststore is to configure the operator using the same truststore and password you use for the cluster itself. First, you will need to convert the truststore to PEM format using the Java keytool:
keytool -export \ -alias my.key -rfc \ -file mycert.pem \ -keystore mykey.jks \ -storepass topsecret \ -keypass topsecret
Once you have your keystore, truststore, and truststore in PEM format, you can configure the cluster and operator with the appropriate SSL properties, using one of three methods:
Configuring TLS/SSL with YAML properties
Using Kubernetes secrets to store and reuse TLS/SSL information
Using cert-manager to create and manage TLS/SSL information for you
The following sections describe the three methods for configuring encryption. In addition, TLS/SSL certificates have an expiration date. It is important you replace the certificate before it expires. If not, the operator will lose the ability to communicate with the cluster pods. See Section 7.3, “Updating TLS/SSL Security Certificates” for instructions on updating the TLS/SSL certificates in Kubernetes.
The following example uses YAML properties to enable TLS/SSL security and specify the truststore and keystore
passwords. First you must enable TLS/SSL encryption, using the cluster.config.deployment.ssl.enabled
property. Then you choose which ports will use SSL encryption (in this example, the internal and external ports, but not
DR). Finally, you specify the passwords for the keystore and truststore. The YAML does not include
the actual content of the truststore and keystore files, since they are in a binary format.
cluster: config: deployment: ssl: enabled: true external: true internal: true keystore: password: topsecret truststore: password: topsecret clusterSpec: ssl: insecure: false
Using the preceding YAML file (calling it ssl.yaml
), we can complete the SSL configuration by
specifying the truststore and keystore files on the helm command line with the
--set-file
argument:
helm install mydb voltdb/voltdb \
--values myconfig.yaml \
--values ssl.yaml \
--set-file cluster.config.deployment.ssl.keystore.file=mykey.jks \
--set-file cluster.config.deployment.ssl.truststore.file=mytrust.jks \
--set-file cluster.clusterSpec.ssl.certificateFile=mycert.pem
Three important notes concerning TLS/SSL configuration:
If you enable SSL for the cluster's external interface and ports and you also want to enable Prometheus metrics, you must provide an appropriate SSL truststore and password for the metrics agent. See Section 6.1, “Using Prometheus to Monitor VoltDB” for more information on configuring the Prometheus agent in Kubernetes.
If you do not require validation of the TLS certificate by the operator, you can avoid setting the truststore
PEM for the operator and, instead, set the cluster.clusterSpec.ssl.insecure
property to
true.
If you enable SSL for the cluster, you must repeat the specification of the truststore and keystore files every
time you update the configuration. Using the --reuse-values
argument on the helm
upgrade
command is not sufficient.
An alternative method is to store the key and trust stores and passwords in a Kubernetes secret. Secrets are a standard feature of Kubernetes that allow you to store sensitive information as key value pairs in a protected space. Three advantages of using a secret are:
You do not have to enter sensitive TLS/SSL information in plain text when configuring or updating your database.
The secret is used automatically for subsequent updates; you do not have to repeatedly specify the TLS/SSL files when updating the database configuration.
You can reuse the same secret for multiple database instances and services.
To use a Kubernetes secret to store the TLS/SSL information for your database, you must first create the necessary files as described in Section 7.2, “Configuring TLS/SSL”. Next you create your Kubernetes secret using the kubectl create secret command, specifying the key names and corresponding artifacts as arguments. For example:
$ kubectl create secret generic my-ssl-creds \ --from-file=keystore_data=mykey.jks \ --from-file=truststore_data=mytrust.jks \ --from-file=certificate=mycert.pem \ --from-literal=keystore_password=topsecret \ --from-literal=truststore_password=topsecret
It is critical you use the key names keystore_data, truststore_data, keystore_password, truststore_password, and certificate for the keystore, truststore, corresponding passwords, and PEM file, respectively. If not, the Volt Operator will not be able to find them. Also, the secret must be the the same Kubenetes namespace as the Helm release you are configuring.
Once you create the secret you can use it to configure your database by not setting any of
standard SSL properties such as the cluster.config.deployment.ssl...
properties or
cluster.clusterSpec.ssl.certificateFile
. Instead, set the property
cluster.config.deployment.ssl.sslSecret.certSecretName
. Using the secret created in the preceding
example, the configuration of your database will look something like this:
cluster:
config:
deployment:
ssl:
sslSecret:
certSecretName: my-ssl-creds
Another alternative for maintaining the TLS/SSL information is to use the Kubernetes cert-manager (cert-manager.io). The cert-manager is an add-on for Kubernetes that helps you create and maintain certificates and other private information in Kubernetes. If you wish to use cert-manager for self-signed certificates, you not only use it to store the certificate and truststore, you create them with cert-manager as well. (For more detailed information concerning cert-manager, see the cert-manager documentation.)
The basic steps for storing self-signed TLS/SSL credentials in cert-manager are:
Create a Kubernetes secret with the TLS password you wish to use.
Create an issuer resource in Kubernetes that will generate and authenticate the certificate. You only need to do this once for the namespace and multiple certificate requests can use the same issuer.
Create a request for the issuer to generate the actual TLS/SSL certificate and store it in a Kubernetes secret.
Specify the resulting certificate secret in the VoltDB configuration and start your cluster.
You create the Kubernetes secret containing the password using the kubectl create secret command. For example, The following command creates a secret (my-ssl-password) with the password "topsecret". The password must be assigned to the label password:
$ kubectl create secret generic my-ssl-password \ --from-literal=password=topsecret
You create the cert-manager issuer and the certificate request using YAML properties. The easiest way to do this is by typing the property declarations into a YAML file. For example, the following two YAML files create a cert-manager issuer service and request a certificate.
create-issuer.yaml
apiVersion: cert-manager.io/v1 kind: Issuer metadata: name: selfsigned-issuer namespace: mydb spec: selfSigned: {}
request-cert.yaml
apiVersion: cert-manager.io/v1 kind: Certificate metadata: name: my-ssl-certificate namespace: mydb spec: commonName: voltdb.com duration: 8766h secretName: my-ssl-creds keystores: jks: create: true passwordSecretRef: name: my-ssl-password key: password issuerRef: name: selfsigned-issuer kind: Issuer privateKey: algorithm: RSA encoding: PKCS1 size: 2048 usages: - server auth
Four key points to note about the certificate request are:
The issuer must be in the same namespace as the database that uses the certificate.
The certificate request references the secret you created containing the password (my-ssl-password in the example).
As mentioned before, the key in the password secret must be "password".
You specify the duration of the certificate in hours. In this example, 8766 hours, or one year.
Once you create the YAML files, you can create the issuer and request the certificate:
$ kubectl apply -f create-issuer.yaml # Do only once $ kubectl apply -f request-cert.yaml
Finally, in your database configuration, you point to the two secrets containing the password and created by the certificate request (in this case, my-ssl-password and my-ssl-creds) the same way you would for a manually created secret:
cluster: config: deployment: ssl: sslSecret: passwordSecretName: my-ssl-password certSecretName: my-ssl-creds