生成一致的机器唯一ID

生成一致的机器唯一ID

我们能否为每台 PC 生成一个唯一的 id,例如 uuuidgen,但除非硬件发生变化,否则它永远不会改变?我正在考虑合并 CPUID 和 MACADDR 并对它们进行哈希处理以生成一致的 ID,但我不知道如何使用 bash 脚本解析它们,我所知道的是如何从中获取 CPUID

dmidecode -t 4 | grep ID

ifconfig | grep ether

然后我需要组合这些十六进制字符串并使用 sha1 或 md5 对它们进行哈希处理以创建固定长度的十六进制字符串。
我如何解析该输出?

答案1

这两个怎么样:

$ sudo dmidecode -t 4 | grep ID | sed 's/.*ID://;s/ //g'
52060201FBFBEBBF
$ ifconfig | grep eth1 | awk '{print $NF}' | sed 's/://g'
0126c9da2c38

然后您可以将它们组合并散列:

$ echo $(sudo dmidecode -t 4 | grep ID | sed 's/.*ID://;s/ //g') \
       $(ifconfig | grep eth1 | awk '{print $NF}' | sed 's/://g') | sha256sum 
59603d5e9957c23e7099c80bf137db19144cbb24efeeadfbd090f89a5f64041f  -

要删除尾部破折号,请再添加一个管道:

$ echo $(sudo dmidecode -t 4 | grep ID | sed 's/.*ID://;s/ //g') \
       $(ifconfig | grep eth1 | awk '{print $NF}' | sed 's/://g') | sha256sum |
  awk '{print $1}'
59603d5e9957c23e7099c80bf137db19144cbb24efeeadfbd090f89a5f64041f

正如@mikeserv 指出的那样在他的回答中, 接口名称可以换靴子之间。这意味着今天的 eth0 明天可能是 eth1,所以如果你 grep foreth0你可能会在不同的启动上得到不同的 MAC 地址。我的系统没有这种行为,所以我无法真正测试,但可能的解决方案是:

  1. grep forHWaddr的输出,ifconfig但保留所有这些内容,而不仅仅是与特定网卡对应的内容。例如,在我的系统上我有:

    $ ifconfig | grep HWaddr
    eth1      Link encap:Ethernet  HWaddr 00:24:a9:bd:2c:28  
    wlan0     Link encap:Ethernet  HWaddr c4:16:19:4f:ac:g5  
    

    通过获取两个 MAC 地址并将它们传递给sha256sum,您应该能够获得一个唯一且稳定的名称,无论哪个 NIC 被称为什么:

    $ echo $(sudo dmidecode -t 4 | grep ID | sed 's/.*ID://;s/ //g') \
         $(ifconfig | grep -oP 'HWaddr \K.*' | sed 's/://g') | sha256sum |
          awk '{print $1}'
    662f0036cba13c2ddcf11acebf087ebe1b5e4044603d534dab60d32813adc1a5    
    

    请注意,哈希值与上面的不同,因为我传递了ifconfigto返回的两个 MAC 地址sha256sum

  2. 根据硬盘驱动器的 UUID 创建一个哈希值:

    $ blkid | grep -oP 'UUID="\K[^"]+' | sha256sum | awk '{print $1}'
    162296a587c45fbf807bb7e43bda08f84c56651737243eb4a1a32ae974d6d7f4
    

答案2

首先请注意CPUID肯定是不是一个通用的唯一识别标记,适用于 Intel Pentium III 之后的任何系统。虽然用 MAC 地址对其进行散列肯定会产生唯一的标记,但这只是由于 MAC 本身的独特品质造成的,在这种情况下,CPUID 只不过是环境因素。此外,生成的哈希值不太可能比主板的 UUID 更独特,而且检索起来更容易,而且过程也更不容易出错。从wikipedia.org/wiki/cpuid:

EAX=3:处理器序列号

也可以看看:Pentium III § 关于隐私问题的争议

这将返回处理器的序列号。处理器序列号是在 Intel Pentium III 上引入的,但出于隐私考虑,此功能在后续型号上不再实现(PSN 功能位始终被清除)。 Transmeta 的 Efficeon 和 Crusoe 处理器也提供此功能。然而,AMD CPU 并未在任何 CPU 型号中实现此功能。

cat /proc/cpuinfo您可以通过执行或 甚至只是查看自己解析的 cpuid lscpu

这将为您提供 Linux 内核识别的网络接口的所有 MAC 地址,我认为:

ip a | sed '\|^ *link[^ ]* |!d;s|||;s| .*||'

如果该列表可能包含具有随机生成的 MAC 的虚拟网卡,则可能需要过滤该列表。您可以使用直接调用中的标志来执行此操作ipip a help有关如何执行此操作的信息,请参阅 参考资料。

另请注意,这个问题并不是唯一的,ip如果您使用,也必须处理ifconfig,但它可以更可靠地处理ip- 这是iproute2网络套件并积极维护 - 比它可以ifconfig- 这是的成员net-tools包裹最后一次看到 Linux2001年发布。由于自上次发布以来内核中的功能发生了变化,ifconfig已知误报一些网络功能标志如果可能的话,应该避免使用它。

不过,请理解,使用内核接口名称进行过滤eth[0-9]并不是一种可靠的方法,因为这些名称可能会根据udev启动过程中并行检测的顺序而发生变化。请参见可预测的网络名称了解更多相关信息。

因为dmidecode我的系统上没有安装,所以我首先想到对生成的硬盘序列号列表进行哈希处理,如下所示:

lsblk -nro SERIAL

寻找lsblk --help一些关于完善该列表的线索 - 例如,按磁盘类型。还要考虑lspci和/或lsusb也许。

将它们组合起来很容易:

{ ip a | sed ... ; lsblk ... ; } | #abbreviated... for brevity...
    tr -dc '[:alnum:]' | #deletes all chars not alphanumeric - including newlines
    sha256sum #gets your hash

正如您告诉我的,您正在将用户的资源绑定到其唯一的 ID,并且不能依赖硬盘的存在,我想改变我的策略。

考虑到这一点,我再次查看文件系统并找到了该/sys/class/dmi/id文件夹。我检查了一些文件:

cat ./board_serial ./product_serial

###OUTPUT###
To be filled by O.E.M.
To be filled by O.E.M.

然而,这个似乎相当不错,但我不会发布输出:

sudo cat /sys/class/dmi/id/product_uuid

我希望这就是dmidecode获取大部分信息的地方,事实上它看起来确实像这样。根据man dmidecode您还可以通过指定参数来极大地简化该工具的使用:

dmidecode -s system-uuid

不过,更简单的是,您只需读取该文件即可。请注意,此特定文件专门标识主板。这是摘录自2007内核补丁最初实现了对/sysfs虚拟文件系统的这些导出:

+DEFINE_DMI_ATTR_WITH_SHOW(bios_vendor,      0444, DMI_BIOS_VENDOR);
+DEFINE_DMI_ATTR_WITH_SHOW(bios_version,         0444, DMI_BIOS_VERSION);
+DEFINE_DMI_ATTR_WITH_SHOW(bios_date,        0444, DMI_BIOS_DATE);
+DEFINE_DMI_ATTR_WITH_SHOW(sys_vendor,       0444, DMI_SYS_VENDOR);
+DEFINE_DMI_ATTR_WITH_SHOW(product_name,         0444, DMI_PRODUCT_NAME);
+DEFINE_DMI_ATTR_WITH_SHOW(product_version,   0444, DMI_PRODUCT_VERSION);
+DEFINE_DMI_ATTR_WITH_SHOW(product_serial,    0400, DMI_PRODUCT_SERIAL);
+DEFINE_DMI_ATTR_WITH_SHOW(product_uuid,         0400, DMI_PRODUCT_UUID);
+DEFINE_DMI_ATTR_WITH_SHOW(board_vendor,         0444, DMI_BOARD_VENDOR);
+DEFINE_DMI_ATTR_WITH_SHOW(board_name,       0444, DMI_BOARD_NAME);
+DEFINE_DMI_ATTR_WITH_SHOW(board_version,     0444, DMI_BOARD_VERSION);
+DEFINE_DMI_ATTR_WITH_SHOW(board_serial,         0400, DMI_BOARD_SERIAL);
+DEFINE_DMI_ATTR_WITH_SHOW(board_asset_tag,   0444, DMI_BOARD_ASSET_TAG);
+DEFINE_DMI_ATTR_WITH_SHOW(chassis_vendor,    0444, DMI_CHASSIS_VENDOR);
+DEFINE_DMI_ATTR_WITH_SHOW(chassis_type,         0444, DMI_CHASSIS_TYPE);
+DEFINE_DMI_ATTR_WITH_SHOW(chassis_version,   0444, DMI_CHASSIS_VERSION);
+DEFINE_DMI_ATTR_WITH_SHOW(chassis_serial,    0400, DMI_CHASSIS_SERIAL);
+DEFINE_DMI_ATTR_WITH_SHOW(chassis_asset_tag, 0444, DMI_CHASSIS_ASSET_TAG);

如果主板足够的话,您也许可以单独使用该数据来识别系统。但是您可以将此信息与系统的 MAC 结合起来,就像我演示的对硬盘所做的那样:

sudo sh <<\CMD | tr -dc '[:alnum:]' | sha256sum
        ip a | sed '\|^ *link[^ ]* |!d;s|||;s| .*||'
        cat /sys/class/dmi/id/product_uuid 
CMD

Linux 内核还可以为您生成 UUID:

cat /proc/sys/kernel/random/uuid #new random uuid each time file is read

或者:

cat /proc/sys/kernel/random/boot_id #randomly generated per boot

当然,它是随机生成的,您将不得不重新考虑 ID 分配,但这非常简单得到至少。如果你能找到一种方法来锁定它,它应该非常坚固。

最后,在 UEFI 系统上,这变得更容易 - 因为每个 EFI 固件环境变量都包含自己的 UUID。环境变量{Platform,}LangCodes-${UUID}应该存在于每个 UEFI 系统上,应该持续重新启动,甚至最多固件升级和修改,任何efivarfs加载该模块的 Linux 系统都可以列出其中一个或两个名称,如下所示:

printf '%s\n' /sys/firmware/efi/efivars/*LangCodes-*

旧的形式 -LangCodes-${UUID}显然是现已弃用,并且在较新的系统上应该是PlatformLangCodes-${UUID},但根据规范,每个 UEFI 系统中都应该存在其中之一。只需很少的努力,您就可以定义自己的重新启动持久变量,并且可能以这种方式更多地利用内核的 UUID 生成器。有兴趣的话可以看看埃菲工具

答案3

许多现代发行版都附带一个文件,/etc/machine-id其中包含最有可能唯一的十六进制 32 字符字符串。它源自systemd,其中联机帮助页包含更多信息,并且可能适合您的目的。

答案4

当硬件改变时,是否需要改变机器ID?机器 ID 是否用于保护某些内容?我认为拥有“一致”机器 ID 的最佳方法是在系统上的某个位置存储随机字符串,这样如果任何硬件发生变化,机器 ID 也不会改变。这对于硬件访问受到限制且 MAC ID 为 00:00:00:00 的虚拟化系统也很有用

尝试类似 sh 脚本来创建并获取 ID:

#!/bin/sh
FILE="/etc/machine-id"

if [ ! -f $FILE ]; then
    cat /dev/urandom|tr -dc A-Z0-9|head -c32 > $FILE;
fi

cat $FILE;

相关内容