Discussion:
Client certificate info in HTTP headers
Karel Sedláček
2012-11-06 13:47:33 UTC
Permalink
I understand that there's some prototypical functionality for analogies to
nginx $ssl_client_s_dn et al. I'd be quite happy to get my hands on this,
as I've been having issues with way too many components of my stack re:
properly extracting certificate info, supporting newer cipher suites, or
even exposing the certificate info in their TLS bindings/implementations.
For better or worse, nginx is also not a solution, as its current
proxy_pass method fully buffers the request, killing websocket. The other
solution is that I write a very small streaming proxy to unwrap the TLS and
inject the relevant headers, but haproxy already does 99% of this, and that
level of code duplication seems like madness. Code beauty is not a major
concern to me at this point, and I'd be happy to contribute back any
changes I make--could we get a feature branch for this in github?

k
Willy Tarreau
2012-11-06 22:16:42 UTC
Permalink
Hi Karel,
Post by Karel Sedláček
I understand that there's some prototypical functionality for analogies to
nginx $ssl_client_s_dn et al. I'd be quite happy to get my hands on this,
properly extracting certificate info, supporting newer cipher suites, or
even exposing the certificate info in their TLS bindings/implementations.
For better or worse, nginx is also not a solution, as its current
proxy_pass method fully buffers the request, killing websocket. The other
solution is that I write a very small streaming proxy to unwrap the TLS and
inject the relevant headers, but haproxy already does 99% of this, and that
level of code duplication seems like madness.
We've already experimented with this at Exceliance. We've not produced code
that makes us happy. We developped the fetches not for ACLs but in order to
extract fields to put into logs and HTTP headers. What we need to do now is
to add the configuration language to map ones to the others. We've made some
attempts but were not much satisfied with them. To be honnest, we're currently
stabilizing the code for dev13 first and we'll get more focused on this just
after.

The code is not much an issue, we need to decide how we'd like users to
configure this in a flexible way to satisfy all needs. This can be discussed
right here on the list if you want, after all it's a development list and
it's where the users are (ie those who decide in the end).
Post by Karel Sedláček
Code beauty is not a major
concern to me at this point, and I'd be happy to contribute back any
changes I make--could we get a feature branch for this in github?
Feel free to do so if you want but better settle on how this should be
configured and used first, it would avoid painful issues at the end.

Willy
Karel Sedláček
2012-11-07 04:55:15 UTC
Permalink
I was hoping that feature branch would start with the code you mentioned.
For my use case, just piping the data into the backend is more than
sufficient, and I'd be happy to flesh out the number of items that are
being parsed. In addition to the already-extant X-Forwarded-For, I'd
recommend the following:

- X-SSL-Protocol
- X-SSL-Cipher
- X-Client-Cert-Verify
- X-Client-Cert
- X-Client-Cert-Serial
- X-Client-Cert-Subject-DN
- X-Client-Cert-Issuer-DN

An additional X-Compromise-Attempt or X-Illegal-Headers would be a nice
option for backends that want to know when clients try to pass these
reserved headers from outside. I can follow this up with a bit of example
code to grab each of these out of the SSL connection object.

k
Post by Willy Tarreau
Hi Karel,
Post by Karel Sedláček
I understand that there's some prototypical functionality for analogies
to
Post by Karel Sedláček
nginx $ssl_client_s_dn et al. I'd be quite happy to get my hands on this,
properly extracting certificate info, supporting newer cipher suites, or
even exposing the certificate info in their TLS bindings/implementations.
For better or worse, nginx is also not a solution, as its current
proxy_pass method fully buffers the request, killing websocket. The other
solution is that I write a very small streaming proxy to unwrap the TLS
and
Post by Karel Sedláček
inject the relevant headers, but haproxy already does 99% of this, and
that
Post by Karel Sedláček
level of code duplication seems like madness.
We've already experimented with this at Exceliance. We've not produced code
that makes us happy. We developped the fetches not for ACLs but in order to
extract fields to put into logs and HTTP headers. What we need to do now is
to add the configuration language to map ones to the others. We've made some
attempts but were not much satisfied with them. To be honnest, we're currently
stabilizing the code for dev13 first and we'll get more focused on this just
after.
The code is not much an issue, we need to decide how we'd like users to
configure this in a flexible way to satisfy all needs. This can be discussed
right here on the list if you want, after all it's a development list and
it's where the users are (ie those who decide in the end).
Post by Karel Sedláček
Code beauty is not a major
concern to me at this point, and I'd be happy to contribute back any
changes I make--could we get a feature branch for this in github?
Feel free to do so if you want but better settle on how this should be
configured and used first, it would avoid painful issues at the end.
Willy
Willy Tarreau
2012-11-07 07:07:03 UTC
Permalink
Hi Karel,
Post by Karel Sedláček
I was hoping that feature branch would start with the code you mentioned.
For my use case, just piping the data into the backend is more than
sufficient, and I'd be happy to flesh out the number of items that are
being parsed. In addition to the already-extant X-Forwarded-For, I'd
- X-SSL-Protocol
- X-SSL-Cipher
- X-Client-Cert-Verify
- X-Client-Cert
- X-Client-Cert-Serial
- X-Client-Cert-Subject-DN
- X-Client-Cert-Issuer-DN
From what I've seen on the net and at various customers, we cannot fix
the header names, we need to have them user-configurable. Also, some
users want some of these info, others want some other info. For example
I know at least one place where the whole PEM cert is desired (we don't
have it right now), because of encoding issues in the ASCII representation
of the subject DN.

What I'd like to do is to be able to define the equivalent of a new reqadd
directive which supports fetch names. It could probably be done in the
http-request rulesets, which can be set both in frontends and backends.
It could also be done somewhere else. The idea would be to reuse the
string construction we have for log-format (which allows to mix text and
tags and adjust the format, quoted vs non-quoted).

For instance it could end up with something approximately like this :

http-request add X-Client-Cert-Subject-DN %{ssl_c_s_dn}
or :
http-request add X-Client-Date %{ssl_c_notbefore}-%{ssl_c_notafter}

Among the points I'm thinking about, maybe http-request is not well
suited because it's called early and we'd prefer something that is
called late, just before use_backend (for the frontend) and just
before use-server (for the backend). That would avoid issues with
conditions based on these headers in the request, and will also allow
us to clear them. We must find a syntax which avoids as much as
possible conflicts with regex so that we can reuse it later when we
want to introduce this in reqrep, redirects, etc... That's why I think
that %{} is not bad at all.
Post by Karel Sedláček
An additional X-Compromise-Attempt or X-Illegal-Headers would be a nice
option for backends that want to know when clients try to pass these
reserved headers from outside.
These ones are obvious to do by configuration and do not need any code,
they're just an ACL. You could even imagine to replace each of these
headers with X-Illegal-$header-name for logging purposes.
Post by Karel Sedláček
I can follow this up with a bit of example
code to grab each of these out of the SSL connection object.
I did this exercise several weeks ago and found that if we systematically
add each header that at least one user is interested in, you have so large
requests that they don't fit in buffers anymore. And that's not counting
the varying header names ! So clearly we need to make them configurable.

Some tricks about encoding are important too. If some headers contain
commas, we're supposed to quote-encode them. Idem for spaces at begin
or end of values. For example, if the following subject is sent as-is
in a header :

C=US, ST=Maryland, L=Pasadena, O=Brent Baccala, OU=FreeSoft, CN=www.freesoft.org/emailAddress=baccala-***@public.gmane.org

Then the header technically contains 6 comma-delimited values, which
intermediaries are free to split into 6 different headers if needed,
or even to remove spaces around commas. So this value must have quotes
around it. But I'm sure that not all software support quotes, so this
again must be a user-selectable encoding format.

Willy
Karel Sedláček
2012-11-07 11:22:11 UTC
Permalink
Post by Willy Tarreau
Hi Karel,
Post by Karel Sedláček
I was hoping that feature branch would start with the code you mentioned.
For my use case, just piping the data into the backend is more than
sufficient, and I'd be happy to flesh out the number of items that are
being parsed. In addition to the already-extant X-Forwarded-For, I'd
- X-SSL-Protocol
- X-SSL-Cipher
- X-Client-Cert-Verify
- X-Client-Cert
- X-Client-Cert-Serial
- X-Client-Cert-Subject-DN
- X-Client-Cert-Issuer-DN
From what I've seen on the net and at various customers, we cannot fix
the header names, we need to have them user-configurable. Also, some
users want some of these info, others want some other info. For example
I know at least one place where the whole PEM cert is desired (we don't
have it right now), because of encoding issues in the ASCII representation
of the subject DN.
Right; my intention with including the header names wasn't to suggest
that they can't be configurable, but because they were c&p'ed out of a
file that I had been working on to fill this functional role. They
should certainly be configurable.
Post by Willy Tarreau
What I'd like to do is to be able to define the equivalent of a new reqadd
directive which supports fetch names. It could probably be done in the
http-request rulesets, which can be set both in frontends and backends.
It could also be done somewhere else. The idea would be to reuse the
string construction we have for log-format (which allows to mix text and
tags and adjust the format, quoted vs non-quoted).
http-request add X-Client-Cert-Subject-DN %{ssl_c_s_dn}
http-request add X-Client-Date %{ssl_c_notbefore}-%{ssl_c_notafter}
Among the points I'm thinking about, maybe http-request is not well
suited because it's called early and we'd prefer something that is
called late, just before use_backend (for the frontend) and just
before use-server (for the backend). That would avoid issues with
conditions based on these headers in the request, and will also allow
us to clear them. We must find a syntax which avoids as much as
possible conflicts with regex so that we can reuse it later when we
want to introduce this in reqrep, redirects, etc... That's why I think
that %{} is not bad at all.
I'm ok with %{}; other common syntaxes include #var $var #{} and ${},
particularly in the case you allow variables to be operated on, e.g.
${var / 2}.
Post by Willy Tarreau
Post by Karel Sedláček
An additional X-Compromise-Attempt or X-Illegal-Headers would be a nice
option for backends that want to know when clients try to pass these
reserved headers from outside.
These ones are obvious to do by configuration and do not need any code,
they're just an ACL. You could even imagine to replace each of these
headers with X-Illegal-$header-name for logging purposes.
Ditto for this regarding configurability. And, glad to hear it.
Post by Willy Tarreau
Post by Karel Sedláček
I can follow this up with a bit of example
code to grab each of these out of the SSL connection object.
I did this exercise several weeks ago and found that if we systematically
add each header that at least one user is interested in, you have so large
requests that they don't fit in buffers anymore. And that's not counting
the varying header names ! So clearly we need to make them configurable.
Ditto again; the headers (i.e. their corresponding properties) that I
listed are an overlapping set of data. Probably you wouldn't need the
whole client cert and each of its individual parts at the same time.
If you're doing certificate-based auth, you probably either just want
the subject CN (and configure HAProxy to reject unverified client
certs) or the CN and verified status. If you want to verify the cert
as well, until HAProxy supports doing OCSP client cert verification,
you could use either the whole cert (preparing the request yourself),
or, if it's your own infrastructure issuing certs, set up a service to
reject serial numbers, and have HAProxy pass the serial along. Because
there are many possible use cases, there should be many possible
options, but only a small subset of them is likely to be used at any
given time.
Post by Willy Tarreau
Some tricks about encoding are important too. If some headers contain
commas, we're supposed to quote-encode them. Idem for spaces at begin
or end of values. For example, if the following subject is sent as-is
Then the header technically contains 6 comma-delimited values, which
intermediaries are free to split into 6 different headers if needed,
or even to remove spaces around commas. So this value must have quotes
around it. But I'm sure that not all software support quotes, so this
again must be a user-selectable encoding format.
There seems to be little incentive to split the headers as described,
so I doubt many applications do that. Testing products to see if they
support quoting is dead easy, though, so if that's the canonical form
I suspect you could get a decent cross section on it pretty easily.
Consider supporting base64 encoding of this value.
Post by Willy Tarreau
Willy
Continue reading on narkive:
Loading...