From e2f36a7960f9c5b1b30b0c2b9f98b0102c395921 Mon Sep 17 00:00:00 2001 From: Tyler Fanelli Date: Tue, 17 Dec 2024 00:55:08 -0500 Subject: [PATCH] aproxy: Use static registrar for backend protocol When other attestation protocols are implemented for the backend attestation server communication, they can register themselves with the BACKEND registrar as well. Signed-off-by: Tyler Fanelli --- Cargo.lock | 7 ++++ aproxy/Cargo.toml | 1 + aproxy/src/attest.rs | 31 +++++++++++++----- aproxy/src/backend/kbs.rs | 23 ++++++-------- aproxy/src/backend/mod.rs | 67 ++++++++++++++++++--------------------- aproxy/src/main.rs | 29 ++++++++++++++--- 6 files changed, 97 insertions(+), 61 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 920df82b2..7c6041ff4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -114,6 +114,7 @@ dependencies = [ "anyhow", "clap", "kbs-types", + "lazy_static", "libaproxy", "reqwest", "serde", @@ -1197,6 +1198,12 @@ dependencies = [ "thiserror 2.0.7", ] +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + [[package]] name = "libaproxy" version = "0.1.0" diff --git a/aproxy/Cargo.toml b/aproxy/Cargo.toml index 55197e8db..75cc88829 100644 --- a/aproxy/Cargo.toml +++ b/aproxy/Cargo.toml @@ -10,6 +10,7 @@ reqwest = { version = "0.12.9", features = ["blocking", "cookies", "json"] } anyhow = "1.0.93" clap = { version = "4.5", features = ["derive"] } kbs-types.workspace = true +lazy_static = "1.5.0" libaproxy.workspace = true serde.workspace = true serde_json.workspace = true diff --git a/aproxy/src/attest.rs b/aproxy/src/attest.rs index a682b76e1..bfdf4ebdd 100644 --- a/aproxy/src/attest.rs +++ b/aproxy/src/attest.rs @@ -5,19 +5,26 @@ // Author: Stefano Garzarella // Author: Tyler Fanelli -use crate::backend; +use crate::backend::BACKEND; use anyhow::Context; use libaproxy::*; +use reqwest::{blocking::Client, cookie::Jar}; use serde::Serialize; use std::{ io::{Read, Write}, os::unix::net::UnixStream, + sync::Arc, }; /// Attest an SVSM client session. -pub fn attest(stream: &mut UnixStream, http: &mut backend::HttpClient) -> anyhow::Result<()> { - negotiation(stream, http)?; - attestation(stream, http)?; +pub fn attest(stream: &mut UnixStream) -> anyhow::Result<()> { + let http = Client::builder() + .cookie_provider(Arc::new(Jar::default())) + .build() + .context("unable to build HTTP client to interact with attestation server")?; + + negotiation(stream, &http)?; + attestation(stream, &http)?; Ok(()) } @@ -27,7 +34,7 @@ pub fn attest(stream: &mut UnixStream, http: &mut backend::HttpClient) -> anyhow /// server and gather all data required (i.e. a nonce) that should be hashed into the attestation /// evidence. The proxy will also reply with the type of hash algorithm to use for the negotiation /// parameters. -fn negotiation(stream: &mut UnixStream, http: &mut backend::HttpClient) -> anyhow::Result<()> { +fn negotiation(stream: &mut UnixStream, http: &Client) -> anyhow::Result<()> { // Read the negotiation parameters from SVSM. let request: NegotiationRequest = { let payload = proxy_read(stream)?; @@ -37,7 +44,11 @@ fn negotiation(stream: &mut UnixStream, http: &mut backend::HttpClient) -> anyho }; // Gather negotiation parameters from the attestation server. - let response: NegotiationResponse = http.negotiation(request)?; + let response = { + let backend = BACKEND.lock().unwrap(); + + backend.clone().unwrap().negotiation(http, request) // Safe to unwrap. + }?; // Write the response from the attestation server to SVSM. proxy_write(stream, response)?; @@ -48,7 +59,7 @@ fn negotiation(stream: &mut UnixStream, http: &mut backend::HttpClient) -> anyho /// Attestation phase of SVSM attestation. SVSM will send an attestation request containing the TEE /// evidence. Proxy will respond with an attestation response containing the status /// (success/failure) and an optional secret upon successful attestation. -fn attestation(stream: &mut UnixStream, http: &backend::HttpClient) -> anyhow::Result<()> { +fn attestation(stream: &mut UnixStream, http: &Client) -> anyhow::Result<()> { let request: AttestationRequest = { let payload = proxy_read(stream)?; serde_json::from_slice(&payload) @@ -56,7 +67,11 @@ fn attestation(stream: &mut UnixStream, http: &backend::HttpClient) -> anyhow::R }; // Attest the TEE evidence with the server. - let response: AttestationResponse = http.attestation(request)?; + let response = { + let backend = BACKEND.lock().unwrap(); + + backend.clone().unwrap().attestation(http, request) // Safe to unwrap. + }?; // Write the response from the attestation server to SVSM. proxy_write(stream, response)?; diff --git a/aproxy/src/backend/kbs.rs b/aproxy/src/backend/kbs.rs index d4d3712bc..4fb651f42 100644 --- a/aproxy/src/backend/kbs.rs +++ b/aproxy/src/backend/kbs.rs @@ -22,8 +22,8 @@ impl AttestationProtocol for KbsProtocol { /// Make this request to /auth, gather the nonce, and return this in the negotiation /// parameter for SVSM to hash these components in the attestation evidence. fn negotiation( - &mut self, - http: &mut HttpClient, + cli: &Client, + url: &str, request: NegotiationRequest, ) -> anyhow::Result { let req = Request { @@ -33,9 +33,8 @@ impl AttestationProtocol for KbsProtocol { }; // Fetch challenge containing a nonce from the KBS /auth endpoint. - let http_resp = http - .cli - .post(format!("{}/kbs/v0/auth", http.url)) + let http_resp = cli + .post(format!("{}/kbs/v0/auth", url)) .json(&req) .send() .context("unable to POST to KBS /auth endpoint")?; @@ -75,8 +74,8 @@ impl AttestationProtocol for KbsProtocol { /// a secret (identified as "svsm_secret"). If able to successfully fetch the secret, return a /// successful AttestationResponse with the secret included. fn attestation( - &self, - http: &HttpClient, + cli: &Client, + url: &str, request: AttestationRequest, ) -> anyhow::Result { // Create a KBS attestation object from the TEE evidence and key. @@ -97,9 +96,8 @@ impl AttestationProtocol for KbsProtocol { }; // Attest TEE evidence at KBS /attest endpoint. - let http_resp = http - .cli - .post(format!("{}/kbs/v0/attest", http.url)) + let http_resp = cli + .post(format!("{}/kbs/v0/attest", url)) .json(&attestation) .send() .context("unable to POST to KBS /attest endpoint")?; @@ -118,9 +116,8 @@ impl AttestationProtocol for KbsProtocol { // Successful attestation. Fetch the secret (which should be stored as "svsm_secret" within // the KBS's RVPS. - let http_resp = http - .cli - .post(format!("{}/kbs/v0/svsm_secret", http.url)) + let http_resp = cli + .post(format!("{}/kbs/v0/svsm_secret", url)) .send() .context("unable to POST to KBS /attest endpoint")?; diff --git a/aproxy/src/backend/mod.rs b/aproxy/src/backend/mod.rs index a8674d570..fce4f6bc0 100644 --- a/aproxy/src/backend/mod.rs +++ b/aproxy/src/backend/mod.rs @@ -5,53 +5,48 @@ // Author: Stefano Garzarella // Author: Tyler Fanelli -mod kbs; +pub mod kbs; -use anyhow::{anyhow, Context}; -use kbs::KbsProtocol; +use anyhow::anyhow; +use lazy_static::lazy_static; use libaproxy::*; -use reqwest::{blocking::Client, cookie::Jar}; -use std::{str::FromStr, sync::Arc}; +use reqwest::blocking::Client; +use std::{str::FromStr, sync::Mutex}; + +lazy_static! { + pub static ref BACKEND: Mutex> = Mutex::new(None); +} /// HTTP client and protocol identifier. #[derive(Clone, Debug)] -pub struct HttpClient { - pub cli: Client, +pub struct ProtocolDispatcher { pub url: String, - protocol: Protocol, + pub negotiation: fn(&Client, &str, NegotiationRequest) -> anyhow::Result, + pub attestation: fn(&Client, &str, AttestationRequest) -> anyhow::Result, } -impl HttpClient { - pub fn new(url: String, protocol: Protocol) -> anyhow::Result { - let cli = Client::builder() - .cookie_provider(Arc::new(Jar::default())) - .build() - .context("unable to build HTTP client to interact with attestation server")?; - - Ok(Self { cli, url, protocol }) - } - - pub fn negotiation(&mut self, req: NegotiationRequest) -> anyhow::Result { - // Depending on the underlying protocol of the attestation server, gather negotiation - // parameters accordingly. - match self.protocol { - Protocol::Kbs(mut kbs) => kbs.negotiation(self, req), - } +impl ProtocolDispatcher { + pub fn negotiation( + &self, + cli: &Client, + n: NegotiationRequest, + ) -> anyhow::Result { + (self.negotiation)(cli, &self.url, n) } - pub fn attestation(&self, req: AttestationRequest) -> anyhow::Result { - // Depending on the underlying protocol of the attestation server, attest TEE evidence - // accoridngly. - match self.protocol { - Protocol::Kbs(kbs) => kbs.attestation(self, req), - } + pub fn attestation( + &self, + cli: &Client, + a: AttestationRequest, + ) -> anyhow::Result { + (self.attestation)(cli, &self.url, a) } } /// Attestation Protocol identifier. #[derive(Clone, Copy, Debug)] pub enum Protocol { - Kbs(KbsProtocol), + Kbs, } impl FromStr for Protocol { @@ -59,7 +54,7 @@ impl FromStr for Protocol { fn from_str(s: &str) -> Result { match &s.to_lowercase()[..] { - "kbs" => Ok(Self::Kbs(KbsProtocol)), + "kbs" => Ok(Self::Kbs), _ => Err(anyhow!("invalid backend attestation protocol selected")), } } @@ -69,13 +64,13 @@ impl FromStr for Protocol { /// protocols. pub trait AttestationProtocol { fn negotiation( - &mut self, - client: &mut HttpClient, + client: &Client, + url: &str, req: NegotiationRequest, ) -> anyhow::Result; fn attestation( - &self, - client: &HttpClient, + client: &Client, + url: &str, req: AttestationRequest, ) -> anyhow::Result; } diff --git a/aproxy/src/main.rs b/aproxy/src/main.rs index 52e9f7c07..d5cc63331 100644 --- a/aproxy/src/main.rs +++ b/aproxy/src/main.rs @@ -8,9 +8,10 @@ mod attest; mod backend; +use crate::backend::{kbs::KbsProtocol, *}; use anyhow::Context; use clap::Parser; -use std::{fs, os::unix::net::UnixListener}; +use std::{fs, os::unix::net::UnixListener, thread}; #[derive(Parser, Debug)] #[clap(version, about, long_about = None)] @@ -21,7 +22,7 @@ struct Args { /// Backend attestation protocol that the server implements. #[clap(long = "protocol")] - backend: backend::Protocol, + backend: Protocol, /// UNIX domain socket path to the SVSM serial port #[clap(long)] @@ -39,13 +40,33 @@ fn main() -> anyhow::Result<()> { let _ = fs::remove_file(args.unix.clone()); } + // Initialize UNIX listener for attestation requests from SVSM. let listener = UnixListener::bind(args.unix).context("unable to bind to UNIX socket")?; + // Initialize HTTP socket for attestation server (with specific protocol). + let (negotiation, attestation) = match args.backend { + Protocol::Kbs => (KbsProtocol::negotiation, KbsProtocol::attestation), + }; + + let http = ProtocolDispatcher { + url: args.url, + attestation, + negotiation, + }; + + { + let mut backend = BACKEND.lock().unwrap(); + *backend = Some(http); + }; + for stream in listener.incoming() { match stream { Ok(mut stream) => { - let mut http_client = backend::HttpClient::new(args.url.clone(), args.backend)?; - attest::attest(&mut stream, &mut http_client)?; + thread::spawn(move || { + if let Err(e) = attest::attest(&mut stream) { + eprintln!("{e}"); + } + }); } Err(_) => { panic!("error");