added mRATLS to tonic examples
This commit is contained in:
parent
e36329c4be
commit
f2f2d545b3
28
Cargo.toml
28
Cargo.toml
@ -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"]
|
||||||
|
21
README.md
21
README.md
@ -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
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
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) {}
|
||||||
|
}
|
64
examples/mratls_grpcs_client.rs
Normal file
64
examples/mratls_grpcs_client.rs
Normal file
@ -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(())
|
||||||
|
}
|
114
examples/mratls_grpcs_server.rs
Normal file
114
examples/mratls_grpcs_server.rs
Normal file
@ -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
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 }))
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user