Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Invalid server extensions error on private grpc service #446

Closed
mbacarella opened this issue Apr 27, 2022 · 5 comments
Closed

Invalid server extensions error on private grpc service #446

mbacarella opened this issue Apr 27, 2022 · 5 comments

Comments

@mbacarella
Copy link
Contributor

Hi there. Thank you for the pure-OCaml TLS implementation.

I'm attempting to port code that uses Async_ssl over to use Tls_async instead.

The code accesses a gRPC service without public endpoints, so I need to provide a ca file copied from the server. This flow actually works fine with Async_ssl, but fails with an "invalid server extensions" error when I provide the ca file to Tls_async.

I also tried Tls_async without the ca file and it connected fine. Maybe I'm configuring the client wrong?

    let host = t.grpc_host in
    let port = t.grpc_port in 
    (* ... *)
    let%bind res =
      let%bind authenticator =
        let module X509 = Tls_async.X509_async in
        let crls = None in
        let allowed_hashes = None in
        let ca_file = t.tls_cert in
        let param = X509.Authenticator.Param.ca_file ?crls ?allowed_hashes ca_file () in
        match%bind
          X509.Authenticator.Param.to_authenticator
            ~time:X509.Authenticator.Param.time
            param
        with
        | Ok a -> return a
        | Error e ->
          failwithf "X509_async.Authenticator: %s: %s" ca_file (Error.to_string_hum e) ()
      in
      let config = Tls.Config.client ~authenticator () in
      let hnp = Host_and_port.create ~host ~port in
      (* XXX: Create a Domain_name.t if host is not an IP address. *)
      let host = None in
      Tls_async.connect ~socket config (Tcp.Where_to_connect.of_host_and_port hnp) ~host
    in
    let%bind reader, writer =
      match res with
      | Ok ((_ : Tls_async.Session.t), r, w) -> return (r, w)
      | Error e ->
        eprintf "Error connecting to %s:%d: %s\n" host port (Error.to_string_hum e);
    in
    (* ... *)

Error message

Error connecting to localhost:10009: (Tls_failure
 (Error
  (AuthenticationFailure
    "leaf certificate X.509 certificate\
   \nversion 3\
   \nserial 101033868549717947339053637548736621515\
   \nalgorithm ECDSA-SHA256\
   \nissuer /O=lnd autogenerated cert/CN=localhost\
   \nvalid from 2021-12-03 00:21:36 +00:00 until 2023-01-28 00:21:36 +00:00\
   \nsubject /O=lnd autogenerated cert/CN=localhost\
   \nextensions subjectKeyIdentifier 1c 0e 63 20 ec ec 3c 50  e1 a3 5f e4 ee c1 55 83\
   \n                                75 70 3a ed\
   \ncritical keyUsage key encipherment, digital signature, key cert sign\
   \nsubjectAlternativeName dns localhost; unix; unixpacket; bufconn\
   \nip 7f 00 00 01;00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 01\
   \n                 critical basicConstraints CA true depth none\
   \nextendedKeyUsage server authentication\
   \n: invalid server extensions")))

Thanks for any light that could be shed here.

@hannesm
Copy link
Member

hannesm commented Apr 27, 2022

Dear @mbacarella, thanks for your issue report. The error is an "Authentication Failure", with "leaf certificate ... invalid server extensions". This originates from the X.509 library which implements the authentication.

Now, the function in question is the one about validating server extensions: https://github.com/mirleft/ocaml-x509/blob/v0.16.0/lib/validation.ml#L176-L187

And this one requires the "BasicConstraints CA=false", while your certificate contains "basicConstraints CA true depth none" -- so I wonder why your server has a CA certificate (which is able to sign other certificates), or whether this has been done accidentally? I know that the X.509 code was written as being as strict as possible, and don't know off the top of my head which RFC or recommendation was to ensure that CA=false. I can see two paths:

  • tweak the certificate generation tooling to not generate a CA certificate
  • tweak/PR the X509 library / authenticator to allow server certificates with CA=true

@mbacarella
Copy link
Contributor Author

mbacarella commented Apr 27, 2022

Hi @hannesm, thanks for the explanation.

I'm connecting to the Lightning Network daemon (lnd).

I now notice that it appears that uses of the rusttls client also go boom for similar reasons when connecting to lnd lightningnetwork/lnd#5450

So, we're in good company I suppose. Not that this is necessarily an argument, but the openssl client supports this and I can connect to that endpoint using Async_ssl with verify peer and that ca file. So this will be an interoperability issue that's challenging to control.

Are we okay with exposing an option from the ocaml X509 library to relax this strictness for the sake of interoperability?

@hannesm
Copy link
Member

hannesm commented Apr 27, 2022

Are we okay with exposing an option from the ocaml X509 library to relax this strictness for the sake of interoperability?

Off the top of my head I don't remember which functionality is exposed by X509 atm -- you could build your own authenticator... But I'd be fine with a specific ?allow_ca_cert:bool argument channeled through (authenticator & validator) and emitting a warning log message if the leaf certificate is a CA certificate (and that option is true, default should be false).

@mbacarella
Copy link
Contributor Author

For the sake of future generations, here's how I resolved this.

I had started working on this but it felt wrong to add this to x509 just to support bad behavior.

I looked into constructing my own X509.Validation.r but the interface, unfortunately, does not expose enough to reuse most of the validation code with this special relaxed handling swapped in.

So, I simply inserted a forked copy of x509 in my project that works around this.

hannesm added a commit to hannesm/opam-repository that referenced this issue Feb 4, 2023
CHANGES:

* Validation: allow self-signed server certificate with BasicConstraints CA=true
  (reported by @mbacarella in mirleft/ocaml-tls#446
   (lightningnetwork/lnd#5450), fix mirleft/ocaml-x509#161 by @hannesm)
@mbacarella
Copy link
Contributor Author

For posterity, this turned out to have a much simpler solution. See here instead mirleft/ocaml-x509#160 (comment)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

No branches or pull requests

2 participants