added mRATLS to tonic examples

This commit is contained in:
Valentyn Faychuk 2024-08-24 03:41:56 +02:00
parent e36329c4be
commit f2f2d545b3
Signed by: valy
GPG Key ID: F1AB995E20FEADC5
10 changed files with 366 additions and 2 deletions

@ -19,6 +19,18 @@ ring = "0.17" # hash256
rcgen = "0.13" rcgen = "0.13"
log = "0.4" log = "0.4"
hex = "0.4" hex = "0.4"
tokio-rustls = "0.26"
tower = { version = "0.5", features = ["full"] }
tower-http = { version = "0.5", features = ["full"] }
hyper = "1.4.1"
hyper-util = "0.1.7"
hyper-rustls = { version = "0.27", features = ["http2"] }
prost = "0.13"
[dependencies.tonic]
version = "0.12"
#features = ["rustls-0_23"]
optional = true
[dependencies.actix-web] [dependencies.actix-web]
version = "4.3" version = "4.3"
@ -52,17 +64,29 @@ version = "1"
default-features = false default-features = false
features = ["precommit-hook", "run-cargo-test", "run-cargo-clippy"] features = ["precommit-hook", "run-cargo-test", "run-cargo-clippy"]
[build-dependencies]
tonic-build = "0.12"
[features] [features]
default = [] default = []
occlum = [] occlum = []
reqwest = ["dep:reqwest"] reqwest = ["dep:reqwest"]
actix-web = ["dep:actix-web", "actix-service", "actix-http"] actix-web = ["dep:actix-web", "actix-service", "actix-http"]
tonic = ["dep:tonic"]
[[example]] [[example]]
name = "server" name = "mratls_https_server"
required-features = ["actix-web"] required-features = ["actix-web"]
[[example]] [[example]]
name = "client" name = "mratls_https_client"
required-features = ["reqwest"] required-features = ["reqwest"]
[[example]]
name = "mratls_grpcs_server"
required-features = ["tonic"]
[[example]]
name = "mratls_grpcs_client"
required-features = ["tonic"]

@ -1,6 +1,7 @@
# Occlum SGX Remote Attestation integrated in TLS connection # Occlum SGX Remote Attestation integrated in TLS connection
Steps to test the project: Steps to test the project:
``` ```
occlum-cargo build --example server --features="occlum,actix-web" occlum-cargo build --example server --features="occlum,actix-web"
strip -s target/x86_64-unknown-linux-musl/debug/examples/server strip -s target/x86_64-unknown-linux-musl/debug/examples/server
@ -10,3 +11,23 @@ occlum-cargo build --example client --features="occlum,reqwest"
strip -s target/x86_64-unknown-linux-musl/debug/examples/client strip -s target/x86_64-unknown-linux-musl/debug/examples/client
./build_client.sh ./build_client.sh
``` ```
## Mutual RATLS examples
Examples show how to use the mRATLS (Mutual Remote Attestation TLS) in different situations:
* The first example shows how to create mRATLS HTTPS server and client
* The second example shows how to create mRATLS GRPCs server and client
Both the server and the client must be running inside the enclave.
So during the remote attestation peers, acquire their RA certificates.
And during the TLS handshake, they verify each other's RA certificates.
The config allows to whitelist MRENCLAVE, MRSIGNER, PRODID, SVN of the peer.
## RATLS examples
Example shows how to create RATLS HTTPS server and client.
The server must be running inside the enclave.
The client can be running anywhere.
The server config allows to whitelist the public ec25519 key of the client.
The client config allows to whitelist MRENCLAVE, MRSIGNER, PRODID, SVN of the server.

6
build.rs Normal file

@ -0,0 +1,6 @@
fn main() {
tonic_build::configure()
.build_server(true)
.compile(&["examples/echo.proto"], &["examples"])
.unwrap_or_else(|e| panic!("Failed to compile protos {:?}", e));
}

37
examples/echo.proto Normal file

@ -0,0 +1,37 @@
/*
*
* Copyright 2018 gRPC authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
syntax = "proto3";
package grpc.examples.unaryecho;
// EchoRequest is the request for echo.
message EchoRequest {
string message = 1;
}
// EchoResponse is the response for echo.
message EchoResponse {
string message = 1;
}
// Echo is the echo service.
service Echo {
// UnaryEcho is unary echo.
rpc UnaryEcho(EchoRequest) returns (EchoResponse) {}
}

@ -0,0 +1,64 @@
pub mod pb {
tonic::include_proto!("/grpc.examples.unaryecho");
}
use hyper::Uri;
use hyper_util::{client::legacy::connect::HttpConnector, rt::TokioExecutor};
use occlum_ratls::prelude::*;
use occlum_ratls::RaTlsConfigBuilder;
use occlum_sgx::SGXMeasurement;
use pb::{echo_client::EchoClient, EchoRequest};
use tokio_rustls::rustls::ClientConfig;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let mrsigner_hex = "83D719E77DEACA1470F6BAF62A4D774303C899DB69020F9C70EE1DFC08C7CE9E";
let mut mrsigner = [0u8; 32];
hex::decode_to_slice(mrsigner_hex, &mut mrsigner).expect("mrsigner decoding failed");
let config = RaTlsConfig::new().allow_instance_measurement(
InstanceMeasurement::new().with_mrsigners(vec![SGXMeasurement::new(mrsigner)]),
);
let tls = ClientConfig::from_ratls_config(config)
.map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, format!("{}", e)))?;
let mut http = HttpConnector::new();
http.enforce_http(false);
// We have to do some wrapping here to map the request type from
// `https://example.com` -> `https://[::1]:50051` because `rustls`
// doesn't accept ip's as `ServerName`.
let connector = tower::ServiceBuilder::new()
.layer_fn(move |s| {
let tls = tls.clone();
hyper_rustls::HttpsConnectorBuilder::new()
.with_tls_config(tls)
.https_or_http()
.enable_http2()
.wrap_connector(s)
})
// Since our cert is signed with `example.com` but we actually want to connect
// to a local server we will override the Uri passed from the `HttpsConnector`
// and map it to the correct `Uri` that will connect us directly to the local server.
.map_request(|_| Uri::from_static("https://[::1]:50051"))
.service(http);
let client = hyper_util::client::legacy::Client::builder(TokioExecutor::new()).build(connector);
// Using `with_origin` will let the codegenerated client set the `scheme` and
// `authority` from the provided `Uri`.
let uri = Uri::from_static("https://example.com");
let mut client = EchoClient::with_origin(client, uri);
let request = tonic::Request::new(EchoRequest {
message: "hello".into(),
});
let response = client.unary_echo(request).await?;
println!("RESPONSE={:?}", response);
Ok(())
}

@ -0,0 +1,114 @@
pub mod pb {
tonic::include_proto!("/grpc.examples.unaryecho");
}
use hyper::server::conn::http2::Builder;
use hyper_util::{
rt::{TokioExecutor, TokioIo},
service::TowerToHyperService,
};
use pb::{EchoRequest, EchoResponse};
use std::sync::Arc;
use tokio::net::TcpListener;
use tokio_rustls::{
rustls::{pki_types::CertificateDer, ServerConfig},
TlsAcceptor,
};
use tonic::{body::boxed, service::Routes, Request, Response, Status};
use tower::ServiceBuilder;
use tower::ServiceExt;
use occlum_ratls::prelude::*;
use occlum_ratls::RaTlsConfigBuilder;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let mrsigner_hex = "83D719E77DEACA1470F6BAF62A4D774303C899DB69020F9C70EE1DFC08C7CE9E";
let mut mrsigner = [0u8; 32];
hex::decode_to_slice(mrsigner_hex, &mut mrsigner).expect("mrsigner decoding failed");
let config = RaTlsConfig::new().allow_instance_measurement(
InstanceMeasurement::new().with_mrsigners(vec![SGXMeasurement::new(mrsigner)]),
);
let mut tls = ServerConfig::from_ratls_config(config)
.map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, format!("{}", e)))?;
tls.alpn_protocols = vec![b"h2".to_vec()];
let server = EchoServer::default();
let svc = Routes::new(pb::echo_server::EchoServer::new(server)).prepare();
let http = Builder::new(TokioExecutor::new());
let listener = TcpListener::bind("[::1]:50051").await?;
let tls_acceptor = TlsAcceptor::from(Arc::new(tls));
loop {
let (conn, addr) = match listener.accept().await {
Ok(incoming) => incoming,
Err(e) => {
eprintln!("Error accepting connection: {}", e);
continue;
}
};
let http = http.clone();
let tls_acceptor = tls_acceptor.clone();
let svc = svc.clone();
tokio::spawn(async move {
let mut certificates = Vec::new();
let conn = tls_acceptor
.accept_with(conn, |info| {
if let Some(certs) = info.peer_certificates() {
for cert in certs {
certificates.push(cert.clone());
}
}
})
.await
.unwrap();
let extension_layer =
tower_http::add_extension::AddExtensionLayer::new(Arc::new(ConnInfo {
addr,
certificates,
}));
let svc = ServiceBuilder::new().layer(extension_layer).service(svc);
http.serve_connection(
TokioIo::new(conn),
TowerToHyperService::new(svc.map_request(|req: hyper::Request<_>| req.map(boxed))),
)
.await
.unwrap();
});
}
}
#[derive(Debug)]
struct ConnInfo {
addr: std::net::SocketAddr,
certificates: Vec<CertificateDer<'static>>,
}
type EchoResult<T> = Result<Response<T>, Status>;
#[derive(Default)]
pub struct EchoServer {}
#[tonic::async_trait]
impl pb::echo_server::Echo for EchoServer {
async fn unary_echo(&self, request: Request<EchoRequest>) -> EchoResult<EchoResponse> {
let conn_info = request.extensions().get::<Arc<ConnInfo>>().unwrap();
println!(
"Got a request from: {:?} with certs: {:?}",
conn_info.addr, conn_info.certificates
);
let message = request.into_inner().message;
Ok(Response::new(EchoResponse { message }))
}
}

@ -2,3 +2,4 @@
pub mod actix_web; pub mod actix_web;
#[cfg(feature = "reqwest")] #[cfg(feature = "reqwest")]
pub mod reqwest; pub mod reqwest;
//mod tonic_server;

97
src/http/tonic_server.rs Normal file

@ -0,0 +1,97 @@
use hyper::server::conn::http2::Builder;
use hyper_util::{
rt::{TokioExecutor, TokioIo},
service::TowerToHyperService,
};
use std::sync::Arc;
use tokio::net::TcpListener;
use tokio_rustls::{
rustls::{pki_types::CertificateDer, ServerConfig},
TlsAcceptor,
};
use tonic::transport::server::TcpIncoming;
use tonic::{body::boxed, service::Routes, Request, Response, Status};
use tower::ServiceExt;
use tower_http::ServiceBuilderExt;
use crate::{config::RaTlsConfig, RaTlsConfigBuilder};
fn bind_ratls(config: RaTlsConfig) -> Result<(), std::io::Error> {
let config = ServerConfig::from_ratls_config(config)
.map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, format!("{}", e)))?;
let tls_acceptor = TlsAcceptor::from(Arc::new(config));
// add tls_acceptor to the server
let incoming = TcpIncoming::new("[::1]:50051".parse().unwrap(), true, None)?;
let svc = tower::ServiceBuilder::new()
.add_extension(Arc::new(ConnInfo { addr, certificates }))
.service(svc);
self.bind_rustls_0_23(addr, config)
}
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let listener = TcpListener::bind("[::1]:50051").await?;
let tls_acceptor = TlsAcceptor::from(Arc::new(tls));
loop {
let (conn, addr) = match listener.accept().await {
Ok(incoming) => incoming,
Err(e) => {
eprintln!("Error accepting connection: {}", e);
continue;
}
};
let http = http.clone();
let tls_acceptor = tls_acceptor.clone();
let svc = svc.clone();
tokio::spawn(async move {
let mut certificates = Vec::new();
let conn = tls_acceptor
.accept_with(conn, |info| {
if let Some(certs) = info.peer_certificates() {
for cert in certs {
certificates.push(cert.clone());
}
}
})
.await
.unwrap();
http.serve_connection(
TokioIo::new(conn),
TowerToHyperService::new(svc.map_request(|req: http::Request<_>| req.map(boxed))),
)
.await
.unwrap();
});
}
}
#[derive(Debug)]
struct ConnInfo {
addr: std::net::SocketAddr,
certificates: Vec<CertificateDer<'static>>,
}
type EchoResult<T> = Result<Response<T>, Status>;
#[derive(Default)]
pub struct EchoServer {}
#[tonic::async_trait]
impl pb::echo_server::Echo for EchoServer {
async fn unary_echo(&self, request: Request<EchoRequest>) -> EchoResult<EchoResponse> {
let conn_info = request.extensions().get::<Arc<ConnInfo>>().unwrap();
println!(
"Got a request from: {:?} with certs: {:?}",
conn_info.addr, conn_info.certificates
);
let message = request.into_inner().message;
Ok(Response::new(EchoResponse { message }))
}
}