Skip to content

Commit d7fa273

Browse files
committed
Merge branch 'main' into peg/attestation-type-detection
* main: Rm logging Typo Update readme Write to standard output regardless of binary or text response Rm unused import Rm logging Attested get and static file server CLI commands Add attested get Error handling Add simple static file server The option `--log-debug` should only enable debug logging for this crate
2 parents ad91b6a + 4939bd4 commit d7fa273

File tree

7 files changed

+495
-15
lines changed

7 files changed

+495
-15
lines changed

Cargo.lock

Lines changed: 84 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,9 @@ license = "MIT"
99

1010
[dependencies]
1111
tokio = { version = "1.48.0", features = ["full"] }
12-
tokio-rustls = { version = "0.26.4", default-features = false, features = ["ring"] }
12+
tokio-rustls = { version = "0.26.4", default-features = false, features = [
13+
"ring",
14+
] }
1315
sha2 = "0.10.9"
1416
x509-parser = "0.18.0"
1517
thiserror = "2.0.17"
@@ -31,7 +33,9 @@ http = "1.3.1"
3133
serde_json = "1.0.145"
3234
serde = "1.0.228"
3335
base64 = "0.22.1"
34-
reqwest = { version = "0.12.23", default-features = false, features = ["rustls-tls-webpki-roots-no-provider"] }
36+
reqwest = { version = "0.12.23", default-features = false, features = [
37+
"rustls-tls-webpki-roots-no-provider",
38+
] }
3539
tracing = "0.1.41"
3640
tracing-subscriber = { version = "0.3.20", features = ["env-filter", "json"] }
3741
parity-scale-codec = "3.7.5"
@@ -42,10 +46,12 @@ num-bigint = "0.4.6"
4246
webpki = { package = "rustls-webpki", version = "0.103.8" }
4347
time = "0.3.44"
4448
once_cell = "1.21.3"
49+
axum = "0.8.6"
50+
tower-http = { version = "0.6.7", features = ["fs"] }
4551

4652
[dev-dependencies]
4753
rcgen = "0.14.5"
48-
axum = "0.8.6"
54+
tempfile = "3.23.0"
4955

5056
[features]
5157
default = ["azure"]

README.md

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ Header value: an attestation type given as a string as described below.
9393

9494
These are the attestation type names used in the HTTP headers, and the measurements file, and when specifying a local attestation type with the `--client-attestation-type` or `--server-attestation-type` command line options.
9595

96+
- `auto` - detect attestation type (used only when specifying the local attestation type as a command-line argument)
9697
- `none` - No attestation provided
9798
- `dummy` - Forwards the attestation to a remote service (for testing purposes, not yet supported)
9899
- `gcp-tdx` - DCAP TDX on Google Cloud Platform
@@ -138,7 +139,13 @@ Following a successful attestation exchange, the client can make HTTP requests u
138139

139140
As described above, the server will inject measurement data into the request headers before forwarding them to the target service, and the client will inject measurement data into the response headers before forwarding them to the source client.
140141

141-
### CLI differences from `cvm-reverse-proxy`
142+
## Dependencies and feature flags
143+
144+
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`.
145+
146+
This feature is enabled by default. For non-azure deployments you can compile without this requirement by specifying `--no-default-features`. But note that this is will disable both generation and verification of azure attestations.
147+
148+
## CLI differences from `cvm-reverse-proxy`
142149

143150
This aims to have a similar command line interface to `cvm-reverse-proxy` but there are some differences:
144151

src/attested_get.rs

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
use crate::{AttestationGenerator, AttestationVerifier, ProxyClient, ProxyError};
2+
use tokio_rustls::rustls::pki_types::CertificateDer;
3+
4+
/// Start a proxy-client, send a single HTTP GET request to the given path and return the
5+
/// [reqwest::Response]
6+
pub async fn attested_get(
7+
target_addr: String,
8+
url_path: &str,
9+
attestation_verifier: AttestationVerifier,
10+
remote_certificate: Option<CertificateDer<'static>>,
11+
) -> Result<reqwest::Response, ProxyError> {
12+
let proxy_client = ProxyClient::new(
13+
None,
14+
"127.0.0.1:0".to_string(),
15+
target_addr,
16+
AttestationGenerator::with_no_attestation(),
17+
attestation_verifier,
18+
remote_certificate,
19+
)
20+
.await?;
21+
22+
attested_get_with_client(proxy_client, url_path).await
23+
}
24+
25+
/// Given a configured [ProxyClient], make a GET request to the given path and return the
26+
/// [reqwest::Response]
27+
async fn attested_get_with_client(
28+
proxy_client: ProxyClient,
29+
url_path: &str,
30+
) -> Result<reqwest::Response, ProxyError> {
31+
let proxy_client_addr = proxy_client.local_addr().unwrap();
32+
33+
// Accept a single connection in a separate task
34+
tokio::spawn(async move {
35+
if let Err(err) = proxy_client.accept().await {
36+
tracing::warn!("Atttested get - failed to accept connection: {err}");
37+
}
38+
});
39+
40+
// Remove leading '/' if present
41+
let url_path = url_path.strip_prefix("/").unwrap_or(url_path);
42+
43+
// Make a GET request
44+
let request = reqwest::Request::new(
45+
reqwest::Method::GET,
46+
reqwest::Url::parse(&format!("http://{proxy_client_addr}/{url_path}")).unwrap(),
47+
);
48+
let client = reqwest::Client::new();
49+
let response = client.execute(request).await.unwrap();
50+
Ok(response)
51+
}
52+
53+
#[cfg(test)]
54+
mod tests {
55+
use super::*;
56+
use crate::{
57+
attestation::AttestationType,
58+
file_server::static_file_server,
59+
test_helpers::{generate_certificate_chain, generate_tls_config},
60+
ProxyServer,
61+
};
62+
use tempfile::tempdir;
63+
64+
#[tokio::test]
65+
async fn test_attested_get() {
66+
// Create a temporary directory with a file to serve
67+
let dir = tempdir().unwrap();
68+
let file_path = dir.path().join("foo.txt");
69+
tokio::fs::write(file_path, b"bar").await.unwrap();
70+
71+
// Start a static file server
72+
let target_addr = static_file_server(dir.path().to_path_buf()).await.unwrap();
73+
74+
// Create TLS configuration
75+
let (cert_chain, private_key) = generate_certificate_chain("127.0.0.1".parse().unwrap());
76+
let (server_config, client_config) = generate_tls_config(cert_chain.clone(), private_key);
77+
78+
// Setup a proxy server targetting the static file server
79+
let proxy_server = ProxyServer::new_with_tls_config(
80+
cert_chain,
81+
server_config,
82+
"127.0.0.1:0",
83+
target_addr,
84+
AttestationGenerator::new_not_dummy(AttestationType::DcapTdx).unwrap(),
85+
AttestationVerifier::expect_none(),
86+
)
87+
.await
88+
.unwrap();
89+
90+
let proxy_addr = proxy_server.local_addr().unwrap();
91+
92+
// Accept a single connction
93+
tokio::spawn(async move {
94+
proxy_server.accept().await.unwrap();
95+
});
96+
97+
// Setup a proxy client
98+
let proxy_client = ProxyClient::new_with_tls_config(
99+
client_config,
100+
"127.0.0.1:0".to_string(),
101+
proxy_addr.to_string(),
102+
AttestationGenerator::with_no_attestation(),
103+
AttestationVerifier::mock(),
104+
None,
105+
)
106+
.await
107+
.unwrap();
108+
109+
// Make a GET request
110+
let response = attested_get_with_client(proxy_client, "foo.txt")
111+
.await
112+
.unwrap();
113+
114+
// Check the response
115+
let content_type = response
116+
.headers()
117+
.get(reqwest::header::CONTENT_TYPE)
118+
.and_then(|h| h.to_str().ok())
119+
.unwrap()
120+
.to_string();
121+
122+
let body = response.bytes().await.unwrap();
123+
assert_eq!(content_type, "text/plain");
124+
assert_eq!(&body.to_vec(), b"bar");
125+
}
126+
}

0 commit comments

Comments
 (0)