mirror of
https://github.com/matter-labs/teepot.git
synced 2025-07-21 07:03:56 +02:00
feat: rewrite google-metadata test as tdx-test
Signed-off-by: Harald Hoyer <harald@matterlabs.dev>
This commit is contained in:
parent
3325312c0d
commit
908579cd60
12 changed files with 771 additions and 251 deletions
|
@ -14,10 +14,12 @@ repository.workspace = true
|
|||
actix-http.workspace = true
|
||||
actix-web.workspace = true
|
||||
anyhow.workspace = true
|
||||
async-trait.workspace = true
|
||||
awc.workspace = true
|
||||
bytemuck.workspace = true
|
||||
bytes.workspace = true
|
||||
clap.workspace = true
|
||||
config.workspace = true
|
||||
const-oid.workspace = true
|
||||
enumset.workspace = true
|
||||
futures-core.workspace = true
|
||||
|
@ -26,10 +28,16 @@ hex.workspace = true
|
|||
intel-tee-quote-verification-rs.workspace = true
|
||||
num-integer.workspace = true
|
||||
num-traits.workspace = true
|
||||
opentelemetry.workspace = true
|
||||
opentelemetry-appender-tracing.workspace = true
|
||||
opentelemetry-otlp.workspace = true
|
||||
opentelemetry-semantic-conventions.workspace = true
|
||||
opentelemetry_sdk.workspace = true
|
||||
p256.workspace = true
|
||||
pgp.workspace = true
|
||||
pkcs8.workspace = true
|
||||
rand.workspace = true
|
||||
reqwest.workspace = true
|
||||
rsa.workspace = true
|
||||
rustls.workspace = true
|
||||
secp256k1 = { workspace = true, features = ["recovery"] }
|
||||
|
@ -42,6 +50,7 @@ signature.workspace = true
|
|||
tdx-attest-rs.workspace = true
|
||||
thiserror.workspace = true
|
||||
tracing.workspace = true
|
||||
tracing-futures.workspace = true
|
||||
tracing-log.workspace = true
|
||||
tracing-subscriber.workspace = true
|
||||
webpki-roots.workspace = true
|
||||
|
|
294
crates/teepot/src/config/mod.rs
Normal file
294
crates/teepot/src/config/mod.rs
Normal file
|
@ -0,0 +1,294 @@
|
|||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright (c) 2025 Matter Labs
|
||||
|
||||
//! Configuration handling
|
||||
|
||||
use async_trait::async_trait;
|
||||
use config::{
|
||||
builder::AsyncState, AsyncSource, Config, ConfigBuilder, ConfigError, File, FileFormat, Format,
|
||||
Map,
|
||||
};
|
||||
use opentelemetry::KeyValue;
|
||||
use opentelemetry_otlp::WithExportConfig;
|
||||
use opentelemetry_sdk::{logs::LoggerProvider, runtime, Resource};
|
||||
use opentelemetry_semantic_conventions::{
|
||||
attribute::{SERVICE_NAME, SERVICE_VERSION},
|
||||
SCHEMA_URL,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt::Debug;
|
||||
use tracing::trace;
|
||||
use tracing_subscriber::{
|
||||
fmt::format::FmtSpan, layer::SubscriberExt, util::SubscriberInitExt, EnvFilter,
|
||||
};
|
||||
|
||||
const DEFAULT_INSTANCE_METADATA_BASE_URL: &str =
|
||||
"http://metadata.google.internal/computeMetadata/v1/instance/attributes/container_config";
|
||||
|
||||
/// Get the configuration via HTTP
|
||||
#[derive(Debug)]
|
||||
pub struct HttpSource<F: Format> {
|
||||
/// the URI
|
||||
pub uri: String,
|
||||
/// the expected format
|
||||
pub format: F,
|
||||
/// if this is required, it will error, if not available
|
||||
pub required: bool,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl<F: Format + Send + Sync + Debug> AsyncSource for HttpSource<F> {
|
||||
async fn collect(&self) -> Result<Map<String, config::Value>, ConfigError> {
|
||||
let response = match reqwest::get(&self.uri)
|
||||
.await
|
||||
.map_err(|e| ConfigError::Foreign(Box::new(e)))
|
||||
{
|
||||
Ok(response) => response,
|
||||
Err(e) => {
|
||||
if self.required {
|
||||
return Err(e);
|
||||
} else {
|
||||
return Ok(Map::new());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// error conversion is possible from custom AsyncSource impls
|
||||
response
|
||||
.text()
|
||||
.await
|
||||
.map_err(|e| ConfigError::Foreign(Box::new(e)))
|
||||
.and_then(|text| {
|
||||
self.format
|
||||
.parse(Some(&self.uri), &text)
|
||||
.map_err(ConfigError::Foreign)
|
||||
})
|
||||
.or_else(|res| {
|
||||
if self.required {
|
||||
Err(res)
|
||||
} else {
|
||||
Ok(Map::new())
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Main telemetry configuration container
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct TelemetryConfig {
|
||||
/// The crate name `env!("CARGO_CRATE_NAME")`
|
||||
pub crate_name: String,
|
||||
/// The package version `env!("CARGO_PKG_VERSION")`
|
||||
pub pkg_version: String,
|
||||
/// OpenTelemetry Protocol (OTLP) specific settings
|
||||
pub otlp: TelemetryOtlpConfig,
|
||||
/// Logging-specific configuration
|
||||
pub logging: TelemetryLoggingConfig,
|
||||
}
|
||||
|
||||
impl TelemetryConfig {
|
||||
/// Create a new TelemetryConfig, usually with
|
||||
/// ```rust,
|
||||
/// # use teepot::config::TelemetryConfig;
|
||||
/// let telemetry_config = TelemetryConfig::new(
|
||||
/// env!("CARGO_CRATE_NAME").into(),
|
||||
/// env!("CARGO_PKG_VERSION").into(),
|
||||
/// );
|
||||
/// ```
|
||||
pub fn new(crate_name: String, pkg_version: String) -> Self {
|
||||
Self {
|
||||
crate_name,
|
||||
pkg_version,
|
||||
otlp: TelemetryOtlpConfig::default(),
|
||||
logging: TelemetryLoggingConfig::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Configuration for logging behavior
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct TelemetryLoggingConfig {
|
||||
/// The logging level (e.g., "debug", "info", "warn", "error")
|
||||
pub level: String,
|
||||
/// Whether to output logs in JSON format
|
||||
pub json: bool,
|
||||
/// Whether to output logs to console
|
||||
pub console: bool,
|
||||
}
|
||||
|
||||
impl Default for TelemetryLoggingConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
level: "warn".into(),
|
||||
json: false,
|
||||
console: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// OpenTelemetry Protocol specific configuration
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct TelemetryOtlpConfig {
|
||||
/// Controls whether OpenTelemetry Protocol (OTLP) export is enabled.
|
||||
/// FIXME: has no effect right now
|
||||
pub enable: bool,
|
||||
/// The endpoint URL for the OpenTelemetry collector
|
||||
pub endpoint: String,
|
||||
/// The protocol to use for OTLP communication (e.g., "grpc", "http/protobuf")
|
||||
pub protocol: String,
|
||||
}
|
||||
|
||||
impl Default for TelemetryOtlpConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
enable: true,
|
||||
endpoint: "127.0.0.1:4317".to_string(),
|
||||
protocol: "grpc".to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn protocol_from_string(protocol: &str) -> Result<opentelemetry_otlp::Protocol, anyhow::Error> {
|
||||
match protocol.to_lowercase().as_str() {
|
||||
"http/protobuf" => Ok(opentelemetry_otlp::Protocol::HttpBinary),
|
||||
"http/json" => Ok(opentelemetry_otlp::Protocol::HttpJson),
|
||||
"grpc" => Ok(opentelemetry_otlp::Protocol::Grpc),
|
||||
_ => Err(anyhow::anyhow!("Invalid protocol")),
|
||||
}
|
||||
}
|
||||
|
||||
/// Loads configuration and sets up logging based on the provided configuration accessor
|
||||
///
|
||||
/// # Type Parameters
|
||||
/// * `S` - Configuration type that implements Default, Serialize, Deserialize, and Send
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `get_telemetry_config` - Function to extract `TelemetryConfig` from type `S`
|
||||
///
|
||||
/// # Returns
|
||||
/// * `Result<S>` - The loaded configuration or error
|
||||
pub async fn load_config_with_telemetry<
|
||||
S: Default + Serialize + for<'a> Deserialize<'a> + Send + 'static,
|
||||
>(
|
||||
get_telemetry_config: fn(&S) -> &TelemetryConfig,
|
||||
) -> Result<S, Box<dyn std::error::Error + Send + Sync>> {
|
||||
with_console_logging(async move {
|
||||
trace!("Loading config");
|
||||
// Load configuration
|
||||
let config = ConfigBuilder::<AsyncState>::default()
|
||||
.add_source(Config::try_from(&S::default())?)
|
||||
.add_source(File::with_name("config/default").required(false))
|
||||
.add_source(
|
||||
config::Environment::with_prefix("APP")
|
||||
.try_parsing(true)
|
||||
.separator("_"),
|
||||
)
|
||||
.add_async_source(HttpSource {
|
||||
uri: DEFAULT_INSTANCE_METADATA_BASE_URL.into(),
|
||||
format: FileFormat::Json,
|
||||
required: false,
|
||||
})
|
||||
.build()
|
||||
.await?
|
||||
.try_deserialize::<S>()?;
|
||||
|
||||
// Initialize telemetry
|
||||
init_telemetry(get_telemetry_config(&config))?;
|
||||
Ok::<S, Box<dyn std::error::Error + Send + Sync>>(config)
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
fn create_console_format_layer<S>() -> tracing_subscriber::fmt::Layer<S>
|
||||
where
|
||||
S: for<'a> tracing::Subscriber + Send + Sync + 'static,
|
||||
{
|
||||
tracing_subscriber::fmt::layer()
|
||||
.with_target(true)
|
||||
.with_thread_ids(true)
|
||||
.with_line_number(true)
|
||||
.with_file(true)
|
||||
.with_span_events(FmtSpan::NEW | FmtSpan::CLOSE)
|
||||
.with_ansi(true)
|
||||
.with_thread_names(true)
|
||||
}
|
||||
|
||||
async fn with_console_logging<F>(fut: F) -> F::Output
|
||||
where
|
||||
F: std::future::Future + Send + 'static,
|
||||
F::Output: Send + 'static,
|
||||
{
|
||||
// Configure console logging
|
||||
let fmt_layer = create_console_format_layer();
|
||||
|
||||
let subs = tracing_subscriber::registry()
|
||||
.with(EnvFilter::new("trace"))
|
||||
.with(fmt_layer.pretty());
|
||||
|
||||
let _default = tracing::subscriber::set_default(subs);
|
||||
|
||||
tracing_futures::WithSubscriber::with_current_subscriber(fut).await
|
||||
}
|
||||
|
||||
fn init_telemetry(
|
||||
config: &TelemetryConfig,
|
||||
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||
std::env::set_var(
|
||||
"RUST_LOG",
|
||||
std::env::var("RUST_LOG").unwrap_or_else(|_| {
|
||||
format!(
|
||||
// `otel::tracing` should be a level info to emit opentelemetry trace & span
|
||||
// `otel::setup` set to debug to log detected resources, configuration read and infered
|
||||
"warn,{crate_name}={log_level},teepot={log_level},otel::tracing=error,otel=error",
|
||||
log_level = config.logging.level,
|
||||
crate_name = config.crate_name
|
||||
)
|
||||
}),
|
||||
);
|
||||
// Configure OpenTelemetry resource
|
||||
let resource = Resource::from_schema_url(
|
||||
[
|
||||
KeyValue::new(SERVICE_NAME, config.crate_name.clone()),
|
||||
KeyValue::new(SERVICE_VERSION, config.pkg_version.clone()),
|
||||
],
|
||||
SCHEMA_URL,
|
||||
);
|
||||
|
||||
// Configure the OTLP exporter
|
||||
let logging_provider = LoggerProvider::builder()
|
||||
.with_batch_exporter(
|
||||
opentelemetry_otlp::LogExporter::builder()
|
||||
.with_tonic()
|
||||
.with_endpoint(&config.otlp.endpoint)
|
||||
.with_protocol(protocol_from_string(&config.otlp.protocol)?)
|
||||
.build()?,
|
||||
runtime::Tokio,
|
||||
)
|
||||
.with_resource(resource)
|
||||
.build();
|
||||
|
||||
let logging_layer =
|
||||
opentelemetry_appender_tracing::layer::OpenTelemetryTracingBridge::new(&logging_provider);
|
||||
|
||||
// Configure console logging
|
||||
let fmt_layer = create_console_format_layer();
|
||||
|
||||
// Combine layers based on configuration
|
||||
let subscriber = tracing_subscriber::registry()
|
||||
.with(EnvFilter::from_default_env())
|
||||
.with(logging_layer);
|
||||
|
||||
// Add console logging if enabled
|
||||
if config.logging.console {
|
||||
// Optionally configure JSON logging
|
||||
if config.logging.json {
|
||||
subscriber.with(fmt_layer.json()).init()
|
||||
} else {
|
||||
subscriber.with(fmt_layer.pretty()).init()
|
||||
}
|
||||
} else {
|
||||
subscriber.init()
|
||||
};
|
||||
|
||||
Ok(())
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright (c) 2023-2024 Matter Labs
|
||||
// Copyright (c) 2023-2025 Matter Labs
|
||||
|
||||
//! Helper functions to verify Intel SGX enclaves and other TEEs.
|
||||
|
||||
|
@ -7,6 +7,7 @@
|
|||
#![deny(clippy::all)]
|
||||
|
||||
pub mod client;
|
||||
pub mod config;
|
||||
pub mod ethereum;
|
||||
pub mod json;
|
||||
pub mod log;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue