fix: replace unstable disk serial with wired MAC for hardware fingerprint
Made-with: Cursor
This commit is contained in:
@@ -9,13 +9,14 @@ const { execSync } = require('child_process');
|
||||
* 生成硬件唯一指纹作为 box_id。
|
||||
*
|
||||
* 策略:
|
||||
* 优先 将 machine-id + CPU serial + 磁盘 serial 拼接后取 SHA-256 前 32 字符,
|
||||
* 将 machine-id + CPU serial + 有线网卡永久 MAC 拼接后取 SHA-256 前 32 字符。
|
||||
* 三者至少能拿到一个即可用此方案,防止 ghost clone 场景下 machine-id 相同的问题。
|
||||
*
|
||||
* 若均拿不到则依次退化:
|
||||
* DMI UUID → 持久化随机 UUID
|
||||
*
|
||||
* 注意:MAC 地址故意排除,网卡更换/虚拟化/Docker 都会导致其变化。
|
||||
* 注意:磁盘序列号排除(lsblk 兜底不稳定,不同内核版本/驱动可能返回空值)。
|
||||
* 有线 MAC 适用于嵌入式设备(网卡焊在主板,由固件烧录,不会更换)。
|
||||
*/
|
||||
|
||||
const PERSIST_FILE = '/etc/clawd/.box_id';
|
||||
@@ -42,37 +43,43 @@ function getCpuSerial() {
|
||||
return null;
|
||||
}
|
||||
|
||||
// ── 3. 主存储设备序列号 ────────────────────────────────────────────────────────
|
||||
function getDiskSerial() {
|
||||
// 按优先级依次尝试常见块设备
|
||||
const candidates = [
|
||||
'/sys/block/sda/device/serial',
|
||||
'/sys/block/sdb/device/serial',
|
||||
'/sys/block/nvme0n1/device/serial',
|
||||
'/sys/block/mmcblk0/device/serial', // SD 卡(RPi)
|
||||
'/sys/block/vda/device/serial', // 虚拟磁盘
|
||||
];
|
||||
// ── 3. 有线网卡永久 MAC 地址 ──────────────────────────────────────────────────
|
||||
// 嵌入式设备网卡焊在主板,MAC 由固件烧录,比磁盘序列号更稳定。
|
||||
// 优先读 ethtool 永久 MAC,其次读 sysfs 且类型为 PERM(0) 的地址。
|
||||
function getEthMac() {
|
||||
const iface = process.env.CLAWD_ETH_IFACE || 'eth0';
|
||||
|
||||
for (const p of candidates) {
|
||||
try {
|
||||
const serial = fs.readFileSync(p, 'utf8').trim().replace(/\s+/g, '');
|
||||
if (serial && serial.length >= 4 && serial !== '0000000000000000') {
|
||||
return crypto.createHash('sha256').update('disk:' + serial).digest('hex').slice(0, 32);
|
||||
}
|
||||
} catch (_) {}
|
||||
}
|
||||
|
||||
// 兜底:尝试 lsblk(需要 util-linux,大多数发行版自带)
|
||||
// 1. ethtool 永久 MAC(最可信)
|
||||
try {
|
||||
const out = execSync(
|
||||
"lsblk --nodeps -o SERIAL --noheadings 2>/dev/null | head -1",
|
||||
{ timeout: 2000, stdio: ['ignore', 'pipe', 'ignore'] }
|
||||
).toString().trim();
|
||||
if (out && out.length >= 4) {
|
||||
return crypto.createHash('sha256').update('disk:' + out).digest('hex').slice(0, 32);
|
||||
const out = execSync(`ethtool -P ${iface} 2>/dev/null`, {
|
||||
timeout: 3000, stdio: ['ignore', 'pipe', 'ignore'],
|
||||
}).toString();
|
||||
const m = out.match(/Permanent address:\s*([0-9a-f:]{17})/i);
|
||||
if (m) {
|
||||
const mac = m[1].replace(/:/g, '').toLowerCase();
|
||||
if (mac && mac !== '000000000000' && mac !== 'ffffffffffff') return mac;
|
||||
}
|
||||
} catch (_) {}
|
||||
|
||||
// 2. sysfs:仅在 address_assign_type=0(NET_ADDR_PERM)时使用
|
||||
try {
|
||||
const assignType = fs.readFileSync(
|
||||
`/sys/class/net/${iface}/addr_assign_type`, 'utf8'
|
||||
).trim();
|
||||
if (assignType === '0') {
|
||||
const mac = fs.readFileSync(`/sys/class/net/${iface}/address`, 'utf8')
|
||||
.trim().replace(/:/g, '').toLowerCase();
|
||||
if (mac && mac.length === 12 && mac !== '000000000000') return mac;
|
||||
}
|
||||
} catch (_) {}
|
||||
|
||||
// 3. 兜底:直接读 address(不验证是否随机化,总比无值强)
|
||||
try {
|
||||
const mac = fs.readFileSync(`/sys/class/net/${iface}/address`, 'utf8')
|
||||
.trim().replace(/:/g, '').toLowerCase();
|
||||
if (mac && mac.length === 12 && mac !== '000000000000') return mac;
|
||||
} catch (_) {}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -112,13 +119,13 @@ function getPersistentUUID() {
|
||||
|
||||
// ── 主函数 ────────────────────────────────────────────────────────────────────
|
||||
function getBoxId() {
|
||||
const machineId = getMachineId();
|
||||
const cpuSerial = getCpuSerial();
|
||||
const diskSerial = getDiskSerial();
|
||||
const machineId = getMachineId();
|
||||
const cpuSerial = getCpuSerial();
|
||||
const ethMac = getEthMac();
|
||||
|
||||
// 只要能拿到其中任意一项,就把三者拼接后取哈希,避免 ghost clone 场景
|
||||
if (machineId || cpuSerial || diskSerial) {
|
||||
const raw = [machineId || '', cpuSerial || '', diskSerial || ''].join(':');
|
||||
// 只要能拿到其中任意一项,就把三者拼接后取哈希
|
||||
if (machineId || cpuSerial || ethMac) {
|
||||
const raw = [machineId || '', cpuSerial || '', ethMac || ''].join(':');
|
||||
return crypto.createHash('sha256').update(raw).digest('hex').slice(0, 32);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user