Skip to content

Commit adada0a

Browse files
committed
Add TLS provider registry with pluggable cert loaders
Replace the inline TLS config (insecure bool + optional cert/key paths) with a tagged-union YAML schema (`type: insecure | file | directory`) and a factory-based TLS provider registry that decouples config resolution from concrete TLS implementations. Key changes: Config schema (parsed_config.h): - Replace flat ParsedTlsConfig with ParsedTlsInsecure, ParsedTlsFile, and ParsedTlsDirectory variants inside an rfl::TaggedUnion keyed on "type". TLS provider hierarchy (include/o_rly/tls/, src/tls/): - TlsCertProvider: base interface producing a FizzServerContext - TlsCertLoader: convenience base for providers that load certs upfront - InsecureCertProvider: compiled-in self-signed cert for development - FileCertLoader: single cert+key pair from PEM files - DirectoryCertLoader: SNI-based selection from a directory of pairs - FizzContext factory helpers (ALPN building, cert manager assembly) - TlsProviderRegistry: string-keyed factory map for provider plugins - registerBuiltinTlsProviders: registers the three built-in factories Config resolution (config_resolver.cpp): - resolveConfig() now takes a TlsProviderRegistry reference - Extracts type tag from the variant via rfl::Literal Tag, looks up the factory, invokes it, and stores the resulting shared_ptr<TlsCertProvider> in ListenerConfig (replacing the old TlsMode variant) ORelayServer: - Collapsed two constructors (insecure vs cert/key) into one that accepts a pre-built FizzServerContext main.cpp: - Creates registry, registers builtins, passes to config init pipeline - Calls tlsProvider->createContext() to get FizzServerContext before constructing the server
1 parent ee04449 commit adada0a

36 files changed

Lines changed: 1371 additions & 194 deletions

CMakeLists.txt

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,11 @@ add_library(o_rly_core STATIC
7474
src/ORelayServer.cpp
7575
src/admin/AdminServer.cpp
7676
src/admin/BuiltinRoutes.cpp
77+
src/tls/tls_cert_loader.cpp
78+
src/tls/insecure_cert_provider.cpp
79+
src/tls/file_cert_loader.cpp
80+
src/tls/directory_cert_loader.cpp
81+
src/tls/fizz_context_factory.cpp
7782
)
7883

7984
target_include_directories(o_rly_core
@@ -87,6 +92,9 @@ target_link_libraries(o_rly_core PUBLIC
8792
moxygen::moxygen_moq_relay_session
8893
moxygen::moxygen_moq_server
8994
proxygen::proxygenhttpserver
95+
proxygen::proxygen_hq_server
96+
fizz::fizz
97+
Folly::folly_file_util
9098
# Workaround: wangle::wangle_acceptor_acceptor_core uses AsyncFdSocket from
9199
# FizzAcceptorHandshakeHelper but omits this dep from its cmake config.
92100
Folly::folly_io_async_fdsock_async_fd_socket
@@ -118,6 +126,7 @@ add_library(o_rly_config_loader STATIC
118126
src/config/loader.cpp
119127
src/config/config_resolver.cpp
120128
src/config/config_init.cpp
129+
src/tls/tls_provider_registry.cpp
121130
)
122131

123132
target_include_directories(o_rly_config_loader
@@ -138,6 +147,7 @@ target_compile_options(o_rly_config_loader PRIVATE -Wall -Wextra -Wpedantic)
138147

139148
add_executable(o_rly
140149
src/main.cpp
150+
src/tls/builtin_tls_providers.cpp
141151
)
142152

143153
target_link_libraries(o_rly PRIVATE o_rly_core o_rly_config_loader)
@@ -189,6 +199,7 @@ if(ORLY_BUILD_TESTS)
189199
)
190200
target_link_libraries(o_rly_config_resolver_test PRIVATE
191201
o_rly_config_loader
202+
o_rly_core
192203
GTest::gtest_main
193204
GTest::gmock
194205
"$<LINK_LIBRARY:WHOLE_ARCHIVE,gflags_nothreads_static>"
@@ -198,6 +209,27 @@ if(ORLY_BUILD_TESTS)
198209
)
199210
gtest_discover_tests(o_rly_config_resolver_test)
200211

212+
add_executable(o_rly_tls_test
213+
tests/tls/file_cert_loader_test.cpp
214+
tests/tls/directory_cert_loader_test.cpp
215+
tests/tls/fizz_context_factory_test.cpp
216+
tests/tls/insecure_cert_provider_test.cpp
217+
tests/tls/tls_provider_registry_test.cpp
218+
)
219+
target_link_libraries(o_rly_tls_test PRIVATE
220+
o_rly_core
221+
o_rly_config
222+
o_rly_config_loader
223+
Folly::folly_testing_test_util
224+
GTest::gtest_main
225+
GTest::gmock
226+
"$<LINK_LIBRARY:WHOLE_ARCHIVE,gflags_nothreads_static>"
227+
)
228+
set_property(TARGET o_rly_tls_test PROPERTY
229+
LINK_LIBRARY_OVERRIDE "WHOLE_ARCHIVE,gflags_nothreads_static"
230+
)
231+
gtest_discover_tests(o_rly_tls_test)
232+
201233
add_test(
202234
NAME admin_info_endpoint
203235
COMMAND bash ${PROJECT_SOURCE_DIR}/tests/test_admin_info.sh $<TARGET_FILE:o_rly>

config.example.yaml

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,15 @@ listeners:
88
address: "::" # Bind address (default: "::" = all interfaces)
99
port: 9668 # Listen port (1-65535)
1010
tls:
11-
# cert_file: /path/to/cert.pem # Required when insecure: false
12-
# key_file: /path/to/key.pem # Required when insecure: false
13-
insecure: true # Skip TLS for local development
11+
type: insecure # Skip TLS for local development
12+
# --- OR single cert+key pair ---
13+
# type: file
14+
# cert_file: /path/to/cert.pem
15+
# key_file: /path/to/key.pem
16+
# --- OR directory with multiple certs (SNI selection) ---
17+
# type: directory
18+
# cert_dir: /etc/ssl/certs.d/ # contains <name>.crt + <name>.key pairs
19+
# default_cert: example.com # optional: SNI identity for default fallback
1420
endpoint: "/moq-relay" # WebTransport endpoint path
1521
# moqt_versions: [14, 16] # MOQT draft versions (empty = all supported)
1622

deps/moxygen

Submodule moxygen updated 45 files

include/o_rly/ORelayServer.h

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -7,20 +7,9 @@ namespace openmoq::o_rly {
77

88
class ORelayServer : public moxygen::MoQServer {
99
public:
10-
// Used when the insecure flag is false
1110
ORelayServer(
12-
const std::string& cert,
13-
const std::string& key,
11+
std::shared_ptr<const fizz::server::FizzServerContext> fizzContext,
1412
const std::string& endpoint,
15-
const std::string& versions,
16-
size_t maxCachedTracks,
17-
size_t maxCachedGroupsPerTrack
18-
);
19-
20-
// Used when the insecure flag is true
21-
ORelayServer(
22-
const std::string& endpoint,
23-
const std::string& versions,
2413
size_t maxCachedTracks,
2514
size_t maxCachedGroupsPerTrack
2615
);

include/o_rly/config/config.h

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,21 +2,16 @@
22

33
#include <cstddef>
44
#include <cstdint>
5+
#include <memory>
56
#include <string>
6-
#include <variant>
77

88
#include <folly/SocketAddress.h>
99

10-
namespace openmoq::o_rly::config {
11-
12-
struct TlsConfig {
13-
std::string certFile;
14-
std::string keyFile;
15-
};
10+
namespace openmoq::o_rly::tls {
11+
class TlsCertProvider;
12+
} // namespace openmoq::o_rly::tls
1613

17-
struct Insecure {};
18-
19-
using TlsMode = std::variant<Insecure, TlsConfig>;
14+
namespace openmoq::o_rly::config {
2015

2116
struct CacheConfig {
2217
size_t maxCachedTracks; // 0 when cache disabled
@@ -26,7 +21,7 @@ struct CacheConfig {
2621
struct ListenerConfig {
2722
std::string name;
2823
folly::SocketAddress address;
29-
TlsMode tlsMode;
24+
std::shared_ptr<tls::TlsCertProvider> tlsProvider;
3025
std::string endpoint;
3126
std::string moqtVersions; // comma-separated string
3227
};

include/o_rly/config/loader/config_init.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@
77

88
#include "o_rly/config/resolved_config.h"
99

10+
namespace openmoq::o_rly::tls {
11+
class TlsProviderRegistry;
12+
} // namespace openmoq::o_rly::tls
13+
1014
namespace openmoq::o_rly::config {
1115

1216
constexpr std::string_view kDumpConfigSchemaCommand = "dump-config-schema";
@@ -21,7 +25,8 @@ folly::Expected<ResolvedConfig, int> handleConfigSubcommand(
2125
std::string_view subcommand,
2226
std::string_view configPath,
2327
bool strictConfig,
24-
const char* programName
28+
const char* programName,
29+
const tls::TlsProviderRegistry& registry
2530
);
2631

2732
} // namespace openmoq::o_rly::config

include/o_rly/config/loader/config_resolver.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,16 @@
77
#include "o_rly/config/loader/parsed_config.h"
88
#include "o_rly/config/resolved_config.h"
99

10+
namespace openmoq::o_rly::tls {
11+
class TlsProviderRegistry;
12+
} // namespace openmoq::o_rly::tls
13+
1014
namespace openmoq::o_rly::config {
1115

1216
/// Validate and resolve a ParsedConfig into concrete Config types.
1317
/// Returns warnings alongside the config on success.
1418
/// On validation failure, returns a combined error string.
15-
folly::Expected<ResolvedConfig, std::string> resolveConfig(const ParsedConfig& config);
19+
folly::Expected<ResolvedConfig, std::string>
20+
resolveConfig(const ParsedConfig& config, const tls::TlsProviderRegistry& registry);
1621

1722
} // namespace openmoq::o_rly::config

include/o_rly/config/loader/parsed_config.h

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,16 +25,39 @@ struct ParsedUdpConfig {
2525
rfl::Description<"Socket configuration", ParsedSocketConfig> socket;
2626
};
2727

28-
struct ParsedTlsConfig {
29-
rfl::Description<"Path to TLS certificate file", std::optional<std::string>> cert_file;
30-
rfl::Description<"Path to TLS private key file", std::optional<std::string>> key_file;
31-
rfl::Description<"Insecure mode, use default compiled-in cert", bool> insecure;
28+
// rfl::ExtraFields absorbs the "type" discriminator key that the TaggedUnion
29+
// parser has already consumed but that would otherwise be rejected by the
30+
// NoExtraFields processor in strict mode.
31+
struct ParsedTlsInsecure {
32+
using Tag = rfl::Literal<"insecure">;
33+
rfl::ExtraFields<rfl::Generic> extra_;
3234
};
3335

36+
struct ParsedTlsFile {
37+
using Tag = rfl::Literal<"file">;
38+
rfl::Description<"Path to TLS certificate file (PEM)", std::string> cert_file;
39+
rfl::Description<"Path to TLS private key file (PEM)", std::string> key_file;
40+
rfl::ExtraFields<rfl::Generic> extra_;
41+
};
42+
43+
struct ParsedTlsDirectory {
44+
using Tag = rfl::Literal<"directory">;
45+
rfl::Description<"Directory containing cert/key pairs (<name>.crt + <name>.key)", std::string>
46+
cert_dir;
47+
rfl::Description<
48+
"SNI identity of the default certificate (optional, first cert if omitted)",
49+
std::optional<std::string>>
50+
default_cert;
51+
rfl::ExtraFields<rfl::Generic> extra_;
52+
};
53+
54+
using ParsedTlsMode =
55+
rfl::TaggedUnion<"type", ParsedTlsInsecure, ParsedTlsFile, ParsedTlsDirectory>;
56+
3457
struct ParsedListenerConfig {
3558
rfl::Description<"Listener name", std::string> name;
3659
rfl::Description<"UDP/QUIC transport config", ParsedUdpConfig> udp;
37-
rfl::Description<"TLS configuration", ParsedTlsConfig> tls;
60+
ParsedTlsMode tls;
3861
rfl::Description<"WebTransport endpoint path", std::string> endpoint;
3962
rfl::Description<
4063
"MOQT draft versions (empty = all supported)",
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
#pragma once
2+
3+
namespace openmoq::o_rly::tls {
4+
5+
class TlsProviderRegistry;
6+
7+
void registerBuiltinTlsProviders(TlsProviderRegistry& registry);
8+
9+
} // namespace openmoq::o_rly::tls
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
#pragma once
2+
3+
#include <string>
4+
5+
#include "o_rly/tls/tls_cert_loader.h"
6+
7+
namespace openmoq::o_rly::tls {
8+
9+
class DirectoryCertLoader : public TlsCertLoader {
10+
public:
11+
DirectoryCertLoader(std::string certDir, std::string defaultCertIdentity);
12+
folly::Expected<LoadedCerts, std::string> load() const override;
13+
14+
private:
15+
std::string certDir_;
16+
std::string defaultCertIdentity_;
17+
};
18+
19+
} // namespace openmoq::o_rly::tls

0 commit comments

Comments
 (0)