remove duplicate hardware

This commit is contained in:
grabbit 2025-08-14 23:40:32 +08:00
parent 2c90276e3e
commit 56f957a346
3 changed files with 17 additions and 156 deletions

View File

@ -1,144 +0,0 @@
use anyhow::{Context, Result};
use std::fs;
/// Extracts a unique hardware identifier from the system
/// This function reads /proc/cpuinfo to find a stable CPU serial number
pub fn get_hardware_id() -> Result<String> {
// Try to read /proc/cpuinfo first (common on Raspberry Pi and other ARM systems)
if let Ok(hardware_id) = read_cpu_serial() {
return Ok(hardware_id);
}
// Fallback: try to read machine-id
if let Ok(machine_id) = read_machine_id() {
return Ok(machine_id);
}
// Last resort: generate a warning and use hostname + MAC address hash
eprintln!("Warning: Could not read CPU serial or machine-id, using fallback method");
get_fallback_id()
}
/// Reads CPU serial number from /proc/cpuinfo
/// This is the most reliable method on Raspberry Pi systems
fn read_cpu_serial() -> Result<String> {
let cpuinfo = fs::read_to_string("/proc/cpuinfo")
.context("Failed to read /proc/cpuinfo")?;
for line in cpuinfo.lines() {
if line.starts_with("Serial") {
if let Some(serial) = line.split(':').nth(1) {
let serial = serial.trim();
if !serial.is_empty() && serial != "0000000000000000" {
return Ok(format!("CPU_{}", serial));
}
}
}
}
anyhow::bail!("No valid CPU serial found in /proc/cpuinfo")
}
/// Reads machine ID from /etc/machine-id (systemd systems)
fn read_machine_id() -> Result<String> {
let machine_id = fs::read_to_string("/etc/machine-id")
.context("Failed to read /etc/machine-id")?;
let machine_id = machine_id.trim();
if machine_id.len() >= 8 {
Ok(format!("MACHINE_{}", &machine_id[..16]))
} else {
anyhow::bail!("Invalid machine-id format")
}
}
/// Fallback method to generate a hardware ID
/// Uses hostname + network interface information
fn get_fallback_id() -> Result<String> {
use std::process::Command;
// Get hostname
let hostname_output = Command::new("hostname")
.output()
.context("Failed to execute hostname command")?;
let hostname = String::from_utf8_lossy(&hostname_output.stdout)
.trim()
.to_string();
// Try to get MAC address from network interfaces
if let Ok(mac) = get_primary_mac_address() {
return Ok(format!("FALLBACK_{}_{}", hostname, mac));
}
// Very last resort: just use hostname with timestamp
let timestamp = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap_or_default()
.as_secs();
Ok(format!("FALLBACK_{}_{}", hostname, timestamp))
}
/// Attempts to get the MAC address of the primary network interface
fn get_primary_mac_address() -> Result<String> {
let interfaces_dir = "/sys/class/net";
let entries = fs::read_dir(interfaces_dir)
.context("Failed to read network interfaces directory")?;
for entry in entries {
let entry = entry?;
let interface_name = entry.file_name();
let interface_name = interface_name.to_string_lossy();
// Skip loopback and common virtual interfaces
if interface_name == "lo" || interface_name.starts_with("docker")
|| interface_name.starts_with("veth") {
continue;
}
let mac_path = format!("{}/{}/address", interfaces_dir, interface_name);
if let Ok(mac) = fs::read_to_string(&mac_path) {
let mac = mac.trim().replace(':', "");
if mac.len() == 12 && mac != "000000000000" {
return Ok(mac.to_uppercase());
}
}
}
anyhow::bail!("No valid MAC address found")
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_hardware_id_format() {
// Test that we can get some kind of hardware ID
// The exact format will depend on the system, but it should not be empty
let result = get_hardware_id();
match result {
Ok(id) => {
assert!(!id.is_empty(), "Hardware ID should not be empty");
assert!(id.len() >= 8, "Hardware ID should be at least 8 characters");
println!("Hardware ID: {}", id);
}
Err(e) => {
println!("Could not get hardware ID: {}", e);
// On systems without the expected files, this might fail
// That's okay for testing purposes
}
}
}
#[test]
fn test_fallback_id() {
let result = get_fallback_id();
assert!(result.is_ok(), "Fallback ID generation should always work");
let id = result.unwrap();
assert!(id.starts_with("FALLBACK_"), "Fallback ID should have correct prefix");
assert!(id.len() > 10, "Fallback ID should be reasonably long");
}
}

View File

@ -1,7 +1,6 @@
use clap::{Parser, Subcommand};
use anyhow::Result;
mod hardware;
mod config;
mod api;
mod frame_data;
@ -17,10 +16,10 @@ mod hardware_fingerprint;
mod device_registration;
mod websocket_client;
use hardware::get_hardware_id;
use config::{Config, ConfigManager};
use api::ApiClient;
use app::Application;
use hardware_fingerprint::HardwareFingerprintService;
#[derive(Parser)]
#[command(name = "meteor-edge-client")]
@ -145,7 +144,9 @@ async fn register_device(jwt_token: String, api_url: String) -> Result<()> {
// Get hardware ID
println!("🔍 Reading hardware identifier...");
let hardware_id = get_hardware_id()?;
let mut fingerprint_service = HardwareFingerprintService::new();
let fingerprint = fingerprint_service.generate_fingerprint().await?;
let hardware_id = fingerprint.computed_hash[..16].to_string();
println!(" Hardware ID: {}", hardware_id);
// Create API client
@ -184,13 +185,17 @@ async fn show_status() -> Result<()> {
println!("📊 Meteor Edge Client Status");
println!("============================");
// Show hardware information
match get_hardware_id() {
Ok(hardware_id) => {
println!("🔧 Hardware ID: {}", hardware_id);
// Show hardware information using the new fingerprint service
let mut fingerprint_service = HardwareFingerprintService::new();
match fingerprint_service.generate_fingerprint().await {
Ok(fingerprint) => {
println!("🔧 Hardware ID: {}", fingerprint.computed_hash[..16].to_string());
println!(" CPU ID: {}", fingerprint.cpu_id);
println!(" Board Serial: {}", fingerprint.board_serial);
println!(" MAC Addresses: {}", fingerprint.mac_addresses.join(", "));
}
Err(e) => {
println!("❌ Could not read hardware ID: {}", e);
println!("❌ Could not read hardware fingerprint: {}", e);
}
}

View File

@ -12,7 +12,7 @@ export interface HardwareFingerprint {
board_serial: string;
mac_addresses: string[];
disk_uuid: string;
tmp_attestation?: string;
tpm_attestation?: string;
}
export interface SecurityChallenge {
@ -131,8 +131,8 @@ export class DeviceSecurityService {
}
// TPM attestation validation if available
if (fingerprint.tmp_attestation) {
const tpmValid = await this.validateTpmAttestation(fingerprint.tmp_attestation);
if (fingerprint.tpm_attestation) {
const tpmValid = await this.validateTpmAttestation(fingerprint.tpm_attestation);
if (!tpmValid) {
riskFactors.push('TPM attestation validation failed');
confidence *= 0.7;
@ -324,7 +324,7 @@ export class DeviceSecurityService {
fingerprint.board_serial,
...fingerprint.mac_addresses.sort(),
fingerprint.disk_uuid,
fingerprint.tmp_attestation || '',
fingerprint.tpm_attestation || '',
].join('|');
return crypto.createHash('sha256').update(data).digest('hex');