mariadb-operator/mariadb-operator

[Feature] TLS support

Opened this issue ยท 8 comments

Is your feature request related to a problem? Please describe.
I would like to manage TLS setup for MariaDB pods in a Kubernetes-native way :)

Describe the solution you'd like
I would like to be able to specify tls property when deploying a MariaDB manifest. It should be possible to specify an existing TLS secret or to ask the operator for an auto-generated one. Updates to this Secret should be reflected in the pod (process or pod restarted?)

Describe alternatives you've considered
I think it is possible now with some myCnf magic and mounting correct Secret into the pod

Additional context

Hey @wasd171 ! Thanks for bringing this up ๐Ÿ™๐Ÿป

Indeed, providing TLS secrets is something we don't directly support, but it can be achieved by mounting an extra volume and pointing to it in the myCnf:

apiVersion: mariadb.mmontes.io/v1alpha1
kind: MariaDB
metadata:
  name: mariadb
spec:
  ...
  volumes: 
    - name: tls
      secret:
          secretName: tls
  volumeMounts: 
    - name: tls
      mountPath: /certs

  myCnf: |
    [mysqld]
    ...
    ssl_cert = /certs/server-cert.pem
    ssl_key = /certs/server-key.pem
    ssl_ca = /certs/ca-cert.pem

This is far from ideal and not the best cloud native experience, so I would suggest the following changes in the MariaDB CRD to support this:

apiVersion: mariadb.mmontes.io/v1alpha1
kind: MariaDB
metadata:
  name: mariadb
spec:
   ...
   tls:
      secretRef: # reference to an existing TLS secret
          name: tls
      certificateRef: # reference to a cert-manager Certificate resource
           name: cert
      selfSigned: true # the operator will generate a self-signed certificate
      autoReload: true # reload the StatefulSet whenever the certificate changes

spec.secretRef, spec.certificateRef and spec.selfSigned will be mutually exclusive i.e. only one certificate source can be provided.

I think your suggestion makes total sense, but it would be worth integrating with cert-manager, which is the de facto standard project for managing certificates inside Kubernetes. This will be and opt-in feature that can be enabled via a value in the Helm chart.

Regarding reloading certificates, the operator will watch the certificate sources and trigger an update in the StatefulSet whenever they are re-issued. To make it as safe as possible, the user will be able to specify a updateStrategy for the StatefulSet in the MariaDB, so the updates are totally under control. Alternatively, we could leverage our sidecar agent to watch the directory where the certificates are mounted and reload the mariadbd process reactively but this can potentially lead to outages as the agents don't work in a coordinated way. For example, reloading certificates in all replicas at the same time in a Galera cluster will require recovering the Galera cluster.

Thoughts? ๐Ÿค”

Hi @mmontes11! Thanks for super detailed feedback :)

I think integrating cert-manager makes total sense, this removes the hassle of rotating certificates, etc.
I am not sure whether the certificateRef field is really needed โ€“ in the end the Certificate would anyway point to a Secret, so just having secretRef should be enough.

Format selfSigned: true is probably not enough to distinguish between the "use cert-manager" and "self-signed". Perhaps something like already used for webhook, tls:certManager = boolean? If tls field is present and there is no secretRef then we either generate the certificate by the operator or via cert-manager

I cannot say much about reloading strategies, I can just point out, that PostgreSQL operators typically allow to set 2 keys: one for the certificate itself and another for the ca.crt (see https://cloudnative-pg.io/documentation/1.20/certificates/#user-provided-certificates-mode, https://access.crunchydata.com/documentation/postgres-operator/4.7.2/tutorial/tls/). I think this allows for a following optimization: if the certificate has changed but the ca.crt is the same then we can simply reload all the processes with new certificates (since clients typically rely on the ca.crt for TLS checks). However, if the ca.crt has changed then the processes need to be reloaded in a specific order โ€“ new master should get the correct ca.crt first, replicas should follow. I am not sure what would be the best strategy for Galera mode :(

As a final note, I guess the backup / restore CRDs should also be TLS-aware if the MariaDB is configured as such

Hey @wasd171 !

I am not sure whether the certificateRef field is really needed โ€“ in the end the Certificate would anyway point to a Secret, so just having secretRef should be enough.

I agree with this. More than a reference to a secret we may want to pass a reference to a cert-manager Issuer or ClusterIssuer so the operator is able to reconcile a Certificate. This way the user don't need to worry about issuing the certificate, only configure an issuer in the first place.

Considering this, and also your other suggestions, I think we can re-iterate my previous API spec. On one hand, we should allow the user to provide a secret with an existing certificate:

apiVersion: mariadb.mmontes.io/v1alpha1
kind: MariaDB
metadata:
  name: mariadb
spec:
   ...
   tls:
      certificateSecretRef: # reference to a Kubernetes TLS secret
           name: my-tls-secret
      caSecretKeyRef: # reference to a Kubernetes secret key containing the CA public key used to establish trust in the client side
           name:  my-tls-secret
           key: ca.crt

As an alternative, the user could just provide a reference to a issuer which will result in the operator reconciling an extra Certificate resource which later on will be used by MariaDB:

apiVersion: mariadb.mmontes.io/v1alpha1
kind: MariaDB
metadata:
  name: mariadb
spec:
   ...
   tls:
      certManager:
           issuerRef:  # reference to a cert-manager Issuer/ClusterIssuer resource
              name: vault-issuer
              namespace: vault
              kind: ClusterIssuer
           secretName: mariadb-tls # secretName to be used

For this case, the operator will perform late initialization by filling up spec.certificateSecretRef and spec.caSecretKeyRef with the details of the Certificate issued by cert-manager:

apiVersion: mariadb.mmontes.io/v1alpha1
kind: MariaDB
metadata:
  name: mariadb
spec:
   ...
   tls:
      certManager:
           issuerRef:  # reference to a cert-manager Issuer/ClusterIssuer resource
              name: vault-issuer
              namespace: vault
              kind: ClusterIssuer
           secretName: mariadb-tls # secretName to be used
      certificateSecretRef: # reference to a Kubernetes TLS secret
           name: mariadb-tls
      caSecretKeyRef: # reference to a Kubernetes secret key containing the CA public key used to establish trust in the client side
           name: mariadb-tls
           key: ca.crt

Regarding reloading, there will be a controller watching the secrets provided in spec.certificateSecretRef and spec.caSecretKeyRef and trigger a StatefulSet restart when that happens. To make it as safe as possible, the user will be able to specify a updateStrategy for the StatefulSet in the MariaDB, so the updates are totally under control.

Does this make sense to you?

Hi @mmontes11

Yes, this proposal makes total sense. The only thing that it is missing is a support for non-cert-manager self-generated certificates โ€“ but since I do not plan to use it myself and believe that TLS should be implemented properly anyway, this would not matter much for me :)

Hey @wasd171 ! That's a good point, we could levarage crypto/x509 to issue a self-signed certificate. I would argue that this is not the best practice for production but definetly useful if we are provisioning MariaDB instances for development purposes.

I think we will initially go with the aforementioned approaches (cert-manager + user provided cert) but happy to introduce spec.tls.selfSigned later on ๐Ÿ‘๐Ÿป .

Besides, cert-manager also supports setting up a self-signed issuer:

Hi,

thanks for your good work!
Can you estimate how long it will take until the TLS support has been added to the operator?

Thanks and regards,
Christian

Hey there @ChristianHeich-bosch ! No ETA unfortunately, but it is the highest priority of the next release, stay tuned!