Skip to content

Commit

Permalink
Add verify_hostname tls flag (#896)
Browse files Browse the repository at this point in the history
  • Loading branch information
rukai committed Nov 2, 2022
1 parent 0b6f7c3 commit da88aca
Show file tree
Hide file tree
Showing 13 changed files with 24 additions and 31 deletions.
8 changes: 8 additions & 0 deletions docs/src/transforms.md
Expand Up @@ -115,6 +115,8 @@ While `system.peers`/`system.peers_v2` will be rewritten to list the configured
# certificate_path: "tls/localhost.crt"
# # Path to the private key file, typically named with a .key extension.
# private_key_path: "tls/localhost.key"
# # Enable/disable verifying the hostname of the certificate provided by the destination.
# #verify_hostname: true

# Timeout in seconds after which to give up waiting for a response from the destination.
# This field is optional, if not provided, timeout will never occur.
Expand Down Expand Up @@ -156,6 +158,8 @@ No cluster discovery or routing occurs with this transform.
# certificate_path: "tls/localhost.crt"
# # Path to the private key file, typically named with a .key extension.
# private_key_path: "tls/localhost.key"
# # Enable/disable verifying the hostname of the certificate provided by the destination.
# #verify_hostname: true

# Timeout in seconds after which to give up waiting for a response from the destination.
# This field is optional, if not provided, timeout will never occur.
Expand Down Expand Up @@ -427,6 +431,8 @@ This transform is a full featured Redis driver that will connect to a Redis clus
# certificate_path: "tls/redis.crt"
# # Path to the private key file, typically named with a .key extension.
# private_key_path: "tls/redis.key"
# # Enable/disable verifying the hostname of the certificate provided by the destination.
# #verify_hostname: true
```

Unlike other Redis cluster drivers, this transform does support pipelining. It does however turn each command from the pipeline into a group of requests split between the master Redis node that owns them, buffering results as within different Redis nodes as needed. This is done sequentially and there is room to make this transform split requests between master nodes in a more concurrent manner.
Expand Down Expand Up @@ -464,6 +470,8 @@ This transform will take a query, serialise it into a RESP2 compatible format an
# certificate_path: "tls/redis.crt"
# # Path to the private key file, typically named with a .key extension.
# private_key_path: "tls/redis.key"
# # Enable/disable verifying the hostname of the certificate provided by the destination.
# #verify_hostname: true
```

Note: this will just pass the query to the remote node. No cluster discovery or routing occurs with this transform.
Expand Down
Expand Up @@ -22,5 +22,6 @@ chain_config:
certificate_authority_path: "example-configs/docker-images/cassandra-tls-4.0.6/certs/localhost_CA.crt"
certificate_path: "example-configs/docker-images/cassandra-tls-4.0.6/certs/localhost.crt"
private_key_path: "example-configs/docker-images/cassandra-tls-4.0.6/certs/localhost.key"
verify_hostname: true
source_to_chain_mapping:
cassandra_prod: main_chain
Expand Up @@ -15,5 +15,6 @@ chain_config:
certificate_authority_path: "example-configs/docker-images/cassandra-tls-4.0.6/certs/localhost_CA.crt"
certificate_path: "example-configs/docker-images/cassandra-tls-4.0.6/certs/localhost.crt"
private_key_path: "example-configs/docker-images/cassandra-tls-4.0.6/certs/localhost.key"
verify_hostname: true
source_to_chain_mapping:
cassandra_prod: main_chain
1 change: 1 addition & 0 deletions shotover-proxy/example-configs/cassandra-tls/topology.yaml
Expand Up @@ -13,5 +13,6 @@ chain_config:
remote_address: "127.0.0.1:9042"
tls:
certificate_authority_path: "example-configs/docker-images/cassandra-tls-4.0.6/certs/localhost_CA.crt"
verify_hostname: false
source_to_chain_mapping:
cassandra_prod: main_chain
Expand Up @@ -11,5 +11,6 @@ chain_config:
certificate_authority_path: "example-configs/redis-tls/certs/ca.crt"
certificate_path: "example-configs/redis-tls/certs/redis.crt"
private_key_path: "example-configs/redis-tls/certs/redis.key"
verify_hostname: true
source_to_chain_mapping:
redis_prod: redis_chain
Expand Up @@ -9,5 +9,6 @@ chain_config:
first_contact_points: ["127.0.0.1:2220", "127.0.0.1:2221", "127.0.0.1:2222", "127.0.0.1:2223", "127.0.0.1:2224", "127.0.0.1:2225"]
tls:
certificate_authority_path: "example-configs/redis-tls/certs/ca.crt"
verify_hostname: true
source_to_chain_mapping:
redis_prod: redis_chain
1 change: 1 addition & 0 deletions shotover-proxy/example-configs/redis-tls/topology.yaml
Expand Up @@ -18,6 +18,7 @@ chain_config:
certificate_authority_path: "example-configs/redis-tls/certs/ca.crt"
certificate_path: "example-configs/redis-tls/certs/redis.crt"
private_key_path: "example-configs/redis-tls/certs/redis.key"
verify_hostname: false
source_to_chain_mapping:
redis_prod: redis_chain_tls
redis_prod_tls: redis_chain_tls
27 changes: 5 additions & 22 deletions shotover-proxy/src/tls.rs
Expand Up @@ -85,11 +85,14 @@ pub struct TlsConnectorConfig {
pub certificate_path: Option<String>,
/// Path to the private key in PEM format
pub private_key_path: Option<String>,
/// enable/disable verifying the hostname of the destination's certificate.
pub verify_hostname: bool,
}

#[derive(Clone, Debug)]
pub struct TlsConnector {
connector: Arc<SslConnector>,
verify_hostname: bool,
}

impl TlsConnector {
Expand Down Expand Up @@ -120,36 +123,16 @@ impl TlsConnector {

Ok(TlsConnector {
connector: Arc::new(builder.build()),
verify_hostname: tls_config.verify_hostname,
})
}

pub async fn connect_unverified_hostname(
&self,
tcp_stream: TcpStream,
) -> Result<SslStream<TcpStream>> {
let ssl = self
.connector
.configure()
.map_err(openssl_stack_error_to_anyhow)?
.verify_hostname(false)
.into_ssl("localhost")
.map_err(openssl_stack_error_to_anyhow)?;

let mut ssl_stream =
SslStream::new(ssl, tcp_stream).map_err(openssl_stack_error_to_anyhow)?;
Pin::new(&mut ssl_stream).connect().await.map_err(|e| {
openssl_ssl_error_to_anyhow(e)
.context("Failed to establish TLS connection to destination")
})?;

Ok(ssl_stream)
}

pub async fn connect(&self, tcp_stream: TcpStream) -> Result<SslStream<TcpStream>> {
let ssl = self
.connector
.configure()
.map_err(openssl_stack_error_to_anyhow)?
.verify_hostname(self.verify_hostname)
.into_ssl("localhost")
.map_err(openssl_stack_error_to_anyhow)?;

Expand Down
2 changes: 1 addition & 1 deletion shotover-proxy/src/transforms/redis/sink_single.rs
Expand Up @@ -106,7 +106,7 @@ impl Transform for RedisSinkSingle {
.map_err(|e| anyhow::Error::new(e).context("Failed to connect to upstream"))?;

let generic_stream = if let Some(tls) = self.tls.as_mut() {
let tls_stream = tls.connect_unverified_hostname(tcp_stream).await?;
let tls_stream = tls.connect(tcp_stream).await?;
Box::pin(tls_stream) as Pin<Box<dyn AsyncStream + Send + Sync>>
} else {
Box::pin(tcp_stream) as Pin<Box<dyn AsyncStream + Send + Sync>>
Expand Down
Expand Up @@ -169,10 +169,7 @@ impl<C: Codec + 'static, A: Authenticator<T>, T: Token> ConnectionPool<C, A, T>
.map_err(ConnectionError::IO)?;

let mut connection = if let Some(tls) = &self.tls {
let tls_stream = tls
.connect_unverified_hostname(stream)
.await
.map_err(ConnectionError::TLS)?;
let tls_stream = tls.connect(stream).await.map_err(ConnectionError::TLS)?;
let (rx, tx) = tokio::io::split(tls_stream);
spawn_read_write_tasks(&self.codec, rx, tx)
} else {
Expand Down
1 change: 1 addition & 0 deletions shotover-proxy/tests/cassandra_int_tests/cluster/mod.rs
Expand Up @@ -21,6 +21,7 @@ pub async fn run_topology_task(ca_path: Option<&str>, port: Option<u32>) -> Vec<
certificate_authority_path: ca_path.into(),
certificate_path: None,
private_key_path: None,
verify_hostname: true,
})
.unwrap()
});
Expand Down
5 changes: 1 addition & 4 deletions shotover-proxy/tests/helpers/mod.rs
Expand Up @@ -121,10 +121,7 @@ impl ShotoverManager {
.await
.unwrap();
let connector = TlsConnector::new(config).unwrap();
let tls_stream = connector
.connect_unverified_hostname(tcp_stream)
.await
.unwrap();
let tls_stream = connector.connect(tcp_stream).await.unwrap();
ShotoverManager::redis_connection_async_inner(
Box::pin(tls_stream) as Pin<Box<dyn AsyncStream + Send + Sync>>
)
Expand Down
1 change: 1 addition & 0 deletions shotover-proxy/tests/redis_int_tests/mod.rs
Expand Up @@ -81,6 +81,7 @@ async fn source_tls_and_single_tls() {
certificate_authority_path: "example-configs/redis-tls/certs/ca.crt".into(),
certificate_path: Some("example-configs/redis-tls/certs/redis.crt".into()),
private_key_path: Some("example-configs/redis-tls/certs/redis.key".into()),
verify_hostname: true,
};

let mut connection = shotover_manager
Expand Down

0 comments on commit da88aca

Please sign in to comment.