-
Notifications
You must be signed in to change notification settings - Fork 115
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Rustls support #1190
base: main
Are you sure you want to change the base?
Rustls support #1190
Conversation
ConnectionKeeper is no longer there.
The impl of Default for PoolConfig is only useful for tests. Co-authored-by: Karol Baryła <[email protected]>
It was only passed around back and forth, but unused apart from that. The keepalive interval that is used in keepaliver comes from ConnectionConfig, not PoolConfig.
This updates openssl to its latest available patch release. The version that we required had some security vulnerabilities reported.
As openssl crate is still before its 1.0 (stable) release, we need to prepare for its possible introduces breaking changes. As it's present in our public API, we hide it behind a versioned feature flag. This way, if in the future openssl is released in 0.11 or 1.0 version, we'll simply add the new feature flag and gradually deprecate the old one. As a side note, openssl crate hasn't issued a major release for 7 years now, so it's plausible that it's not going to happen (at least soon). Co-authored-by: Wojciech Przytuła <[email protected]>
26473ca
to
148cabf
Compare
See the following report for details: cargo semver-checks output
|
This allows for future TLS providers to be added, including rustls. Co-authored-by: Wojciech Przytuła <[email protected]>
otherwise the error shows up too late in a non-terminal way whereas the error is terminal
The idea is that `{Connection,Pool}Config` is the same for all connections, whereas `Host{Connection,Pool}Config` is customized for a specific endpoint. This commit leverages type system to better convey this difference.
This step improves types safety and prepares for next steps.
This just changes the style from `if let` to `let else` for better readability. This commit should be viewed without whitespace difference.
…nfig It makes great sense, because the CloudConfig is the subject of the operation.
TlsProvider is the last abstraction about TLS in the driver. The full picture looks like this: ┌─←─ TlsContext (openssl::SslContext / rustls::ClientConfig) │ ├─←─ CloudConfig (powered by either TLS backend) │ │ gets wrapped in │ ↳ TlsProvider (same for all connections) │ │ produces │ ↳ TlsConfig (specific for the particular connection) │ │ produces │ ↳ Tls (wrapper over TCP stream which adds encryption)
As HostPoolConfig is created by cloning required fields of PoolConfig anyway, there's no point in taking PoolConfig by move.
Before, PoolConfig would be re-created for each attempt to recreate the control connection pool. It's redundant, because each time it would be exactly the same, and it requires cloning ConnectionConfig. Now, PoolConfig is only created once and stored in MetadataReader. Before, only ConnectionConfig would be stored.
It seems that when I originally wrote serverless, for some reason I hand-crafted address translation logic specifically for serverless, making it fully separate from the standard AddressTranslator trait. In this commit I successfully implement cloud address translation inside the standard AddressTranslator framework, decluttering PoolRefiller logic from cloud-related code. CloudConfig becomes an AddressTranslator itself, so it needn't be passed separately anymore. This further reduces code complication arising from cloud-related codepaths.
It's no longer necessary, as CloudConfig is now passed inside TlsProvider.
If in the future we choose to store data differently in UntranslatedPeer, we will now have freedom to do so, only preserving the getters. Otherwise, we would have to retain legacy fields in such case.
As address translation should not need ownership of neither datacenter nor rack, let's not require cloning them.
dbaba83
to
a07efdc
Compare
Rustls is now supported as ordinary TLS provider, for far only for the non-cloud case. Co-authored-by: Wojciech Przytuła <[email protected]>
rustls is now supported in cloud, too. The choice between rustls and openssl is done based on enabled features: - if openssl-010 feature is enabled, openssl is used for cloud; - else if rustls-023 feature is enabled, rustls is used for cloud; - else compile error is raised. It is subject to discussion in the PR is such logic based on features is acceptable. Co-authored-by: Andres Medina <[email protected]>
Co-authored-by: Wojciech Przytuła <[email protected]>
Co-authored-by: Wojciech Przytuła <[email protected]>
This is analogous to tls-rustls.rs example.
As address translation no longer needs owned Strings, it's enough to pass references to UntranslatedEndpoint to `open_connection` & friends. Note that, unfortunately, we can't fully elide the clone in `start_opening_connection()`, because the endpoint may be mutated (in the shard-aware case) for the purpose of opening a connection to the shard-aware port. Nonetheless, now the limitation that requires us to clone is in internal code, not in the user-facing API, which is better for us.
As the Serverless Cloud is a highly unstable feature, it must be marked as such in order not to break API after 1.0 in case we need to introduce breaking changes to the feature.
As the tls module got quite large, it makes sense to extract it to the network supermodule.
a07efdc
to
a59b80d
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I noticed that we do not do cargo checks when unstable-cloud
and rustls-x
features are enabled. I think we should do that. Probably should check the following combinations:
openssl-x
,rustls-x
,unstable-cloud
openssl-x
,unstable-cloud
,rustls-x
,unstable-cloud
openssl-x
,rustls-x
openssl-x
(done intls.yml
, but it currently does not work)rustls-x
(makes sense to put it intls.yml
once it's fixed )
Also, previously we would run a tls example in tls.yml
. We should add a step to run tls-rustls example as well (once workflow is fixed).
scylla/src/network/connection.rs
Outdated
#[cfg(feature = "openssl-010")] | ||
impl From<openssl::error::ErrorStack> for TlsError { | ||
fn from(error: openssl::error::ErrorStack) -> Self { | ||
TlsError::OpenSsl010(error) | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not use #[from]
attribute?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I took this code from @nrxus; will fix this.
@@ -369,7 +460,7 @@ pub(crate) struct ConnectionConfig { | |||
pub(crate) tcp_keepalive_interval: Option<Duration>, | |||
pub(crate) timestamp_generator: Option<Arc<dyn TimestampGenerator>>, | |||
#[cfg(feature = "__tls")] | |||
pub(crate) tls_config: Option<TlsConfig>, | |||
pub(crate) tls_provider: Option<TlsProvider>, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, now I understand the difference between HostConnectionConfig
and ConnectionConfig
.
No cover letter, because GitHub ate it.The Big Picture
Before
Before,
openssl
was the only supported TLS backend. (therefore, all TLS abstractions in the driver use theSsl
prefix). Written in C, it's a bit cumbersome to enable in the driver: one needs to installopenssl
library using the system package manager.There were two distinct cases:
SSL abstractions before
After
The main goal of this PR is to add support for another TLS backend:
rustls
, which is written in Rust and hence easier to include in the project as a usual Rust dependency. As a bonus, its authors claims it's faster thanopenssl
.As we're going to support more than one TLS backend, we need to create abstractions that hide the implementation details, including which backend is used.
Ssl*
abstractions specific foropenssl
(e.g.,SslContext
), toTls*
(e.g.,TlsContext
), which are made "generic" over the backend (actually, they are enums with#[cfg(feature)]
-activated backend-specific variants).Now, the two cases (global context and cloud) can both be done with
openssl
orrustls
as a backend, so there are 4 combinations in total. As all combinations should be supported, new abstractions were designed to encapsulate the exact case and backend used. Thanks to that, the code is not only not more complicated after this PR, but it's actually less complicated! Comparison of the following schema with the previous one should convince you.TLS abstractions after
The Details
Features
"__tls"
, which gates the commonTls*
abstractions.Rationale:
As thoseTls*
are enums that are empty (and this is problematic) when no TLS backend is enabled, an explicit compile error is raised in such scenario. Users should not enable this feature on their own; it's a dependency of all TLS-dependant features."openssl"
->"openssl-010"
(Remove types from pre-1.0 crates from the public API, or hide them behind features #771)"rustls-023"
(Remove types from pre-1.0 crates from the public API, or hide them behind features #771)"cloud"
->"unstable-cloud"
Rationale:
The Serverless Cloud is by no means a stable feature and it's likely that we'll have to break its API in the future.rustls
supportGlobal TLS context case
SessionBuilder
now acceptsOption<impl Into<TlsContext>>
, allowing passing bothopenssl::SslContext
andrustls::ClientConfig
.Session
s that differ by the TLS backend use, which may be handy for comparative benchmarks between the backends and choosing the best fit.Serverless Cloud
Which backend is employed is now chosen based on the set of enabled features:
"openssl-010"
feature is enabled,openssl
is used for cloud;"rustls-023"
feature is enabled,rustls
is used for cloud;Miscellaneous
Newly-introduced
Host*Config
As @Lorak-mmk correctly observed, we could leverage the type system to distinguish global pool/connection config from one that is already made specific for a particular endpoint. This way we avoid non-obvious mutation that happened in ConnectionConfig before this PR in the cloud case.
Host{Pool,Connection}Config
is introduced to bring type safety and hide the necessary adjustments done in the cloud case (setting up SNI for a particular node) in theConnectionConfig
->HostConnectionConfig
conversion. In the future, there might be more differences between*Config
andHost*Config
, which will further justify this duplication.Refactored Serverless address translation
Use AddressTranslator instead of hand-crafted logic for serverless.
It seems that when I originally wrote serverless, for some reason I hand-crafted address translation logic specifically for serverless, making it fully separate from the standard
AddressTranslator
trait.In this PR I successfully implement cloud address translation inside the standard
AddressTranslator
framework, declutteringPoolRefiller
logic from cloud-related code.CloudConfig
becomes anAddressTranslator
itself, so it needn't be passed separately anymore. This further reduces code complication arising from cloud-related codepaths.Changes to
UntranslatedEndpoint
UntranslatedEndpoint
now:#[non_exhaustive]
public fields before) - for more flexibility in the future;&str
instead ofString
, in order to elide allocations upon translation.UntranslatedEndpoint
is constructed right before translation fromPeerEndpoint
, so now we avoidString
allocation.Bumped
openssl
versionOlder releases miss important security patches.
Other fixes
Minor ones, you'll see them yourselves in the commits.
Credits
Special thanks to @nrxus, who created a PR that I based mine on.
Thanks to @Lorak-mmk and @nemosupremo for ideas and code inspiration.
Fixes: #293
Closes: #1182
Closes: #911
Pre-review checklist
./docs/source/
.Fixes:
annotations to PR description.