Skip to content

Cannot Establish Streaming Connection to FedRAMP Instance #127

@drewpc

Description

@drewpc

Describe the bug
Beginning around May 7, 2024, the Erlang Server SDK stopped connecting to the streaming FedRAMP server at https://stream.launchdarkly.us. This seems to be due to a cipher mismatch while negotiating the TLS connection. I believe what is happening is that the server now supports both TLS v1.2 and v1.3, but the Erlang Server SDK attempts to negotiate a TLS v1.3 connection with only ciphers for TLS v1.2. Wireshark packet captures show the client initiating a TLS connection and the server immediately closing with an Alert "close notify" packet.

The issue appears to be in the code that filters ciphers based on key exchange algorithms. This code filters out any ciphers for only TLS v1.2:

-spec tls_base_options() -> [ssl:tls_client_option()].
tls_base_options() ->
DefaultCipherSuites = ssl:cipher_suites(default, 'tlsv1.2'),
CipherSuites = ssl:filter_cipher_suites(DefaultCipherSuites, [
{key_exchange, fun
(ecdhe_ecdsa) -> true;
(ecdhe_rsa) -> true;
(_) -> false
end
},
{mac, fun
(sha) -> false;
(_) -> true
end
}
]),
[{verify, verify_peer},
{ciphers, CipherSuites},
{depth, 3},
{customize_hostname_check, [
{match_fun, public_key:pkix_verify_hostname_match_fun(https)}
]}].

If LaunchDarkly's intent is to only support TLS v1.2, then tls_base_options/0 must be updated to restrict the connection to TLS v1.2 only. If the intent is to support both TLS v1.2 and v1.3, then ciphers for both TLS v1.2 and v1.3 must be included in the list. Specific information can be found on the Erlang Hardening SSL page.

To reproduce
The following code samples are implemented in Elixir using iex -S mix.

This fails:

opts = %{
  :base_uri => 'https://sdk.launchdarkly.us',
  :events_uri => 'https://events.launchdarkly.us',
  :http_options => %{
    :tls_options => :ldclient_config.tls_basic_options()
  },
  :stream_uri => 'https://stream.launchdarkly.us'
}

:ldclient.start_instance(ld_sdk_key, :default, opts)

This succeeds by explicitly only allowing TLS v1.2 connections to the server:

opts = %{
  :base_uri => 'https://sdk.launchdarkly.us',
  :events_uri => 'https://events.launchdarkly.us',
  :http_options => %{
    :tls_options => :ldclient_config.tls_basic_options() ++ [
      versions: [:"tlsv1.2"]
    ]
  },
  :stream_uri => 'https://stream.launchdarkly.us'
}

:ldclient.start_instance(ld_sdk_key, :default, opts)

Expected behavior
Prior to May 7, 2024, these connections to the stream server succeeded and all flag data was retrieved.

Logs

18:49:12.887 [notice] Starting streaming update server for :default
18:49:12.891 [notice] Starting streaming connection to URL: ~c"https://stream.launchdarkly.us/all"
18:49:13.238 [warning] Error establishing streaming connection (:temporary): ~c"Could not open connection to host", will retry in 546 ms
18:49:13.786 [notice] Reconnecting streaming connection...

image
SDK version
version 3.2.0

Language version, developer tools
erlang 25.3.2
elixir 1.15.4-otp-25

OS/platform
macOS 14.4.1 and Ubuntu 20.04

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions