cloudfoundry / korifi

Cloud Foundry on Kubernetes
Apache License 2.0
317 stars 65 forks source link

[Explore]: Support client certificates via mTLS and user impersonation #771

Closed gcapizzi closed 2 years ago

gcapizzi commented 2 years ago

Background

Currently our API shim accepts client certificates in the Authorization header, using the ClientCert scheme. It then uses the cert and key stored in the header to build a user-scoped client that it uses to talk to the Kubernetes API.

We believe this approach is as secure as tokens: in both cases we're sending a secret over the wire which could allow an attacker to impersonate the user. But while this is always true for tokens, this is not the case for certificates when used in the intended way: when establishing a mutual TLS connection, the user's private key never leaves their machine. In other words, mTLS is intrinsically more secure than tokens, and our treatment downgrades them to the same security level as tokens.

We'd like to explore a different way of accepting client certificates, which doesn't require the user's private key to ever be sent to the API. Here's what we could do:

  1. Let the client establish a mutual TLS connection with the shim. This should be possible as long as:
    • the client can verify the shim's certificate (so the shim's CA is installed on the client machine),
    • the server can verify the client's certificate (so the client's CA is configured on the server).
  2. Terminate the TLS connection in the shim and extract the username from the client certificate (available via http.Request.TLS.PeerCertificates).
  3. Use the impersonation headers to call the Kubernetes API on behalf of the user.

Let's try implementing this and assess which changes would be needed both on the shim and on the CLI.

Acceptance Criteria

A git branch containing a working proof-of-concept of what described above.

Dev Notes

Some work has already been done on this, see eirini-forks/cli@spikes/mTLS and cloudfoundry/cf-k8s-controllers@spikes/mTLS.

kieron-dev commented 2 years ago

We are now happy with this spike. We moved the client certificate CA and validity date checking to the shim https server, where it should be, and now we don't do any custom security ourselves.

kieron-dev commented 2 years ago

How to run this spike:

  1. Generate shim API server cert and key:
    cd certs
    openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -nodes -subj '/CN=localhost' -addext "subjectAltName=DNS:localhost,IP:127.0.0.1" -days 3650
  2. Store the cluster CA cert in certs/ca.pem (extract from the kubeconfig and base64 -d)
  3. Run API shim locally: APICONFIG=api/config/base/apiconfig go run ./api/main.go
  4. Build the CF CLI: make build in the cli source
  5. Export the API shim cert: export SSL_CERT_FILE=~/workspace/cf-k8s-controllers/certs/cert.pem
  6. Set the API: cf api https://localhost:9000
  7. Login: cf login