Skip to content

Commit 0422d35

Browse files
feat: add ability to skip TLS peer verification (#399)
This PR adds in a new configuration option allowing users to disable peer verification in TLS handshake. Additionally, it implements two new contract test capabilities to verify this behavior (`tls:verify-peer` and `tls:skip-verify-peer`.) Because the functionality was added to the sse library and the shared asio requester, both the server and client gain this functionality. This PR comes with bindings for both along with the contract test implementations. --------- Co-authored-by: Matthew M. Keeler <[email protected]>
1 parent b9b5d38 commit 0422d35

File tree

33 files changed

+452
-40
lines changed

33 files changed

+452
-40
lines changed

.github/workflows/client.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ jobs:
2929
run_tests: false
3030
- name: 'Launch test service as background task'
3131
run: $TEST_SERVICE_BINARY $TEST_SERVICE_PORT 2>&1 &
32-
- uses: launchdarkly/gh-actions/actions/[email protected].0
32+
- uses: launchdarkly/gh-actions/actions/[email protected].2
3333
with:
3434
# Inform the test harness of test service's port.
3535
test_service_port: ${{ env.TEST_SERVICE_PORT }}

.github/workflows/server.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ jobs:
2828
run_tests: false
2929
- name: 'Launch test service as background task'
3030
run: $TEST_SERVICE_BINARY $TEST_SERVICE_PORT 2>&1 &
31-
- uses: launchdarkly/gh-actions/actions/[email protected].0
31+
- uses: launchdarkly/gh-actions/actions/[email protected].2
3232
with:
3333
# Inform the test harness of test service's port.
3434
test_service_port: ${{ env.TEST_SERVICE_PORT }}

contract-tests/client-contract-tests/src/entity_manager.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,14 @@ std::optional<std::string> EntityManager::create(ConfigParams const& in) {
129129
}
130130
}
131131

132+
if (in.tls) {
133+
auto builder = TlsBuilder();
134+
if (in.tls->skipVerifyPeer) {
135+
builder.SkipVerifyPeer(*in.tls->skipVerifyPeer);
136+
}
137+
config_builder.HttpProperties().Tls(std::move(builder));
138+
}
139+
132140
auto config = config_builder.Build();
133141
if (!config) {
134142
LD_LOG(logger_, LogLevel::kWarn)

contract-tests/client-contract-tests/src/main.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ int main(int argc, char* argv[]) {
2121
launchdarkly::Logger logger{
2222
std::make_unique<ConsoleBackend>("client-contract-tests")};
2323

24-
const std::string default_port = "8123";
24+
std::string const default_port = "8123";
2525
std::string port = default_port;
2626
if (argc == 2) {
2727
port =
@@ -43,6 +43,8 @@ int main(int argc, char* argv[]) {
4343
srv.add_capability("client-independence");
4444
srv.add_capability("inline-context");
4545
srv.add_capability("anonymous-redaction");
46+
srv.add_capability("tls:verify-peer");
47+
srv.add_capability("tls:skip-verify-peer");
4648

4749
net::signal_set signals{ioc, SIGINT, SIGTERM};
4850

contract-tests/data-model/include/data_model/data_model.hpp

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,12 @@ struct adl_serializer<std::optional<T>> {
2929
};
3030
} // namespace nlohmann
3131

32+
struct ConfigTLSParams {
33+
std::optional<bool> skipVerifyPeer;
34+
};
35+
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(ConfigTLSParams,
36+
skipVerifyPeer);
37+
3238
struct ConfigStreamingParams {
3339
std::optional<std::string> baseUri;
3440
std::optional<uint32_t> initialRetryDelayMs;
@@ -98,6 +104,7 @@ struct ConfigParams {
98104
std::optional<ConfigServiceEndpointsParams> serviceEndpoints;
99105
std::optional<ConfigClientSideParams> clientSide;
100106
std::optional<ConfigTags> tags;
107+
std::optional<ConfigTLSParams> tls;
101108
};
102109
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(ConfigParams,
103110
credential,
@@ -108,7 +115,8 @@ NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(ConfigParams,
108115
events,
109116
serviceEndpoints,
110117
clientSide,
111-
tags);
118+
tags,
119+
tls);
112120

113121
struct ContextSingleParams {
114122
std::optional<std::string> kind;

contract-tests/server-contract-tests/src/entity_manager.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,14 @@ std::optional<std::string> EntityManager::create(ConfigParams const& in) {
120120
}
121121
}
122122

123+
if (in.tls) {
124+
auto builder = config::builders::TlsBuilder();
125+
if (in.tls->skipVerifyPeer) {
126+
builder.SkipVerifyPeer(*in.tls->skipVerifyPeer);
127+
}
128+
config_builder.HttpProperties().Tls(std::move(builder));
129+
}
130+
123131
auto config = config_builder.Build();
124132
if (!config) {
125133
LD_LOG(logger_, LogLevel::kWarn)

contract-tests/server-contract-tests/src/main.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ int main(int argc, char* argv[]) {
2121
launchdarkly::Logger logger{
2222
std::make_unique<ConsoleBackend>("server-contract-tests")};
2323

24-
const std::string default_port = "8123";
24+
std::string const default_port = "8123";
2525
std::string port = default_port;
2626
if (argc == 2) {
2727
port =
@@ -42,6 +42,8 @@ int main(int argc, char* argv[]) {
4242
srv.add_capability("server-side-polling");
4343
srv.add_capability("inline-context");
4444
srv.add_capability("anonymous-redaction");
45+
srv.add_capability("tls:verify-peer");
46+
srv.add_capability("tls:skip-verify-peer");
4547

4648
net::signal_set signals{ioc, SIGINT, SIGTERM};
4749

libs/client-sdk/include/launchdarkly/client_side/bindings/c/config/builder.h

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ typedef struct _LDClientConfigBuilder* LDClientConfigBuilder;
2121
typedef struct _LDDataSourceStreamBuilder* LDDataSourceStreamBuilder;
2222
typedef struct _LDDataSourcePollBuilder* LDDataSourcePollBuilder;
2323
typedef struct _LDPersistenceCustomBuilder* LDPersistenceCustomBuilder;
24+
typedef struct _LDClientHttpPropertiesTlsBuilder*
25+
LDClientHttpPropertiesTlsBuilder;
2426

2527
typedef void (*SetFn)(char const* storage_namespace,
2628
char const* key,
@@ -333,7 +335,6 @@ LD_EXPORT(void) LDDataSourceStreamBuilder_Free(LDDataSourceStreamBuilder b);
333335
*
334336
* @return New builder for Polling method.
335337
*/
336-
337338
LD_EXPORT(LDDataSourcePollBuilder)
338339
LDDataSourcePollBuilder_New();
339340

@@ -390,6 +391,51 @@ LDClientConfigBuilder_HttpProperties_Header(LDClientConfigBuilder b,
390391
char const* key,
391392
char const* value);
392393

394+
/**
395+
* Sets the TLS options builder. The builder is consumed; do not free it.
396+
* @param b Client config builder. Must not be NULL.
397+
* @param tls_builder The TLS options builder. Must not be NULL.
398+
*/
399+
LD_EXPORT(void)
400+
LDClientConfigBuilder_HttpProperties_Tls(
401+
LDClientConfigBuilder b,
402+
LDClientHttpPropertiesTlsBuilder tls_builder);
403+
404+
/**
405+
* Creates a new TLS options builder for the HttpProperties builder.
406+
*
407+
* If not passed into the HttpProperties
408+
* builder, must be manually freed with LDClientHttpPropertiesTlsBuilder_Free.
409+
*
410+
* @return New builder for TLS options.
411+
*/
412+
LD_EXPORT(LDClientHttpPropertiesTlsBuilder)
413+
LDClientHttpPropertiesTlsBuilder_New(void);
414+
415+
/**
416+
* Frees a TLS options builder. Do not call if the builder was consumed by
417+
* the HttpProperties builder.
418+
*
419+
* @param b Builder to free.
420+
*/
421+
LD_EXPORT(void)
422+
LDClientHttpPropertiesTlsBuilder_Free(LDClientHttpPropertiesTlsBuilder b);
423+
424+
/**
425+
* Configures TLS peer certificate verification. Peer verification
426+
* is enabled by default.
427+
*
428+
* Disabling peer verification is not recommended unless a specific
429+
* use-case calls for it.
430+
*
431+
* @param b Client config builder. Must not be NULL.
432+
* @param skip_verify_peer True to skip verification.
433+
*/
434+
LD_EXPORT(void)
435+
LDClientHttpPropertiesTlsBuilder_SkipVerifyPeer(
436+
LDClientHttpPropertiesTlsBuilder b,
437+
bool skip_verify_peer);
438+
393439
/**
394440
* Disables the default SDK logging.
395441
* @param b Client config builder. Must not be NULL.

libs/client-sdk/src/bindings/c/builder.cpp

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,11 @@ using namespace launchdarkly::client_side;
4141
#define FROM_CUSTOM_PERSISTENCE_BUILDER(ptr) \
4242
(reinterpret_cast<LDPersistenceCustomBuilder>(ptr))
4343

44+
#define TO_TLS_BUILDER(ptr) (reinterpret_cast<TlsBuilder*>(ptr))
45+
46+
#define FROM_TLS_BUILDER(ptr) \
47+
(reinterpret_cast<LDClientHttpPropertiesTlsBuilder>(ptr))
48+
4449
class PersistenceImplementationWrapper : public IPersistence {
4550
public:
4651
explicit PersistenceImplementationWrapper(LDPersistence impl)
@@ -306,6 +311,37 @@ LDClientConfigBuilder_HttpProperties_Header(LDClientConfigBuilder b,
306311
TO_BUILDER(b)->HttpProperties().Header(key, value);
307312
}
308313

314+
LD_EXPORT(void)
315+
LDClientConfigBuilder_HttpProperties_Tls(
316+
LDClientConfigBuilder b,
317+
LDClientHttpPropertiesTlsBuilder tls_builder) {
318+
LD_ASSERT_NOT_NULL(b);
319+
LD_ASSERT_NOT_NULL(tls_builder);
320+
321+
TO_BUILDER(b)->HttpProperties().Tls(*TO_TLS_BUILDER(tls_builder));
322+
323+
LDClientHttpPropertiesTlsBuilder_Free(tls_builder);
324+
}
325+
326+
LD_EXPORT(void)
327+
LDClientHttpPropertiesTlsBuilder_SkipVerifyPeer(
328+
LDClientHttpPropertiesTlsBuilder b,
329+
bool skip_verify_peer) {
330+
LD_ASSERT_NOT_NULL(b);
331+
332+
TO_TLS_BUILDER(b)->SkipVerifyPeer(skip_verify_peer);
333+
}
334+
335+
LD_EXPORT(LDClientHttpPropertiesTlsBuilder)
336+
LDClientHttpPropertiesTlsBuilder_New(void) {
337+
return FROM_TLS_BUILDER(new TlsBuilder());
338+
}
339+
340+
LD_EXPORT(void)
341+
LDClientHttpPropertiesTlsBuilder_Free(LDClientHttpPropertiesTlsBuilder b) {
342+
delete TO_TLS_BUILDER(b);
343+
}
344+
309345
LD_EXPORT(void)
310346
LDClientConfigBuilder_Logging_Disable(LDClientConfigBuilder b) {
311347
LD_ASSERT_NOT_NULL(b);

libs/client-sdk/src/data_sources/polling_data_source.cpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ PollingDataSource::PollingDataSource(
7474
status_manager_(status_manager),
7575
data_source_handler_(
7676
DataSourceEventHandler(context, handler, logger, status_manager_)),
77-
requester_(ioc),
77+
requester_(ioc, http_properties.Tls().PeerVerifyMode()),
7878
timer_(ioc),
7979
polling_interval_(
8080
std::get<
@@ -88,6 +88,10 @@ PollingDataSource::PollingDataSource(
8888
auto const& polling_config = std::get<
8989
config::shared::built::PollingConfig<config::shared::ClientSDK>>(
9090
data_source_config.method);
91+
if (http_properties.Tls().PeerVerifyMode() ==
92+
config::shared::built::TlsOptions::VerifyMode::kVerifyNone) {
93+
LD_LOG(logger_, LogLevel::kDebug) << "TLS peer verification disabled";
94+
}
9195
if (polling_interval_ < polling_config.min_polling_interval) {
9296
LD_LOG(logger_, LogLevel::kWarn)
9397
<< "Polling interval too frequent, defaulting to "

libs/client-sdk/src/data_sources/streaming_data_source.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,11 @@ void StreamingDataSource::Start() {
127127
client_builder.header(header.first, header.second);
128128
}
129129

130+
if (http_config_.Tls().PeerVerifyMode() ==
131+
config::shared::built::TlsOptions::VerifyMode::kVerifyNone) {
132+
client_builder.skip_verify_peer(true);
133+
}
134+
130135
auto weak_self = weak_from_this();
131136

132137
client_builder.receiver([weak_self](launchdarkly::sse::Event const& event) {

libs/client-sdk/tests/client_config_test.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,14 +65,29 @@ TEST(ClientConfigBindings, AllConfigs) {
6565
LDDataSourceStreamBuilder_InitialReconnectDelayMs(stream_builder, 500);
6666
LDClientConfigBuilder_DataSource_MethodStream(builder, stream_builder);
6767

68+
LDDataSourceStreamBuilder stream_builder2 = LDDataSourceStreamBuilder_New();
69+
LDDataSourceStreamBuilder_Free(stream_builder2);
70+
6871
LDDataSourcePollBuilder poll_builder = LDDataSourcePollBuilder_New();
6972
LDDataSourcePollBuilder_IntervalS(poll_builder, 10);
7073
LDClientConfigBuilder_DataSource_MethodPoll(builder, poll_builder);
7174

75+
LDDataSourcePollBuilder poll_builder2 = LDDataSourcePollBuilder_New();
76+
LDDataSourcePollBuilder_Free(poll_builder2);
77+
7278
LDClientConfigBuilder_HttpProperties_Header(builder, "foo", "bar");
7379
LDClientConfigBuilder_HttpProperties_WrapperName(builder, "wrapper");
7480
LDClientConfigBuilder_HttpProperties_WrapperVersion(builder, "v1.2.3");
7581

82+
LDClientHttpPropertiesTlsBuilder tls_builder =
83+
LDClientHttpPropertiesTlsBuilder_New();
84+
LDClientHttpPropertiesTlsBuilder_SkipVerifyPeer(tls_builder, false);
85+
LDClientConfigBuilder_HttpProperties_Tls(builder, tls_builder);
86+
87+
LDClientHttpPropertiesTlsBuilder tls_builder2 =
88+
LDClientHttpPropertiesTlsBuilder_New();
89+
LDClientHttpPropertiesTlsBuilder_Free(tls_builder2);
90+
7691
LDClientConfigBuilder_Logging_Disable(builder);
7792

7893
LDLoggingBasicBuilder log_builder = LDLoggingBasicBuilder_New();

libs/common/include/launchdarkly/config/client.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ using HttpPropertiesBuilder =
2222
using DataSourceBuilder = config::shared::builders::DataSourceBuilder<SDK>;
2323
using LoggingBuilder = config::shared::builders::LoggingBuilder;
2424
using PersistenceBuilder = config::shared::builders::PersistenceBuilder<SDK>;
25+
using TlsBuilder = config::shared::builders::TlsBuilder<SDK>;
2526

2627
using Config = config::Config<SDK>;
2728

libs/common/include/launchdarkly/config/shared/builders/http_properties_builder.hpp

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,45 @@
1010

1111
namespace launchdarkly::config::shared::builders {
1212

13+
/**
14+
* Class used for building TLS options used within HttpProperties.
15+
* @tparam SDK The SDK type to build options for. This affects the default
16+
* values of the built options.
17+
*/
18+
template <typename SDK>
19+
class TlsBuilder {
20+
public:
21+
/**
22+
* Construct a new TlsBuilder. The builder will use the default
23+
* properties based on the SDK type. Setting a property will override
24+
* the default value.
25+
*/
26+
TlsBuilder();
27+
28+
/**
29+
* Create a TLS builder from an initial set of options.
30+
* This can be useful when extending a set of options for a request.
31+
*
32+
* @param tls The TLS options to start with.
33+
*/
34+
TlsBuilder(built::TlsOptions const& tls);
35+
36+
/**
37+
* Whether to skip verifying the remote peer's certificates.
38+
* @param skip_verify_peer True to skip verification, false to verify.
39+
* @return A reference to this builder.
40+
*/
41+
TlsBuilder& SkipVerifyPeer(bool skip_verify_peer);
42+
43+
/**
44+
* Builds the TLS options.
45+
* @return The built options.
46+
*/
47+
[[nodiscard]] built::TlsOptions Build() const;
48+
49+
private:
50+
enum built::TlsOptions::VerifyMode verify_mode_;
51+
};
1352
/**
1453
* Class used for building a set of HttpProperties.
1554
* @tparam SDK The SDK type to build properties for. This affects the default
@@ -116,6 +155,13 @@ class HttpPropertiesBuilder {
116155
HttpPropertiesBuilder& Header(std::string key,
117156
std::optional<std::string> value);
118157

158+
/**
159+
* Sets the builder for TLS properties.
160+
* @param builder The TLS property builder.
161+
* @return A reference to this builder.
162+
*/
163+
HttpPropertiesBuilder& Tls(TlsBuilder<SDK> builder);
164+
119165
/**
120166
* Build a set of HttpProperties.
121167
* @return The built properties.
@@ -130,6 +176,7 @@ class HttpPropertiesBuilder {
130176
std::string wrapper_name_;
131177
std::string wrapper_version_;
132178
std::map<std::string, std::string> base_headers_;
179+
TlsBuilder<SDK> tls_;
133180
};
134181

135182
} // namespace launchdarkly::config::shared::builders

0 commit comments

Comments
 (0)