remove duplicate hardware
This commit is contained in:
parent
2c90276e3e
commit
56f957a346
@ -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");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,7 +1,6 @@
|
|||||||
use clap::{Parser, Subcommand};
|
use clap::{Parser, Subcommand};
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
|
|
||||||
mod hardware;
|
|
||||||
mod config;
|
mod config;
|
||||||
mod api;
|
mod api;
|
||||||
mod frame_data;
|
mod frame_data;
|
||||||
@ -17,10 +16,10 @@ mod hardware_fingerprint;
|
|||||||
mod device_registration;
|
mod device_registration;
|
||||||
mod websocket_client;
|
mod websocket_client;
|
||||||
|
|
||||||
use hardware::get_hardware_id;
|
|
||||||
use config::{Config, ConfigManager};
|
use config::{Config, ConfigManager};
|
||||||
use api::ApiClient;
|
use api::ApiClient;
|
||||||
use app::Application;
|
use app::Application;
|
||||||
|
use hardware_fingerprint::HardwareFingerprintService;
|
||||||
|
|
||||||
#[derive(Parser)]
|
#[derive(Parser)]
|
||||||
#[command(name = "meteor-edge-client")]
|
#[command(name = "meteor-edge-client")]
|
||||||
@ -145,7 +144,9 @@ async fn register_device(jwt_token: String, api_url: String) -> Result<()> {
|
|||||||
|
|
||||||
// Get hardware ID
|
// Get hardware ID
|
||||||
println!("🔍 Reading hardware identifier...");
|
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);
|
println!(" Hardware ID: {}", hardware_id);
|
||||||
|
|
||||||
// Create API client
|
// Create API client
|
||||||
@ -184,13 +185,17 @@ async fn show_status() -> Result<()> {
|
|||||||
println!("📊 Meteor Edge Client Status");
|
println!("📊 Meteor Edge Client Status");
|
||||||
println!("============================");
|
println!("============================");
|
||||||
|
|
||||||
// Show hardware information
|
// Show hardware information using the new fingerprint service
|
||||||
match get_hardware_id() {
|
let mut fingerprint_service = HardwareFingerprintService::new();
|
||||||
Ok(hardware_id) => {
|
match fingerprint_service.generate_fingerprint().await {
|
||||||
println!("🔧 Hardware ID: {}", hardware_id);
|
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) => {
|
Err(e) => {
|
||||||
println!("❌ Could not read hardware ID: {}", e);
|
println!("❌ Could not read hardware fingerprint: {}", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -12,7 +12,7 @@ export interface HardwareFingerprint {
|
|||||||
board_serial: string;
|
board_serial: string;
|
||||||
mac_addresses: string[];
|
mac_addresses: string[];
|
||||||
disk_uuid: string;
|
disk_uuid: string;
|
||||||
tmp_attestation?: string;
|
tpm_attestation?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface SecurityChallenge {
|
export interface SecurityChallenge {
|
||||||
@ -131,8 +131,8 @@ export class DeviceSecurityService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TPM attestation validation if available
|
// TPM attestation validation if available
|
||||||
if (fingerprint.tmp_attestation) {
|
if (fingerprint.tpm_attestation) {
|
||||||
const tpmValid = await this.validateTpmAttestation(fingerprint.tmp_attestation);
|
const tpmValid = await this.validateTpmAttestation(fingerprint.tpm_attestation);
|
||||||
if (!tpmValid) {
|
if (!tpmValid) {
|
||||||
riskFactors.push('TPM attestation validation failed');
|
riskFactors.push('TPM attestation validation failed');
|
||||||
confidence *= 0.7;
|
confidence *= 0.7;
|
||||||
@ -324,7 +324,7 @@ export class DeviceSecurityService {
|
|||||||
fingerprint.board_serial,
|
fingerprint.board_serial,
|
||||||
...fingerprint.mac_addresses.sort(),
|
...fingerprint.mac_addresses.sort(),
|
||||||
fingerprint.disk_uuid,
|
fingerprint.disk_uuid,
|
||||||
fingerprint.tmp_attestation || '',
|
fingerprint.tpm_attestation || '',
|
||||||
].join('|');
|
].join('|');
|
||||||
|
|
||||||
return crypto.createHash('sha256').update(data).digest('hex');
|
return crypto.createHash('sha256').update(data).digest('hex');
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user