Skip to content

Commit

Permalink
aproxy: Use static registrar for backend protocol
Browse files Browse the repository at this point in the history
Signed-off-by: Tyler Fanelli <tfanelli@redhat.com>
  • Loading branch information
tylerfanelli committed Dec 17, 2024
1 parent 14123ff commit 39e6fef
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 <sgarzare@redhat.com>
// Author: Tyler Fanelli <tfanelli@redhat.com>

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 <sgarzare@redhat.com>
// Author: Tyler Fanelli <tfanelli@redhat.com>

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 39e6fef

Please sign in to comment.