Class: SafeDb::MachineId
- Inherits:
-
Object
- Object
- SafeDb::MachineId
- Defined in:
- lib/utils/identity/machine.id.rb
Overview
This class knows how to derive information from the machine environment to aide in producing identifiers unique to the machine and/or workstation, with functionality similar to that required by licensing software.
Identity is Similar to Licensing Software | Except Deeper
Deriving the identity string follows similar principles to licensing software that attempts to determine whether the operating environment is the same or different. But it goes deeper than licensing software as it is not only concerned about the same workstation - it is also concerned about the same shell or command line interface.
Known Issues
The dependent macaddr gem is known to fail in scenarios where a VPN tunnel is active and a tell tale sign is the ifconfig command returning the tun0 interface rather than “eth0” or something that resembles “ensp21”.
This is one of the error messages resulting from such a case.
macaddr.rb:86 from_getifaddrs undefined method pfamily (NoMethodError)
Class Method Summary collapse
-
.derive_shell_identifier ⇒ String
This method returns a plaintext string hat is guaranteed to be the same whenever called within the same shell for the same user on the same workstation, virtual machine, container or SSH branch and different whenever a new shell is acquired.
-
.derive_user_machine_id ⇒ String
This method uses a one-way function to return a combinatorial digested machine identification string using a number of distinct input parameters to deliver the characteristic of producing the same identifier for the same machine, virtual machine, workstation and/or compute element, and reciprocally, a different one on a different machine.
-
.get_ancestor_pid(use_grandparent_pid) ⇒ String
Return an ancestor process ID meaning return either the parent process ID or the grandparent process ID.
-
.get_bootup_id ⇒ String
If you need to know whether a Linux computer has been rebooted or you need an identifier that stays the same until the computer reboots, look no further than the read only (non sudoer accessible) **boot id**.
-
.get_machine_id ⇒ String
The machine identifier is a UUID based hash value that is tied to the CPU and motherboard of the machine.
-
.log_reboot_times ⇒ Object
Logs a list of the last few times that this machine has rebooted.
Class Method Details
.derive_shell_identifier ⇒ String
This method returns a plaintext string hat is guaranteed to be the same whenever called within the same shell for the same user on the same workstation, virtual machine, container or SSH branch and different whenever a new shell is acquired.
What is really important is that the shell identity string changes when
-
the command shell changes
-
the user switches to another workstation user
-
the workstation or machine host is changed
-
the user SSH’s into another shell
Unchanged | When Should it Remain Unchanged?
Remaining unchanged is a feature that is as important and this must be so when and/or after
-
the user returns to a command shell
-
the user switches back to using a domain
-
the user exits their remote SSH branch
-
sudo is used to execute the commands
-
the user comes back to their workstation
-
the clock ticks into another day, month, year …
112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 |
# File 'lib/utils/identity/machine.id.rb', line 112 def self.derive_shell_identifier require 'socket' # -- Ensure that the most significant data points # -- come first just like with numbers. identity_text = [ get_bootup_id(), ENV[ "USER" ], Socket.gethostname() ].join return identity_text.to_alphanumeric end |
.derive_user_machine_id ⇒ String
This method uses a one-way function to return a combinatorial digested machine identification string using a number of distinct input parameters to deliver the characteristic of producing the same identifier for the same machine, virtual machine, workstation and/or compute element, and reciprocally, a different one on a different machine.
The userspace is also a key machine identifier so a different machine user generates a different identifier when all other things remain equal.
43 44 45 46 47 48 49 50 51 52 53 54 55 |
# File 'lib/utils/identity/machine.id.rb', line 43 def self.derive_user_machine_id require 'socket' identity_text = [ ENV[ "USER" ], get_machine_id(), Socket.gethostname() ].join.reverse return identity_text end |
.get_ancestor_pid(use_grandparent_pid) ⇒ String
Return an ancestor process ID meaning return either the parent process ID or the grandparent process ID. The one returned depends on the paremeter boolean value.
Command Used to find the grandparent process ID.
$ ps -fp 31870 | awk "/tty/"' { print $3 } '
$ ps -fp 31870 | awk "/31870/"' { print $3 } '
The one liner finds the parental process ID of the process with the given parameter process ID.
$ ps -fp 31870
UID PID PPID C STIME TTY TIME CMD
joe 31870 2618 0 12:55 tty2 00:01:03 /usr/bin/emacs25
The ps command outputs two (2) lines and awk is employed to select the line containing the already known ID. We then print the 3rd string in the line which we expect to be the parent PID of the PID.
Warning | Do Not Use $PPID
Using $PPID is fools gold because the PS command itself runs as another process so $PPID is this (calling) process ID and the number returned is exactly the same as the parent ID of this process - which is actually the grandparent of the invoked ps process.
201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 |
# File 'lib/utils/identity/machine.id.rb', line 201 def self.get_ancestor_pid( use_grandparent_pid ) parental_process_id = Process.ppid.to_s() grandparent_pid_cmd = "ps -fp #{parental_process_id} | awk \"/#{parental_process_id}/\"' { print $3 } '" raw_grandparent_pid = %x[#{grandparent_pid_cmd}] the_grandparent_pid = raw_grandparent_pid.chomp log.debug(x) { "QQQQQ ~> QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ" } log.debug(x) { "QQQQQ ~> Request Bool Use GPPID is ~> [[ #{use_grandparent_pid} ]]" } log.debug(x) { "QQQQQ ~> Main Parent Process ID is ~> [[ #{parental_process_id} ]]" } log.debug(x) { "QQQQQ ~> GrandParent Process ID is ~> [[ #{the_grandparent_pid} ]]" } log.debug(x) { "QQQQQ ~> QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ" } return ( use_grandparent_pid ? the_grandparent_pid : parental_process_id ) end |
.get_bootup_id ⇒ String
If you need to know whether a Linux computer has been rebooted or you need an identifier that stays the same until the computer reboots, look no further than the read only (non sudoer accessible) **boot id**.
In the modern era of virtualization you should always check the behaviour of the above identifiers when used inside
-
docker containers
-
Amazon EC2 servers (or Azure or GCE)
-
vagrant (VirtualBox/VMWare)
-
Windows MSGYWIN (Ubuntu) environments
-
Kubernetes pods
145 146 147 148 149 150 151 |
# File 'lib/utils/identity/machine.id.rb', line 145 def self.get_bootup_id() bootup_id_cmd = "cat /proc/sys/kernel/random/boot_id" bootup_id_str = %x[ #{bootup_id_cmd} ] return bootup_id_str.chomp end |
.get_machine_id ⇒ String
The machine identifier is a UUID based hash value that is tied to the CPU and motherboard of the machine. This read-only identifier can be accessed without sudoer permissions so is perfect for license generators and environment sensitive software.
In the modern era of virtualization you should always check the behaviour of the above identifiers when used inside
-
docker containers
-
Amazon EC2 servers (or Azure or GCE)
-
vagrant (VirtualBox/VMWare)
-
Windows MSGYWIN (Ubuntu) environments
-
Kubernetes pods
73 74 75 76 77 78 79 |
# File 'lib/utils/identity/machine.id.rb', line 73 def self.get_machine_id machine_id_cmd = "cat /etc/machine-id" machine_id_str = %x[ #{machine_id_cmd} ] return machine_id_str.chomp end |
.log_reboot_times ⇒ Object
Logs a list of the last few times that this machine has rebooted. This log can be useful when used in conjunction with the behaviour that gets the bootup identifier.
157 158 159 160 161 162 163 |
# File 'lib/utils/identity/machine.id.rb', line 157 def self.log_reboot_times() the_cmd = "last reboot" the_str = %x[ #{the_cmd} ] the_str.log_lines() end |