How do I get this SSL listener to work?

I appreciate this question is highly similar to another I asked yesterday. However, while the solution there works on MacOS, on Debian I am getting different errors, and a different set of problems.

First I create a new project with mix new tls_question. I have added :crypto and :ssl to mix.exs like so:

def application do
    [
      extra_applications: [:logger, :crypto, :ssl]
    ]
end

I have then generated an SSL certificate with

openssl req  -nodes -new -x509  -keyout key.pem -out cert.pem

and moved key.pem and cert.pem into the project folder.

I then have the following minimal program

defmodule TlsQuestion do
  def main do
    :ssl.start()
    {:ok, listen_socket} = :ssl.listen(8000,
      [ certs_keys: [%{
          :keyfile => "key.pem",
          :certfile => "cert.pem",
        }],
        reuseaddr: true,
      ])
  end
end
TlsQuestion.main()

Calling mix run, I get the error:

== Compilation error in file lib/tls_question.ex ==
** (exit) :badarg
    (kernel) inet_tcp.erl:142: :inet_tcp.listen/2
    (ssl) tls_socket.erl:51: :tls_socket.listen/3
    (ssl) ssl.erl:145: :ssl.listen/2
    lib/tls_question.ex:4: TlsQuestion.main/0

The badarg here is caused by do_connect not finding an address to bind to (the big when clause):

Based on the Compilation error in the message, I believe the issue is the TlsQuestion.main() on the last line - that tries to start the listener when the file is compiled. That’s not needed if you’re launching it with mix run 'TlsQuestion.main()'.

I tried running mix run 'TlsQuestion.main()' without changing the code. That gives the same error.
I then tried removing the line

TlsQuestion.main()

which gave the error:

Compiling 1 file (.ex)
** (Mix) No such file: TlsQuestion.main()

I then tried making a similar project.

defmodule Apptestversion.Application do
  use Application
  def start(_type, _args) do
    children = [
      Apptestversion,
    ]
    opts = [strategy: :one_for_one, name: Apptestversion.Supervisor]
    Supervisor.start_link(children, opts)
  end
end
defmodule TlsQuestion do
  use GenServer
  def start_link([] = _opts) do
    GenServer.start_link(__MODULE__, [], name: __MODULE__)
  end

  @impl true
  def init(_) do
    :ssl.start()
    {:ok, listen_socket} = :ssl.listen(8000,
      [ certs_keys: [%{
          :keyfile => "key.pem",
          :certfile => "cert.pem",
        }],
        reuseaddr: true,
      ])
  end
end

which gives the error

** (Mix) Could not start application apptestversion: Apptestversion.Application.start(:normal, []) returned an error: shutdown: failed to start child: Apptestversion
    ** (EXIT) :badarg

Oops, that was a typo - there’s a flag missing. Should be mix run -e 'TlsQuestion.main()'

That gives me

mix run -e 'TlsQuestion.main()'
** (exit) :badarg
    (kernel) inet_tcp.erl:142: :inet_tcp.listen/2
    (ssl) tls_socket.erl:51: :tls_socket.listen/3
    (ssl) ssl.erl:145: :ssl.listen/2
    (tls_question) lib/tls_question.ex:4: TlsQuestion.main/0
    (stdlib) erl_eval.erl:680: :erl_eval.do_apply/6
    (elixir) lib/code.ex:232: Code.eval_string/3
    (elixir) lib/enum.ex:765: Enum."-each/2-lists^foreach/1-0-"/2

which seems to the same error with a slightly longer stack trace?

I don’t believe reuseaddr is a valid listen option (according to Erlang -- gen_tcp)

Erlang SSL options can be confusing because they are all effectively included in the specs, but run through a very specific validation when attempting to run

I’ve taken a look and I’m pretty sure the option is in there. Here is what it’s for.
I have attempted to remove the option anyway to see if anything different happens and the answer is no.

You’re right. After a closer look, that option should be fine.

I tried the initial code you posted and it worked for me with mix run -e 'TlsQuestion.main(). I might suggest hardcoding full paths to the cert/key and testing again. Also, what OTP version are you using? :certs_keys is a newer option in OTP 25. I tried this same code with OTP 24 and got the same error you see which makes me believe that the badarg is referencing the certs_keys option and your lower OTP version.

You could instead just use this as well:

    {:ok, listen_socket} = :ssl.listen(8000, [keyfile: "key.pem", certfile: "cert.pem", reuseaddr: true])

I’m running OTP 21 on my Raspberry Pi. Is there an alternative way to get SSL working on OTP 21? I’ve rolled my own TLS 1.2 implementation before but that was in another language and I don’t fancy doing it again.

asdf install erlang latest gives me:

asdf_25.2.3 is not a kerl-managed Erlang/OTP installation
The asdf_25.2.3 build has been deleted
Extracting source code
Building Erlang/OTP 25.2.3 (asdf_25.2.3), please wait...
APPLICATIONS DISABLED (See: /home/freddiewoodruff/.asdf/plugins/erlang/kerl-home/builds/asdf_25.2.3/otp_build_25.2.3.log)
 * jinterface     : No Java compiler found
 * odbc           : ODBC library - link check failed

APPLICATIONS INFORMATION (See: /home/freddiewoodruff/.asdf/plugins/erlang/kerl-home/builds/asdf_25.2.3/otp_build_25.2.3.log)
 * wx             : No GLU headers found, wx will NOT be usable
 * wxWidgets was not compiled with --enable-webview or wxWebView developer package is not installed, wxWebView will NOT be available
 *         wxWidgets must be installed on your system.
 *         Please check that wx-config is in path, the directory
 *         where wxWidgets libraries are installed (returned by
 *         'wx-config --libs' or 'wx-config --static --libs' command)
 *         is in LD_LIBRARY_PATH or equivalent variable and
 *         wxWidgets version is 3.0.2 or above.

DOCUMENTATION INFORMATION (See: /home/freddiewoodruff/.asdf/plugins/erlang/kerl-home/builds/asdf_25.2.3/otp_build_25.2.3.log)
 * documentation  : 
 *                  xsltproc is missing.
 *                  fop is missing.
 *                  xmllint is missing.
 *                  The documentation cannot be built.

then CCL seems to peg the CPU to 95% for hours. Attempts to install Java tell me ARMv6 isn’t good enough. It all suggests to me I’m stuck with OTP 21.

Well, you have a few options:

  • Do you need multiple certs with :certs_keys?

    • If not, just use the certfile and keyfile option directly which should work with OTP 21
  • If yes, do you need Java? That can be disabled. You would also need to install openSSL on the rpi and specify both of these in KERL_CONFIGURE_OPTIONS.

    • Internet search would probably serve up several sources that show how to compile for raspberry pi, but the just is you would need KERL_CONFIGURE_OPTIONS="--without-javac --with-ssl=/path/to/openssl/install"
    • OTP also has some documentation on compilation, including who to cross compile OTP for raspberry pi on MacOS (you may not have MacOS available, but this would at least be a good starting point to cross-compile)
    • The Erlang Forum may be the best place to find info and help for compiling OTP on raspberry pi
    • Also note that it will take hours and really peg the CPU of the raspberry pi just due to the design of the processor and intensity of the compilation
  • Are you using the whole raspberry pi OS? Or just to run Erlang/Elixir on it?

    • If you only need Erlang/Elixir, then Nerves is also an option. In a nutshell, it is all the tooling to compile a firmware and run the beam+Elixir app directly on devices and Raspberry pi is officially supported. OTP is already precompiled for you as part of the tooling (See HexDocs for more documentation)
1 Like

This solves it thank you!!! Cool I’ll try that for installing Java. It might come in handy at some point.
I am currently using the whole Raspberry Pi OS. I can see you work at Nerves and I’ll take a look at it and spread the word!