Skip to content
Merged
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
7 changes: 5 additions & 2 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,11 @@ jobs:
- name: Check formatting
run: cargo fmt --all -- --check

- name: Check attested-tls with default features
run: cargo check -p attested-tls --locked

- name: Run cargo clippy
run: cargo clippy --workspace --features azure -- -D warnings
run: cargo clippy --workspace --features azure,attested-tls/ws,attested-tls/rpc -- -D warnings

- name: Run cargo test
run: cargo test --workspace --features azure --all-targets -- --test-threads=1
run: cargo test --workspace --features azure,attested-tls/ws,attested-tls/rpc --all-targets -- --test-threads=1
2 changes: 0 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,9 @@ Omit `--features azure` on systems without the TPM dependencies described below.
To install from a local checkout, use `cargo install --path crates/attested-tls-proxy --locked`.
Docker and Compose commands also run from the repository root.

The `attested-tls` library's `ws` and `rpc` features are opt-in. To include their
tests, add `attested-tls/ws,attested-tls/rpc` to the test command's feature list.

## Dependencies and feature flags

The `azure` feature, for Microsoft Azure attestation requires [tpm2](https://tpm2-software.github.io) to be installed. On Debian-based systems this is provided by [`libtss2-dev`](https://packages.debian.org/trixie/libtss2-dev), and on nix `tpm2-tss`. This dependency is currently not packaged for MacOS, meaning currently it is not possible to compile or run with the `azure` feature on MacOS.
Expand Down
4 changes: 1 addition & 3 deletions crates/attested-tls-proxy/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ keywords = ["attested-TLS", "CVM", "TDX"]
attested-tls = { path = "../attested-tls", default-features = false }
tokio = { workspace = true, features = ["full"] }
tokio-rustls = { workspace = true, features = ["aws_lc_rs"] }
x509-parser = { workspace = true, features = ["verify"] }
thiserror.workspace = true
clap.workspace = true
rustls-pemfile.workspace = true
Expand All @@ -30,16 +29,15 @@ serde = "1.0.228"
reqwest = { version = "0.13.4", default-features = false, features = [
"rustls-no-provider",
] }
webpki-roots.workspace = true
tracing.workspace = true
tracing-subscriber.workspace = true
axum = "0.8.8"
tower-http = { version = "0.6.7", features = ["fs"] }
rcgen.workspace = true
pin-project-lite = "0.2.16"
pccs = { git = "https://github.com/flashbots/attested-tls", branch = "main" }

[dev-dependencies]
rcgen.workspace = true
tempfile.workspace = true
tdx-quote = { version = "0.0.5", features = ["mock"] }
attested-tls = { path = "../attested-tls", features = ["test-helpers", "mock"] }
Expand Down
2 changes: 1 addition & 1 deletion crates/attested-tls-proxy/TCP_TUNNEL.md
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ client.serve_until(async {
Dropping a `serve_until` future aborts its connection tasks. For graceful shutdown,
resolve the supplied shutdown future and await completion instead. Embedding
applications own runtime shutdown, including outstanding blocking attestation
work. The shared `attested_tls_proxy::tls` module provides TLS configuration helpers, including self-signed
work. The shared `attested_tls::tls` module provides TLS configuration helpers, including self-signed
verification that retains client credentials.

Both client constructors accept a `startup_check` boolean before `options`.
Expand Down
2 changes: 1 addition & 1 deletion crates/attested-tls-proxy/src/cli/http.rs
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ impl ClientArgs {
let client_attestation_generator =
AttestationGenerator::new_with_detection(client_attestation_type, dev_dummy_dcap)?;

let client_tls_config = attested_tls_proxy::tls::client_config(
let client_tls_config = attested_tls::tls::client_config(
tls_cert_and_chain.as_ref(),
remote_tls_cert,
allow_self_signed,
Expand Down
4 changes: 1 addition & 3 deletions crates/attested-tls-proxy/src/cli/pem.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,7 @@ pub(super) fn load_tls_cert_and_key_server(
return Err(anyhow!("Certificate chain provided but no private key"));
}
tracing::warn!("No TLS ceritifcate provided - generating self-signed");
Ok(attested_tls_proxy::self_signed::generate_self_signed_cert(
ip,
)?)
Ok(attested_tls::self_signed::generate_self_signed_cert(ip)?)
}
}

Expand Down
4 changes: 2 additions & 2 deletions crates/attested-tls-proxy/src/cli/tcp_tunnel.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
use anyhow::{anyhow, ensure};
use attested_tls::self_signed::generate_self_signed_cert;
use attested_tls::tls;
use attested_tls::{
TlsCertAndKey,
attestation::{AttestationGenerator, AttestationVerifier},
};
use attested_tls_proxy::self_signed::generate_self_signed_cert;
use attested_tls_proxy::tcp_tunnel::{TunnelClient, TunnelOptions, TunnelServer};
use attested_tls_proxy::tls;
use clap::Args;
use std::{
net::SocketAddr,
Expand Down
3 changes: 2 additions & 1 deletion crates/attested-tls-proxy/src/http/attested_get.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@ pub async fn attested_get(
remote_certificate: Option<CertificateDer<'static>>,
allow_self_signed: bool,
) -> Result<reqwest::Response, ProxyError> {
let client_config = crate::tls::client_config(None, remote_certificate, allow_self_signed)?;
let client_config =
attested_tls::tls::client_config(None, remote_certificate, allow_self_signed)?;
let proxy_client = ProxyClient::new_with_tls_config(
client_config,
"127.0.0.1:0".to_string(),
Expand Down
2 changes: 1 addition & 1 deletion crates/attested-tls-proxy/src/http/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ pub mod attested_get;
pub mod file_server;
pub mod health_check;
use crate::measurements::MeasurementHeaders;
use crate::tls;
use attested_tls::tls;

pub use attested_tls;
pub use attested_tls::attestation;
Expand Down
2 changes: 0 additions & 2 deletions crates/attested-tls-proxy/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
//! HTTP and TCP proxies over attested TLS.
pub mod http;
pub mod measurements;
pub mod self_signed;
mod target;
pub mod tcp_tunnel;
pub mod tls;
pub use target::InvalidTarget;

// Preserve the original HTTP proxy API at the crate root.
Expand Down
2 changes: 1 addition & 1 deletion crates/attested-tls-proxy/src/tcp_tunnel/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
//! the calling application.
//!
//! Assumes a Rustls crypto provider is already installed.
use crate::tls;
use attested_tls::tls;

use crate::target::{InvalidTarget, normalize_target};

Expand Down
4 changes: 2 additions & 2 deletions crates/attested-tls-proxy/tests/http/attested_get_redirect.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
use std::time::Duration;

use attested_tls::self_signed::generate_self_signed_cert;
use attested_tls_proxy::{
AttestationGenerator, ProxyServer, attestation::AttestationVerifier,
attested_get::attested_get, self_signed::generate_self_signed_cert,
AttestationGenerator, ProxyServer, attestation::AttestationVerifier, attested_get::attested_get,
};
use axum::{Router, routing::get};
use tokio::{net::TcpListener, process::Command, time::timeout};
Expand Down
3 changes: 2 additions & 1 deletion crates/attested-tls-proxy/tests/http/target.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
use std::time::Duration;

use attested_tls::self_signed::generate_self_signed_cert;
use attested_tls_proxy::{
AttestationGenerator, ProxyClient, ProxyError, ProxyServer, attestation::AttestationVerifier,
attested_get::attested_get, self_signed::generate_self_signed_cert,
attested_get::attested_get,
};
use axum::{Router, http::HeaderMap, routing::get};
use tokio::{net::TcpListener, time::timeout};
Expand Down
2 changes: 1 addition & 1 deletion crates/attested-tls-proxy/tests/tcp_tunnel/common.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#![allow(dead_code)]
use attested_tls::attestation::{AttestationGenerator, AttestationVerifier};
use attested_tls_proxy::self_signed::generate_self_signed_cert;
use attested_tls::self_signed::generate_self_signed_cert;
use attested_tls_proxy::tcp_tunnel::{TunnelClient, TunnelError, TunnelOptions, TunnelServer};
use std::{future::Future, net::SocketAddr, time::Duration};
use tokio::{net::TcpListener, sync::oneshot, task::JoinHandle};
Expand Down
4 changes: 2 additions & 2 deletions crates/attested-tls-proxy/tests/tcp_tunnel/tunnel.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use attested_tls_proxy::self_signed::generate_self_signed_cert;
use attested_tls_proxy::tls;
use attested_tls::self_signed::generate_self_signed_cert;
use attested_tls::tls;

use super::common::*;
use attested_tls::{
Expand Down
16 changes: 10 additions & 6 deletions crates/attested-tls/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ keywords = ["attested-TLS", "CVM", "TDX"]
tokio = { workspace = true, features = ["full"] }
tokio-rustls.workspace = true
sha2 = "0.10.9"
x509-parser.workspace = true
x509-parser = { workspace = true, features = ["verify"] }
thiserror.workspace = true
webpki-roots.workspace = true
http.workspace = true
Expand All @@ -34,16 +34,20 @@ hyper-util = { workspace = true, features = ["tokio"], optional = true }
bytes = { workspace = true, optional = true }
http-body-util = { workspace = true, optional = true }

# Used by test helpers
rcgen = { workspace = true, optional = true }
# Used for self-signed certificates and test helpers
rcgen.workspace = true

[dev-dependencies]
rcgen.workspace = true
bytes.workspace = true
http-body-util.workspace = true
hyper = { workspace = true, features = ["client", "server", "http2"] }
hyper-util = { workspace = true, features = ["tokio"] }
tokio-rustls = { workspace = true, features = ["aws_lc_rs"] }
tempfile.workspace = true
attestation = { git = "https://github.com/flashbots/attested-tls", branch = "main", features = ["mock"] }

[features]
default = ["ws", "rpc"]
default = []

# Adds support for Microsoft Azure attestation generation and verification
azure = ["attestation/azure"]
Expand All @@ -65,6 +69,6 @@ rpc = [
]

# Exposes helper functions for testing - do not enable in production as this allows dangerous configuration
test-helpers = ["rcgen", "attestation/mock"]
test-helpers = ["attestation/mock"]

mock = ["attestation/mock"]
9 changes: 9 additions & 0 deletions crates/attested-tls/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,15 @@ It uses session binding through exported key material from the TLS session. This

Attestation may be provided by either the server, or the client, or both.

## Optional features

No features are enabled by default. Enable `ws` for the `websockets` module or
`rpc` for the `attested_rpc` module in your Cargo dependency:

```toml
attested-tls = { path = "../attested-tls", features = ["ws", "rpc"] }
```

## Protocol Specification

A TLS 1.3 handshake is made between server and client. The protocol name `flashbots-ratls/1` is included in ALPN. Future versions of the protocol may add additional protocol names which increment the number given after the slash, but backwards compatibility will be provided through also specifying `flashbots-ratls/1`.
Expand Down
84 changes: 84 additions & 0 deletions crates/attested-tls/examples/self_signed_http2.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
//! Serve HTTP/2 after a TLS handshake and server attestation exchange.
//!
//! Run with `cargo run -p attested-tls --example self_signed_http2`.
//! This example uses mock attestation through the crate's development dependencies.
//! Applications using real attestation must build without `attestation/mock`.

use std::convert::Infallible;

use attested_tls::{
AttestedTlsServer,
attestation::{AttestationGenerator, AttestationVerifier},
tls::self_signed_server_config,
};
use bytes::Bytes;
use http_body_util::Full;
use hyper::{Request, Response, body::Incoming, service::service_fn};
use hyper_util::rt::{TokioExecutor, TokioIo};
use tokio::net::{TcpListener, TcpStream};
use tokio_rustls::rustls;

type Error = Box<dyn std::error::Error + Send + Sync>;

#[tokio::main]
async fn main() -> Result<(), Error> {
rustls::crypto::aws_lc_rs::default_provider()
.install_default()
.map_err(|_| "could not install the TLS crypto provider")?;

let listener = TcpListener::bind("127.0.0.1:8443").await?;
let address = listener.local_addr()?;

// Generate self-signed cert and rustls server config
let (mut config, cert_chain) = self_signed_server_config(address.ip(), false)?;

// Supply the application protocol as http2. AttestedTlsServer maps it to
// `flashbots-ratls/1+h2` and also advertises its bare protocol fallback.
config.alpn_protocols = vec![b"h2".to_vec()];

let server = AttestedTlsServer::new_with_tls_config(
cert_chain,
config,
AttestationGenerator::detect()?,
// This server attests itself and expects no client attestation.
AttestationVerifier::expect_none(),
)?;

println!("Listening on {address}");

loop {
let (socket, peer) = listener.accept().await?;
let server = server.clone();
tokio::spawn(async move {
if let Err(error) = serve_connection(server, socket).await {
eprintln!("Connection from {peer} failed: {error}");
}
});
}
}

// Handle an incoming TCP connection
async fn serve_connection(server: AttestedTlsServer, socket: TcpStream) -> Result<(), Error> {
// This completes both TLS and attestation before returning the stream
let (stream, _client_measurements, _client_attestation_type) =
server.handle_connection(socket).await?;

// Enforce http2
if stream.get_ref().1.alpn_protocol() != Some(b"flashbots-ratls/1+h2".as_slice()) {
return Err("client did not negotiate HTTP/2".into());
}

// Serve http2 connection
hyper::server::conn::http2::Builder::new(TokioExecutor::new())
.serve_connection(TokioIo::new(stream), service_fn(handle_request))
.await?;
Ok(())
}

// Respond with a hello message
async fn handle_request(request: Request<Incoming>) -> Result<Response<Full<Bytes>>, Infallible> {
println!("{} {}", request.method(), request.uri());
Ok(Response::new(Full::new(Bytes::from_static(
b"Hello over attested TLS and HTTP/2!\n",
))))
}
Loading
Loading