Skip to content
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

scylla: Add support for rustls Fixes #293 #911

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/book.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ jobs:
- name: Install mdbook
run: cargo install mdbook --no-default-features
- name: Build the project
run: cargo build --verbose --examples
run: cargo build --verbose --features ssl --features cloud --examples
- name: Build the book
run: mdbook build docs
- name: Build the book using the script
Expand Down
14 changes: 7 additions & 7 deletions .github/workflows/serverless.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@ name: Serverless
on:
push:
branches:
- main
- 'branch-*'
- main
- "branch-*"
pull_request:
branches:
- main
- 'branch-*'
- main
- "branch-*"

env:
CARGO_TERM_COLOR: always
Expand All @@ -33,9 +33,9 @@ jobs:
- name: Check
run: cargo check --verbose
- name: Run cloud example
run: cargo run --example cloud -- $HOME/.ccm/serverless/config_data.yaml
run: cargo run --features cloud --example cloud -- $HOME/.ccm/serverless/config_data.yaml
- name: Run cloud tests
run: CLOUD_CONFIG_PATH=$HOME/.ccm/serverless/config_data.yaml RUSTFLAGS="--cfg scylla_cloud_tests" cargo test --verbose
run: CLOUD_CONFIG_PATH=$HOME/.ccm/serverless/config_data.yaml RUSTFLAGS="--cfg scylla_cloud_tests" cargo test --features cloud --verbose

- name: Remove serverless cluster
run: ccm remove serverless
run: ccm remove serverless
11 changes: 8 additions & 3 deletions .github/workflows/tls.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,13 @@ jobs:
working-directory: ./scylla
steps:
- uses: actions/checkout@v3
- name: Check
- name: Check OpenSSL
run: cargo check --verbose --features "ssl"
working-directory: ${{env.working-directory}}
- name: Run tls example
run: cargo run --example tls
- name: Check OpenSSL
run: cargo check --verbose --features "rustls"
working-directory: ${{env.working-directory}}
- name: Run OpenSSL example
run: cargo run --features "ssl" --example tls
- name: Run rustls example
run: cargo run --features "rustls" --example rustls
19 changes: 14 additions & 5 deletions docs/source/connecting/tls.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
# TLS

Driver uses the [`openssl`](https://github.com/sfackler/rust-openssl) crate for TLS functionality.\
It was chosen because [`rustls`](https://github.com/ctz/rustls) doesn't support certificates for ip addresses
(see [issue](https://github.com/briansmith/webpki/issues/54)), which is a common use case for Scylla.
Enabling TLS can be done with the [`openssl`](https://github.com/sfackler/rust-openssl) crate or
the [`rustls`](https://github.com/rustls/rustls) crate.

Using the `openssl` crate with the `ssl` feature easily supports most common use cases.

### Enabling OpenSSL

### Enabling feature
`openssl` is not a pure Rust library so you need enable a feature and install the proper package.

To enable the `tls` feature add in `Cargo.toml`:
Expand Down Expand Up @@ -37,7 +38,7 @@ Then install the package with `openssl`:
pacman -S openssl pkg-config
```

### Using TLS
### Using TLS with OpenSSL
To use tls you will have to create an openssl
[`SslContext`](https://docs.rs/openssl/0.10.33/openssl/ssl/struct.SslContext.html)
and pass it to `SessionBuilder`
Expand Down Expand Up @@ -67,3 +68,11 @@ let session: Session = SessionBuilder::new()
```

See the full [example](https://github.com/scylladb/scylla-rust-driver/blob/main/examples/tls.rs) for more details

### Using TLS with rustls

Rustls is a pure Rust crate and does not require installing and C packages. However,
Rustls is a more strict and requires more boilerplate for less secure setups, such as
certifcates with empty common names.

See the full [example](https://github.com/scylladb/scylla-rust-driver/blob/main/examples/rustls.rs) for more details
18 changes: 16 additions & 2 deletions examples/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,19 @@ name = "examples"
publish = false
version = "0.0.0"

[features]
rustls = ["scylla/rustls"]
cloud = ["scylla/cloud"]
ssl = ["scylla/ssl"]

[dev-dependencies]
anyhow = "1.0.33"
futures = "0.3.6"
openssl = "0.10.32"
rustyline = "9"
rustyline-derive = "0.6"
scylla = {path = "../scylla", features = ["ssl", "cloud", "chrono", "time"]}
tokio = {version = "1.1.0", features = ["full"]}
scylla = { path = "../scylla", features = ["chrono", "time"] }
tokio = { version = "1.1.0", features = ["full"] }
tracing = "0.1.25"
tracing-subscriber = { version = "0.3.14", features = ["env-filter"] }
chrono = { version = "0.4", default-features = false }
Expand All @@ -20,6 +25,8 @@ uuid = "1.0"
tower = "0.4"
stats_alloc = "0.1"
clap = { version = "3.2.4", features = ["derive"] }
tokio-rustls = "0.25"
rustls-pemfile = "2"

[[example]]
name = "auth"
Expand All @@ -36,6 +43,12 @@ path = "logging.rs"
[[example]]
name = "tls"
path = "tls.rs"
required-features = ["ssl"]

[[example]]
name = "rustls"
path = "rustls.rs"
required-features = ["rustls"]

[[example]]
name = "cqlsh-rs"
Expand Down Expand Up @@ -108,6 +121,7 @@ path = "query_history.rs"
[[example]]
name = "cloud"
path = "cloud.rs"
required-features = ["cloud"]


[[example]]
Expand Down
163 changes: 163 additions & 0 deletions examples/rustls.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
use anyhow::Result;
use scylla::transport::session::{IntoTypedRows, Session};
use scylla::SessionBuilder;
use std::env;
use std::sync::Arc;

use tokio_rustls::rustls::{
self,
client::danger::{HandshakeSignatureValid, ServerCertVerified, ServerCertVerifier},
pki_types::{CertificateDer, ServerName, UnixTime},
ClientConfig, DigitallySignedStruct, RootCertStore,
};

// How to run scylla instance with TLS:
//
// Edit your scylla.yaml file and add paths to certificates
// ex:
// client_encryption_options:
// enabled: true
// certificate: /etc/scylla/db.crt
// keyfile: /etc/scylla/db.key
//
// If using docker mount your scylla.yaml file and your cert files with option
// --volume $(pwd)/tls.yaml:/etc/scylla/scylla.yaml
//
// If python returns permission error 13 use "Z" flag
// --volume $(pwd)/tls.yaml:/etc/scylla/scylla.yaml:Z
//
// In your Rust program connect to port 9142 if it wasn't changed
// Create new a ClientConfig with your certificate added to it's
// root store.
//
// If your server is using a certifcate that does not have it's IP address
// as a Common Name or Subject Alternate Name you will need to skip
// name verification as part of rustls's configuration.
//
// Build it and add to scylla-rust-driver's SessionBuilder

#[tokio::main]
async fn main() -> Result<()> {
// Create connection
let uri = env::var("SCYLLA_URI").unwrap_or_else(|_| "127.0.0.1:9142".to_string());

println!("Connecting to {} ...", uri);
let mut root_cert_store = RootCertStore::empty();
let rustls_pemfile::Item::X509Certificate(cert) = rustls_pemfile::read_one_from_slice(
&tokio::fs::read("./test/tls/ca.crt")
.await
.expect("Failed to load cert"),
)
.expect("Failed to parse pem")
.expect("No certificates in file")
.0
else {
panic!("not a certificate")
};
root_cert_store
.add(cert)
.expect("Failed to add cert to root cert");

let mut config = ClientConfig::builder()
.with_root_certificates(root_cert_store)
.with_no_client_auth();

config
.dangerous()
.set_certificate_verifier(Arc::new(NoCertificateVerification::default()));

let session: Session = SessionBuilder::new()
.known_node(uri)
.rustls_config(Some(Arc::new(config)))
.build()
.await?;

session.query("CREATE KEYSPACE IF NOT EXISTS ks WITH REPLICATION = {'class' : 'NetworkTopologyStrategy', 'replication_factor' : 1}", &[]).await?;

session
.query(
"CREATE TABLE IF NOT EXISTS ks.t (a int, b int, c text, primary key (a, b))",
&[],
)
.await?;

session
.query("INSERT INTO ks.t (a, b, c) VALUES (?, ?, ?)", (3, 4, "def"))
.await?;

session
.query("INSERT INTO ks.t (a, b, c) VALUES (1, 2, 'abc')", &[])
.await?;

let prepared = session
.prepare("INSERT INTO ks.t (a, b, c) VALUES (?, 7, ?)")
.await?;
session
.execute(&prepared, (42_i32, "I'm prepared!"))
.await?;
session
.execute(&prepared, (43_i32, "I'm prepared 2!"))
.await?;
session
.execute(&prepared, (44_i32, "I'm prepared 3!"))
.await?;

// Rows can be parsed as tuples
if let Some(rows) = session.query("SELECT a, b, c FROM ks.t", &[]).await?.rows {
for row in rows.into_typed::<(i32, i32, String)>() {
let (a, b, c) = row?;
println!("a, b, c: {}, {}, {}", a, b, c);
}
}
println!("Ok.");

Ok(())
}

#[derive(Debug)]
struct NoCertificateVerification {
supported: rustls::crypto::WebPkiSupportedAlgorithms,
}

impl Default for NoCertificateVerification {
fn default() -> Self {
Self {
supported: rustls::crypto::ring::default_provider().signature_verification_algorithms,
}
}
}

impl ServerCertVerifier for NoCertificateVerification {
fn verify_server_cert(
&self,
_end_entity: &CertificateDer<'_>,
_intermediates: &[CertificateDer<'_>],
_server_name: &ServerName<'_>,
_ocsp_response: &[u8],
_now: UnixTime,
) -> Result<ServerCertVerified, rustls::Error> {
return Ok(ServerCertVerified::assertion());
}

fn verify_tls12_signature(
&self,
_message: &[u8],
_cert: &CertificateDer<'_>,
_dss: &DigitallySignedStruct,
) -> Result<HandshakeSignatureValid, rustls::Error> {
return Ok(HandshakeSignatureValid::assertion());
}

fn verify_tls13_signature(
&self,
_message: &[u8],
_cert: &CertificateDer<'_>,
_dss: &DigitallySignedStruct,
) -> Result<HandshakeSignatureValid, rustls::Error> {
return Ok(HandshakeSignatureValid::assertion());
}

fn supported_verify_schemes(&self) -> Vec<rustls::SignatureScheme> {
self.supported.supported_schemes()
}
}
2 changes: 2 additions & 0 deletions scylla/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ rustdoc-args = ["--cfg", "docsrs"]
[features]
default = []
ssl = ["dep:tokio-openssl", "dep:openssl"]
rustls = ["dep:tokio-rustls"]
cloud = ["ssl", "scylla-cql/serde", "dep:serde_yaml", "dep:serde", "dep:url", "dep:base64"]
secret = ["scylla-cql/secret"]
chrono = ["scylla-cql/chrono"]
Expand All @@ -42,6 +43,7 @@ tracing = "0.1.36"
chrono = { version = "0.4.20", default-features = false, features = ["clock"] }
openssl = { version = "0.10.32", optional = true }
tokio-openssl = { version = "0.6.1", optional = true }
tokio-rustls = { version = "0.25", optional = true }
arc-swap = "1.3.0"
dashmap = "5.2"
strum = "0.23"
Expand Down
3 changes: 3 additions & 0 deletions scylla/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -142,3 +142,6 @@ pub use transport::retry_policy;
pub use transport::speculative_execution;

pub use transport::metrics::Metrics;

#[cfg(all(feature = "ssl", feature = "rustls"))]
compile_error!("both rustls and ssl should not be enabled together.");
Loading
Loading