Explore LicenseSpring's tutorial on handling product versioning. Learn best practices and techniques for managing versioning in software products effectively.
Licensing Apps Running on Virtualization and Containers: Tutorial and Best Practices
The core of node-locking revolves around using a specific unique identifier (usually the hardware ID), per device, to identify which license belongs to which device. This becomes a lot more complicated when virtualization and containers are involved, as the hardware being read is not the physical device's hardware, but the hardware generated by the virtual machine. Luckily, LicenseSpring has a few methods to deal with containers and virtualization that help identify different virtual environments, and lock the license to a specific device.
In this tutorial, we will discuss:
- Limitations of "Node-Locking" in Virtualized Environments
- Considerations of Checking for Virtualized Environments
- Working with Hypervisors
- How to Activate Licenses in Virtualized Environments and Containers
Limitations of “Node-Locking” in Virtualized Environments
Certain features will be more difficult to enforce on virtual environments. For example: OS, hostname, MAC and IP address will match the virtual environment's specs, instead of the physical device's. Because of this, the virtual machine and the physical machine may have different hardware IDs, which can result in both of them being recognized as different devices. It is up to the developer to decide how these issues will be handled (or whether they will be handled at all).
Considerations of Checking for Virtualized Environments
LicenseSpring SDKs come with the ability to check for certain virtual environments, that will help identify if a device is currently operating on a virtual machine. Checking for virtual machines is turned off by default. This is because doing so will increase the runtime of your program, because it will have to check for many different virtual environments and collect the correct data. You should only turn on VM checking if you believe it is required for your project.
Working with Hypervisors
Detecting a Hypervisor
As noted earlier, LicenseSpring SDKs come with the ability to detect when it is being run on a virtual machine. This feature will need to be manually turned on within the program through the SDK.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
std::string appName = "NAME"; //input name of application
std::string appVersion = "VERSION"; //input version of application
ExtendedOptions options;
//Turning on VM detection before your configuration.
options.enableVMDetection( true );
std::shared_ptr<Configuration> configuration = Configuration::Create(
EncryptStr( "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX" ), // your LicenseSpring API key (UUID)
EncryptStr( "XXXXXXXXX-XXXXX-XXXXXXXXXXXXX_XXXXXX_XXXXXX" ), // your LicenseSpring Shared key
EncryptStr( "XXXXXX" ), // product code that you specified in LicenseSpring for your application
appName, appVersion, options );
auto licenseManager = LicenseManager::create( configuration );
if ( configuration->isVMDetectionEnabled() ) {
//check if VM detection is enabled.
}
This is the basis of VM detection using the SDK. Without enabling this feature, none of the other features we will discuss about VM detection will work. Note: If not specified, by default, this feature is disabled within the SDK.
With VM detection enabled, you can check from within the program, whether you are currently running on a VM using:
1
2
3
if ( configuration->isVM() ) {
//Do something if we are currently running on a VM
}
Preventing/Allowing a Hypervisor
If your product should not be run on a virtual machine, you can prevent users from being able to activate their license on a virtual machine. You can accomplish this by editing your license, and checking off the "Prevent virtual machine" box.
When this feature is checked off, any actions involving our license, will result in an error/exception, making the license unusable on a VM. Note: as stated above, VM detection will need to be enabled using the SDK for this feature to work.
If you want the license to work on VMs, but behave differently, you can leave "Prevent virtual machine" unchecked, and use the code from earlier to detect, from within the program, if you are currently on a virtual machine. Then, create internal logic to determine how to handle the program, based on whether it is currently on a virtual machine or not. This tutorial has more information on preventing virtual machines with the LicenseSpring C++ SDK.
Sending/Retrieving Information About a Device’s Hypervisor
It is possible to also see information about a virtual machine such as its name. As with before, VM detection will need to be enabled. Once VM detection is properly enabled on a device, and that device sends a request to the backend (this can be an activation request, an online check etc…), the LicenseSpring platform will update to display the virtual machine information. You will be able to see whether each device using that license is using a virtual machine and what the name of the virtual machine is under the devices tab for that license.
You can also extract the name of the VM from within the program using the SDK:
1
std::string VM = configuration->getDetectedVMName();
You can use this information however you like, or even send it back to the server under device variables. For more information on how to do that, see our tutorial on custom fields and device variables. Note: Just as before, VM detection will need to be enabled for any of this to work.
How to Activate Licenses in Virtualized Environments and Containers
Since node-locking is tied to the hardware ID of a device, and virtual machines give us a virtual hardware that could be different each time, activating and maintaining a license will be much more difficult. Fortunately, there are two ways of dealing with hardware ID in a virtualized environment.
The first method is probably the easiest, but also does not account for every virtual environment. The SDK actually comes with tools that can create a hardware ID specific to the virtual environment of a device, once VM detection is enabled. These hardware IDs vary per use-case, but some examples include: Using the EC2 instance for AWS and using the website_instance_id for azure.
1
//Azure and AWS automatically handled by hardware id once VM detection is enabled
With this method, you simply need to make sure that VM detection is enabled within your program, you are generating a hardware ID based off of the VM algorithm, and that your VM is accounted for within the SDK.
The second method gives more control to the developer, but requires more effort to implement. The SDK allows developers to overwrite the automatically generated hardware ID, letting them create their own hardware ID however they choose. Although this gives developers more control, they will also need to be more careful. If a hardware ID is not unique per device, then multiple devices can share and use the same license file with only one activation and no consequences. One good example of where this can be used is with Docker containers. You can overwrite the hardware ID per docker container with something unique, such as the container ID. This can be gathered by getting the hostname from within the program, which will return the container ID. Thus, each local license file will be unique, and bound to each container. Note: getting the hostname will differ per OS, furthermore, the container ID/hostname may be changeable through the Docker command line. Finally, since the identifier is container-specific, it is possible to activate a license for each container created, so it is generally recommended to use a floating license to limit the concurrent usage of a license instead.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
std::string appName = "NAME"; //input name of application
std::string appVersion = "VERSION"; //input version of application
//Collecting network info
ExtendedOptions options;
//Turning on VM detection before your configuration.
options.enableVMDetection( true );
//Setting the hardware ID manually
options.setHardwareID("This_is_my_hardware_id");
std::shared_ptr<Configuration> configuration = Configuration::Create(
EncryptStr("XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"), // your LicenseSpring API key (UUID)
EncryptStr("XXXXXXXXX-XXXXX-XXXXXXXXXXXXX_XXXXXX_XXXXXX"), // your LicenseSpring Shared key
EncryptStr("XXXXXX"), // product code that you specified in LicenseSpring for your application
appName, appVersion, options);
auto licenseManager = LicenseManager::create(configuration);
Implementing your own hardware ID comes with its own caveats and will depend on the developer's intention/implementation, but it does allow for more control.
It should also be noted, that any license created in a virtual environment with these methods, are bound to those virtual environments. For example, the license file in a Docker container is created within the container, not the device, so you will only be able to retrieve that license from within the Docker program.
Conclusion
In this tutorial, we went through how virtual machines can interact with the LicenseSpring SDK and platform. Although we offer a lot of tools to deal with virtual environments, each use-case will depend on the developer and their implementation. Working with virtual machines is especially tricky given the nature of node-locking licenses, so it is important to take into account how virtual machines will be handled for your application.