Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions opentelemetry-exporter-geneva/geneva-uploader/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ futures = "0.3"
[features]
self_signed_certs = [] # Empty by default for security
mock_auth = [] # Disabled by default. Not to be enabled in the prod release.
msi_auth = [] # Enable MSI authentication support (requires MSINATIVE_LIB_PATH at build time)
default = ["self_signed_certs"] # TODO - remove this feature before release

[dev-dependencies]
Expand All @@ -39,5 +40,8 @@ lz4_flex = { version = "0.11" }
criterion = {version = "0.6"}
rand = {version = "0.9"}

[build-dependencies]
cc = "1.0"

[lints]
workspace = true
170 changes: 170 additions & 0 deletions opentelemetry-exporter-geneva/geneva-uploader/build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
//! Build script for Geneva Uploader with optional MSI authentication support

fn main() {
println!("cargo:warning=---> Running build script for Geneva Uploader");
// Always output cargo rerun instructions for environment changes
println!("cargo:rerun-if-env-changed=MSINATIVE_LIB_PATH");
println!("cargo:rerun-if-changed=build.rs");

// Tell cargo about our custom cfg
println!("cargo:rustc-check-cfg=cfg(msi_native_available)");

// Only run MSI build logic if the msi_auth feature is enabled
#[cfg(feature = "msi_auth")]
{
println!("cargo:warning=---> Checking for MSI native library support");
let msi_available = check_msi_library();
if msi_available {
println!("cargo:rustc-cfg=msi_native_available");
eprintln!("INFO: MSI native authentication support enabled");
} else {
eprintln!("INFO: MSI native authentication support disabled - using stub implementation");
}
}
}

#[cfg(feature = "msi_auth")]
fn check_msi_library() -> bool {
use std::env;
use std::path::Path;

// Check if MSINATIVE_LIB_PATH is provided
match env::var("MSINATIVE_LIB_PATH") {
Ok(msinative_lib_path) => {
println!("cargo:warning=---> MSINATIVE_LIB_PATH is set to: {}", msinative_lib_path);
println!("cargo:rerun-if-changed={}", msinative_lib_path);

// Check if the path points to a valid static library file
let lib_path = Path::new(&msinative_lib_path);
if !lib_path.exists() {
eprintln!("WARNING: MSINATIVE_LIB_PATH points to non-existent file: {}", msinative_lib_path);
eprintln!("MSI authentication will be disabled.");
return false;
}

if !lib_path.is_file() {
eprintln!("WARNING: MSINATIVE_LIB_PATH must point to a static library file, not a directory: {}", msinative_lib_path);
eprintln!("MSI authentication will be disabled.");
return false;
}

// Check file extension to ensure it's a static library
let extension = lib_path.extension().and_then(|ext| ext.to_str()).unwrap_or("");
let is_static_lib = match extension {
"a" => true, // Unix static library
"lib" => true, // Windows static library
_ => false,
};

if !is_static_lib {
eprintln!("WARNING: MSINATIVE_LIB_PATH should point to a static library file (.a or .lib): {}", msinative_lib_path);
eprintln!("Found file with extension: {}", extension);
eprintln!("MSI authentication will be disabled.");
return false;
}

println!("cargo:warning=---> Valid static library found: {}", msinative_lib_path);

// Extract library directory and name for linking
if let (Some(lib_dir), Some(lib_name)) = (lib_path.parent(), lib_path.file_stem().and_then(|n| n.to_str())) {
println!("cargo:warning=---> Library directory: {}", lib_dir.display());

// Determine XPlatLib include directory
let xplatlib_inc = env::var("XPLATLIB_INC_PATH")
.unwrap_or_else(|_| "/home/labhas/strato/Compute-Runtime-Tux/external/GenevaMonAgent-Shared-CrossPlat/src/XPlatLib/inc".to_string());

// Compile the C++ bridge file
let bridge_path = "src/msi/native/bridge.cpp";
println!("cargo:warning=---> Compiling C++ bridge: {}", bridge_path);
println!("cargo:rerun-if-changed={}", bridge_path);

cc::Build::new()
.cpp(true)
.file(bridge_path)
.include(&xplatlib_inc)
.flag("-std=c++17")
.flag("-fPIC")
.compile("msi_bridge");

// Add library search path
println!("cargo:rustc-link-search=native={}", lib_dir.display());

// Add library to link against (remove 'lib' prefix if present)
let link_name = if lib_name.starts_with("lib") {
&lib_name[3..]
} else {
lib_name
};
println!("cargo:warning=---> Linking against library: {}", link_name);
println!("cargo:rustc-link-lib=static={}", link_name);

// Add platform-specific system libraries that MSI typically depends on
add_platform_libraries();

eprintln!("INFO: Successfully configured MSI native library: {}", msinative_lib_path);
true
} else {
eprintln!("WARNING: Could not extract library name from path: {}", msinative_lib_path);
eprintln!("MSI authentication will be disabled.");
false
}
}
Err(_) => {
// MSINATIVE_LIB_PATH not set - provide helpful message but don't fail
eprintln!("INFO: MSINATIVE_LIB_PATH environment variable is not set.");
eprintln!("MSI authentication will use stub implementation (disabled at runtime).");
eprintln!("");
eprintln!("To enable full MSI authentication, please:");
eprintln!("1. Set MSINATIVE_LIB_PATH to point to your pre-built MSI static library file");
eprintln!("2. Ensure the library file exists and is accessible");
eprintln!("");
eprintln!("Example:");
eprintln!(" export MSINATIVE_LIB_PATH=/path/to/libmsi.a # Linux/macOS");
eprintln!(" export MSINATIVE_LIB_PATH=/path/to/msi.lib # Windows");
eprintln!(" cargo build --features msi_auth");
eprintln!("");
eprintln!("If you don't need MSI authentication, build without the msi_auth feature:");
eprintln!(" cargo build");

// Return false to indicate MSI native support is not available
false
}
}
}

#[cfg(feature = "msi_auth")]
fn add_platform_libraries() {
use std::env;

let target_os = env::var("CARGO_CFG_TARGET_OS").unwrap_or_default();

// Add platform-specific system libraries that MSI authentication typically needs
match target_os.as_str() {
"windows" => {
println!("cargo:rustc-link-lib=advapi32");
println!("cargo:rustc-link-lib=winhttp");
println!("cargo:rustc-link-lib=crypt32");
println!("cargo:rustc-link-lib=ws2_32");
println!("cargo:rustc-link-lib=secur32");
println!("cargo:rustc-link-lib=bcrypt");
}
"linux" => {
println!("cargo:rustc-link-lib=stdc++");
println!("cargo:rustc-link-lib=pthread");
println!("cargo:rustc-link-lib=dl");
println!("cargo:rustc-link-lib=ssl");
println!("cargo:rustc-link-lib=crypto");
// Only link system libraries - XPlatLib is static and contains its dependencies
}
"macos" => {
println!("cargo:rustc-link-lib=c++");
println!("cargo:rustc-link-lib=pthread");
println!("cargo:rustc-link-lib=dl");
println!("cargo:rustc-link-lib=ssl");
println!("cargo:rustc-link-lib=crypto");
}
_ => {
eprintln!("WARNING: Unsupported target OS for MSI authentication: {}", target_os);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
//! Example demonstrating MSI (Managed Service Identity) authentication with Geneva uploader
//!
//! This example shows how to configure the Geneva client to use Azure Managed Identity
//! for authentication instead of certificate-based authentication.
//!
//! Run with: cargo run --example msi_auth_example

use geneva_uploader::{AuthMethod, GenevaClient, GenevaClientConfig, MsiIdentityType};
use opentelemetry_proto::tonic::logs::v1::{LogRecord, ResourceLogs, ScopeLogs};
use std::collections::HashMap;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
println!("Geneva MSI Authentication Example");
println!("=================================");

// Example 1: System-assigned managed identity
println!("\n1. System-assigned Managed Identity");
let system_config = GenevaClientConfig {
endpoint: "https://geneva.example.com".to_string(),
environment: "prod".to_string(),
account: "myaccount".to_string(),
namespace: "myservice".to_string(),
region: "westus2".to_string(),
config_major_version: 1,
auth_method: AuthMethod::ManagedIdentity {
identity: None, // System-assigned identity
fallback_to_default: false,
},
tenant: "mytenant".to_string(),
role_name: "myrole".to_string(),
role_instance: "instance1".to_string(),
max_concurrent_uploads: None,
};

println!("Config: {:?}", system_config);

// Alternative using builder method
let system_config_builder = GenevaClientConfig {
endpoint: "https://geneva.example.com".to_string(),
environment: "prod".to_string(),
account: "myaccount".to_string(),
namespace: "myservice".to_string(),
region: "westus2".to_string(),
config_major_version: 1,
auth_method: AuthMethod::Certificate {
path: "placeholder.p12".into(),
password: "placeholder".to_string(),
}, // Will be overridden
tenant: "mytenant".to_string(),
role_name: "myrole".to_string(),
role_instance: "instance1".to_string(),
max_concurrent_uploads: None,
}
.with_system_assigned_identity();

println!("Builder config: {:?}", system_config_builder);

// Example 2: User-assigned managed identity by Client ID
println!("\n2. User-assigned Managed Identity (Client ID)");
let user_config = GenevaClientConfig {
endpoint: "https://geneva.example.com".to_string(),
environment: "prod".to_string(),
account: "myaccount".to_string(),
namespace: "myservice".to_string(),
region: "westus2".to_string(),
config_major_version: 1,
auth_method: AuthMethod::Certificate {
path: "placeholder.p12".into(),
password: "placeholder".to_string(),
}, // Will be overridden
tenant: "mytenant".to_string(),
role_name: "myrole".to_string(),
role_instance: "instance1".to_string(),
max_concurrent_uploads: None,
}
.with_user_assigned_client_id("12345678-1234-1234-1234-123456789012".to_string(), true);

println!("User-assigned config: {:?}", user_config);

// Example 3: User-assigned managed identity by Object ID
println!("\n3. User-assigned Managed Identity (Object ID)");
let object_id_config = GenevaClientConfig {
endpoint: "https://geneva.example.com".to_string(),
environment: "prod".to_string(),
account: "myaccount".to_string(),
namespace: "myservice".to_string(),
region: "westus2".to_string(),
config_major_version: 1,
auth_method: AuthMethod::Certificate {
path: "placeholder.p12".into(),
password: "placeholder".to_string(),
}, // Will be overridden
tenant: "mytenant".to_string(),
role_name: "myrole".to_string(),
role_instance: "instance1".to_string(),
max_concurrent_uploads: None,
}
.with_user_assigned_object_id("87654321-4321-4321-4321-210987654321".to_string(), false);

println!("Object ID config: {:?}", object_id_config);

// Example 4: User-assigned managed identity by Resource ID
println!("\n4. User-assigned Managed Identity (Resource ID)");
let resource_id_config = GenevaClientConfig {
endpoint: "https://geneva.example.com".to_string(),
environment: "prod".to_string(),
account: "myaccount".to_string(),
namespace: "myservice".to_string(),
region: "westus2".to_string(),
config_major_version: 1,
auth_method: AuthMethod::Certificate {
path: "placeholder.p12".into(),
password: "placeholder".to_string(),
}, // Will be overridden
tenant: "mytenant".to_string(),
role_name: "myrole".to_string(),
role_instance: "instance1".to_string(),
max_concurrent_uploads: None,
}
.with_user_assigned_resource_id(
"/subscriptions/sub-id/resourceGroups/rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/my-identity".to_string(),
false,
);

println!("Resource ID config: {:?}", resource_id_config);

// Example 5: Manual construction with MsiIdentityType
println!("\n5. Manual MSI Configuration");
let manual_config = GenevaClientConfig {
endpoint: "https://geneva.example.com".to_string(),
environment: "prod".to_string(),
account: "myaccount".to_string(),
namespace: "myservice".to_string(),
region: "westus2".to_string(),
config_major_version: 1,
auth_method: AuthMethod::ManagedIdentity {
identity: Some(MsiIdentityType::ClientId(
"abcdef12-3456-7890-abcd-ef1234567890".to_string(),
)),
fallback_to_default: true, // Fallback to system-assigned if user-assigned fails
},
tenant: "mytenant".to_string(),
role_name: "myrole".to_string(),
role_instance: "instance1".to_string(),
max_concurrent_uploads: Some(8), // Custom concurrency
};

println!("Manual config: {:?}", manual_config);

// Note: In a real application, you would create the client and use it like this:
//
// let client = GenevaClient::new(system_config).await?;
//
// // Create some sample log data
// let log_record = LogRecord {
// body: Some("Test log message".into()),
// severity_text: "INFO".to_string(),
// attributes: vec![],
// ..Default::default()
// };
//
// let scope_logs = ScopeLogs {
// log_records: vec![log_record],
// ..Default::default()
// };
//
// let resource_logs = ResourceLogs {
// scope_logs: vec![scope_logs],
// ..Default::default()
// };
//
// // Upload the logs
// client.upload_logs(&[resource_logs]).await?;

println!("\n✅ MSI configuration examples completed successfully!");
println!("\nKey Points:");
println!("• System-assigned identity: Use `identity: None`");
println!("• User-assigned identity: Use `identity: Some(MsiIdentityType::...)`");
println!("• Fallback option: Set `fallback_to_default: true` to try system-assigned if user-assigned fails");
println!("• Builder methods: Use `.with_system_assigned_identity()`, `.with_user_assigned_client_id()`, etc.");
println!("• The MSI library handles token acquisition and refresh automatically");
println!("• Existing token caching and expiry logic is reused for MSI tokens");

Ok(())
}
Loading
Loading