Erlang cluster on Kubernetes: Using the generated certificates

22 Dec 2022 10:04 erlang kubernetes

In the previous two posts, we generated signing requests with OpenSSL and submitted them to cert-manager. In this post, we’ll actually use the generated certificates for mutual TLS.

Mounting the /certs volume

The init container generated some certificates and wrote them to the /certs volume. We need to mount that volume into the main container:

volumeMounts:
  - name: erlclu-dist-tls
    mountPath: /secrets
  - name: tls-dist
    mountPath: /certs

I ought to get rid of the /secrets mount as well – but Many More Much Smaller Steps – so I’ll do that later.

Use the certificates for the server

While doing small steps, I’ll make the initial change (in inet_tls_dist.config) to only have the server use our new certificates:

[
    {server, [
        {certfile, "/certs/tls-dist.crt"},
        {keyfile, "/certs/tls-dist.key"},
        %...

Tiny steps really helps at this point, because TLS (and particularly mutual TLS) is fiddly. It’s a lot easier if you can identify precisely what you just changed that broke everything.

Server verification

Since that works, we can move on to the next step. The client should verify the server:

    %...
    {client, [
        {verify, verify_peer},
        {cacertfile, "/certs/ca.crt"},
    %...

Require client certs

The server should require client certificates, and the client should offer them:

[
    {server, [
        %...
        {verify, verify_peer},
        {fail_if_no_peer_cert, true},
        {cacertfile, "/certs/ca.crt"},
        %...
    ]},
    {client, [
        {certfile, "/certs/tls-dist.crt"},
        {keyfile, "/certs/tls-dist.key"},
        {verify, verify_peer},
        {cacertfile, "/certs/ca.crt"},
        {secure_renegotiate, true}
    ]}
].

Remember: peer verification requires adding certfile and keyfile to the peer that provides the certificate, and {verify, verify_peer} and cacertfile to the peer that verifies the certificate.

For the server, we also have to add {fail_if_no_peer_cert, true} to require client certificates.

Note: In reality, I actually did this as two tiny steps, accepting that it would fail after the first one.

Stop using secrets for certs

Now that the self-provisioned certificates are working, we can stop using the secret that we created previously.

This means deleting the erlclu-dist-tls volumes from the deployment, and then deleting the secret:

kubectl --namespace erlclu delete secret erlclu-dist-tls

Limitations

Because we use an init container to generate the certificates, they will not be renewed when they expire. This can be addressed by simply restarting the pods before this happens, or by adding a sidecar container (based on the init container) to renew the certificates periodically.