mirror of
https://github.com/matter-labs/teepot.git
synced 2025-07-21 15:13:56 +02:00
feat: use real RA-TLS for everything
* add `tee-ratls-preexec` for creating the vault certificate * remove the old attestation API Signed-off-by: Harald Hoyer <harald@matterlabs.dev>
This commit is contained in:
parent
020159b9d7
commit
0b60abc030
21 changed files with 837 additions and 834 deletions
21
bin/tee-ratls-preexec/Cargo.toml
Normal file
21
bin/tee-ratls-preexec/Cargo.toml
Normal file
|
@ -0,0 +1,21 @@
|
|||
[package]
|
||||
name = "tee-ratls-preexec"
|
||||
version.workspace = true
|
||||
edition.workspace = true
|
||||
authors.workspace = true
|
||||
license.workspace = true
|
||||
repository.workspace = true
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
anyhow.workspace = true
|
||||
clap.workspace = true
|
||||
rsa.workspace = true
|
||||
rustls-pemfile.workspace = true
|
||||
rustls.workspace = true
|
||||
teepot.workspace = true
|
||||
tracing-log.workspace = true
|
||||
tracing-subscriber.workspace = true
|
||||
tracing.workspace = true
|
||||
x509-cert.workspace = true
|
104
bin/tee-ratls-preexec/src/main.rs
Normal file
104
bin/tee-ratls-preexec/src/main.rs
Normal file
|
@ -0,0 +1,104 @@
|
|||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright (c) 2024 Matter Labs
|
||||
|
||||
//! Pre-exec for binary running in a TEE needing attestation of a secret signing key
|
||||
|
||||
#![deny(missing_docs)]
|
||||
#![deny(clippy::all)]
|
||||
|
||||
use anyhow::{Context, Result};
|
||||
use clap::Parser;
|
||||
use rsa::pkcs1v15::SigningKey;
|
||||
use rsa::pkcs8::DecodePrivateKey;
|
||||
use rsa::sha2::Sha256;
|
||||
use rsa::RsaPrivateKey;
|
||||
use std::fs::File;
|
||||
use std::io::Write;
|
||||
use std::os::unix::process::CommandExt;
|
||||
use std::path::PathBuf;
|
||||
use std::process::Command;
|
||||
use teepot::server::pki::make_signed_cert;
|
||||
use tracing::error;
|
||||
use tracing_log::LogTracer;
|
||||
use tracing_subscriber::{fmt, prelude::*, EnvFilter, Registry};
|
||||
use x509_cert::der::asn1::Ia5String;
|
||||
use x509_cert::der::DecodePem;
|
||||
use x509_cert::ext::pkix::name::GeneralName;
|
||||
use x509_cert::Certificate;
|
||||
|
||||
#[derive(Parser, Debug)]
|
||||
#[command(author, version, about, long_about = None)]
|
||||
struct Args {
|
||||
/// ca cert file
|
||||
#[arg(long, env = "CA_CERT_FILE", default_value = "/opt/vault/cacert.pem")]
|
||||
ca_cert_file: PathBuf,
|
||||
/// ca key file
|
||||
#[arg(long, env = "CA_KEY_FILE", default_value = "/opt/vault/cakey.pem")]
|
||||
ca_key_file: PathBuf,
|
||||
/// out cert file
|
||||
#[arg(long, env = "TLS_CERT_FILE", default_value = "/opt/vault/tls/tls.crt")]
|
||||
tls_cert_file: PathBuf,
|
||||
/// out key file
|
||||
#[arg(long, env = "TLS_KEY_FILE", default_value = "/opt/vault/tls/tls.key")]
|
||||
tls_key_file: PathBuf,
|
||||
/// DNS names, comma separated
|
||||
#[arg(long, env = "DNS_NAMES", required = true)]
|
||||
dns_names: String,
|
||||
/// program to exec [args...] (required)
|
||||
#[arg(required = true, allow_hyphen_values = true, last = true)]
|
||||
cmd_args: Vec<String>,
|
||||
}
|
||||
|
||||
fn main_with_error() -> Result<()> {
|
||||
LogTracer::init().context("Failed to set logger")?;
|
||||
|
||||
let subscriber = Registry::default()
|
||||
.with(EnvFilter::from_default_env())
|
||||
.with(fmt::layer().with_writer(std::io::stderr));
|
||||
tracing::subscriber::set_global_default(subscriber).context("Failed to set logger")?;
|
||||
|
||||
let args = Args::parse();
|
||||
|
||||
// read `issuer_cert_bytes` from file
|
||||
let ca_cert = std::fs::read(args.ca_cert_file).context("Failed to read ca_cert")?;
|
||||
|
||||
let issuer_cert = Certificate::from_pem(&ca_cert)?;
|
||||
let issuer_key =
|
||||
RsaPrivateKey::read_pkcs8_pem_file(args.ca_key_file).context("Failed to read ca_key")?;
|
||||
let issuer_key_pair = SigningKey::<Sha256>::new(issuer_key);
|
||||
|
||||
// TODO: read values from config file or env or args
|
||||
let dn = "O=system:nodes,CN=system:node";
|
||||
let mut an = vec![std::net::IpAddr::from(std::net::Ipv4Addr::LOCALHOST).into()];
|
||||
an.extend(
|
||||
args.dns_names
|
||||
.split(',')
|
||||
.map(|s| GeneralName::DnsName(Ia5String::try_from(s.to_string()).unwrap())),
|
||||
);
|
||||
|
||||
let (_report_data, cert, priv_key) =
|
||||
make_signed_cert(dn, Some(an), &issuer_cert, &issuer_key_pair)?;
|
||||
|
||||
// open args.tls_cert_file and write cert and ca_cert
|
||||
let mut file = File::create(&args.tls_cert_file).context("Failed to create tls_cert")?;
|
||||
file.write_all(cert.as_bytes())
|
||||
.context("Failed to write tls_cert")?;
|
||||
file.write_all(&ca_cert)
|
||||
.context("Failed to write tls_cert")?;
|
||||
|
||||
std::fs::write(args.tls_key_file, priv_key).context("Failed to write tls_cert")?;
|
||||
|
||||
let err = Command::new(&args.cmd_args[0])
|
||||
.args(&args.cmd_args[1..])
|
||||
.exec();
|
||||
|
||||
Err(err).with_context(|| format!("exec of `{cmd}` failed", cmd = args.cmd_args.join(" ")))
|
||||
}
|
||||
|
||||
fn main() -> Result<()> {
|
||||
let ret = main_with_error();
|
||||
if let Err(e) = &ret {
|
||||
error!("Error: {}", e);
|
||||
}
|
||||
ret
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright (c) 2024 Matter Labs
|
||||
|
||||
//! Server to handle requests to the Vault TEE
|
||||
|
||||
|
@ -45,7 +46,7 @@ async fn main() -> Result<()> {
|
|||
|
||||
let args = Arguments::parse();
|
||||
|
||||
let (report_data, _cert_chain, _priv_key) = make_self_signed_cert()?;
|
||||
let (report_data, _cert_chain, _priv_key) = make_self_signed_cert("CN=localhost", None)?;
|
||||
if let Err(e) = get_quote_and_collateral(Some(args.my_sgx_allowed_tcb_levels), &report_data) {
|
||||
error!("failed to get quote and collateral: {e:?}");
|
||||
// don't return for now, we can still serve requests but we won't be able to attest
|
||||
|
|
|
@ -1,25 +0,0 @@
|
|||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright (c) 2023 Matter Labs
|
||||
|
||||
//! attestation
|
||||
|
||||
use crate::ServerState;
|
||||
use actix_web::http::StatusCode;
|
||||
use actix_web::web::{Data, Json};
|
||||
use anyhow::{Context, Result};
|
||||
use std::sync::Arc;
|
||||
use teepot::json::http::AttestationResponse;
|
||||
use teepot::server::attestation::get_quote_and_collateral;
|
||||
use teepot::server::{HttpResponseError, Status};
|
||||
use tracing::instrument;
|
||||
|
||||
/// Get attestation
|
||||
#[instrument(level = "info", name = "/v1/sys/attestation", skip_all)]
|
||||
pub async fn get_attestation(
|
||||
worker: Data<Arc<ServerState>>,
|
||||
) -> Result<Json<AttestationResponse>, HttpResponseError> {
|
||||
get_quote_and_collateral(None, &worker.report_data)
|
||||
.context("Error getting attestation")
|
||||
.map(Json)
|
||||
.status(StatusCode::INTERNAL_SERVER_ERROR)
|
||||
}
|
|
@ -1,12 +1,10 @@
|
|||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright (c) 2023 Matter Labs
|
||||
// Copyright (c) 2023-2024 Matter Labs
|
||||
|
||||
//! Server to handle requests to the Vault TEE
|
||||
|
||||
#![deny(missing_docs)]
|
||||
#![deny(clippy::all)]
|
||||
|
||||
mod attestation;
|
||||
mod command;
|
||||
mod digest;
|
||||
mod sign;
|
||||
|
@ -14,7 +12,6 @@ mod sign;
|
|||
use actix_web::web::Data;
|
||||
use actix_web::{web, App, HttpServer};
|
||||
use anyhow::{Context, Result};
|
||||
use attestation::get_attestation;
|
||||
use clap::Parser;
|
||||
use command::post_command;
|
||||
use digest::get_digest;
|
||||
|
@ -22,7 +19,7 @@ use rustls::ServerConfig;
|
|||
use sign::post_sign;
|
||||
use std::net::Ipv6Addr;
|
||||
use std::sync::Arc;
|
||||
use teepot::json::http::{SignRequest, VaultCommandRequest, ATTESTATION_URL, DIGEST_URL};
|
||||
use teepot::json::http::{SignRequest, VaultCommandRequest, DIGEST_URL};
|
||||
use teepot::server::attestation::{get_quote_and_collateral, VaultAttestationArgs};
|
||||
use teepot::server::new_json_cfg;
|
||||
use teepot::server::pki::make_self_signed_cert;
|
||||
|
@ -65,7 +62,7 @@ async fn main() -> Result<()> {
|
|||
|
||||
let args = Arguments::parse();
|
||||
|
||||
let (report_data, cert_chain, priv_key) = make_self_signed_cert()?;
|
||||
let (report_data, cert_chain, priv_key) = make_self_signed_cert("CN=localhost", None)?;
|
||||
|
||||
if let Err(e) = get_quote_and_collateral(Some(args.server_sgx_allowed_tcb_levels), &report_data)
|
||||
{
|
||||
|
@ -94,7 +91,6 @@ async fn main() -> Result<()> {
|
|||
.wrap(TracingLogger::default())
|
||||
.app_data(new_json_cfg())
|
||||
.app_data(Data::new(server_state.clone()))
|
||||
.service(web::resource(ATTESTATION_URL).route(web::get().to(get_attestation)))
|
||||
.service(web::resource(VaultCommandRequest::URL).route(web::post().to(post_command)))
|
||||
.service(web::resource(SignRequest::URL).route(web::post().to(post_sign)))
|
||||
.service(web::resource(DIGEST_URL).route(web::get().to(get_digest)))
|
||||
|
|
|
@ -1,27 +0,0 @@
|
|||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright (c) 2023 Matter Labs
|
||||
|
||||
use crate::Worker;
|
||||
use actix_web::http::StatusCode;
|
||||
use actix_web::web::{Data, Json};
|
||||
use anyhow::{Context, Result};
|
||||
use teepot::json::http::AttestationResponse;
|
||||
use teepot::server::attestation::get_quote_and_collateral;
|
||||
use teepot::server::{HttpResponseError, Status};
|
||||
use tracing::instrument;
|
||||
|
||||
#[instrument(level = "info", name = "/v1/sys/attestation", skip_all)]
|
||||
pub async fn get_attestation(
|
||||
worker: Data<Worker>,
|
||||
) -> Result<Json<AttestationResponse>, HttpResponseError> {
|
||||
let report_data: [u8; 64] = worker
|
||||
.config
|
||||
.report_data
|
||||
.clone()
|
||||
.try_into()
|
||||
.map_err(|_| "Error getting attestation")?;
|
||||
get_quote_and_collateral(None, &report_data)
|
||||
.context("Error getting attestation")
|
||||
.map(Json)
|
||||
.status(StatusCode::INTERNAL_SERVER_ERROR)
|
||||
}
|
|
@ -1,12 +1,13 @@
|
|||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright (c) 2023 Matter Labs
|
||||
// Copyright (c) 2023-2024 Matter Labs
|
||||
|
||||
use crate::{create_https_client, get_vault_status, UnsealServerState, Worker};
|
||||
use crate::{get_vault_status, UnsealServerState, Worker};
|
||||
use actix_web::error::ErrorBadRequest;
|
||||
use actix_web::{web, HttpResponse};
|
||||
use anyhow::{anyhow, Context, Result};
|
||||
use awc::http::StatusCode;
|
||||
use serde_json::json;
|
||||
use teepot::client::TeeConnection;
|
||||
use teepot::json::http::{Init, InitResponse, VaultInitRequest};
|
||||
use teepot::json::secrets::AdminConfig;
|
||||
use teepot::server::{HttpResponseError, Status};
|
||||
|
@ -25,7 +26,8 @@ pub async fn post_init(
|
|||
admin_threshold,
|
||||
admin_tee_mrenclave,
|
||||
} = init.into_inner();
|
||||
let client = create_https_client(worker.client_tls_config.clone());
|
||||
let conn = TeeConnection::new(&worker.vault_attestation);
|
||||
let client = conn.client();
|
||||
let vault_url = &worker.config.vault_url;
|
||||
|
||||
let vault_init = VaultInitRequest {
|
||||
|
@ -62,7 +64,7 @@ pub async fn post_init(
|
|||
return Err(anyhow!("Vault already initialized")).status(StatusCode::BAD_REQUEST);
|
||||
}
|
||||
UnsealServerState::Undefined => {
|
||||
let state = get_vault_status(vault_url, client.clone()).await;
|
||||
let state = get_vault_status(vault_url, client).await;
|
||||
*worker.state.write().unwrap() = state;
|
||||
continue;
|
||||
}
|
||||
|
|
|
@ -1,55 +1,45 @@
|
|||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright (c) 2023 Matter Labs
|
||||
// Copyright (c) 2023-2024 Matter Labs
|
||||
|
||||
//! Server to initialize and unseal the Vault TEE.
|
||||
|
||||
#![deny(missing_docs)]
|
||||
#![deny(clippy::all)]
|
||||
|
||||
mod attestation;
|
||||
mod init;
|
||||
mod unseal;
|
||||
|
||||
use actix_web::http::header;
|
||||
use actix_web::rt::time::sleep;
|
||||
use actix_web::web::Data;
|
||||
use actix_web::{web, App, HttpServer};
|
||||
use anyhow::{bail, Context, Result};
|
||||
use attestation::get_attestation;
|
||||
use awc::{Client, Connector};
|
||||
use anyhow::{Context, Result};
|
||||
use awc::Client;
|
||||
use clap::Parser;
|
||||
use init::post_init;
|
||||
use rustls::client::danger::{HandshakeSignatureValid, ServerCertVerified, ServerCertVerifier};
|
||||
use rustls::client::WebPkiServerVerifier;
|
||||
use rustls::pki_types::{CertificateDer, ServerName, UnixTime};
|
||||
use rustls::{ClientConfig, DigitallySignedStruct, Error, ServerConfig, SignatureScheme};
|
||||
use rustls_pemfile::{certs, read_one};
|
||||
use sha2::{Digest, Sha256};
|
||||
use rustls::ServerConfig;
|
||||
use std::fmt::Debug;
|
||||
use std::net::Ipv6Addr;
|
||||
use std::sync::{Arc, RwLock};
|
||||
use std::time::Duration;
|
||||
use std::{fs::File, io::BufReader};
|
||||
use teepot::json::http::{Init, Unseal, ATTESTATION_URL};
|
||||
use teepot::client::{AttestationArgs, TeeConnection};
|
||||
use teepot::json::http::{Init, Unseal};
|
||||
use teepot::json::secrets::AdminConfig;
|
||||
use teepot::server::attestation::get_quote_and_collateral;
|
||||
use teepot::server::attestation::{get_quote_and_collateral, VaultAttestationArgs};
|
||||
use teepot::server::new_json_cfg;
|
||||
use teepot::server::pki::make_self_signed_cert;
|
||||
use teepot::sgx::{parse_tcb_levels, EnumSet, TcbLevel};
|
||||
use tracing::{error, info, trace};
|
||||
use tracing::{error, info};
|
||||
use tracing_log::LogTracer;
|
||||
use tracing_subscriber::{fmt, prelude::*, EnvFilter, Registry};
|
||||
use unseal::post_unseal;
|
||||
use x509_cert::der::Decode as _;
|
||||
use x509_cert::der::Encode as _;
|
||||
use x509_cert::Certificate;
|
||||
|
||||
const VAULT_AUTH_TEE_SHA256: &str = include_str!("../../../assets/vault-auth-tee.sha256");
|
||||
const VAULT_TOKEN_HEADER: &str = "X-Vault-Token";
|
||||
|
||||
/// Worker thread state and data
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Worker {
|
||||
/// TLS config for the HTTPS client
|
||||
pub client_tls_config: Arc<ClientConfig>,
|
||||
pub vault_attestation: Arc<AttestationArgs>,
|
||||
/// Server config
|
||||
pub config: Arc<UnsealServerConfig>,
|
||||
/// Server state
|
||||
|
@ -62,9 +52,13 @@ pub struct UnsealServerConfig {
|
|||
/// Vault URL
|
||||
pub vault_url: String,
|
||||
/// The expected report_data for the Vault TEE
|
||||
pub report_data: Vec<u8>,
|
||||
pub report_data: Box<[u8]>,
|
||||
/// allowed TCB levels
|
||||
pub allowed_tcb_levels: Option<EnumSet<TcbLevel>>,
|
||||
/// SHA256 of the vault_auth_tee plugin binary
|
||||
pub vault_auth_tee_sha: String,
|
||||
/// version string of the vault_auth_tee plugin
|
||||
pub vault_auth_tee_version: String,
|
||||
}
|
||||
|
||||
/// Server state
|
||||
|
@ -90,21 +84,6 @@ pub enum UnsealServerState {
|
|||
VaultUnsealed,
|
||||
}
|
||||
|
||||
impl UnsealServerConfig {
|
||||
/// Create a new ServerState
|
||||
pub fn new(
|
||||
vault_url: String,
|
||||
report_data: [u8; 64],
|
||||
allowed_tcb_levels: Option<EnumSet<TcbLevel>>,
|
||||
) -> Self {
|
||||
Self {
|
||||
report_data: report_data.to_vec(),
|
||||
vault_url,
|
||||
allowed_tcb_levels,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Parser, Debug)]
|
||||
#[command(author, version, about, long_about = None)]
|
||||
struct Args {
|
||||
|
@ -114,9 +93,12 @@ struct Args {
|
|||
/// port to listen on
|
||||
#[arg(long, env = "PORT", default_value = "8443")]
|
||||
port: u16,
|
||||
/// vault url
|
||||
#[arg(long, env = "VAULT_ADDR", default_value = "https://vault:8210")]
|
||||
vault_url: String,
|
||||
#[arg(long, env = "VAULT_AUTH_TEE_SHA256")]
|
||||
vault_auth_tee_sha: String,
|
||||
#[arg(long, env = "VAULT_AUTH_TEE_VERSION")]
|
||||
vault_auth_tee_version: String,
|
||||
#[clap(flatten)]
|
||||
pub attestation: VaultAttestationArgs,
|
||||
}
|
||||
|
||||
#[actix_web::main]
|
||||
|
@ -134,55 +116,50 @@ async fn main() -> Result<()> {
|
|||
|
||||
let args = Args::parse();
|
||||
|
||||
let tls_ok = std::path::Path::new("/opt/vault/tls/tls.ok");
|
||||
loop {
|
||||
info!("Waiting for TLS key/cert files to be generated");
|
||||
|
||||
// Wait for the file `data/tls.key` to exist
|
||||
if tls_ok.exists() {
|
||||
break;
|
||||
}
|
||||
sleep(Duration::from_secs(1)).await;
|
||||
}
|
||||
|
||||
info!("Starting up");
|
||||
|
||||
let (config, client_tls_config, report_data) = load_rustls_config().or_else(|e| {
|
||||
error!("failed to load rustls config: {e:?}");
|
||||
Err(e).context("Failed to load rustls config")
|
||||
})?;
|
||||
|
||||
if let Err(e) = get_quote_and_collateral(Some(args.allowed_tcb_levels), &report_data) {
|
||||
if let Err(e) = get_quote_and_collateral(Some(args.allowed_tcb_levels), &[0u8; 64]) {
|
||||
error!("failed to get quote and collateral: {e:?}");
|
||||
// don't return for now, we can still serve requests but we won't be able to attest
|
||||
}
|
||||
|
||||
let client = create_https_client(client_tls_config.clone());
|
||||
let (report_data, cert_chain, priv_key) = make_self_signed_cert("CN=localhost", None)?;
|
||||
|
||||
let server_state = get_vault_status(&args.vault_url, client).await;
|
||||
// init server config builder with safe defaults
|
||||
let config = ServerConfig::builder()
|
||||
.with_no_client_auth()
|
||||
.with_single_cert([cert_chain].into(), priv_key)
|
||||
.context("Failed to load TLS key/cert files")?;
|
||||
|
||||
let attestation_args: AttestationArgs = args.attestation.clone().into();
|
||||
|
||||
let conn = TeeConnection::new(&attestation_args);
|
||||
|
||||
let server_state = get_vault_status(&args.attestation.vault_addr, conn.client()).await;
|
||||
|
||||
info!("Starting HTTPS server at port {}", args.port);
|
||||
let server_config = Arc::new(UnsealServerConfig::new(
|
||||
args.vault_url,
|
||||
report_data,
|
||||
Some(args.allowed_tcb_levels),
|
||||
));
|
||||
let server_config = Arc::new(UnsealServerConfig {
|
||||
vault_url: args.attestation.vault_addr,
|
||||
report_data: Box::from(report_data),
|
||||
allowed_tcb_levels: Some(args.allowed_tcb_levels),
|
||||
vault_auth_tee_sha: args.vault_auth_tee_sha,
|
||||
vault_auth_tee_version: args.vault_auth_tee_version,
|
||||
});
|
||||
|
||||
let server_state = Arc::new(RwLock::new(server_state));
|
||||
|
||||
let server = match HttpServer::new(move || {
|
||||
let worker = Worker {
|
||||
client_tls_config: client_tls_config.clone(),
|
||||
config: server_config.clone(),
|
||||
state: server_state.clone(),
|
||||
};
|
||||
let worker = Worker {
|
||||
vault_attestation: Arc::new(attestation_args),
|
||||
config: server_config,
|
||||
state: server_state,
|
||||
};
|
||||
|
||||
let server = match HttpServer::new(move || {
|
||||
App::new()
|
||||
// enable logger
|
||||
//.wrap(TracingLogger::default())
|
||||
.app_data(new_json_cfg())
|
||||
.app_data(Data::new(worker))
|
||||
.service(web::resource(ATTESTATION_URL).route(web::get().to(get_attestation)))
|
||||
.app_data(Data::new(worker.clone()))
|
||||
.service(web::resource(Init::URL).route(web::post().to(post_init)))
|
||||
.service(web::resource(Unseal::URL).route(web::post().to(post_unseal)))
|
||||
})
|
||||
|
@ -203,7 +180,7 @@ async fn main() -> Result<()> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
async fn get_vault_status(vault_url: &str, client: Client) -> UnsealServerState {
|
||||
async fn get_vault_status(vault_url: &str, client: &Client) -> UnsealServerState {
|
||||
loop {
|
||||
let r = client
|
||||
.get(format!("{}/v1/sys/health", vault_url))
|
||||
|
@ -234,130 +211,3 @@ async fn get_vault_status(vault_url: &str, client: Client) -> UnsealServerState
|
|||
sleep(Duration::from_secs(1)).await;
|
||||
}
|
||||
}
|
||||
|
||||
// Save the hash of the public server key to `REPORT_DATA` to check
|
||||
// the attestations against it and it does not change on reconnect.
|
||||
fn make_verifier(server_cert: Box<[u8]>) -> impl ServerCertVerifier {
|
||||
#[derive(Debug)]
|
||||
struct V {
|
||||
server_cert: Box<[u8]>,
|
||||
server_verifier: Arc<WebPkiServerVerifier>,
|
||||
}
|
||||
impl ServerCertVerifier for V {
|
||||
fn verify_server_cert(
|
||||
&self,
|
||||
end_entity: &CertificateDer,
|
||||
_intermediates: &[CertificateDer],
|
||||
_server_name: &ServerName,
|
||||
_ocsp_response: &[u8],
|
||||
_now: UnixTime,
|
||||
) -> std::result::Result<ServerCertVerified, Error> {
|
||||
let data = &self.server_cert;
|
||||
|
||||
if data.as_ref() == end_entity.as_ref() {
|
||||
info!("Server certificate matches expected certificate");
|
||||
Ok(ServerCertVerified::assertion())
|
||||
} else {
|
||||
error!("Server certificate does not match expected certificate");
|
||||
Err(rustls::Error::General(
|
||||
"Server certificate does not match expected certificate".to_string(),
|
||||
))
|
||||
}
|
||||
}
|
||||
fn verify_tls12_signature(
|
||||
&self,
|
||||
message: &[u8],
|
||||
cert: &CertificateDer<'_>,
|
||||
dss: &DigitallySignedStruct,
|
||||
) -> std::result::Result<HandshakeSignatureValid, Error> {
|
||||
self.server_verifier
|
||||
.verify_tls12_signature(message, cert, dss)
|
||||
}
|
||||
|
||||
fn verify_tls13_signature(
|
||||
&self,
|
||||
message: &[u8],
|
||||
cert: &CertificateDer<'_>,
|
||||
dss: &DigitallySignedStruct,
|
||||
) -> std::result::Result<HandshakeSignatureValid, Error> {
|
||||
self.server_verifier
|
||||
.verify_tls13_signature(message, cert, dss)
|
||||
}
|
||||
|
||||
fn supported_verify_schemes(&self) -> Vec<SignatureScheme> {
|
||||
self.server_verifier.supported_verify_schemes()
|
||||
}
|
||||
}
|
||||
let root_store = Arc::new(rustls::RootCertStore::empty());
|
||||
let server_verifier = WebPkiServerVerifier::builder(root_store).build().unwrap();
|
||||
V {
|
||||
server_cert,
|
||||
server_verifier,
|
||||
}
|
||||
}
|
||||
|
||||
/// Load TLS key/cert files
|
||||
pub fn load_rustls_config() -> Result<(ServerConfig, Arc<ClientConfig>, [u8; 64])> {
|
||||
// init server config builder with safe defaults
|
||||
let config = ServerConfig::builder().with_no_client_auth();
|
||||
|
||||
// load TLS key/cert files
|
||||
let cert_file = &mut BufReader::new(
|
||||
File::open("/opt/vault/tls/tls.crt").context("Failed to open TLS cert file")?,
|
||||
);
|
||||
let key_file = &mut BufReader::new(
|
||||
File::open("/opt/vault/tls/tls.key").context("Failed to open TLS key file")?,
|
||||
);
|
||||
|
||||
// convert files to key/cert objects
|
||||
let cert_chain = certs(cert_file)
|
||||
.collect::<Result<Vec<_>, _>>()
|
||||
.context("Failed to load TLS cert file")?;
|
||||
|
||||
let priv_key: rustls::pki_types::PrivateKeyDer =
|
||||
match read_one(key_file).context("Failed to read TLS key file")? {
|
||||
Some(rustls_pemfile::Item::Sec1Key(key)) => key.into(),
|
||||
Some(rustls_pemfile::Item::Pkcs1Key(key)) => key.into(),
|
||||
Some(rustls_pemfile::Item::Pkcs8Key(key)) => key.into(),
|
||||
_ => bail!("no keys found in TLS key file"),
|
||||
};
|
||||
|
||||
let tls_config = Arc::new(
|
||||
ClientConfig::builder()
|
||||
.dangerous()
|
||||
.with_custom_certificate_verifier(Arc::new(make_verifier(
|
||||
cert_chain[0].as_ref().into(),
|
||||
)))
|
||||
.with_no_client_auth(),
|
||||
);
|
||||
|
||||
let cert = Certificate::from_der(cert_chain[0].as_ref()).unwrap();
|
||||
let pub_key = cert
|
||||
.tbs_certificate
|
||||
.subject_public_key_info
|
||||
.to_der()
|
||||
.unwrap();
|
||||
|
||||
let hash = Sha256::digest(pub_key);
|
||||
let mut report_data = [0u8; 64];
|
||||
report_data[..32].copy_from_slice(&hash[..32]);
|
||||
|
||||
let report_data_hex = hex::encode(report_data);
|
||||
trace!(report_data_hex);
|
||||
|
||||
let config = config
|
||||
.with_single_cert(cert_chain, priv_key)
|
||||
.context("Failed to load TLS key/cert files")?;
|
||||
|
||||
Ok((config, tls_config, report_data))
|
||||
}
|
||||
|
||||
/// Create an HTTPS client with the default headers and config
|
||||
pub fn create_https_client(client_tls_config: Arc<ClientConfig>) -> Client {
|
||||
Client::builder()
|
||||
.add_default_header((header::USER_AGENT, "teepot/1.0"))
|
||||
// a "connector" wraps the stream into an encrypted connection
|
||||
.connector(Connector::new().rustls_0_22(client_tls_config))
|
||||
.timeout(Duration::from_secs(12000))
|
||||
.finish()
|
||||
}
|
||||
|
|
|
@ -1,10 +1,7 @@
|
|||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright (c) 2023 Matter Labs
|
||||
// Copyright (c) 2023-2024 Matter Labs
|
||||
|
||||
use crate::{
|
||||
create_https_client, get_vault_status, UnsealServerConfig, UnsealServerState, Worker,
|
||||
VAULT_AUTH_TEE_SHA256, VAULT_TOKEN_HEADER,
|
||||
};
|
||||
use crate::{get_vault_status, UnsealServerConfig, UnsealServerState, Worker, VAULT_TOKEN_HEADER};
|
||||
use actix_web::http::StatusCode;
|
||||
use actix_web::rt::time::sleep;
|
||||
use actix_web::{web, HttpResponse};
|
||||
|
@ -16,6 +13,7 @@ use std::future::Future;
|
|||
use std::io::Read;
|
||||
use std::time::Duration;
|
||||
use teepot::client::vault::VaultConnection;
|
||||
use teepot::client::TeeConnection;
|
||||
use teepot::json::http::Unseal;
|
||||
use teepot::json::secrets::{AdminConfig, AdminState};
|
||||
use teepot::server::{HttpResponseError, Status};
|
||||
|
@ -26,7 +24,8 @@ pub async fn post_unseal(
|
|||
worker: web::Data<Worker>,
|
||||
item: web::Json<Unseal>,
|
||||
) -> Result<HttpResponse, HttpResponseError> {
|
||||
let client = create_https_client(worker.client_tls_config.clone());
|
||||
let conn = TeeConnection::new(&worker.vault_attestation);
|
||||
let client = conn.client();
|
||||
let app = &worker.config;
|
||||
let vault_url = &app.vault_url;
|
||||
|
||||
|
@ -46,7 +45,7 @@ pub async fn post_unseal(
|
|||
break;
|
||||
}
|
||||
UnsealServerState::Undefined => {
|
||||
let state = get_vault_status(vault_url, client.clone()).await;
|
||||
let state = get_vault_status(vault_url, client).await;
|
||||
*worker.state.write().unwrap() = state;
|
||||
continue;
|
||||
}
|
||||
|
@ -106,15 +105,13 @@ pub async fn post_unseal(
|
|||
} => {
|
||||
debug!(root_token);
|
||||
info!("Vault is unsealed");
|
||||
let app = &worker.config;
|
||||
let client = create_https_client(worker.client_tls_config.clone());
|
||||
|
||||
vault_configure_unsealed(
|
||||
app,
|
||||
&admin_config,
|
||||
&root_token,
|
||||
&admin_tee_mrenclave,
|
||||
&client,
|
||||
client,
|
||||
)
|
||||
.await
|
||||
.context("Failed to configure unsealed vault")
|
||||
|
@ -204,9 +201,9 @@ pub async fn vault_configure_unsealed(
|
|||
)),
|
||||
root_token,
|
||||
json!({
|
||||
"sha256": VAULT_AUTH_TEE_SHA256,
|
||||
"sha256": app.vault_auth_tee_sha,
|
||||
"command": "vault-auth-tee",
|
||||
"version": "0.1.0+dev"
|
||||
"version": app.vault_auth_tee_version
|
||||
}),
|
||||
)
|
||||
.await
|
||||
|
@ -374,7 +371,7 @@ async fn plugin_is_already_running(
|
|||
.and_then(|v| v.as_str())
|
||||
.and_then(|v| if v.is_empty() { None } else { Some(v) })
|
||||
.and_then(|v| {
|
||||
if v == VAULT_AUTH_TEE_SHA256 {
|
||||
if v == app.vault_auth_tee_sha {
|
||||
Some(v)
|
||||
} else {
|
||||
None
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright (c) 2023 Matter Labs
|
||||
// Copyright (c) 2023-2024 Matter Labs
|
||||
|
||||
use anyhow::{anyhow, bail, Context, Result};
|
||||
use clap::{Args, Parser, Subcommand};
|
||||
|
@ -13,7 +13,7 @@ use std::path::{Path, PathBuf};
|
|||
use teepot::client::{AttestationArgs, TeeConnection};
|
||||
use teepot::json::http::{
|
||||
SignRequest, SignRequestData, SignResponse, VaultCommandRequest, VaultCommands,
|
||||
VaultCommandsResponse, ATTESTATION_URL, DIGEST_URL,
|
||||
VaultCommandsResponse, DIGEST_URL,
|
||||
};
|
||||
use teepot::server::signatures::verify_sig;
|
||||
use teepot::sgx::sign::Signature;
|
||||
|
@ -221,7 +221,7 @@ async fn send_commands(args: SendArgs) -> Result<()> {
|
|||
signatures,
|
||||
};
|
||||
|
||||
let conn = TeeConnection::new(&args.attestation, ATTESTATION_URL).await?;
|
||||
let conn = TeeConnection::new(&args.attestation);
|
||||
|
||||
let mut response = conn
|
||||
.client()
|
||||
|
@ -299,7 +299,7 @@ async fn send_sig_request(args: SignTeeArgs) -> Result<()> {
|
|||
signatures,
|
||||
};
|
||||
|
||||
let conn = TeeConnection::new(&args.attestation, ATTESTATION_URL).await?;
|
||||
let conn = TeeConnection::new(&args.attestation);
|
||||
|
||||
let mut response = conn
|
||||
.client()
|
||||
|
@ -338,7 +338,7 @@ async fn send_sig_request(args: SignTeeArgs) -> Result<()> {
|
|||
}
|
||||
|
||||
async fn digest(args: DigestArgs) -> Result<()> {
|
||||
let conn = TeeConnection::new(&args.attestation, ATTESTATION_URL).await?;
|
||||
let conn = TeeConnection::new(&args.attestation);
|
||||
|
||||
let mut response = conn
|
||||
.client()
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright (c) 2023 Matter Labs
|
||||
// Copyright (c) 2023-2024 Matter Labs
|
||||
|
||||
use anyhow::{anyhow, bail, Context, Result};
|
||||
use base64::{engine::general_purpose, Engine as _};
|
||||
|
@ -8,7 +8,7 @@ use serde_json::Value;
|
|||
use std::fs::File;
|
||||
use std::io::Read;
|
||||
use teepot::client::{AttestationArgs, TeeConnection};
|
||||
use teepot::json::http::{Init, InitResponse, Unseal, ATTESTATION_URL};
|
||||
use teepot::json::http::{Init, InitResponse, Unseal};
|
||||
use tracing::{error, info, trace, warn};
|
||||
use tracing_log::LogTracer;
|
||||
use tracing_subscriber::Registry;
|
||||
|
@ -70,7 +70,7 @@ async fn main() -> Result<()> {
|
|||
}
|
||||
|
||||
async fn init(args: Arguments) -> Result<()> {
|
||||
let conn = TeeConnection::new(&args.attestation, ATTESTATION_URL).await?;
|
||||
let conn = TeeConnection::new(&args.attestation);
|
||||
|
||||
info!("Quote verified! Connection secure!");
|
||||
|
||||
|
@ -177,7 +177,7 @@ async fn unseal(args: Arguments) -> Result<()> {
|
|||
bail!("Error reading key from stdin");
|
||||
}
|
||||
|
||||
let conn = TeeConnection::new(&args.attestation, ATTESTATION_URL).await?;
|
||||
let conn = TeeConnection::new(&args.attestation);
|
||||
|
||||
info!("Quote verified! Connection secure!");
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue