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

cacerts_path may not be enough #1045

Closed
lovett opened this issue Jun 14, 2024 · 2 comments
Closed

cacerts_path may not be enough #1045

lovett opened this issue Jun 14, 2024 · 2 comments

Comments

@lovett
Copy link

lovett commented Jun 14, 2024

Hello, I've run into a unusual situation when it comes to SSL certs.

I have an Erlang 27 installation that is unable to run mix deps.get because of a bogus expired certificate problem as discussed here. It looks like this:

mix deps.get --only prod
 (Mix) httpc request failed with: {:failed_connect, [{:to_address, {~c"builds.hex.pm", 443}}, {:inet, [:inet], {:tls_alert, {:certificate_expired, ~c"TLS client: In state wait_cert_cr at ssl_handshake.erl:2158 generated CLIENT ALERT: Fatal - Certificate Expired\n"}}}]}

I think there might be an underlying problem on the Erlang side which is an issue for a different repository. Here I wanted to ask about the difference between cacerts_path in hex, and cacertfile in httpc.

When cacerts_path is set, its contents are sent to :public_key.pem_decode/1. Could an underlying issue with that call explain why I get a failure from mix, but can get httpc to behave if I configure it directly: like this:

:httpc.request(:get, {"https://builds.hex.pm/", []}, [
  ssl: [
    cacertfile: "/opt/local/etc/openssl/cert.pem",
  ]
], [])

That works fine. I think what is happening is Erlang doesn't like my system's default cert chain for whatever reason, but the alternative that hex provides in the form of cacert_path is susceptible to the same breakage. Meanwhile, cacertfile works because it is presumably handled in a different way. But that option is not otherwise available through hex.

The alternate cert file I'm passing to httpc happens to be the same as the one shipped with Hex, apart from the timestamp. It also works if I pass in Hex's ca-bundle.crt.

Version details:

$ mix --version
Erlang/OTP 27 [erts-15.0] [source] [64-bit] [smp:2:2] [ds:2:2:10] [async-threads:1]

Mix 1.17.0 (compiled with Erlang/OTP 27)

$ openssl version
OpenSSL 3.1.6 4 Jun 2024 (Library: OpenSSL 3.1.6 4 Jun 2024)
@ericmj
Copy link
Member

ericmj commented Jun 14, 2024

Can you show the full output of the mix deps.get command. It's making a request to builds.hex.pm which suggests it's trying to install hex or rebar? It could also be the update checker failing but that shouldn't fully error like that.

If it's trying to install rebar or hex then the cacerts_path won't be used as it's Elixir doing that. Elixir will use the environment variable HEX_CACERTS_PATH though which you can set to the same value.

@lovett
Copy link
Author

lovett commented Jun 15, 2024

Thanks for the environment variable suggestion, I think that was it.

I was indeed getting stuck on rebar. And I had been favoring hex.config over the env approach. I'm not yet sure why, but it seems like the macports-based Erlang installation I started with was somehow not right (apart from the OS cert chain not working out-of-the-box). Setting HEX_CACERTS_PATH via environment variable did not work there, but it did work when I moved to an asdf-based Erlang installation. Not sure if it's an OpenSSL complication or not, but for this issue it's probably moot.

If it's helpful, the dynamics of the environment variable and the hex config weren't clear. They don't seem to be as equal as the documentation suggests, at least when it comes to rebar.

For completeness, the full output I was seeing previously is below. Probably nothing more to be done here.

Thanks again for the help.


Run 1: Erlang + OpenSSL 3.1.6 (from macports)

$ mix deps.get --only prod
** (Mix) httpc request failed with: {:failed_connect, [{:to_address, {~c"builds.hex.pm", 443}}, {:inet, [:inet], {:tls_alert, {:certificate_expired, ~c"TLS client: In state wait_cert_cr at ssl_handshake.erl:2158 generated CLIENT ALERT: Fatal - Certificate Expired\n"}}}]}

Could not install Hex because Mix could not download metadata at https://builds.hex.pm/installs/hex-1.x.csv.

Alternatively, you can compile and install Hex directly with this command:

    $ mix archive.install github hexpm/hex branch latest

Run 2: After installing hex from GitHub

$ mix deps.get --only prod
Resolving Hex dependencies...
Resolution completed in 0.647s
Unchanged:
...[package list omitted]...
** (Mix) httpc request failed with: {:failed_connect, [{:to_address, {~c"builds.hex.pm", 443}}, {:inet, [:inet], {:tls_alert, {:certificate_expired, ~c"TLS client: In state wait_cert_cr at ssl_handshake.erl:2158 generated CLIENT ALERT: Fatal - Certificate Expired\n"}}}]}

Could not install Rebar because Mix could not download metadata at https://builds.hex.pm/installs/rebar3-1.x.csv.

Run 3: With HEX_CACERTS_PATH via enironment

$ HEX_CACERTS_PATH=/opt/local/etc/openssl/cert.pem mix deps.get --only prod
...
Could not install Rebar...

Run 4: Erlang + OpenSSL 1.1 (from asdf)

sudo port install openssl11
KERL_CONFIGURE_OPTIONS="--without-javac --without-wx --without-odbc --with-ssl=/opt/local/libexec/openssl11/" asdf install erlang latest
asdf install elixir latest
mix deps.get --only prod

same as run 1

Run 5: Setting HEX_CACERTS_PATH via enironment

HEX_CACERTS_PATH=/opt/local/etc/openssl/cert.pem mix deps.get --only prod

Works

Run 6: Erlang + OpenSSL 3 (from asdf)

KERL_CONFIGURE_OPTIONS="--without-javac --without-wx --without-odbc" asdf install erlang latest
asdf install elixir latest
mix archive.install github hexpm/hex branch latest
HEX_CACERTS_PATH=/opt/local/etc/openssl/cert.pem mix deps.get --only prod

Works

@lovett lovett closed this as completed Jun 15, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants