Firstly, please note that the CPUID is definitely not a commonly accessible uniquely identifying marker for any system later than an Intel Pentium III. While hashing it with MAC addresses may lead to unique markers certainly, this is due only to the unique qualities of the MACs themselves and the CPUID in that case is nothing more than circumstantial. Moreover, the resulting hash is not likely to be any more unique than the motherboard's UUID, and that is far easier to retrieve and the process is a lot less prone to err. From wikipedia.org/wiki/cpuid:
EAX=3: Processor Serial Number
See also: Pentium III § Controversy about privacy issues
This returns the processor's serial number. The processor serial number was introduced on Intel Pentium III, but due to privacy concerns, this feature is no longer implemented on later models (PSN feature bit is always cleared). Transmeta's Efficeon and Crusoe processors also provide this feature. AMD CPUs however, do not implement this feature in any CPU models.
You can view a parsed cpuid yourself by doing cat /proc/cpuinfo
or even just lscpu
.
This gets you all of the MAC addresses for the network interfaces recognized by the linux kernel, I think:
ip a | sed '\|^ *link[^ ]* |!d;s|||;s| .*||'
It may be necessary to filter that list if it might include virtual nics with randomly generated MACs. You can do this with flags in the call to ip
directly. See ip a help
for information on how to do so.
Also note that this problem is not unique to ip
and must also be dealt with if you use ifconfig
, but that it can be more reliably handled with ip
- which is part of the iproute2
network suite and is actively maintained - than it can with ifconfig
- which is a member of the net-tools
package and last saw a Linux release in 2001. Due to changing features in the kernel since its last release, ifconfig
is known to misreport some networking feature flags and its use should be avoided if at all possible.
Understand, though, that filtering with kernel interface names like eth[0-9]
is not a reliable means of doing so, as these can change based on their order of paralleled detection by udev
during the boot process. Please see Predictable Network Names for more on that.
Because dmidecode
is not installed on my system I at first thought to hash a list of hard disk serials generated like:
lsblk -nro SERIAL
Do lsblk --help
for some clues on refining that list - by disk type, say. Also consider lspci
and/or lsusb
maybe.
Combining them is easy:
{ ip a | sed ... ; lsblk ... ; } | #abbreviated... for brevity...
tr -dc '[:alnum:]' | #deletes all chars not alphanumeric - including newlines
sha256sum #gets your hash
As you've informed me you're keying user's resources on your end to their unique ids, and hard disks can not be relied upon to exist I thought to change my tack.
That considered, I looked into the filesystem again and found the /sys/class/dmi/id
folder. I checked a few of the files:
cat ./board_serial ./product_serial
###OUTPUT###
To be filled by O.E.M.
To be filled by O.E.M.
However, this one seems to be pretty good, but I won't publish the output:
sudo cat /sys/class/dmi/id/product_uuid
I expect that's where dmidecode
gets much of its information anyway and in fact it does look like it. According to man dmidecode
you can also simplify your use of that tool a great deal by specifying the argument:
dmidecode -s system-uuid
More simple still, though, you can just read the file. Note that this particular file specifically identifies a motherboard. Here's an excerpt from the 2007 kernel patch that originally implemented these exports to the /sysfs
virtual filesystem:
+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);
You may be able to use that data alone to identify the system - if the motherboard is enough. But you can combine this information with the system's MACs in the same way I demonstrated you might do with hard disks:
sudo sh <<\CMD | tr -dc '[:alnum:]' | sha256sum
ip a | sed '\|^ *link[^ ]* |!d;s|||;s| .*||'
cat /sys/class/dmi/id/product_uuid
CMD
The Linux kernel can also generate UUIDs for you:
cat /proc/sys/kernel/random/uuid #new random uuid each time file is read
Or:
cat /proc/sys/kernel/random/boot_id #randomly generated per boot
Granted, it's randomly generated and you will have to rethink ID assignment, but it's about as easy as it gets to get at least. And it should be pretty solid if you can find a means to key it.
Last, on UEFI systems this becomes far easier to do - as every EFI firmware environment variable includes its own UUID. The environment variable {Platform,}LangCodes-${UUID}
should be present on every UEFI system, should persist reboots and even most firmware upgrades and modifications, and any Linux system with the efivarfs
module loaded can list either or both names as simply as:
printf '%s\n' /sys/firmware/efi/efivars/*LangCodes-*
The older form - LangCodes-${UUID}
is apparently now deprecated, and on newer systems should be PlatformLangCodes-${UUID}
but, according to spec, one or the other should be present in every UEFI system. With little effort, you can define your own reboot persistent variables, and maybe make more use of the kernel's UUID generator in that way. If interested, look into efitools.
bash
- to generate you a UUID on any linux.cat /proc/sys/kernel/random/uuid
. – mikeserv Jul 21 '14 at 9:52