Securing DSC resources for VMware

Recently DSC Resources for VMware 2.0 was released. This new version comes with a lot of new resources and other features, like availability in the PowerShell Gallery. If DSC Resources for VMware is completely new,
I recommended reading the “Getting started” blog post, but do not follow the installation instructions. Instead install directly from the PowerShell Gallery, use something like this:

PS> Find-Module *VMware.vSphereDSC* | Install-Module

So after exploring “Vester”, the other DSC solution, it is now time to have a look at the DSC Resources for VMware 2.0.

Disclaimer: Windows PowerShell Desired State Configuration (from now on “DSC”) is often used for configuration management of Windows systems and as such is new to me. This post focuses on the use of DSC in a vSphere environment.

My setup;  I used an old Windows Server 2012R2 as a LCM. The vSphere environment is a VCSA version 6.5 and two ESXi hosts.
This post contains links to some script. All files mentioned in this post can be downloaded from this location. Then on the LCM, create a new folder named C:\VMwareDSC and place all the files in this folder.

One of my first goals was to understand how to create a good configuration. Luckily, the VMware DSC module contains an example folder, and I selected the VMHost_Config.ps1 configuration, an sample script for configuring an ESXi host.

File VMHost_Config1.ps1 is the first attempt to configure some services on both ESXi hosts. Besides the Configuration section starting on line 22, there is also a Configuration data section, starting on line 1, that needs to be populated.

$script:configurationData = @{
    AllNodes = @(
        @{
            NodeName = 'localhost'
            PSDscAllowPlainTextPassword = $true
            VMHosts = @(
                @{
                    Server = 'esx01.virtual.local'
                    User = 'root'
                    Password = 'VMware1!'
                },
                @{
                    Server = 'esx02.virtual.local'
                    User = 'root'
                    Password = 'VMware1!'
                }
            )
        }
    )
}

Configuration VMHostSettings_Config1 {
    Import-DscResource -ModuleName VMware.vSphereDSC

    Node $AllNodes.NodeName {
        foreach ($vmHost in $AllNodes.VMHosts) {
            $Server = $vmHost.Server
            $User = $vmHost.User
            $Password = $vmHost.Password | ConvertTo-SecureString -asPlainText -Force
            $Credential = New-Object System.Management.Automation.PSCredential($User, $Password)

            VMHostNtpSettings "VMHostNtpSettings_$($Server)" {
                Name = $Server
                Server = $Server
                Credential = $Credential
                NtpServer = @('0.bg.pool.ntp.org', '1.bg.pool.ntp.org', '2.bg.pool.ntp.org')
                NtpServicePolicy = 'automatic'
            }

Code fragment VMHost_Config1.ps1, for the complete script use this link.

In this example, credentials must be provided for each ESXi host. If you want to see this code in action, the following lines will create the .mof file, apply the configuration on the two ESXi hosts and will finally retrieve and test the configuration.

cd C:\VMwareDSC

# Create MOF file
VMHostSettings_Config1 -ConfigurationData $script:configurationData

# Start configuration
$paramHash = @{
    ComputerName = "localhost"
    Path = "C:\VMwareDSC\VMHostSettings_Config1"
    Wait = $true
    Verbose = $True
    Force = $True
}

Start-DscConfiguration  @paramHash

# View configuration 
Get-DscConfiguration -CimSession localhost

# Validate Configuration
$cs = New-CimSession -ComputerName localhost
Test-DscConfiguration -CimSession $cs 

Code fragment VMHost_Config1.ps1, for the complete script use this link.

As an alternative, the vCenter Server credentials can also be used.

All ESXi host resources, like “VMHostNtpSettings” have properties, which can be requested with the Get-DscResource cmdlet. Besides the Property Server, there is also Name.

PS> Get-DscResource VMHostNtpSettings | 
    Select-Object -ExpandProperty Properties | ft -AutoSize

 Name                 PropertyType   IsMandatory Values                     
 ----                 ------------   ----------- ------                     
 Credential           [PSCredential]        True {}                         
 Name                 [string]              True {}                         
 Server               [string]              True {}                         
 DependsOn            [string[]]           False {}                         
 NtpServer            [string[]]           False {}                         
 NtpServicePolicy     [string]             False {Automatic, Off, On, Unset}
 PsDscRunAsCredential [PSCredential]       False {}     

Server is the vCenter Server used for authentication and Name represents the ESXi hosts.
After some adjustments to the Configuration and the Configuration data, the result is VMHost_Config3.ps1.

$script:configurationData = @{
    AllNodes = @(
        @{
            NodeName = 'localhost'
            PSDscAllowPlainTextPassword = $true
            PSDscAllowDomainUser = $true

            Server = 'vc06.virtual.local'
            User = 'administrator@vsphere.local'
            Password = 'VMware1!'

            VMHosts = @(
                @{
                    Name = 'esx01.virtual.local'
                },
                @{
                    Name = 'esx02.virtual.local'
                }
            )
        }
    )
}

Configuration VMHostSettings_Config3 {
    Import-DscResource -ModuleName VMware.vSphereDSC

    Node $AllNodes.NodeName {
        $Server = $AllNodes.Server

        $User = $AllNodes.User
        $Password = $AllNodes.Password | ConvertTo-SecureString -asPlainText -Force
        $Credential = New-Object System.Management.Automation.PSCredential($User, $Password)

        foreach ($vmHost in $AllNodes.VMHosts) {
            $Name = $vmHost.Name
            VMHostNtpSettings "VMHostNtpSettings_$($Name)" {
                Name = $Name
                Server = $Server
                Credential = $Credential
                NtpServer = @('0.bg.pool.ntp.org', '1.bg.pool.ntp.org', '2.bg.pool.ntp.org')
                NtpServicePolicy = 'automatic'
            }


Code fragment VMHost_Config3.ps1, for the complete script use this link.

However, when you take a close look at these configuration scripts and the compiled configurations (the localhost.mof files in the folders VMHost_Config1 and VMHost_Config3), you will notice that these files contain passwords in clear text!

 
/* 
@TargetNode='localhost' 
@GeneratedBy=Administrator 
@GenerationDate=08/21/2019 20:56:38 
@GenerationHost=WINJUMP1 
*/ 

instance of MSFT_Credential as $MSFT_Credential1ref 
{ 
Password = "VMware1!"; 
UserName = "administrator@vsphere.local"; 
}; 

instance of VMHostNtpSettings as $VMHostNtpSettings1ref 

Fragment localhost.mof with clear text passwords

A commonly used, more secure method is to encrypt the passwords in the compiled configuration files using a certificate. This can be achieved in three steps:

1. Generate a self-signed certificate

2. Configure the LCM with certifcates

3. Edit the configuration script

You can generate a self-signd certificate with PowerShell. Windows 10 and Windows Server 2016 have the New-SelfSignedCertificate cmdlet.
You can also use the Self-signed certificate generator, in case of older Windows versions. After downloading, put the script in the folder C:\VMwareDSC.

The certificate needs to be of type “Document Encryption”.
The following lines will generate and export the certificate.


#load function

. "C:\VMwareDSC\New-SelfSignedCertificateEx.ps1"

# generate Cert
New-SelfSignedCertificateEx -Subject 'CN=Cert' -EnhancedKeyUsage 'Document Encryption' -StoreLocation LocalMachine -FriendlyName 'SelfSigned'

# Check Cert
$cert = Get-ChildItem Cert:\LocalMachine\My | Where-Object {$_.FriendlyName -eq 'SelfSigned'}
$cert

# Cert can be exported
Export-Certificate -Cert $cert -FilePath C:\VMwareDSC\Cert.cer -Force

The second step is to configure the LCM to work with certificates.
This can be done using the function DSCLocalConfigurationmanger.
Just like a DSC configuration, it has a set of parameters.
The CertificateID is the thumbprint of the certificate which have been generated.
Line 32 creates the config file, which is applied on line 33.
The final line retrieves the new configuration.

[DSCLocalConfigurationManager()]
Configuration LCM_SelfSigned
{
    param
        (
            [Parameter(Mandatory=$true)]
            [string[]]$ComputerName,

            [Parameter(Mandatory=$true)]
            [string]$guid,

            [Parameter(Mandatory=$true)]
            [string]$thumbprint

        )
	Node $ComputerName {
		Settings {
		AllowModuleOverwrite = $True
		ConfigurationMode = 'ApplyAndMonitor'
		RefreshMode = 'Push'
		ConfigurationID = $guid
		CertificateID = $thumbprint
		}
	}
}

$ComputerName = 'localhost'
$cert = Get-ChildItem Cert:\LocalMachine\My | Where-Object {$_.FriendlyName -eq 'SelfSigned'}
$cim = New-CimSession -ComputerName $ComputerName
$guid=[guid]::NewGuid()

LCM_SelfSigned -ComputerName $ComputerName -Guid $guid -Thumbprint $Cert.Thumbprint -OutputPath C:\VMwareDSC\S2
Set-DscLocalConfigurationManager -CimSession $cim -Path C:\VMwareDSC\S2 -Verbose
Get-DscLocalConfigurationManager -CimSession $cim

The final step is to edit the Configuration script for certificate usage, see VMHost_Config_Secure.ps1.

$script:configurationData = @{
    AllNodes = @(
        @{
            PSDscAllowDomainUser = $true
            NodeName = 'localhost'
            CertificateFile = 'C:\VMwareDSC\Cert.cer'
            Server = 'vc06.virtual.local'
            Credential = (Get-Credential -UserName 'administrator@vsphere.local' -Message 'Enter Password')

            VMHosts = @(
                @{
                    Name = 'esx01.virtual.local'
                },
                @{
                    Name = 'esx02.virtual.local'
                }
            )
        }
    )
}

Configuration VMHostSettings_Config_Secure {
    Import-DscResource -ModuleName VMware.vSphereDSC

    Node $AllNodes.NodeName {
        $Server = $AllNodes.Server
        $Credential = $AllNodes.Credential

        foreach ($vmHost in $AllNodes.VMHosts) {
            $Name = $vmHost.Name
            VMHostNtpSettings "VMHostNtpSettings_$($Name)" {
                Name = $Name
                Server = $Server
                Credential = $Credential
                NtpServer = @('0.bg.pool.ntp.org', '1.bg.pool.ntp.org', '2.bg.pool.ntp.org')
                NtpServicePolicy = 'automatic'
            }

Code fragment VMHost_Config_Secure

The Configuration Data now has a reference to the certificate, the password will be provided while running the configuration.
As with the other provided scripts, it will apply and test the configration.
The resulting .mof file now shows the password in an encrypted form instead of plain text.

@GeneratedBy=Administrator
@GenerationDate=08/21/2019 20:59:47
@GenerationHost=WINJUMP1
*/

instance of MSFT_Credential as $MSFT_Credential1ref
{
Password = "-----BEGIN CMS-----\nMIIBlwYJKoZIhvcNAQcDoIIBiDCCAYQCAQAxggE/
...
qX0sp0DA8BgkqhkiG9w0BBwEwHQYJYIZIAWUDBAEqBBCRMnG9mU356LTd8mNAoBHagBAwv5TR\n4zJ/b4E2knfRnPA6\n-----END CMS-----";
 UserName = "administrator@vsphere.local";

Fragment localhost.mof with encrypted passwords

In later posts, I will dive deeper into the DSC Resources for VMware. As allways, I thank you for reading.

Sources:

New Release – DSC Resources for VMware 2.0

Getting Started with Desired State Configuration Resources for VMware

Pluralsight, Practical Desired State Configuration (DSC) by Josh Duffney.

Credential encryption and security in MOF files

This is the sixth part of a series about configuration drift, Vester and DSC.
Overview of all posts in this series:

About Configuration Drift, Pester and Vester

Tips for writing Vester test files, part 1

Tips for writing Vester test files, part 2

Creating Dashboards for Vester

Vester Test file generator

 

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.

%d bloggers like this: