TLS certificates

When Kalix services are exposed to the internet using routes, all requests are served using Transport Layer Security (TLS). By default Kalix will automatically provision a server certificate for you using Let’s Encrypt.

Using TLS ensures that a client can trust that the server they are connecting to is the server that they intended to connect to, and ensures that the connection is encrypted and can’t be tampered with. However, the default configuration doesn’t offer the server any guarantees about the identity of the client. By default, anyone on the internet can connect to your exposed services.

Kalix offers the ability to require clients to supply a valid certificate, as well as the ability to use a custom server provisioned certificate, rather than a certificate automatically provisioned by Let’s Encrypt.

Client certificates

Kalix’s client certificate support, also known as Mutual TLS (mTLS) support, allows you to configure routes to require a client certificate. The supplied certificates will be validated using a trusted Certificate Authority that you supply.

Client certificates allow you to control who can connect to your services, by requiring all clients to present a certificate when they connect. This is a very course grained level of authentication, and is ideal for situations where you only want your service to be accessible to other services that you control.

To enable this, you will need a Certificate Authority (CA) that is capable of issuing client certificates to the clients that you want to consume your Kalix services. If this CA is used to issue certificates to more than just the services you want to invoke your service, such as if using a public CA, you will also need to configure Client certificate validation. Otherwise, Kalix will trust any client that presents a certificate issued by that CA.

Many organisations will already have a CA that they can use for this, perhaps using products like HashiCorp Vault or Kubernetes cert-manager. If you already have a way of issuing client certificates from a CA, then to enable this feature, you just need that CA’s certificate, and you can skip the next step.

Creating your own CA

If you don’t have a mechanism for issuing client certificates with a CA, this guide will walk you through the process. We will use the smallstep CLI, a tool that makes it very easy.

First you will need to install the CLI using the instructions linked above.

Then we’ll create a CA certificate and key:

step certificate create --profile root-ca rootca.acme.org \
  my-root-ca.crt my-root-ca.key --insecure --no-password

Now that we have the CA, we’ll also create a client certificate. We don’t actually need this to configure the client certificate support in Kalix. Only the CA certificate is needed for that. The client certificate will be used by our client when we try to connect to the service:

step certificate create client.acme.org my-client.crt my-client.key \
  --ca my-root-ca.crt --ca-key my-root-ca.key --insecure --no-password

Configuring the CA secret

Now that we have a CA certificate, we can configure it as a secret in Kalix. The type of secret we’re creating is called a TLS CA secret:

kalix secret create tls-ca my-root-ca --cert ./my-root-ca.crt

Configuring a route to use the secret

We now need to configure a route to use a secret. Routes can be created by following the instructions in exposing services to the internet.

CLI with command line arguments

If you haven’t yet created your route, then using the kalix route create command, you can pass the flag --client-ca-secret my-root-ca when you create it. Otherwise, you can update it:

kalix route update my-route --client-ca-secret my-root-ca
CLI with a descriptor
  • Using either the kalix route edit command or updating the route descriptor:

    host: ecommerce.acme.org
    tls:
      clientValidationCa:
        name: my-root-ca
    routes:
    - prefix: /
      route:
        service: shopping-cart

Testing that the service is secured

Your service should now be secured. You can test that it’s secured using curl. Let’s say the URL that your service is exposed on is spring-tooth-3406.us-east1.kalix.app. Try issuing a simple curl request on it:

$ curl https://spring-tooth-3406.us-east1.kalix.app -I
curl: (56) OpenSSL SSL_read: error:1409445C:SSL routines:ssl3_read_bytes:tlsv13 alert certificate required, errno 0

You can see that curl is reporting that a certificate is required to connect to this service. Now if you have a client certificate issued by the CA handy, like the one created above using smallstep, you can try the same request with the client certificate:

$ curl https://spring-tooth-3406.us-east1.kalix.app -I --key my-client.key --cert my-client.crt
HTTP/2 404
content-length: 0
date: Wed, 10 Nov 2021 05:00:59 GMT
server: envoy
x-envoy-upstream-service-time: 19

Unless you have defined HTTP transcoding rules for the root path, your service is expected to respond with 404, as shown above. This verifies that the client certificate was accepted and the service is secured.

Client certificate validation

If you wish to validate more than just that the certificate was signed by your configured CA, and what to validate the identity attached to the certificate, you can do that by specifying assertions to run against the subject of the client certificate. The client certificates subject can either be the Common Name (CN) in the Subject field of the certificate, or a DNS Subject Alternative Name in the certificate.

Configuration client certificate validation can be done either with command line arguments or route descriptors, though route descriptors are a little more powerful:

CLI with command line arguments

If you haven’t yet created your route, then using the kalix route create command, you can pass the flag --client-certificate-subject some.client.name when you create it. Otherwise, you can update it:

kalix route update my-route --client-certificate-subject client.acme.org

You can supply multiple --client-certificate-subject arguments to match multiple certificate subject names. Additionally, if the argument starts with a , a suffix match will be done, and if it ends with a , a prefix match will be done. For example:

kalix route update my-route --client-certificate-subject *.acme.org

Will match any subject name under the acme.org domain name.

CLI with a descriptor

Using either the kalix route edit command or updating the route descriptor:

host: ecommerce.acme.org
tls:
  clientValidationCa:
    name: my-root-ca
validation:
  clientCertificate:
    subjectMatches:
    - exact: client.acme.org
routes:
- prefix: /
  route:
    service: shopping-cart

Multiple subject matchers can be defined. hasPrefix, hasSuffix and regex can also be used to match the subject. For example, this will allow any certificate subject name under the domain acme.org:

host: ecommerce.acme.org
tls:
  clientValidationCa:
    name: my-root-ca
validation:
  clientCertificate:
    subjectMatches:
    - hasSuffix: .acme.org
routes:
- prefix: /
  route:
    service: shopping-cart

Custom server certificates

There may be multiple reasons why you don’t want to use the TLS certificates automatically provisioned by Let’s Encrypt:

  • The domain you want to provision the certificates at has a Certification Authority Authorization (CAA) policy configured in DNS, which does not permit Let’s Encrypt to provision certificates for hostnames at it’s domain, and you don’t want to or can’t change this policy.

  • You want to use certificates that are not publicly trusted, but rather are explicitly configured in your client and server.

Configuring a TLS secret

To configure a custom server TLS secret, you will need the key and certificate for the servers hostname in unencrypted PEM format. Once that has been provisioned, create it by running:

kalix secret create tls my-tls-cert --key ./my-key.pem --cert ./my-cert.pem

Configuring a route to use the secret

Now that you’ve created the secret, you can update or create your route to use it:

CLI with command line arguments

If you haven’t yet created your route, then using the kalix route create command, you can pass the flag --server-certificate-secret my-tls-cert when you create it. Otherwise, you can update it:

kalix route update my-route --server-certificate-secret my-tls-cert
CLI with a descriptor

Using either the kalix route edit command or updating the route descriptor:

host: ecommerce.acme.org
tls:
  serverCertificate:
    name: my-tls-cert
routes:
- prefix: /
  route:
    service: shopping-cart