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 Jan 16, 2025
1 parent ad7b3e9 commit e2f36a7
Show file tree
Hide file tree
Showing 6 changed files with 97 additions and 61 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
31 changes: 23 additions & 8 deletions aproxy/src/attest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,26 @@
// Author: Stefano Garzarella <[email protected]>
// Author: Tyler Fanelli <[email protected]>

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(())
}
Expand All @@ -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)?;
Expand All @@ -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)?;
Expand All @@ -48,15 +59,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, http: &Client) -> 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(http, 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
67 changes: 31 additions & 36 deletions aproxy/src/backend/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,61 +5,56 @@
// 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<ProtocolDispatcher>> = 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<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(&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),
}
impl ProtocolDispatcher {
pub fn negotiation(
&self,
cli: &Client,
n: NegotiationRequest,
) -> anyhow::Result<NegotiationResponse> {
(self.negotiation)(cli, &self.url, n)
}

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,
cli: &Client,
a: AttestationRequest,
) -> anyhow::Result<AttestationResponse> {
(self.attestation)(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 +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<NegotiationResponse>;
fn attestation(
&self,
client: &HttpClient,
client: &Client,
url: &str,
req: AttestationRequest,
) -> anyhow::Result<AttestationResponse>;
}
29 changes: 25 additions & 4 deletions aproxy/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
Expand All @@ -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)]
Expand All @@ -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");
Expand Down

0 comments on commit e2f36a7

Please sign in to comment.