Skip to content

How to Configure TLS for the LINSTOR API

This guide shows you how to set up TLS for the LINSTORĀ® API. The API, served by the LINSTOR Controller, is used by clients such as the CSI Driver and the Operator itself to control the LINSTOR Cluster.

To complete this guide, you should be familiar with:

  • editing LinstorCluster resources.
  • using either cert-manager or openssl to create TLS certificates.

Provision Keys and Certificates Using cert-manager

This method requires a working cert-manager deployment in your cluster. For an alternative way to provision keys and certificates, see the openssl section below.

When using TLS, the LINSTOR API uses client certificates for authentication. It is good practice to have a separate CA just for these certificates.

---
apiVersion: cert-manager.io/v1
kind: Issuer
metadata:
  name: ca-bootstrapper
  namespace: piraeus-datastore
spec:
  selfSigned: { }
---
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
  name: linstor-api-ca
  namespace: piraeus-datastore
spec:
  commonName: linstor-api-ca
  secretName: linstor-api-ca
  duration: 87600h # 10 years
  isCA: true
  usages:
    - signing
    - key encipherment
    - cert sign
  issuerRef:
    name: ca-bootstrapper
    kind: Issuer
---
apiVersion: cert-manager.io/v1
kind: Issuer
metadata:
  name: linstor-api-ca
  namespace: piraeus-datastore
spec:
  ca:
    secretName: linstor-api-ca

Then, configure this new issuer to let the Operator provision the needed certificates:

---
apiVersion: piraeus.io/v1
kind: LinstorCluster
metadata:
  name: linstorcluster
spec:
  apiTLS:
    certManager:
      name: linstor-api-ca
      kind: Issuer

This completes the necessary steps for secure the LINSTOR API with TLS using cert-manager. Skip to the last section to verify TLS is working.

Provision Keys and Certificates Using openssl

If you completed the cert-manager section above, skip directly to the verifying the configuration below.

This method requires the openssl program on the command line. For an alternative way to provision keys and certificates, see the cert-manager section above.

First, create a new Certificate Authority using a new key and a self-signed certificate, valid for 10 years:

openssl req -new -newkey rsa:4096 -days 3650 -nodes -x509 -keyout ca.key -out ca.crt -subj "/CN=linstor-api-ca"

Then, create two new keys, one for the LINSTOR API server, one for all LINSTOR API clients:

openssl genrsa -out api-server.key 4096
openssl genrsa -out api-client.key 4096

Next, we will create a certificate for the server. The clients might use different shortened service names, so we need to specify multiple subject names:

cat /etc/ssl/openssl.cnf > api-csr.cnf
cat >> api-csr.cnf <<EOF
[ v3_req ]
subjectAltName = @alt_names
[ alt_names ]
DNS.0 = linstor-controller.piraeus-datastore.svc.cluster.local
DNS.1 = linstor-controller.piraeus-datastore.svc
DNS.2 = linstor-controller
EOF
openssl req -new -sha256 -key api-server.key -subj "/CN=linstor-controller" -config api-csr.cnf -extensions v3_req -out api-server.csr
openssl x509 -req -in api-server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -config api-csr.cnf -extensions v3_req -out api-server.crt -days 3650 -sha256

For the client certificate, simply setting one subject name is enough:

openssl req -new -sha256 -key api-client.key -subj "/CN=linstor-client" -out api-client.csr
openssl x509 -req -in api-client.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out api-client.crt -days 3650 -sha256

Now, we create Kubernetes secrets from the created keys and certificates:

kubectl create secret generic linstor-api-tls -n piraeus-datastore --type=kubernetes.io/tls --from-file=ca.crt=ca.crt --from-file=tls.crt=api-server.crt --from-file=tls.key=api-server.key
kubectl create secret generic linstor-client-tls -n piraeus-datastore --type=kubernetes.io/tls --from-file=ca.crt=ca.crt --from-file=tls.crt=api-client.crt --from-file=tls.key=api-client.key

Finally, configure the Operator resources to reference the newly created secrets. We configure the same client secret for all components for simplicity.

apiVersion: piraeus.io/v1
kind: LinstorCluster
metadata:
  name: linstorcluster
spec:
  apiTLS:
    apiSecretName: linstor-api-tls
    clientSecretName: linstor-client-tls
    csiControllerSecretName: linstor-client-tls
    csiNodeSecretName: linstor-client-tls

Verifying TLS Configuration

You can verify that the secure API is running by manually connecting to the HTTPS endpoint using curl

$ kubectl exec -n piraeus-datastore deploy/linstor-controller -- curl --key /etc/linstor/client/tls.key --cert /etc/linstor/client/tls.crt --cacert /etc/linstor/client/ca.crt https://linstor-controller.piraeus-datastore.svc:3371/v1/controller/version
{"version":"1.20.2","git_hash":"58a983a5c2f49eb8d22c89b277272e6c4299457a","build_time":"2022-12-14T14:21:28+00:00","rest_api_version":"1.16.0"}%

If the command is successful, the API is using HTTPS and clients are able to connect with their certificates.

If you see an error, make sure that the client certificates are trusted by the API secret, and vice versa. The following script provides a quick way to verify that one TLS certificate is trusted by another:

function k8s_secret_trusted_by() {
    kubectl get secret -n piraeus-datastore -ogo-template='{{ index .data "tls.crt" | base64decode }}' "$1" > $1.tls.crt
    kubectl get secret -n piraeus-datastore -ogo-template='{{ index .data "ca.crt" | base64decode }}' "$2" > $2.ca.crt
    openssl verify -CAfile $2.ca.crt $1.tls.crt
}
k8s_secret_trusted_by linstor-client-tls linstor-api-tls
# Expected output:
# linstor-client-tls.tls.crt: OK

Another issue might be the API endpoint using a Certificate not using the expected service name. A typical error message for this issue would be:

curl: (60) SSL: no alternative certificate subject name matches target host name 'linstor-controller.piraeus-datastore.svc'

In this case, make sure you have specified the right subject names when provisioning the certificates.

All available options are documented in the reference for LinstorCluster.