OpenSSL/HAProxy verify client certificates using a non-CA certificate

haproxyopenssl

I am facing a problem while configuring a HAProxy instance (v1.8.13) with compiled OpenSSL support to only accept client certificates which have been signed by a non-CA certificate. In particular, I want to use an intermediate certificate (non self-signed) certificate as the trust anchor when verifying client certificates.

This is the config I'm using:

frontend myfrontend
     bind *:${PORT} ssl crt /certs/haproxy-server-cert-bundle.pem ca-file /certs/intermediate_cert2.pem verify required

Clients connect via certificates that fulfil the following certificate chain:

client certificate -> intermediate cert1 -> intermediate cert2 (this should become the new trust anchor) -> root CA certificate

When using this configuration connecting to the HAProxy via the client certificate results in the following error:
Feb 13 09:17:26 my-forwarder haproxy[108]: <ip>:<port> [13/Feb/2019:09:17:26.619] my-forwarder/1: SSL client CA chain cannot be verified

If I specify the root CA certificate as trust anchor (ca-file config parameter in the HAProxy config) the TLS handshake can be established successfully. However, I only want to accept certificates which were signed by the certificate branch intermediate cert 2 -> root CA certificate, not by i.e. some other intermediate cert -> root CA certificate.

I tried to reproduce this behaviour using plain OpenSSL verify and via some OpenSSL s_server/s_client setup. It seems that the same problem exists using those tools.

Some evidence:

$ openssl verify -CAfile test-certificate-chain.pem test-cert.pem
test-cert.pem: C = ..., O = ..., OU = ..., ST = ..., CN = intermediate-cert2
error 2 at 2 depth lookup:unable to get issuer certificate

Appending the root CA certificate to -CAfile results in:

$ openssl verify -CAfile <(cat test-certificate-chain.pem root-ca.pem) test-cert.pem
test-cert.pem: OK

The only two options I was able to come up with in order to solve this problem are the following:

  • Create a self-signed CA certificate which signs the intermediate cert2 certificate's public key. The self-signed CA certificate must then be installed in the HAProxy configuration's ca-file config key.
  • Configure the HAProxy to ignore the OpenSSL error number 2 (unable to get issuer certificate) via the HAProxy configuration key ca-file /certs/intermediate_cert2.pem verify required ca-ignore-err 2

I am not yet satisfied in terms of security level when using either one of the options above. Hence, I would be curios to know whether there is a "cleaner" solution to this problem.

Has anyone else faced a similar issue?

Thanks!

Best Answer

Update: I solved the problem a bit differently as I was unable to find a way to trust an intermediate (non-CA) certificate without trusting the parent (CA) certificate via the verification of the certificate chain via OpenSSL.

The alternative was to utilize HAProxy's powerful ACL feature. In particular:

frontend my-frontend
    ...
    acl my_tls_ca_issuer_acl ssl_c_i_dn(cn) -m reg ^<some-regex-matching-the-client-cert's-issuer-cn>$
    tcp-request content reject unless my_tls_ca_issuer_acl

I'm configuring HAProxy to only accept client certificates issued by an entity which matches a given Common Name regex. This is what the matcher ssl_c_i_dn(cn) is doing (see the documentation for details).

By implementing this I could unfortunately only match to the distinguished name (DN) of the issuer/parent of the client certificate (intermediate cert 1), not its grandparent (intermediate cert 2).

Still I hope this helps someone..