Search code examples
curlssl-certificateopam

opam init fails because curl fails to verify the certificate


I am trying to set up OPAM on Debian Stretch (in a chroot jail within a CI environment). After installing opam via apt, I run opam init -y, which fails with the following output:

Error:  Command "curl --write-out %{http_code}\\n --retry 3 --retry-delay 2 --compressed -OL https://opam.ocaml.org/urls.txt" failed:
# opam-version         1.2.2
# os                   linux
# command              curl --write-out %{http_code}\n --retry 3 --retry-delay 2 --compressed -OL https://opam.ocaml.org/urls.txt
# path                 /tmp/opam-57077-c61e7b
# exit-code            60
# env-file             /home/runner/.opam/log/log-57077-d2d111.env
# stdout-file          /home/runner/.opam/log/log-57077-d2d111.out
# stderr-file          /home/runner/.opam/log/log-57077-d2d111.err
### stdout ###
# 000
### stderr ###
# [...]
# curl performs SSL certificate verification by default, using a "bundle"
#  of Certificate Authority (CA) public keys (CA certs). If the default
#  bundle file isn't adequate, you can specify an alternate file
#  using the --cacert option.
# If this HTTPS server uses a certificate signed by a CA represented in
#  the bundle, the certificate verification probably failed due to a
#  problem with the certificate (it might be expired, or the name might
#  not match the domain name in the URL).
# If you'd like to turn off curl's verification of the certificate, use
#  the -k (or --insecure) option.

All I can see here is that the certificate fails, but not why: Has it expired (or is not valid yet)? Is the issuer unknown? What is it being verified against?

Re-running opam init with the -verbose switch did not provide any further info that I found helpful.

Installing ca-certificates prior to running opam init also did not help.

Manually running curl -v https://opam.ocaml.org/urls.txt tells me “the certificate” has expired (but not if it is the server certificate or one of the certificates in the chain). However, when I do the same on my local machine, the request succeeds. Running date in the chroot jail returns the correct time. What gives?


Solution

  • Updating the VM image from stretch to buster (which is the immediate successor – bullseye resulted in other errors which I am still investigating) fixed the error.

    What presumably happened is that, while the server certificate is still valid (which I verified with a browser), one of the issuing CA (there is a root cert and an intermediate one) had expired. (Not sure if the server supplied one, but apparently curl used a local copy.) Later Debian releases seem to have a more recent version of that certificate, with the same key but a new expiration date.

    For everybody hitting the same issue, here’s what to do:

    • Check the error message from opam init for the URL that fails.
    • Run curl -v for the failing URL:
      • What CA cert path is being used by curl? Does it have the latest certificates installed? (On Debian-based distros, this should be in the ca-certificates package.)
      • What is the precise certificate verification error?
    • Do the same on another machine, possibly also with your regular browser. Are you getting the same results?
    • If the certificate is reported expired (or not yet valid) on one machine but OK on another:
      • Do all machines have the correct date and time? If the date/time is off (and outside the validity period of at least one certificate in the chain), verification will fail. Solution: set the correct date and time.
      • Does the machine have the latest version of all CA and intermediate certificates? If one certificate in the chain has expired, replace it with a new one – either by updating the package that holds the certificates (ca-certificates on Debian), by updating to a newer version of your distribution (if that is an option), or by manually adding the missing cert file.