Skip to content

Commit

Permalink
Make tls optional between prover and notary. (#387)
Browse files Browse the repository at this point in the history
* Make tls optional between prover and notary.

* Parameterize test, flatten code.
  • Loading branch information
yuroitaki authored Dec 11, 2023
1 parent 6152883 commit 31708c0
Show file tree
Hide file tree
Showing 6 changed files with 166 additions and 104 deletions.
1 change: 1 addition & 0 deletions notary-server/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ http = "0.2.9"
hyper = { version = "0.14", features = ["client", "http1", "server", "tcp"] }
opentelemetry = { version = "0.19" }
p256 = "0.13"
rstest = "0.18"
rustls = { version = "0.21" }
rustls-pemfile = { version = "1.0.2" }
serde = { version = "1.0.147", features = ["derive"] }
Expand Down
3 changes: 2 additions & 1 deletion notary-server/config/config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ server:
notarization:
max-transcript-size: 16384

tls-signature:
tls:
enabled: true
private-key-pem-path: "./fixture/tls/notary.key"
certificate-pem-path: "./fixture/tls/notary.crt"

Expand Down
10 changes: 6 additions & 4 deletions notary-server/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ pub struct NotaryServerProperties {
pub server: ServerProperties,
/// Setting for notarization
pub notarization: NotarizationProperties,
/// File path of private key and certificate (in PEM format) used for establishing TLS with prover
pub tls_signature: TLSSignatureProperties,
/// Setting for TLS connection between prover and notary
pub tls: TLSProperties,
/// File path of private key (in PEM format) used to sign the notarization
pub notary_signature: NotarySignatureProperties,
/// Setting for logging/tracing
Expand All @@ -23,7 +23,7 @@ pub struct AuthorizationProperties {
/// Switch to turn on or off auth middleware
pub enabled: bool,
/// File path of the whitelist API key csv
pub whitelist_csv_path: Option<String>,
pub whitelist_csv_path: String,
}

#[derive(Clone, Debug, Deserialize)]
Expand All @@ -44,7 +44,9 @@ pub struct ServerProperties {

#[derive(Clone, Debug, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub struct TLSSignatureProperties {
pub struct TLSProperties {
/// Flag to turn on/off TLS between prover and notary (should always be turned on unless TLS is handled by external setup e.g. reverse proxy, cloud)
pub enabled: bool,
pub private_key_pem_path: String,
pub certificate_pem_path: String,
}
Expand Down
2 changes: 1 addition & 1 deletion notary-server/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ mod util;

pub use config::{
AuthorizationProperties, NotarizationProperties, NotaryServerProperties,
NotarySignatureProperties, ServerProperties, TLSSignatureProperties, TracingProperties,
NotarySignatureProperties, ServerProperties, TLSProperties, TracingProperties,
};
pub use domain::{
cli::CliFields,
Expand Down
139 changes: 76 additions & 63 deletions notary-server/src/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ use tower::MakeService;
use tracing::{debug, error, info};

use crate::{
config::{NotaryServerProperties, NotarySignatureProperties, TLSSignatureProperties},
config::{NotaryServerProperties, NotarySignatureProperties},
domain::{
auth::{authorization_whitelist_vec_into_hashmap, AuthorizationWhitelistRecord},
notary::NotaryGlobals,
Expand All @@ -40,69 +40,68 @@ use crate::{
util::parse_csv_file,
};

/// Start a TLS-secured TCP server to accept notarization request for both TCP and WebSocket clients
/// Start a TCP server (with or without TLS) to accept notarization request for both TCP and WebSocket clients
#[tracing::instrument(skip(config))]
pub async fn run_server(config: &NotaryServerProperties) -> Result<(), NotaryServerError> {
// Load the private key and cert needed for TLS connection from fixture folder — can be swapped out when we stop using static self signed cert
let (tls_private_key, tls_certificates) = load_tls_key_and_cert(&config.tls_signature).await?;
// Load the private key for notarized transcript signing from fixture folder — can be swapped out when we use proper ephemeral signing key
// Load the private key for notarized transcript signing
let notary_signing_key = load_notary_signing_key(&config.notary_signature).await?;
// Build TLS acceptor if it is turned on
let tls_acceptor = if !config.tls.enabled {
debug!("Skipping TLS setup as it is turned off.");
None
} else {
let (tls_private_key, tls_certificates) = load_tls_key_and_cert(
&config.tls.private_key_pem_path,
&config.tls.certificate_pem_path,
)
.await?;

let mut server_config = ServerConfig::builder()
.with_safe_defaults()
.with_no_client_auth()
.with_single_cert(tls_certificates, tls_private_key)
.map_err(|err| eyre!("Failed to instantiate notary server tls config: {err}"))?;

// Set the http protocols we support
server_config.alpn_protocols = vec![b"http/1.1".to_vec()];
let tls_config = Arc::new(server_config);
Some(TlsAcceptor::from(tls_config))
};

// Load the authorization whitelist csv if it is turned on
let authorization_whitelist = if !config.authorization.enabled {
debug!("Skipping authorization as it is turned off.");
None
} else {
// Get the path of whitelist csv from config
let whitelist_csv_path = config
.authorization
.whitelist_csv_path
.as_ref()
.ok_or(eyre!(
"Failed to load authorization whitelist as its csv path is absent in config"
))?;
// Load the csv
let whitelist_csv = parse_csv_file::<AuthorizationWhitelistRecord>(whitelist_csv_path)
.map_err(|err| eyre!("Failed to parse authorization whitelist csv: {:?}", err))?;
let whitelist_csv = parse_csv_file::<AuthorizationWhitelistRecord>(
&config.authorization.whitelist_csv_path,
)
.map_err(|err| eyre!("Failed to parse authorization whitelist csv: {:?}", err))?;
// Convert the whitelist record into hashmap for faster lookup
Some(authorization_whitelist_vec_into_hashmap(whitelist_csv))
};

// Build a TCP listener with TLS enabled
let mut server_config = ServerConfig::builder()
.with_safe_defaults()
.with_no_client_auth()
.with_single_cert(tls_certificates, tls_private_key)
.map_err(|err| eyre!("Failed to instantiate notary server tls config: {err}"))?;

// Set the http protocols we support
server_config.alpn_protocols = vec![b"http/1.1".to_vec()];
let tls_config = Arc::new(server_config);

let notary_address = SocketAddr::new(
IpAddr::V4(config.server.host.parse().map_err(|err| {
eyre!("Failed to parse notary host address from server config: {err}")
})?),
config.server.port,
);

let acceptor = TlsAcceptor::from(tls_config);
let listener = TcpListener::bind(notary_address)
.await
.map_err(|err| eyre!("Failed to bind server address to tcp listener: {err}"))?;
let mut listener = AddrIncoming::from_listener(listener)
.map_err(|err| eyre!("Failed to build hyper tcp listener: {err}"))?;

info!(
"Listening for TLS-secured TCP traffic at {}",
notary_address
);
info!("Listening for TCP traffic at {}", notary_address);

let protocol = Arc::new(Http::new());
let notary_globals = NotaryGlobals::new(
notary_signing_key,
config.notarization.clone(),
authorization_whitelist.map(Arc::new),
// Use Arc to prevent cloning the whitelist for every request
authorization_whitelist.map(Arc::new),
);

// Parameters needed for the info endpoint
Expand Down Expand Up @@ -155,34 +154,48 @@ pub async fn run_server(config: &NotaryServerProperties) -> Result<(), NotarySer
};
debug!(?prover_address, "Received a prover's TCP connection");

let acceptor = acceptor.clone();
let tls_acceptor = tls_acceptor.clone();
let protocol = protocol.clone();
let service = MakeService::<_, Request<hyper::Body>>::make_service(&mut app, &stream);

// Spawn a new async task to handle the new connection
tokio::spawn(async move {
match acceptor.accept(stream).await {
Ok(stream) => {
info!(
?prover_address,
"Accepted prover's TLS-secured TCP connection",
);
// Serve different requests using the same hyper protocol and axum router
let _ = protocol
// Can unwrap because it's infallible
.serve_connection(stream, service.await.unwrap())
// use with_upgrades to upgrade connection to websocket for websocket clients
// and to extract tcp connection for tcp clients
.with_upgrades()
.await;
}
Err(err) => {
error!(
?prover_address,
"{}",
NotaryServerError::Connection(err.to_string())
);
// When TLS is enabled
if let Some(acceptor) = tls_acceptor {
match acceptor.accept(stream).await {
Ok(stream) => {
info!(
?prover_address,
"Accepted prover's TLS-secured TCP connection",
);
// Serve different requests using the same hyper protocol and axum router
let _ = protocol
// Can unwrap because it's infallible
.serve_connection(stream, service.await.unwrap())
// use with_upgrades to upgrade connection to websocket for websocket clients
// and to extract tcp connection for tcp clients
.with_upgrades()
.await;
}
Err(err) => {
error!(
?prover_address,
"{}",
NotaryServerError::Connection(err.to_string())
);
}
}
} else {
// When TLS is disabled
info!(?prover_address, "Accepted prover's TCP connection",);
// Serve different requests using the same hyper protocol and axum router
let _ = protocol
// Can unwrap because it's infallible
.serve_connection(stream, service.await.unwrap())
// use with_upgrades to upgrade connection to websocket for websocket clients
// and to extract tcp connection for tcp clients
.with_upgrades()
.await;
}
});
}
Expand All @@ -207,19 +220,20 @@ pub async fn read_pem_file(file_path: &str) -> Result<BufReader<StdFile>> {

/// Load notary tls private key and cert from static files
async fn load_tls_key_and_cert(
config: &TLSSignatureProperties,
private_key_pem_path: &str,
certificate_pem_path: &str,
) -> Result<(PrivateKey, Vec<Certificate>)> {
debug!("Loading notary server's tls private key and certificate");

let mut private_key_file_reader = read_pem_file(&config.private_key_pem_path).await?;
let mut private_key_file_reader = read_pem_file(private_key_pem_path).await?;
let mut private_keys = rustls_pemfile::pkcs8_private_keys(&mut private_key_file_reader)?;
ensure!(
private_keys.len() == 1,
"More than 1 key found in the tls private key pem file"
);
let private_key = PrivateKey(private_keys.remove(0));

let mut certificate_file_reader = read_pem_file(&config.certificate_pem_path).await?;
let mut certificate_file_reader = read_pem_file(certificate_pem_path).await?;
let certificates = rustls_pemfile::certs(&mut certificate_file_reader)?
.into_iter()
.map(Certificate)
Expand All @@ -235,11 +249,10 @@ mod test {

#[tokio::test]
async fn test_load_notary_key_and_cert() {
let config = TLSSignatureProperties {
private_key_pem_path: "./fixture/tls/notary.key".to_string(),
certificate_pem_path: "./fixture/tls/notary.crt".to_string(),
};
let result: Result<(PrivateKey, Vec<Certificate>)> = load_tls_key_and_cert(&config).await;
let private_key_pem_path = "./fixture/tls/notary.key";
let certificate_pem_path = "./fixture/tls/notary.crt";
let result: Result<(PrivateKey, Vec<Certificate>)> =
load_tls_key_and_cert(private_key_pem_path, certificate_pem_path).await;
assert!(result.is_ok(), "Could not load tls private key and cert");
}

Expand Down
Loading

0 comments on commit 31708c0

Please sign in to comment.