我们能否为每台 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 地址。我的系统没有这种行为,所以我无法真正测试,但可能的解决方案是:
grep for
HWaddr
的输出,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
请注意,哈希值与上面的不同,因为我传递了
ifconfig
to返回的两个 MAC 地址sha256sum
。根据硬盘驱动器的 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 的虚拟网卡,则可能需要过滤该列表。您可以使用直接调用中的标志来执行此操作ip
。ip 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;