In the Elixir application we use on our platform, we utilize the Elixir library arc to manage file uploads (similar to Carrierwave for the Rails community). An interesting feature of this library is its ability to upload a file from a remote url. A basic use case is a user being able to provide an endpoint for a form rather than a file to upload. Obviously the endpoint must be public and reachable.

Recently, we noticed a lot of invalid changeset errors were occurring with the message invalid_file_path during this operation (changesets allow systems to filter, cast, validate, and define constraints when manipulating structs). The weird thing is that requested files come from our own content delivery network (CDN) and files can be fetched directly via the browser.

Understanding the issue

First, we decided to locally reproduce the error using our own code. Unfortunately, we got exactly the same error message and it didn’t help us understand what was happening at all.

To get a more detailed error description, our second idea was to run a code snippet taken directly from the arc library. The part of the code required is the bit that deals with downloading the file from our remote server before storing it locally or remotely (on Amazon S3, for example).

From this message we can easily tell that we had an SSL problem—the handshake can’t be done. The library can’t fetch the file to store it and so returns an invalid_file_path error message.

Quick fixes

After multiple searches on the Internet, we surmised the issue was due to a problem with either the protocol version (tls/ssl) or with the server name indication (SNI), which corresponds to an extension of the TLS protocol via the hostname it is attempting to connect with at the start of the handshake. Many posts suggest fixing the issue by providing ssl_options for the hackney requests (or an SSL option for HTTPoison).

We tried two fixes by providing additional options for the initial snippet, the first time by forcing the protocol version to tlsV1.2, and the second time by providing the server_name_indication, as seen below:

We were therefore able to understand quite quickly that, by providing ssl_options, the verification is not performed and it seems to be a bad idea to provide any custom ssl_options or at least partial ssl_options.

By default, hackney set the protocol versions to ['tlsv1.2', 'tlsv1.1', ‘tlsv1’, ‘sslv3’], so the SSL module from Erlang tries tlsv1.2 first and, if it can’t connect, will try tlsv1.1. So if our server is set up to accept tlsv1.2, our quick fix above is useless because it’s the default option in hackney.

Our problem therefore looked to be with the certificate verification. By carrying out tests using curl (and other systems), we were able to confirm that these systems are able to verify the certificate without any issue.

The openssl command log shows us that the certificate chain is not perfect: The first link and the second link are the same. The hackney implementation of CA verification is stricter than others and the chain must be perfect to be traversed correctly.

So, here is the invalid_key_usage: It looks like we had a problem with the way we were serving our certificate. Our CDN is served by Amazon CloudFront and the SSL configuration by AWS Certificate Manager (ACM). To configure the certificate, ACM requires three separate files: Certificate body, certificate private key, and certificate chain. For Nginx users, the certificate body and certificate chain are concatenated into a single file.

By reimporting our certificate correctly, the error disappeared, without the need for code fixes.

Conclusion

Sometimes a basic error can signal more important problems. The Internet contains a lot of resources to fix most issues but it’s really important to understand why the fixes work. Applying a quick fix without knowing what it does can easily lead to a bigger issue.

This article is part of Behind the Code, the media for developers, by developers. Discover more articles and videos by visiting Behind the Code!