29

Can we generate a unique id for each PC, something like uuuidgen, but it will never change unless there are hardware changes? I was thinking about merging CPUID and MACADDR and hash them to generate a consistent ID, but I have no idea how to parse them using bash script, what I know is how can I get CPUID from

dmidecode -t 4 | grep ID

and

ifconfig | grep ether

then I need to combine those hex strings and hash them using sha1 or md5 to create fixed length hex string.
How can I parse that output?

7
  • 4
    What is exactly the problem you are trying to solve using this method?
    – Darkhogg
    Jul 16, 2014 at 12:57
  • 2
    I'm with Darkhogg. It's generally a bad idea to try to do these kinds of things this day-and-age. Virtualization has made the practice of binding software to physical hardware kind of pointless. There's usually a better answer if you closely examine your requirements (which is what Darkhogg is driving at).
    – Calphool
    Jul 16, 2014 at 15:48
  • 4
    i dont use this to bind a software to a machine, it is linux mining rig that need to identify itself to cloud control and monitoring service, instead of naming thousands of rig manually, i need to uniquely identify them using their hardware ID
    – uray
    Jul 16, 2014 at 15:58
  • 1
    @user77710: If that's the case, then do you really care about the hardware at all? Why not just generate a UUID on the machine if one doesn't exist. That's the point of a UUID -- they're universally unique (their odds of overlapping are astronomically improbable). serverfault.com/questions/103359/how-to-create-a-uuid-in-bash
    – Calphool
    Jul 18, 2014 at 17:10
  • 1
    @JoeRounceville - I didn't mean that SecureBoot itself was the solution - though it does support self-signed certificates - but rather its method. But it uses an API offered by the system's firmware - and any UEFI system will have as many UUIDs as you could need already setup in the name of each firmware variable - see my answer. Also, you don't need any application - or bash - to generate you a UUID on any linux. cat /proc/sys/kernel/random/uuid.
    – mikeserv
    Jul 21, 2014 at 9:52

6 Answers 6

30

How about these two:

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

You can then combine and hash them with:

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

To remove the trailing dash, add one more pipe:

$ 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

As @mikeserv points out in his answer, the interface name can change between boots. This means that what is eth0 today might be eth1 tomorrow, so if you grep for eth0 you might get a different MAC address on different boots. My system does not behave this way so I can't really test but possible solutions are:

  1. Grep for HWaddr in the output of ifconfig but keep all of them, not just the one corresponding to a specific NIC. For example, on my system I have:

    $ 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  
    

    By grabbing both MAC addresses and passing them through sha256sum, you should be able to get a unique and stable name, irrespective of which NIC is called what:

    $ 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    
    

    Note that the hash is different from the ones above because I am passing both MAC addresses returned by ifconfig to sha256sum.

  2. Create a hash based on the UUIDs of your hard drive(s) instead:

    $ blkid | grep -oP 'UUID="\K[^"]+' | sha256sum | awk '{print $1}'
    162296a587c45fbf807bb7e43bda08f84c56651737243eb4a1a32ae974d6d7f4
    
6
  • its nice, but how to get rid trailing dash '-' ?
    – uray
    Jul 16, 2014 at 16:18
  • @user77710 see updated answer.
    – terdon
    Jul 16, 2014 at 16:24
  • 1
    I guess cpuid is worse... wikipedia.org/wiki/cpuid
    – mikeserv
    Jul 16, 2014 at 23:06
  • @mikeserv ah, yes indeed, I see your point. Thanks, answer edited.
    – terdon
    Jul 16, 2014 at 23:55
  • 1
    Generates same ID for all guest OS on same host. Sep 30, 2015 at 13:49
26

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.

7
  • it doesn't even have harddisk or any disk running, its mining rig, if you dont know what it is see this diit.cz/sites/default/files/images/3988/… virtual machine can't run 6 GPU on single motherboard
    – uray
    Jul 16, 2014 at 16:12
  • anyway it should never use any random number as it beat the purpose of consistenly naming the machine, as they boot up the ID shouldn't change
    – uray
    Jul 16, 2014 at 16:20
  • @user77710 - Dang, man, that thing is cool. Is that one machine? Probably you're right, but it might be possible with some combo of XDMX and Chromium - some distributed graphics stuff. Anyway, it wouldn't matter - I had it backwards. Who would want to beat themselves out of their money? I was thinking some software licensing thing or something - you're doing bank accounts.
    – mikeserv
    Jul 16, 2014 at 16:30
  • Them's some nifty tricks you got there, +1.
    – terdon
    Jul 17, 2014 at 0:02
  • @terdon - the compliment is much appreciated, but, for the most part, they're not tricks. ip2 is specifically designed to be parsed, and it may be that I'm not doing it well enough - I suspect the same thing can be done almost without grep/sed. Probably the same could be done as easily with udevadm. And each EFI environment variable name is designed to be uniquely identified for exactly these sorts of situations.
    – mikeserv
    Jul 17, 2014 at 1:41
25

Many modern distributions ship a file /etc/machine-id containing a most probably unique hexadecimal 32-character string. It originates from systemd, where a manpage has more information, and may be appropriate for your purpose.

3
  • +1, @XZS, you could add the relevant information from the URL here as well.
    – Ramesh
    Jul 16, 2014 at 17:54
  • 1
    This is good, and I found similar information and almost used it... but I was already committed to a different route. Still, it should be noted that this is dbus specific, I think, and changes if an operating system is wiped/reinstalled.
    – mikeserv
    Jul 16, 2014 at 17:58
  • This is pretty darn awesome.
    – GeneCode
    Aug 20, 2018 at 0:05
9

On a lot of Linux machines, the file /var/lib/dbus/machine-id contains a unique id for each Linux distribution and can be accessed by a call to dbus_get_local_machine_id(). This is probably the same as the /etc/machine-id mentioned above. It works on virtual Linux installations too. I've checked it on current Ubuntu, SuSE and CentOS distributions.

2
  • 1
    In Fedora 19 + 20 that file does not exist. It's here: /etc/machine-id.
    – slm
    Dec 9, 2014 at 3:07
  • Perhaps I wasn't clear enough. My point was that if you don't find it one place, look in the other. Or write your own program using the function call.
    – rankeney
    Dec 10, 2014 at 16:38
0

Do you need the machine ID to change when the hardware changes? Is the machine ID being used to protect something? The best way I believe to have a "consistent" machine ID is by storing a random string somewhere on the system and that way if any of the hardware changes, then the machine ID won't change either. This is also good for virtualized systems where hardware access is restricted and the MAC ID is 00:00:00:00

Try something like this sh script to create and get the 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;
2
  • 1
    Since your /dev/urandom indicates Linux anyway, you can just do cat /proc/sys/kernel/random/uuid >$FILE which randomly generates a properly formatted UUID on every read. Still, any disk-implemented persistence is subject to deletion, and, provided that is acceptable, and provided dbus is installed, you should probably do as @XZS has suggested instead.
    – mikeserv
    Jul 17, 2014 at 2:30
  • @mikeserv Since /etc/machine-id is used by systemd, I think you should not change the format of the uuid.
    – frodeborli
    Mar 2, 2020 at 10:38
0

The other answers give a number of ways of extracting ids from hardware. You could decide to use one piece of hardware as the identifier or many. This is problematic if you need to swap or replace pieces of hardware arbitrarily.

Some people might instead store id a generated id on their harddrive (or use the UUID) but harddrides can be clone.

TPM modules and secure boot might provide a means of binding motherboard and other hardware with an install onto a harddrive.

It's always a bit easier in these cases if you give more information about what you want to accomplish.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .