sozu icon indicating copy to clipboard operation
sozu copied to clipboard

Client Certificate Authentication

Open moschroe opened this issue 5 years ago • 9 comments

Are there plans to handle client certificates? This might be used to offload/centralise authentication and even take routing decisions. I have mulled it over a bit and came up with the following points:

  • implement configurable client verifier with changeable trust roots to make it as dynamic as the rest of sozu (needed as everything in sozu is dynamic)
  • integrate into routing engine? (could be added later)

For handling downstream servers/signalling auth to them, I thought of the following scenarios:

  • trust sozu: low-effort, bad, also how does CN reach downstream server?
  • set special header: must be configurable (simple templates?), sozu must be able to actively strip/overwrite header to prevent clients from controlling the header value directly
  • inject header with signed value: configurable, downstream can check presence and validity, not forgeable by random client. more computational effort but should be cacheable.

Since sozu currently has an unencrypted connection to the backend anyway, trusting the proxy to set some headers according to DN/CN/whatever does not sound to be a big leap.

Time allowing, I'd be willing to take this on. I have already looked at how the various config messages flow to get the necessary information from one part to the other.

moschroe avatar Jan 24 '21 19:01 moschroe

Also looked into the rustls dependency. Upgrading just one version to 0.17.0 would provide a necessary API change where the ClientCertVerifier does get an Option<&webpki::DNSName> everywhere so usable trust roots could be determined.

moschroe avatar Jan 26 '21 20:01 moschroe

that would be interesting. Note that I'm also planning to add encrypted connections to the backend

Geal avatar Feb 09 '21 08:02 Geal

I have configuration (from parsing to propagating the value through the application) and verification completed. It is almost a copy of the server cert handling process (from the architectural POV), so optimisations are surely possible.

Now the more interesting problem remains: To enable the modification of headers and making the knowledge of client cert attributes accessible at the right places. And more generally: Inspecting and modifying headers.

Any suggestions architecture-wise?

moschroe avatar Feb 15 '21 15:02 moschroe

Since the client certificate will be checked during the handshake, it can be passed to the HTTP implementation in the upgrade() method, then added through the added_request_headers method. For inspecting and replacing headers, you can take a look at the Forwarded header handling.

Could you do this work starting from the 0.12 branch instead of master? New features with breaking API changes aee happening there

Geal avatar Feb 15 '21 16:02 Geal

The upgrade method: https://github.com/sozu-proxy/sozu/blob/0.12/lib/src/https_rustls/session.rs#L182 Generating headers: https://github.com/sozu-proxy/sozu/blob/0.12/lib/src/protocol/http/mod.rs#L1591 Deleting a known header from the request: https://github.com/sozu-proxy/sozu/blob/0.12/lib/src/protocol/http/parser/mod.rs#L615 (let's do a static header name first, then we'll see to make it configurable)

Geal avatar Feb 15 '21 16:02 Geal

So I hacked a preliminary implementation, feel free to check it out: https://github.com/moschroe/sozu/tree/feature_clientauth The history is a complete mess but I tried to provide sensible commit messages, if you look at those last commits it should be clear how I implemented it. I'll clean it up some time this week

moschroe avatar Feb 16 '21 15:02 moschroe

ok, I took a look, a few things:

  • for the cleanup, it should be rebased over 0.12. Unfortunately, this branch is also regularly rebased over master, so it will tend to drift a bit
  • client cert verification should be per application, not globally. Some applications might not require it, others would need different certificate chains. Since the target application is not decided yet during the handshake, client certificates should be carried with the certificate chain for a list of server names
  • it's unfortunate that rustls does not provide a usable structure to inspect the certificate, so another certificate parser has to be added

Currently I think that's a lot of code to add, that would benefit from other planned features (like better header edition, pluggable routing, or SNI based routing), so could you wait on this for a bit?

Geal avatar Feb 22 '21 09:02 Geal

I have no pressing need for this and will gladly wait for less churn in the codebase :)

moschroe avatar Feb 28 '21 13:02 moschroe

I have resumed development on client TLS auth and the new way to have an SNI-specific server configuration for rustls (see https://github.com/rustls/rustls/issues/859#issuecomment-984714438) involves changing how a ServerConnection is created.

My current plan is to handle this within TlsHandshake as much as possible, so the remaining code can continue to work with a ServerConnection as before.

moschroe avatar Dec 02 '21 17:12 moschroe