Skip to content
Draft
Show file tree
Hide file tree
Changes from 3 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
176 changes: 176 additions & 0 deletions opentelemetry-exporter-geneva/geneva-uploader/build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
//! 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");
// Additional libraries required by XPlatLib (cpprestsdk dependencies)
println!("cargo:rustc-link-lib=cpprest");
println!("cargo:rustc-link-lib=boost_system");
println!("cargo:rustc-link-lib=boost_thread");
println!("cargo:rustc-link-lib=boost_atomic");
println!("cargo:rustc-link-lib=boost_chrono");
println!("cargo:rustc-link-lib=boost_regex");
}
"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