Skip to content

Commit

Permalink
aproxy: Use static registrar for backend protocol
Browse files Browse the repository at this point in the history
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 <[email protected]>
  • Loading branch information
tylerfanelli committed Dec 18, 2024
1 parent 4052f13 commit 9abbf4c
Show file tree
Hide file tree
Showing 6 changed files with 81 additions and 58 deletions.
7 changes: 7 additions & 0 deletions Cargo.lock

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

1 change: 1 addition & 0 deletions aproxy/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
24 changes: 16 additions & 8 deletions aproxy/src/attest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
// Author: Stefano Garzarella <[email protected]>
// Author: Tyler Fanelli <[email protected]>

use crate::backend;
use crate::backend::BACKEND;
use anyhow::Context;
use libaproxy::*;
use serde::Serialize;
Expand All @@ -17,9 +17,9 @@ use std::{
};

/// 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<()> {
negotiation(stream)?;
attestation(stream)?;

Ok(())
}
Expand All @@ -29,7 +29,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) -> anyhow::Result<()> {
// Read the negotiation parameters from SVSM.
let request: NegotiationRequest = {
let payload = proxy_read(stream)?;
Expand All @@ -39,7 +39,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(request) // Safe to unwrap.
}?;

// Write the response from the attestation server to SVSM.
proxy_write(stream, response)?;
Expand All @@ -50,15 +54,19 @@ 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) -> anyhow::Result<()> {
let request: AttestationRequest = {
let payload = proxy_read(stream)?;
serde_json::from_slice(&payload)
.context("unable to deserialize attestation request from JSON")?
};

// Attest the TEE evidence with the server.
let response: AttestationResponse = http.attestation(request)?;
let response = {
let backend = BACKEND.lock().unwrap();

backend.clone().unwrap().attestation(request) // Safe to unwrap.
}?;

// Write the response from the attestation server to SVSM.
proxy_write(stream, response)?;
Expand Down
23 changes: 10 additions & 13 deletions aproxy/src/backend/kbs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<NegotiationResponse> {
let req = Request {
Expand All @@ -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")?;
Expand Down Expand Up @@ -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<AttestationResponse> {
// Create a KBS attestation object from the TEE evidence and key.
Expand All @@ -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")?;
Expand All @@ -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")?;

Expand Down
54 changes: 21 additions & 33 deletions aproxy/src/backend/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,61 +5,49 @@
// Author: Stefano Garzarella <[email protected]>
// Author: Tyler Fanelli <[email protected]>

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<Option<HttpClient>> = Mutex::new(None);
}

/// HTTP client and protocol identifier.
#[derive(Clone, Debug)]
pub struct HttpClient {
pub cli: Client,
pub url: String,
protocol: Protocol,
pub negotiation: fn(&Client, &str, NegotiationRequest) -> anyhow::Result<NegotiationResponse>,
pub attestation: fn(&Client, &str, AttestationRequest) -> anyhow::Result<AttestationResponse>,
}

impl HttpClient {
pub fn new(url: String, protocol: Protocol) -> anyhow::Result<Self> {
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(&self, n: NegotiationRequest) -> anyhow::Result<NegotiationResponse> {
(self.negotiation)(&self.cli, &self.url, n)
}

pub fn negotiation(&mut self, req: NegotiationRequest) -> anyhow::Result<NegotiationResponse> {
// Depending on the underlying protocol of the attestation server, gather negotiation
// parameters accordingly.
match self.protocol {
Protocol::Kbs(mut kbs) => kbs.negotiation(self, req),
}
}

pub fn attestation(&self, req: AttestationRequest) -> anyhow::Result<AttestationResponse> {
// 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, a: AttestationRequest) -> anyhow::Result<AttestationResponse> {
(self.attestation)(&self.cli, &self.url, a)
}
}

/// Attestation Protocol identifier.
#[derive(Clone, Copy, Debug)]
pub enum Protocol {
Kbs(KbsProtocol),
Kbs,
}

impl FromStr for Protocol {
type Err = anyhow::Error;

fn from_str(s: &str) -> Result<Self, Self::Err> {
match &s.to_lowercase()[..] {
"kbs" => Ok(Self::Kbs(KbsProtocol)),
"kbs" => Ok(Self::Kbs),
_ => Err(anyhow!("invalid backend attestation protocol selected")),
}
}
Expand All @@ -69,13 +57,13 @@ impl FromStr for Protocol {
/// protocols.
pub trait AttestationProtocol {
fn negotiation(
&mut self,
client: &mut HttpClient,
client: &Client,
url: &str,
req: NegotiationRequest,
) -> anyhow::Result<NegotiationResponse>;
fn attestation(
&self,
client: &HttpClient,
client: &Client,
url: &str,
req: AttestationRequest,
) -> anyhow::Result<AttestationResponse>;
}
30 changes: 26 additions & 4 deletions aproxy/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,11 @@
mod attest;
mod backend;

use crate::backend::{kbs::KbsProtocol, *};
use anyhow::Context;
use clap::Parser;
use std::{fs, os::unix::net::UnixListener};
use reqwest::{blocking::Client, cookie::Jar};
use std::{fs, mem, os::unix::net::UnixListener, sync::Arc, thread};

#[derive(Parser, Debug)]
#[clap(version, about, long_about = None)]
Expand All @@ -21,7 +23,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)]
Expand All @@ -39,13 +41,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 = HttpClient {
cli: Client::builder()
.cookie_provider(Arc::new(Jar::default()))
.build()
.context("unable to build HTTP client to interact with attestation server")?,
url: args.url,
attestation,
negotiation,
};

thread::spawn(move || {
let mut backend = BACKEND.lock().unwrap();
let _ = mem::replace(&mut *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)?;
attest::attest(&mut stream)?;
}
Err(_) => {
panic!("error");
Expand Down

0 comments on commit 9abbf4c

Please sign in to comment.