Skip to main content

Frontend Mutual TLS Configuration

After platform deployment, TLS server-side (one-way) authentication is enabled by default. This article introduces how to enable client verification (mutual) authentication when server-side authentication already exists.

The entire cloud platform runs on Kubernetes, and frontend services are exposed through ingress. We use the open-source traefik component to handle ingress implementation, so client authentication is set on traefik.

Generate Certificatesโ€‹

The following uses self-signed certificate generation for configuration. If you already have server and client certificates issued by a certificate authority, you can skip this step.

Generate CAโ€‹

$ openssl req -x509 -sha256 -newkey rsa:4096 -keyout ca.key -out ca.crt -days 356 -nodes -subj '/CN=My Cert Authority'

Generate Server Certificateโ€‹

Issue server certificate based on the CA generated above:

$ openssl req -new -newkey rsa:4096 -keyout server.key -out server.csr -nodes -subj '/CN=mydomain.com'
$ openssl x509 -req -sha256 -days 365 -in server.csr -CA ca.crt -CAkey ca.key -set_serial 01 -out server.crt

Generate Client Certificateโ€‹

Issue client certificate based on the CA generated above:

$ openssl req -new -newkey rsa:4096 -keyout client.key -out client.csr -nodes -subj '/CN=My Client'
$ openssl x509 -req -sha256 -days 365 -in client.csr -CA ca.crt -CAkey ca.key -set_serial 02 -out client.crt

Upload Certificates to Kubernetesโ€‹

# Upload ca to kube-system namespace
$ kubectl -n kube-system create secret generic ca-secret --from-file=ca.crt=ca.crt

# Upload server certificate to onecloud namespace
$ kubectl -n onecloud create secret generic tls-secret --from-file=tls.crt=server.crt --from-file=tls.key=server.key

Modify Frontend default-web ingressโ€‹

The frontend is accessed through the default-web ingress in the onecloud namespace. Replace the tls certificate used by the frontend with tls-secret.

$ kubectl edit ingress -n onecloud default-web
...
tls:
# Modify this secretName to tls-secret
- secretName: tls-secret
...

Modify traefik Configurationโ€‹

Modify traefik configmapโ€‹

traefik's configuration is in the traefik-ingress-lb config in the kube-system namespace. Enabling client verification mainly configures entryPoints.https.tls.ClientCA.

$ kubectl edit configmaps -n kube-system traefik-ingress-lb
...
[entryPoints.https]
address = ":443"
# Add the following ClientCA configuration
[entryPoints.https.tls]
[entryPoints.https.tls.ClientCA]
files = ["/tests/ca.crt"]
optional = false
...

Modify traefik daemonsetโ€‹

Then modify the traefik-ingress-controller daemonset in the kube-system namespace, mainly mounting the ca-secret created earlier to the configured /tests/ca.crt directory.

$ kubectl edit daemonsets -n kube-system traefik-ingress-controller
...
volumeMounts:
- mountPath: /config
name: config
# Add this volume mount, name is ca
- mountPath: /tests
name: ca
...
volumes:
- configMap:
defaultMode: 420
name: traefik-ingress-lb
name: config
- name: ca
secret:
defaultMode: 420
# Here reference the ca-secret created earlier
secretName: ca-secret
...

Restart traefik serviceโ€‹

$ kubectl get pods -n kube-system | grep traefik | awk '{print $1}' | xargs kubectl delete pods -n kube-system

Wait for traefik container to become Running.

$ kubectl get pods -n kube-system | grep traefik
traefik-ingress-controller-fk54h 1/1 Running 0 9s

Test Using curlโ€‹

Assuming the deployed frontend access address is https://192.168.121.21, verify if client authentication is enabled:

# Access without client certificate fails, as expected
$ curl -k https://192.168.121.21
curl: (58) NSS: client certificate not found (nickname not specified)

# Access with client certificate succeeds
$ curl -k --cert ./client.crt --key ./client.key https://192.168.121.21
<!DOCTYPE html><html lang=en translate=no><head><meta charset=utf-8><meta http-equiv=X-UA-Compatible content="IE=edge"><meta name=viewport content="width=device-width,initial-scale=1"><meta name=google content=notranslate><link rel=icon href=/favicon.ico><title>ไบ‘่”ๅฃนไบ‘</title><link href=/js/chunk-2d216214.5f7b7e0c.js rel=prefetch><link href=/js/chunk-39bb5eb4.8512e62d.js rel=prefetch><link href=/css/app.fb52a32e.css rel=preload as=style><link href=/css/chunk-vendors.09e9c25d.css rel=preload as=style><link href=/js/app.74cda7af.js rel=preload as=script><link href=/js/chunk-vendors.a7b5c015.js rel=preload as=script><link href=/css/chunk-vendors.09e9c25d.css rel=stylesheet><link href=/css/app.fb52a32e.css rel=stylesheet></head><body><noscript><strong>We're sorry but OneCloud doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id=app></div><script src=/vendor.b82688a471b737ceddd1.js></script><script src=/js/chunk-vendors.a7b5c015.js></script><script src=/js/app.74cda7af.js></script></body></html>

Browser Configurationโ€‹

Through the above configuration, client configuration is found to be successful, but to access through a browser, you also need to put the client certificate in the browser, otherwise access will show the following interface:

The following uses Chrome browser as an example. Need to convert the previously generated client.crt and client.key to pfx/pkcs12 format to import into the browser, command as follows:

# Combine client.crt and client.key together
$ cat client.crt client.key > pkcs12.pem

# Convert to pkcs12 format
$ openssl pkcs12 -in pkcs12.pem -export -out pkcs12.p12

Import pkcs12.p12 into the browser:

Select the pkcs12.p12 certificate generated earlier:

After importing, refresh and access the frontend, and you can successfully access the interface.