Invalid Unknown Critical Extensions
Describe the bug Apologies in advance for any inaccuracies - complete Erlang novice and only running into this as a result of using RabbitMQ (3.9.10, running Erlang 24.1.7). Believe this is a bug in Erlang however.
Received an intermediate CA that was renewed; replacing in a certificate chain (ssl_options.cacertfile in the rabbitmq.conf and cacertfile parameter in the shovel) began throwing various errors between two RabbitMQ instances and their shovels. Example from reproduced environment below:
rabbit2 | 2022-08-04 13:58:41.150884+00:00 [noti] <0.987.0> TLS server: In state certify at ssl_handshake.erl:2006 generated SERVER ALERT: Fatal - Unsupported Certificate
rabbit2 | 2022-08-04 13:58:41.150884+00:00 [noti] <0.987.0>
rabbit1 | 2022-08-04 13:58:41.158154+00:00 [noti] <0.988.0> TLS client: In state cipher received SERVER ALERT: Fatal - Unsupported Certificate
rabbit1 | 2022-08-04 13:58:41.158154+00:00 [noti] <0.988.0>
rabbit1 | 2022-08-04 13:58:41.158393+00:00 [erro] <0.967.0> Shovel 'Shovel.rabbit1.all.send' failed to connect (URI: amqps://rabbit2:5671/c.vhrabbit1): {tls_alert,{unsupported_certificate,"TLS client: In state cipher received SERVER ALERT: Fatal - Unsupported Certificate\n"}}
rabbit1 | 2022-08-04 13:58:41.158482+00:00 [erro] <0.967.0> Shovel 'Shovel.rabbit1.all.send' has no more URIs to try for connection
rabbit1 | 2022-08-04 13:58:41.158514+00:00 [erro] <0.967.0> Shovel 'Shovel.rabbit1.all.send' could not connect to destination
Certificate looked the same, but the noted bug led me to - https://github.com/erlang/otp/blob/master/lib/ssl/src/ssl_handshake.erl#L2088 (note line number difference is a result of difference between running version and master branch).
This indicates a bad certificate resulting from unknown_critical_extension. Doing a complete diff on the renewed CA certificate and the old version identified two extensions had been marked critical that were not before - specifically:
X509v3 Policy Constraints: critical
Require Explicit Policy:0, Inhibit Policy Mapping:0
X509v3 Inhibit Any Policy: critical
0
Per RFC 5280, "Conforming CAs MUST mark this extension as critical." However, it appears Erlang reports these as unknown critical extensions - unfortunately I've hit the limit of my Erlang knowledge to know how to dive deeper for confirmation.
References: https://datatracker.ietf.org/doc/html/rfc5280#section-4.2.1.11 https://datatracker.ietf.org/doc/html/rfc5280#section-4.2.1.14
To Reproduce
- Create a standard root CA
- Create an intermediate CA with the following configuration options (block extracted from openssl.cnf used to create - note policyConstraints and inhibitAnyPolicy marked as critical):
[ v3_intermediate_ca ]
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid:always,issuer
basicConstraints = critical, CA:true
keyUsage = critical, cRLSign, keyCertSign
policyConstraints = critical,requireExplicitPolicy:0,inhibitPolicyMapping:0
inhibitAnyPolicy = critical,0
- Create appropriate client and server certificates for RabbitMQ and run
If using an intermediate CA without the extensions marked as critical, everything runs fine; if using the intermediate CA with the extensions marked as critical, the errors noted above are thrown. Note creating 2 levels of CAs is likely not necessary, but was done to more accurately reproduce the production scenario in the lab.
Expected behavior The extensions marked as critical are defined in RFC 5280, so should not be flagged as unknown; no errors should be produced.
Affected versions Tested with 24.1.7 -> 24.3.4.2 (latest version bundled with latest RabbitMQ Docker image).
Additional context
Unsure if pursuing a custom verify_fun would help to resolve this or not - appreciate any guidance if that is worthwhile, or if this requires an update within Erlang - or if I am simply way off base :)
It is a missing feature of the public_key application function pkix_path_validation/3. (There is some placeholder code there for it). A long story short many years ago policies were not that commonly used and support for it was missing even from OpenSSL. So other things were prioritized. Some years ago now OpenSSL started supporting it and now we have seen some uses for it, so it is now on the TODO list. It is possible to work around it by writing your own code that accepts the policy extension during the execution of the verify_fun.
@IngelaAndin Thank you very much for the confirmation and note that verify_fun would be on the right track to resolve this for now - will investigate further.
@IngelaAndin This has some potentially large impacts for my environment. Glad its on the TODO list, but can you give me some type of indication on what that means in terms of when this might make it into a release? We might have to make certain decisions based on that information. Thank you.
@IngelaAndin Sorry to ping you again on this as I know timeline questions aren't always the easiest to answer. As I mentioned in my last msg, we need to make some important decisions based off of this information. Can you possibly just give me a rough idea like "next release", a couple months, or 6+ months. Even something like that would be very helpful. Thank you.
@jbyroads I am actually working on this functionality right now. If everything goes well it might make it for OTP-25.1, but I can not promise. There are so many unknowns that potentially could disturb the workflow. But before the end of the year I think is fairly safe to say.
@IngelaAndin Thank you very much! That is very helpful and much appreciated.
Well, making good progress on this but it will take a little longer!
Thank you for the update @IngelaAndin . Do you think you might be able to give an updated timeframe for completion?
If you want to try it out you can find the implementation in the ingela/public_key/policies/OTP-17844 in my GitHub repo [email protected]:IngelaAndin/otp.git
so-called policy mappings are just a prototype and not working at the moment. But more common policy handling is working. It should not be so far out now, the problem is all the things that come in-between!
Also, note that the format in which the policy tree is returned from the path validation is not necessarily the format that we will support when it is included.
This will still happen, it has just been squished out by so many other things :(
@jbyroads I realize this have take dragged out much longer than I hoped, but finally this is almost ready for merge. The policy handling turned out to be quite complex, but I hope you will still benefit from it becoming implemented. Is planed for OTP26.2