Making SSL tests all pass for Phoenix + Let's Encrypt

Pre-info:

webserver : Cowboy only, not using nginx
My certs is from Let’s Encrypt (certbot)
Debian 8
Generated dh-param.pem file using openssl

1/
SSL Tester: https://www.htbridge.com/ssl

Right now, it can’t fulfill the “elliptic curves” criteria.

“The server supports elliptic curves that are considered weak.”

(currently I get A-, compared to capped at B- for the “do nothing” config)

Q. How do I pass in these values as in Erlang -- ssl

to my Phoenix config? (let’s say I am using dev.exs for this)

I am getting

[warn] Transport option {:eccs,
 ["sect571r1", "sect571k1", "secp521r1", "brainpoolP512r1", "sect409k1",
  "sect409r1", "brainpoolP384r1", "secp384r1", "sect283k1", "sect283r1",
  "brainpoolP256r1", "secp256k1", "secp256r1", "sect239k1", "sect233k1",
  "sect233r1", "secp224k1", "secp224r1"]} unknown or invalid.

OR

[warn] Transport option {:honor_ecc_order, true} unknown or invalid.

[warn] Transport option {:eccs,
 [:sect571r1, :sect571k1, :secp521r1, :brainpoolP512r1, :sect409k1, :sect409r1,
  :brainpoolP384r1, :secp384r1, :sect283k1, :sect283r1, :brainpoolP256r1,
  :secp256k1, :secp256r1, :sect239k1, :sect233k1, :sect233r1, :secp224k1,
  :secp224r1]} unknown or invalid.

for the keys eccs and honor_ecc_order .

2/
SSL Tester: SSL Server Test (Powered by Qualys SSL Labs)

I am getting A- (improved from the basic grade of B for ‘do nothing’ config)

“The server does not support Forward Secrecy with the reference browsers. Grade reduced to A-.”

Q. Could anyone give a clue on how to resolve that?

In short:
Although I do already get A- now, but I would like to know how to pass those options that I mentioned in order to get the satisfying “full compliance as per recommended” result. (For reference, I have achieved this before in my other tech stack’s webserver.)

The main point for me is to learn how to use Phoenix’s config file to pass the options I mentioned(through cowboy?) all the way to ssl options as stated as available in Erlang -- ssl

2 Likes

You should have a look at this post. Phoenix config is at the bottom http://ezgr.net/increasing-security-erlang-ssl-cowboy/

4 Likes

Thanks, actually, I already implemented everything from that link before testing with SSLlabs + HtBridge and asking the questions in the first post.

The issue is that the options ‘eccs’ don’t get taken into use.

Here is my config:

snip...

  https: [  port: 443,
            otp_app: :hello_phoenix,
            keyfile: "/etc/letsencrypt/live/asdf.com/privkey.pem",
            certfile: "/etc/letsencrypt/live/asdf.com/cert.pem",
            cacertfile: "/etc/letsencrypt/live/asdf.com/chain.pem",
            versions: [:"tlsv1.2", :"tlsv1.1", :"tlsv1"],
            ciphers: ~w(
              TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
              TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384
              TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA
              TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA
              TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256
              TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384
              TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
              TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
              TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA
              TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA
              TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256
              TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384
              TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256
              TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384
              TLS_DHE_RSA_WITH_AES_128_GCM_SHA256
              TLS_DHE_RSA_WITH_AES_256_GCM_SHA384
              TLS_DHE_RSA_WITH_AES_128_CBC_SHA
              TLS_DHE_RSA_WITH_AES_256_CBC_SHA
              TLS_DHE_RSA_WITH_AES_128_CBC_SHA256
              TLS_DHE_RSA_WITH_AES_256_CBC_SHA256
            ),
            dhfile: "/home/asdf/projects/hello_phoenix/dh-params.pem",
            secure_renegotiate: true,
            reuse_sessions: true,
            honor_cipher_order: true,
            # http://erlang.org/doc/man/ssl.html#type-ssloption
            eccs: [
              :sect571r1, :sect571k1, :secp521r1, :brainpoolP512r1, :sect409k1,
              :sect409r1, :brainpoolP384r1, :secp384r1, :sect283k1, :sect283r1,
              :brainpoolP256r1, :secp256k1, :secp256r1, :sect239k1, :sect233k1,
              :sect233r1, :secp224k1, :secp224r1
            ],
          ],

snip...

The HtBridge test results tell me that a group of ‘weak elliptic curves’ are in use. So my intention is to explicitly specify the elliptic curves that I want to use and not use others. The only way that looks possible is the one in the erlang ssl docs. However, the key-value as you see in my config provided above, is not recognized. So I still need some help there… hope this helps to clarify.

4 Likes

The warning messages are from Ranch, which is filtering out SSL options it does not recognize. Prior to version 1.3.0, Ranch only allowed whitelisted SSL options to be passed in. This was changed a few months ago to a blacklist: https://github.com/ninenines/ranch/commit/b2b099627424ce42b7f0ac02e5ddd8d0bf2c3381

Unfortunately, the latest version of Ranch on Hex appears to be 1.2.1, so you’d have to override the dependency in your mix.exs file and pull in 1.3.x from GitHub. I haven’t tried this myself.

4 Likes

hey @Gazler, you’re listed as a Ranch package owner: could you push 1.3.x to Hex? If your child process isn’t flooding your mailbox, that is :stuck_out_tongue_winking_eye: (congrats!)

6 Likes

Thanks! @ericmj and I have pushed ranch 1.3 to hex now.

6 Likes

Thank you everybody, after spending some time testing and trying to fulfill every single one of the items in the ssl tests, I have

  • Used the version of Ranch, 1.3.1, which was just pushed to Hex.
  • That did the trick; the params (in the :atom format) are now recognized
  • (did not put honor_ecc_order param, as it might be unnecessary)
  • Added an additional client_renegotiation: false, param as without this the test score for HtBridge will be capped lower

Now I get A+ on HtBridge SSL test, and

capped to A- on SSLlabs test - because “The server does not support Forward Secrecy with the reference browsers. Grade reduced to A-.”

  • Without knowing what the “reference browsers” need (or in fact, what they even are) I don’t think I can improve on this Forward Secrecy thing (might not be necessary to also)
  • HtBridge indicates “SERVER DOES NOT SUPPORT OCSP STAPLING” which is required for “Non-compliant with HIPAA guidance” but I think this is about the webserver implementation.

All in all, an immensely gratifying result from the built-in default webserver of Phoenix alone, guess that might be good enough for me and anyone else who might like to repeat this result can use the config params mentioned…

(If one wants to fulfil everything, then perhaps phoenix needs to be run behind nginx with its well-known params configured but this option didn’t appeal to me (for now.))

I am so impressed and grateful with the help given by this community… one of the best tech communities I’ve ever joined so far. :grinning:

6 Likes

Thanks for sharing your experience, would love to read your article about this setup a-z :slight_smile:

5 Likes

Yes this would be awesome to have!

3 Likes

Good, we’re getting closer, but we’re not there yet.

The message regarding forward secrecy from SSLLabs suggests you have cipher suites enabled that do not use a DH exchange. The :ciphers parameter in your configuration file does list only DH-enabled suites, but unfortunately Erlang’s :ssl module is silently ignoring the list and using its built-in defaults instead. I think you’ll find that the cipher list in the SSLLabs report does not match the list in your config file.

Erlang’s :ssl module expects cipher suite names to be passed in as charlists (not as Elixir strings, which are Erlang binaries; not sure why it’s silently ignoring binaries, though). And moreover, the names need to use OpenSSL naming conventions. So instead of…

ciphers: ~w(
  TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
  TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384
  # ...
)

…you’d have to use…

ciphers: ~w(
  ECDHE-ECDSA-AES128-GCM-SHA256
  ECDHE-ECDSA-AES256-GCM-SHA384
  # ...
)c

(Note the c modifier at the end of the ~w sigil)

Shameless plug: you can use https://hex.pm/packages/cipher_suites to select cipher suites using the OpenSSL filtering syntax often used in Apache/Nginx/… instead.

Regarding OCSP stapling: this is not currently supported by Erlang’s SSL/TLS implementation.

7 Likes

Hi everyone here, many thanks for your help, and for your interest! :slight_smile:

So, now I am Proud Asian Dad, as it is possible to get A+ for BOTH ssllabs and htbridge’s ssl tests:

This:

and this:

Ok, let me see if I can provide a step by step here as an article might take too long.

1/ Basically, googling for “let’s encrypt” may eventually bring you to “certbot” which following the instructions here, you ssh into your server and follow step by step.

This obtains free SSL certs and auto-renews them using cron jobs.
A word here, the scripts by default run as root, so you may want to explore further at this stage “automated but not as root”

But if you want to just get everything running quickly to try out, you can just follow the original instructions.

2/
Next is you put the settings in your config file (e.g. dev.exs or another)
I just put the settings here that get you the A+ result above…
Also left in the commented-out options, to show that I found that they were not necessary (but others could tell more about these if they know more about them)

config :hello_phoenix, HelloPhoenix.Endpoint,
  http: [port: 80],

  #force_ssl: [rewrite_on: [:x_forwarded_proto]],
  url: [host: "asdf.qwer.com"],
  force_ssl: [],
  https: [port: 443,
          otp_app: :hello_phoenix,
          keyfile: "/PATH/TO/asdf.qwer.com/privkey.pem",
          certfile: "/PATH/TO/asdf.qwer.com/cert.pem",
          cacertfile: "/PATH/TO/asdf.qwer.com/chain.pem",
          versions: [:"tlsv1.2", :"tlsv1.1", :"tlsv1"],
          ciphers: ~w(
            ECDHE-ECDSA-AES256-GCM-SHA384
            ECDHE-ECDSA-AES256-SHA384
            ECDHE-ECDSA-AES128-GCM-SHA256
            ECDHE-ECDSA-AES128-SHA256
            ECDHE-ECDSA-AES256-SHA
            ECDHE-ECDSA-AES128-SHA

            ECDHE-RSA-AES256-GCM-SHA384
            ECDHE-RSA-AES256-SHA384
            ECDHE-RSA-AES128-GCM-SHA256
            ECDHE-RSA-AES128-SHA256
            ECDHE-RSA-AES256-SHA
            ECDHE-RSA-AES128-SHA

            ECDH-ECDSA-AES256-GCM-SHA384
            ECDH-ECDSA-AES256-SHA384
            ECDH-ECDSA-AES128-GCM-SHA256
            ECDH-ECDSA-AES128-SHA256

            DHE-RSA-AES256-GCM-SHA384
            DHE-RSA-AES256-SHA256
            DHE-DSS-AES256-GCM-SHA384
            DHE-DSS-AES256-SHA256
            DHE-RSA-AES256-SHA
            DHE-DSS-AES256-SHA

            DHE-DSS-AES128-GCM-SHA256
            DHE-RSA-AES128-GCM-SHA256
            DHE-RSA-AES128-SHA256
            DHE-DSS-AES128-SHA256
            DHE-RSA-AES128-SHA
            DHE-DSS-AES128-SHA

            AES128-GCM-SHA256
            AES128-SHA
            DES-CBC3-SHA
          )c,
          dhfile: "/PATH/TO/projects/hello_phoenix/dh-params.pem",
          secure_renegotiate: true,
          reuse_sessions: true,
          honor_cipher_order: true,
          # http://erlang.org/doc/man/ssl.html#type-ssloption
###          honor_ecc_order: true,
          client_renegotiation: false,
          eccs: [
            :sect571r1, :sect571k1, :secp521r1, :brainpoolP512r1, :sect409k1,
            :sect409r1, :brainpoolP384r1, :secp384r1, :sect283k1, :sect283r1,
            :brainpoolP256r1, :secp256k1, :secp256r1, :sect239k1, :sect233k1,
            :sect233r1, :secp224k1, :secp224r1
          ],
  ],

3/ As @voltone pointed out, if you used a wrong format for the ciphers, they will be silently ignored and the default suites used, that gets you A- or something else. If you use the one as shown here, they will be correct.

4/ So now running the tests on your server would give the same result.
‘OSCP Stapling’ item is not supported by the webserver, but that’s not quite important and there’s nothing you can do about it as well.

5/ I did not happen to try out (plug!) @voltone’s cipher_suites | Hex since I only got to know of it so late, but I expect that you will get the same good result in one step rather than doing it by hand as I did (looking up and copying the openssl aliases) :smiley: If you do try it, do let us know how it works!

11 Likes

Nice. great and compact tutorial for others. :smiley: Thanks again for sharing your experience! :slight_smile:

3 Likes

Most welcome, glad to help, also, before I forget, these params are necessary, and give the behaviour of:

1/
If you access asdf.qwer.com on your browser, the webserver will automatically redirect from port 80 to port 443.

If you didn’t specify the http: [port: 80] param, then your server will not be listening on port 80 to give you this behaviour, which is likely what you would want to have

2/
The force_ssl: , param is what specifies this behaviour also, although the “blank param” is confusing, but putting it like this in fact specifies a truthy value that will cause the auto-redirect mentioned

3/
Also, if you have a webserver served out http at port 5440, and your https is served out at port 5443, you need to put url: [host: "asdf.qwer.com:5443"], so that the auto-redirect happens correctly.

In my case, I had a webserver with a self-signed cert in development, so I did it like so: url: [host: "192.168.56.101:5443"],

So it takes a combination of these 3 params to get the behaviour. Excerpt pasted below.

4 Likes

I believe the force_ssl: [] works because it has a default of hsts: true. So leaving it blank means its actually
force_ssl: [hsts: true]

3 Likes

This greatly helped me to configure my standalone Erlang server running a Phoenix app to go from grade B to grade A+.

The current recommend ciphers suite from SSL labs are as per I have in my configuration:

# LINKS:
#   - Phoenix:
#     + https://elixirforum.com/t/making-ssl-tests-all-pass-for-phoenix-lets-encrypt/3507/11
#   - Erlang:
#     + http://ezgr.net/increasing-security-erlang-ssl-cowboy
#   - Cipher Suites:
#     + Best Ciphers - https://github.com/ssllabs/research/wiki/SSL-and-TLS-Deployment-Best-Practices#23-use-secure-cipher-suites
#     + Mapping - https://testssl.sh/openssl-rfc.mapping.html
#     + OWASP - https://www.owasp.org/index.php/TLS_Cipher_String_Cheat_Sheet
config :rumbl, Rumbl.Endpoint,
  http: [port: 4000],
  url: [
    host: System.get_env("APP_URL") || "${APP_URL}",
    port: System.get_env("APP_URL_HTTPS_PORT") || "${APP_URL_HTTPS_PORT}"
  ],
  force_ssl: [
    hsts: true
  ],
  https: [
    port: System.get_env("APP_HTTPS_PORT") || "${APP_HTTPS_PORT}",
    keyfile: System.get_env("APP_SSL_KEY_PATH") || "${APP_SSL_KEY_PATH}",
    certfile: System.get_env("APP_SSL_CERT_PATH") || "${APP_SSL_CERT_PATH}",
    cacertfile: System.get_env("APP_SSL_INTERMEDIATE_CERT_PATH") || "${APP_SSL_INTERMEDIATE_CERT_PATH}",
    dhfile: System.get_env("APP_SSL_DHPARAMS_PATH") || "${APP_SSL_DHPARAMS_PATH}",
    versions: [:'tlsv1.2'],
    ciphers: ~w(
      ECDHE-ECDSA-AES128-GCM-SHA256
      ECDHE-ECDSA-AES256-GCM-SHA384
      ECDHE-ECDSA-AES128-SHA
      ECDHE-ECDSA-AES256-SHA
      ECDHE-ECDSA-AES128-SHA256
      ECDHE-ECDSA-AES256-SHA384
      ECDHE-RSA-AES128-GCM-SHA256
      ECDHE-RSA-AES256-GCM-SHA384
      ECDHE-RSA-AES128-SHA
      ECDHE-RSA-AES256-SHA
      ECDHE-RSA-AES128-SHA256
      ECDHE-RSA-AES256-SHA384
      DHE-RSA-AES128-GCM-SHA256
      DHE-RSA-AES256-GCM-SHA384
      DHE-RSA-AES128-SHA
      DHE-RSA-AES256-SHA
      DHE-RSA-AES128-SHA256
      DHE-RSA-AES256-SHA256
    )c,
    secure_renegotiate: true,
    client_renegotiation: false,
    reuse_sessions: true,
    honor_cipher_order: true,
    max_connections: :infinity
  ],
  cache_static_manifest: "priv/static/manifest.json",
  server: true
1 Like

In case anyone comes across this post like I did, and wonders what the minimal config would be as of July 2020 to get an A+ on both SSL Labs and ImmuniwebSSL (htbridge) using a Let’s Encrypt certificate, here it is:

 https: [
    port: System.get_env("HTTPS_PORT"),
    keyfile: System.get_env("TLS_KEY_FILE"),
    certfile: System.get_env("TLS_CERT_FILE"),
    dhfile: "/etc/ssl/dhparam.pem",
    cipher_suite: :strong,
    client_renegotiation: false,
    transport_options: [socket_opts: [:inet6]]
  ]

The TLS_KEY_FILE being the privkey.pem and the TLS_CERT_FILE the fullchain.pem that are generated by certbot.

1 Like

Hi, I have tried most of these but i still get this error here:

setting up SSL correctly on an erlang/ elixir server - Stack Overflow.

Any help please.

1 Like

openssl s_client -connect paperlesssolutionsltd.com.ng:8443

gives:

λ openssl s_client -connect paperlesssolutionsltd.com.ng:8443
CONNECTED(000001B8)
depth=0 CN = www.paperlesssolutionsltd.com.ng
verify error:num=20:unable to get local issuer certificate
verify return:1
depth=0 CN = www.paperlesssolutionsltd.com.ng
verify error:num=21:unable to verify the first certificate
verify return:1
---
Certificate chain
 0 s:CN = www.paperlesssolutionsltd.com.ng
   i:C = US, O = Let's Encrypt, CN = R3
---
Server certificate
-----BEGIN CERTIFICATE-----
MIIGiTCCBXGgAwIBAgISBJEvL3uT8YZMvG0HGiFugEe9MA0GCSqGSIb3DQEBCwUA
MDIxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MQswCQYDVQQD
EwJSMzAeFw0yMTAyMDUxMTAyNTNaFw0yMTA1MDYxMTAyNTNaMCsxKTAnBgNVBAMT
IHd3dy5wYXBlcmxlc3Nzb2x1dGlvbnNsdGQuY29tLm5nMIICIjANBgkqhkiG9w0B
AQEFAAOCAg8AMIICCgKCAgEA/pJFz2InXO8UiTggt3ThP3tiIiipVWfGzteHLI7S
6VhJRjv+/W+Mcu5EXXdVum/1ZTe6E1Ko/i0qrqvwvBNhuCCI0EnK5H299jAICt0/
JnSTDp1JDz2k7Nm0MIiIEZfvaIRXuoopR4iM91svIku6D3rzZ+OwoTUvZXvEscky
pBJc+fVUJvfnGhDwLqLXvXyzXqapKphrGxAAHD2GzxOEGo33N3N97m18qbyeG+hA
UOcIRVfLP2jPrWDolKchtM9AyUj4lAiMsPU7Jc+Rt6AnMyTr1hsXduUCNNn3i/h/
s7Kg3xIo++izDhC9Qj5Pedgy2pbZ6z4ZMKQ9UsPGHJ6X3BmT7lgX796nGeaySC+L
HqwBtoAlFMwE96i4yNEhFlQuM5roNhlVc4Vv5yyvtVNtxoYMGpsrJ0upAWHmJ+0J
UJVlCcemng+7eSfNBrdZ1XvSvHxSqPEOmmn7gqdBzad3PKj9inkibtmSqt9gNOGR
RzI0DZK8CE52LUrqBNrUsifmE8uw/FQ11+7eahL65b0nNj0wreExONb0aQEYOR1/
/4YXK742K3l4hZn5r/sIuzgYFFIZwIKckYi821EHE3ugwPG3asBXBaZ9vSZ5CFt+
vAzedpTNV3nN/55KlqHhll98FF6JQAFNsGRY5kcWC2SVv/KRIczrfVm2R/USVXOX
iWcCAwEAAaOCAp4wggKaMA4GA1UdDwEB/wQEAwIFoDAdBgNVHSUEFjAUBggrBgEF
BQcDAQYIKwYBBQUHAwIwDAYDVR0TAQH/BAIwADAdBgNVHQ4EFgQUnReY/FdZAB8x
NTbPm+A1kBqwgAYwHwYDVR0jBBgwFoAUFC6zF7dYVsuuUAlA5h+vnYsUwsYwVQYI
KwYBBQUHAQEESTBHMCEGCCsGAQUFBzABhhVodHRwOi8vcjMuby5sZW5jci5vcmcw
IgYIKwYBBQUHMAKGFmh0dHA6Ly9yMy5pLmxlbmNyLm9yZy8wbAYDVR0RBGUwY4Ih
bWFpbC5wYXBlcmxlc3Nzb2x1dGlvbnNsdGQuY29tLm5nghxwYXBlcmxlc3Nzb2x1
dGlvbnNsdGQuY29tLm5ngiB3d3cucGFwZXJsZXNzc29sdXRpb25zbHRkLmNvbS5u
ZzBMBgNVHSAERTBDMAgGBmeBDAECATA3BgsrBgEEAYLfEwEBATAoMCYGCCsGAQUF
BwIBFhpodHRwOi8vY3BzLmxldHNlbmNyeXB0Lm9yZzCCAQYGCisGAQQB1nkCBAIE
gfcEgfQA8gB3AESUZS6w7s6vxEAH2Kj+KMDa5oK+2MsxtT/TM5a1toGoAAABd3IS
1lkAAAQDAEgwRgIhAODIbmogAX5fAQ8pKfAlEbZMFJYqksp3iInqCepNymN3AiEA
zdJOaSOHe2A78x9thbdf8WCndV/rPVLw0tME6hzpW6QAdwD2XJQv0XcwIhRUGAgw
lFaO400TGTO/3wwvIAvMTvFk4wAAAXdyEtZZAAAEAwBIMEYCIQD8FomGRVUtXy5e
XByxhM383UUKt+35L217GUAZ7FUgjwIhANrw1gax+fOWp6qRLpBFDz7S3D9SL5RA
KHWQYOJmM8ciMA0GCSqGSIb3DQEBCwUAA4IBAQCU1EzS7Lt1hAZ4+WtPisM+B8R/
RKJ72PTE+E8dHB+SlNeFoJGpj5Z4FO9bp69fdOcG6D2NxJBDVc1VCR7Gx20Sxngs
wm9CexnRlsRaWuXLTJoxn/QoQ+kEWRIIYVXaa7MmlkljpTGLHkBHpwOT7/rtmgpB
geUmgjia+hlah2jltYP6QwhXriOXuhUU/DltAm8S2yS8ITtIIe/0yCXy3CilY7Um
0qtQW5lDecCqZtYlBGwNtoJbAWJrAnlMavdlM6Z1ziRh33BiPQXKNFfBk2P9kBmA
Yud7vIIOK5SB5KhZ45WWm2Tmu2ZsHPu+iaDR+rQB68BOi1aQY0P6+JbY6V1j
-----END CERTIFICATE-----
subject=CN = www.paperlesssolutionsltd.com.ng

issuer=C = US, O = Let's Encrypt, CN = R3

---
No client certificate CA names sent
Peer signing digest: SHA256
Peer signature type: RSA-PSS
Server Temp Key: X25519, 253 bits
---
SSL handshake has read 2489 bytes and written 410 bytes
Verification error: unable to verify the first certificate
---
New, TLSv1.3, Cipher is TLS_AES_256_GCM_SHA384
Server public key is 4096 bit
Secure Renegotiation IS NOT supported
Compression: NONE
Expansion: NONE
No ALPN negotiated
Early data was not sent
Verify return code: 21 (unable to verify the first certificate)
---

HTTP/1.1 400 Bad Request
connection: close
content-type: text/plain
content-length: 19

Invalid line:


closed

c:\Projects
λ

The intermediate certs are not being sent. Please is there aa config needed for erlang ssl to send the intermediate certs?

I have tried both Elli and Ace servers

The cert.pem file from Let’s Encrypt contains only the end certificate. You should also have a file called fullchain.pem containing the end certificate and any associated intermediates. If you point the :certfile option to the fullchain.pem file, your server should send those intermediates in the handshake.

(There are some more subtleties, but in most cases this should be enough to get going)

I have obtained the full-chain.pem by combining both my cert.pem and the intermediate cert from lets-encrypt.

The result is still the same.