In this chapter, you will learn to:
Hardening a system involves closing the potential security holes. Examples of security holes include unnecessary software and user accounts, unused services, and unused network ports. Hardening a system improves the security of that system by decreasing the number of potential attack vectors. Don’t risk running an insecure system, unless you run a completely isolated system or you simply do not care if the system becomes compromised. And, don’t forget that there are many legal requirements that oblige you to secure your systems.
If you had to discover all of the potential risks and harden your vSphere environment from scratch, you would be facing a tremendous task. Thankfully, we need not undertake such a huge task on our own. Since vSphere 4.0, VMware has published Security Hardening Guides (www.vmware.com/security/hardening-guides) that list most, if not all, of the potential holes. The Hardening Guides not only list the potential holes, but also show you actions you can take to close those holes.
You can use the Hardening Guides to, well, guide you as to the settings and configurations on which to focus in your virtual infrastructure from the security perspective. While locking down every setting and closing every potential hole listed will help with making the environment more secure, there needs to be, of course, the balance of security with usability and functionality. The virtual infrastructure must provide the required services and resources, or else it is not providing value. So, you get to consider the guidelines in the Hardening Guides and determine which to follow and which to note as “accepted security risks,” as appropriate for your environment.
As vSphere has progressed, VMware has continued a trend toward more secure default configurations. As the admin, you should therefore have fewer and fewer tasks in the hardening process. When creating the vSphere 5.0 Hardening Guide, VMware also made things much easier on admins by including information about how to assess and remediate the suggested settings by ESXi Shell, vCLI, and PowerCLI commands for most of the items. They also included a link to the pertinent vSphere API involved.
With the vSphere 6.0 Hardening Guide, VMware again eased use and consumption of the guide by updating its layout. They moved all programmatic guidelines to a single Microsoft Excel worksheet. (Best practice and operational guidelines are now separate from the guidelines that lend themselves to automated remediation. The operational guidance is now available as part of the vSphere Security documentation and in a separate Excel file at the Hardening Guides website.) The Guideline IDs have been expanded by prefixing the target type, making the IDs more useful and informative.
The vSphere 6.0 Security Hardening Guide uses a straightforward classification scheme. The guidelines are organized around these four areas (followed here by the Guideline ID prefix):
Each guideline has been assigned one or more of three risk profiles as follows:
For each guideline, the document describes a method to remediate the potential threat. These methods include parameter settings (what to set or unset) and component configuration (installing and/or configuring components, enabling/disabling items). Despite all of these improvements to the Hardening Guides, there are still some guidelines that do not include PowerCLI assessment and/or remediation information. So, here we have a chapter for that.
This chapter focuses on the security guidance from the Hardening Guides and particularly on the guidelines that do not have PowerCLI assessment and/or remediation commands available. Although we do not address the operational guidance, you should understand that such guidance is equally important to follow. Operational guideline VM.minimize-console-use
is such an example. It advises you to minimize the access to and use of the VM console, because this access also provides the user with control over power actions and removable devices that “might potentially allow a malicious user to bring down a virtual machine.” That’s sound advice, but it’s hard to automate. The next sections show ways to automate the assessment and/or remediation of the programmatic guidelines.
Of the ESXi.*
guidelines in the 6.0 Hardening Guide, there are several for which VMware has not yet provided assessment and/or remediation code. So, for these, we need to make our own assessment and remediation code. Of these guidelines, many require setting values for advanced settings of the ESXi hosts for remediation.
First, let’s look at the ESXi.set-dcui-access
guideline. This item deals with the list of authorized users with Direct Console User Interface (DCUI) access. By using the Get-
and Set-AdvancedSetting
cmdlets, you can assess and, as needed, remediate this advanced ESXi setting, DCUI.Access
. Listing 13-1 gives an example of assessing this setting, and then remediating the setting to limit the DCUI access and keep the target VMHosts that much safer. Now you won’t have to worry about that potential attack vector, because we removed the extraneous access that was somehow left over from when vPiney was doing some testing and added this access for his local account.
Listing 13-1: Assessing/remediating guideline ESXi.set-dcui-access
Get-VMHost | Get-AdvancedSetting -Name DCUI.Access |
Select-Object Entity,Name,Value
Entity Name Value
------ ---- -----
desxi07.dom.int DCUI.Access vPiney_local, root
desxi08.dom.int DCUI.Access vPiney_local, root
## remediate/remove user
Get-VMHost | Get-AdvancedSetting -Name DCUI.Access | Set-AdvancedSetting `
-Value root -Confirm:$false | Select-Object Entity,Name,Value
Entity Name Value
------ ---- -----
desxi07.dom.int DCUI.Access root
desxi08.dom.int DCUI.Access root
The remediation portion of the script removed user vPiney_local
from the list of users with DCUI access; the testing that involved that account has long since been completed, and we want to maintain “just enough access” to help keep things more secure. Notice that setting this value wasn’t additive but instead it overwrote the original value with the new value that we supplied, root
. So, we effectively removed the given user by specifying the value of just the users whom we want to retain access. If normal lockdown mode is enabled, the DCUI.Access
setting allows a list of highly trusted users to override lockdown mode and to access the DCUI. Remediation might go the other direction, and require you to add users who need access through the DCUI to the authorized users list. To do so, specify users who should retain access as the value of the -Value
parameter of Set-AdvancedSetting
.
A related guideline, ESXi.audit-exception-users
, deals with host-wide access (not DCUI access as addressed in the previous guideline). Lockdown exception users do not lose their permissions when the host enters lockdown mode. The Hardening Guide assigns an Audit Only action type to this guideline. The PowerCLI commands provided in this guideline to report on the current lockdown exceptions for hosts write much of the data to the PowerShell console, whereas automated operations generally prefer objects (for further manipulation and consumption down the pipeline). The code in Listing 13-2 will get you such objects.
Listing 13-2: Getting VMHost lockdown exception users
Get-View -ViewType HostSystem -Property Name,ConfigManager.HostAccessManager |
Foreach-Object {
$oThisHostSystem = $_
$oThisHostAccessMgr = Get-View -Id $_.ConfigManager.HostAccessManager
New-Object -Type PSObject -Property ([ordered]@{
Name = $oThisHostSystem.Name
LockdownExceptionUser = $oThisHostAccessMgr.QueryLockdownExceptions()
HostAccessManager = $oThisHostAccessMgr
MoRef = $oThisHostSystem.MoRef
})
}
Listing 13-2 returns informational objects like the following:
Name LockdownExcept... HostAccessManager MoRef
---- ----------------- ----------------- -----
desxi07v.dom.int {myAcct0} VMware.Vim.HostA... HostSystem-host-148
desxi08v.dom.int {myAcct0} VMware.Vim.HostA... HostSystem-host-219
Notice that these returned objects show that the myAcct0
account is in the lockdown exception list on two of our VMHosts. Auditing is handled for this guideline; you can now easily export the resulting data to your favorite data format—for example, CSV, XML, or JSON.
But what if you need to remediate this item and remove a lockdown exception? PowerCLI and the vSphere API are there for you, of course. Using the HostAccessManager
object for a given ESXi HostSystem
, you can run the UpdateLockdownExceptions
method (which is why we returned HostAccessManager
as a property for the output objects from Listing 13-2). Let’s say that you assigned the objects returned by the code from Listing 13-2 to the variable $arrLockdownExceptionInfo
. Now you can use that information to remove the exception user from your environment as follows:
$arrLockdownExceptionInfo | Foreach-Object {
$_.HostAccessManager.UpdateLockdownExceptions($null)}
Now, running the code in Listing 13-2 again, you can see that the lockdown exception is gone for each host:
Name LockdownExcept... HostAccessManager MoRef
---- ----------------- ----------------- -----
desxi07v.dom.int {} VMware.Vim.HostA... HostSystem-host-148
desxi08v.dom.int {} VMware.Vim.HostA... HostSystem-host-219
Some environments may have a legitimate need to have accounts in the lockdown exception list. So, for convenience, we created a function for setting lockdown exception users (see Listing 13-3).
Listing 13-3: Setting VMHost lockdown exception users
function Set-VMHostLockdownException {
<# .Description
Function to set the Lockdown exception users for a VMHost.
.Example
Set-VMHostLockdownException -UserName dommySvcAcct
Set given user as the only Lockdown exception for all VMHosts (effectively
removes other users)
.Example
Get-VMHost *dev.dom.int | Set-VMHostLockdownException -UserName `
domMySvcAcct2 -Append
Add given user to the Lockdown exception list of users for targeted VMHosts
.Example
Set-VMHostLockdownException -UserName:$null -Id $arrHostSystems.MoRef
Clear the list of Lockdown exception users for VMHosts from myCluster;
the $arrHostSystems array holds the HostSystem View objects for the
VMHosts in cluster "myCluster", and accessing the .MoRef property of the
array gives all of the HostSystems' MoRefs as the value to -Id
.Outputs
PSObject with information about the new Lockdown exceptions for the given
VMHost
#>
[CmdletBinding(SupportsShouldProcess=$true,
DefaultParameterSetName="ByVMHost")]
Param(
## User name(s) with which to update lockdown exceptions. To remove all
# user exceptions, specify $null as the value. For Active Directory
# user, specify name in "domainuser" format
[parameter(Mandatory=$true)][AllowNull()][string[]]$UserName,
## VMHost for which to set Lockdown exceptions. If none specified, will act
# on all VMHosts
[parameter(ValueFromPipeline=$true,ParameterSetName="ByVMHost")]
[VMware.VimAutomation.ViCore.Impl.V1.Inventory.VMHostImpl[]]$VMHost,
## ID of VMHost for which to set lockdown exceptions
[parameter(Mandatory=$true,ValueFromPipelineByPropertyName=$true,
ParameterSetName="ByVMHostId")][Alias("MoRef","Id")]
[VMware.Vim.ManagedObjectReference[]]$VMHostId,
## Switch: append given user to existing list of exceptions? If not,
# replaces current list with specified user
[Switch]$Append
)
begin {
$hshParamForGetHostView = @{Property = "Name",
"ConfigManager.HostAccessManager"}
}
process {
## get other params for Get-View call for getting the desired HostSystems
Switch ($PsCmdlet.ParameterSetName) {
"ByVMHost" {
if ($null -ne $VMHost) {$hshParamForGetHostView["Id"] = $VMHost.Id}
## else, add param that will get all HostSystems
else {$hshParamForGetHostView["ViewType"] = "HostSystem"}
break}
"ByVMHostId" {$hshParamForGetHostView["Id"] = $VMHostId}
}
## get the HostSystem View objects for which to make updates, then do it
Get-View @hshParamForGetHostView | Foreach-Object {
$oThisHostSystem = $_
$oThisHostAccessMgr = Get-View -Id $_.ConfigManager.HostAccessManager
## make the list of Lockdown exceptions to set
$arrLockdownExcepToSet = if ($Append) {
@($oThisHostAccessMgr.QueryLockdownExceptions()) + $UserName
}
else {$UserName}
$strShouldProcessOperationMsg = if ($null -eq $arrLockdownExcepToSet) {
"Remove Lockdown exceptions"
} else {
"Set Lockdown exceptions to '$($arrLockdownExcepToSet -join ', ')'"
}
if ($PsCmdlet.ShouldProcess("VMHost '$($oThisHostSystem.Name)'", `
$strShouldProcessOperationMsg)) {
try {
## update the Lockdown exceptions
$oThisHostAccessMgr.UpdateLockdownExceptions($arrLockdownExcepToSet)
$arrNewLockdownExceptions =
$oThisHostAccessMgr.QueryLockdownExceptions()
## return info object
New-Object -Type PSObject -Property ([ordered]@{
Name = $oThisHostSystem.Name
LockdownExceptionUser =
$(if ($arrNewLockdownExceptions.Count -ne 0) {
$arrNewLockdownExceptions})
MoRef = $oThisHostSystem.MoRef
})
}
catch {Throw "encountered issue setting Lockdown exceptions for `
VMHost '$($oThisHostSystem.Name)'. The error: $_"}
}
}
}
}
Suppose you need to add a lockdown exception for the service account that a third-party application uses for host access. You can use the Set-VMHostLockdownException
function from Listing 13-3:
Set-VMHostLockdownException -UserName dommySvcAcct0
Name LockdownExceptionUser MoRef
---- --------------------- -----
desxi07v.dom.int DOMmySvcAcct0 HostSystem-host-148
desxi08v.dom.int DOMmySvcAcct0 HostSystem-host-219
You should be aware that the UpdateLockdownExceptions
API method requires you to specify the full list of lockdown exceptions—it is not additive. The function in Listing 13-3 takes this into account, and provides for appending users by specifying the –Append
switch parameter.
A possibly more handy feature (and more in keeping with tighter security), the Set-VMHostLockdownException
function in Listing 13-3 makes it easy to empty the lockdown exception list. You can give the $null
value to the -UserName
parameter to do so. The result is similar to the direct API call to UpdateLockdownExceptions
, but nicely encapsulated in a function that returns the resulting configuration.
Set-VMHostLockdownException -UserName $null
Name LockdownExceptionUser MoRef
---- --------------------- -----
desxi07v.dom.int HostSystem-host-148
desxi08v.dom.int HostSystem-host-219
As for some of the PowerShell niceties in the Set-VMHostLockdownException
function from Listing 13-3, you see an example of using parameter splatting, which dynamically builds a hash table of parameters/values, and then uses that hash table as the parameters for a given cmdlet (Get-View
in this case, everyone’s favorite!). See the PowerShell help topic about_Splatting
for more about splatting. The additional PowerShell nicety, the -WhatIf
support in this function, helps answer the question: “What is this going to do if I issue this line?”
Now for some more advanced ESXi host settings. There are currently four other guidelines that involve ESXi advanced settings and for which the Hardening Guide has no PowerCLI assessment or remediation code. Table 13-1 lists the Guideline IDs and setting names.
Table 13-1: Additional ESXi guidelines and the corresponding advanced setting names
Guideline ID | ESXi advanced setting name |
ESXi.set-account-auto-unlock-time | Security.AccountUnlockTime |
ESXi.set-account-lockout | Security.AccountLockFailures |
ESXi.set-dcui-timeout | UserVars.DcuiTimeOut |
vNetwork.enable-bpdu-filter | Net.BlockGuestBPDU |
Although you could essentially repeat the code we provided earlier in Listing 13-1 for setting each of these advanced options individually, this is an interesting opportunity to create an advanced function that you can use to assess and remediate multiple advanced settings. Listing 13-4 provides such a function.
Listing 13-4: Assessing/remediating multiple ESXi advanced settings
function Assert-HardeningGuideESXiAdvSetting {
<# .Description
Function to assess and/or remediate several of the advanced settings
called out in the VMware Hardening Guide (but that do not have PowerCLI
assessment/remediation code in the guide)
.Example
Assert-HardeningGuideESXiAdvSetting
Report on the current values of the given advanced settings for all
VMHosts,
and whether they meet the values that the hardening guide recommends
.Example
Get-VMHost myhost0, myhost1 |
Assert-HardeningGuideESXiAdvSetting -Remediate:$true
Set the advanced settings' values to those that the
hardening guide recommends
.Outputs
If remediating,
Selected.VMware.VimAutomation.ViCore.Impl.V1.AdvancedSettingImpl of
any settings that were updated, or
Selected.VMware.VimAutomation.ViCore.Impl.V1.AdvancedSettingImpl of
all associated settings if reporting only (default)
#>
[CmdletBinding(SupportsShouldProcess=$true)]
Param(
## VMHost to assess/remediate. If none specified, will act on all
# VMHosts in the VIServer to which current session is connected
[parameter(ValueFromPipeline=$true)]
[VMware.VimAutomation.ViCore.Impl.V1.Inventory.VMHostImpl[]]$VMHost,
## Switch: Remediate the entity? Default is $false, which
# causes function to only report
[switch]$Remediate = $false
)
begin {
## array of objects with info about advanced settings and desired values to
# set per the hardening guide (for guidelines that have no PowerCLI
# assessment/remediation code in the hardening guide)
$arrHardGuideSettingInfo =
@{Name = "Security.AccountUnlockTime";
GuidelineId = "ESXi.set-account-auto-unlock-time"; Value = 900},
@{Name = "Security.AccountLockFailures";
GuidelineId = "ESXi.set-account-lockout"; Value = 3},
@{Name = "UserVars.DcuiTimeOut";
GuidelineId = "ESXi.set-dcui-timeout"; Value = 600},
@{Name = "Net.BlockGuestBPDU";
GuidelineId = "vNetwork.enable-bpdu-filter"; Value = 1} |
Foreach-Object {New-Object -Type PSObject -Property $_}
}
process {
$arrVMHostOfInterest = if ($PsBoundParameters.ContainsKey("VMHost"))
{$VMHost} else {Get-VMHost}
$arrAdvSettings_theseHosts = Get-AdvancedSetting -Entity `
$arrVMHostOfInterest -Name $arrHardGuideSettingInfo.Name
$arrAdvSettings_theseHosts | Foreach-Object {
$oThisSetting = $_
$oThisHGItem = $arrHardGuideSettingInfo |
Where-Object {$_.Name -eq $oThisSetting.Name}
$bIsSuggestedValue = $oThisHGItem.Value -eq $_.Value
if ($Remediate) {
if (-not $bIsSuggestedValue) {
if ($PsCmdlet.ShouldProcess($oThisSetting.Entity, `
("Set {0} from {1} to {2}" -f $oThisSetting.Name, `
$oThisSetting.Value, $oThisHGItem.Value))) {
Set-AdvancedSetting -AdvancedSetting $oThisSetting -Value `
$oThisHGItem.Value -Confirm:$false |
Select-Object Entity, Name, Value
}
}
}
## else, report the current settings' values, and whether they
# match the values that the hardening guide suggests
else {
Select-Object -InputObject $_ Entity, Name, Value,
@{n="GuidelineId"; e={$oThisHGItem.GuidelineId}},
@{n="IsSuggestedValue"; e={$bIsSuggestedValue}}
}
}
}
}
Using this function is straightforward. To assess the four settings for all VMHosts (the default scope if no particular VMHost is specified), use this:
Assert-HardeningGuideESXiAdvSetting
Entity Name Value GuidelineId IsSuggestedValue
------ ---- ----- ----------- ----------------
desxi07 UserVars.DcuiTimeOut 600 ESXi.set-dcui-timeo... True
desxi07 Net.BlockGuestBPDU 0 vNetwork.enable-bpd... False
desxi07 Security.AccountLockF... 3 ESXi.set-account-lo... True
desxi07 Security.AccountUnloc... 120 ESXi.set-account-au... False
desxi08 UserVars.DcuiTimeOut 600 ESXi.set-dcui-timeo... True
desxi08 Net.BlockGuestBPDU 1 vNetwork.enable-bpd... True
desxi08 Security.AccountLockF... 10 ESXi.set-account-lo... False
desxi08 Security.AccountUnloc... 900 ESXi.set-account-au... True
To remediate any of the settings on any of the hosts that do not have the value suggested by the guideline, add the -Remediate
parameter, like so:
Assert-HardeningGuideESXiAdvSetting -Remediate:$true
Entity Name Value
------ ---- -----
desxi07.dom.int Net.BlockGuestBPDU 1
desxi07.dom.int Security.AccountUnlockTime 900
desxi08.dom.int Security.AccountLockFailures 3
Notice that the remediation returns just the changed advanced settings (it only acts on the settings that were not of the suggested value). And, of course, the function supports -WhatIf
so that you can give it a dry run and see what settings would change, if any. To verify that all of the VMHosts have the suggested/desired values per the Hardening Guide, you can just invoke the function again:
Assert-HardeningGuideESXiAdvSetting
Entity Name Value GuidelineId IsSuggestedValue
------ ---- ----- ----------- ----------------
desxi07 UserVars.DcuiTimeOut 600 ESXi.set-dcui-timeo... True
desxi07 Net.BlockGuestBPDU 1 vNetwork.enable-bpd... True
desxi07 Security.AccountLockF... 3 ESXi.set-account-lo... True
desxi07 Security.AccountUnloc... 900 ESXi.set-account-au... True
desxi08 UserVars.DcuiTimeOut 600 ESXi.set-dcui-timeo... True
desxi08 Net.BlockGuestBPDU 1 vNetwork.enable-bpd... True
desxi08 Security.AccountLockF... 3 ESXi.set-account-lo... True
desxi08 Security.AccountUnloc... 900 ESXi.set-account-au... True
Now the VMHosts all have values for these advanced settings as suggested by the Hardening Guide. We made the function in Listing 13-4 easily extensible, too. By using the variable $arrHardGuideSettingInfo
, an array of PSObject
s in the begin
script block of the Assert-HardeningGuideESXiAdvSetting
function, it is trivial to add more guidelines or advanced settings which to assess/remediate for VMHosts. For example, let’s say that VMware has now put forth a new guideline recommending that the “Net.SuperSecureSetting.Enable
” advanced setting should be set to $true on all ESXi hosts. You can just add another hash table with the pertinent advanced setting name, Guideline ID, and value. The subsequent call in the function to the New-Object
cmdlet makes another new PSObject
from the hash table that you added. The process
script block will assess and remediate that new setting (“Net.SuperSecureSetting.Enable
”) in addition to the others, and now you have extended the function to be that much more useful! (Be sure to share it with the world when you do.)
One other ESXi-related guideline is ID ESXi.firewall-enabled
. The Hardening Guide suggests that you leverage the ESXi host firewall to restrict access to services running on the host. While the PowerCLI remediation value in the Hardening Guide is N/A
, that may be due to the semi-operational nature of this guideline. It is not a blanket, enable-these-firewall-rules-for-all-environments scenario—it depends on your particular environment and your needs for your ESXi hosts. For example, if your environment uses NFS v4.1 datastores, syslog, and NSX, you would have the nfs41Client
, NSX Distributed Logical Router Service
, and syslog
VMHost firewall exceptions enabled, allowing such traffic in and out of the ESXi host. So, when looking at enabled firewall exceptions on a VMHost, you need to consider what features you are using, which will dictate which exceptions you should enable.
Out of the box, the ESXi default firewall policy is that IncomingEnabled
and OutgoingEnabled
are both $false
. So, if there is not a firewall exception, traffic to and from a VMHost should be blocked. You can verify VMHosts’ default firewall policy by using this:
Get-VMHost vMixty0[56].dom.int | Get-VMHostFirewallDefaultPolicy
IncomingEnabled OutgoingEnabled
--------------- ---------------
False False
False False
Should you ever need to configure an ESXi host’s firewall default policy, you could use something like the following code. This example assumes that someone changed the default firewall policies on the given VMHosts to $true
at some point, and shows the resulting DefaultPolicy
values are again at $false
:
Get-VMHostFirewallDefaultPolicy -VMHost vMixty0[56].dom.int |
Set-VMHostFirewallDefaultPolicy -AllowIncoming $false -AllowOutgoing $false
IncomingEnabled OutgoingEnabled
--------------- ---------------
False False
False False
The Hardening Guide has an assessment command example that allows you to get the current VMHost firewall exceptions using the Get-VMHostFirewallException
cmdlet. You can use the associated Set-VMHostFirewallException
cmdlet when you need to change an exception. For example, suppose that it is time to use Network Time Protocol (NTP) to synchronize the clocks in your environment across the network. You would have used the Set-VMHostService
cmdlet to set the policy of the ntpd
service to Automatic, but you need to enable a firewall exception so that the related NTP communications succeed. The following will enable the NTP firewall exceptions on the given VMHosts:
Get-VMHost vMixty0[56].* | Get-VMHostFirewallException -Name "NTP Client" |
Set-VMHostFirewallException -Enabled:$true
Name Enabled IncomingPorts OutgoingPorts Protocols ServiceRunning
---- ------- ------------- ------------- --------- --------------
NTP Client True 123 UDP True
NTP Client True 123 UDP True
Granted, if you were using something like the vSphere Web Client to configure NTP on a VMHost (or some other host-related configurations that require firewall exceptions), the Web Client takes care of enabling and disabling the corresponding firewall exception. But, this is PowerCLI time. GUI? What GUI? So we take care of the firewall exception configuration on our own.
There are several guidelines for virtual machines in the Hardening Guide (about 40 of them), many of which deal with advanced settings of the virtual machine objects. VMware has done a great job with these virtual machine guidelines; PowerCLI assessment and remediation commands are provided for nearly all of them.
One VM guideline whose remediation command could use a bit of expansion is Guideline ID VM.disable-independent-nonpersistent
. This guideline helps you avoid using independent nonpersistent disks and removes the ability of attackers to cover their tracks by rebooting and power-cycling a VM with an IndependentNonPersistent
disk. The PowerCLI remediation command for this guideline shows the cmdlets to use, but it does not include the full set of parameters needed for remediation. The code in Listing 13-5 changes IndependentNonPersistent
virtual disks to IndependentPersistent
disks.
Listing 13-5: Changing persistence of independent-nonpersistent vDisks
## the disk persistence mode to which to change
$strTargetMode = "IndependentPersistent"
Get-VM | Foreach-Object {
$VM = $_
## if this VM has any IndependentNonPersistent disks
if ($arrIndNonPerDiskThisVM = Get-HardDisk -VM $VM | Where-Object {
$_.Persistence -eq "IndependentNonPersistent"}) {
## if the VM has a snapshot, write a warning that cannot convert its disk
if ($arrSnapsThisVM = Get-Snapshot -VM $VM) {
$intNumSnapsThisVM = ($arrSnapsThisVM | Measure-Object).Count
## compose a warning message to write out
$strMsgHasSnap = "VM '$($VM.Name)' has {0}. Cannot convert its `
IndependentNonPersistent disks to '$strTargetMode'" -f `
$(if ($intNumSnapsThisVM -eq 1) {"a snapshot"} else {"snapshots"})
Write-Warning $strMsgHasSnap
}
## else, set the persistence of its IndependentNonPersistent disks
else {
Set-HardDisk -HardDisk $arrIndNonPerDiskThisVM -Persistence `
$strTargetMode -Confirm:$false
}
}
}
The code in Listing 13-5 also takes advantage of PowerShell’s ability to perform an assignment operation inside the conditional expression for the if
statements. This can help to keep the code tight, versus performing those two actions separately. It’s handy for efficiency, though it does require a bit more understanding for future code maintenance.
The only other VM guideline that is currently short on PowerCLI commands in the Hardening Guide is Guideline ID VM.verify-network-filter
. This guideline is also assigned an Audit Only action type. Using the PowerCLI assessment command documented in the Hardening Guide, you can find all the VMs with a VMX setting for an Ethernet filter. Although it would be better to manage the VM network filters via the products that created them (using the vSphere Network Appliance API, or DvFilter API), you may have to remove an Ethernet filter “manually” from a VM. If, in auditing, you determine that there is a VM with settings that you decide should no longer be there, you can use the Remove-AdvancedSetting
cmdlet to remove the VM advanced setting. Say you have a VM with an unwanted ethernet0.filter0.name
advanced setting with a filter module value; you can remove this setting by using the following:
Get-VM vmCourntie0 | Get-AdvancedSetting -Name ethernet0.filter0.name |
Remove-AdvancedSetting
The Hardening Guide provides assessment PowerCLI commands for most of the vNetwork guidelines. As PowerCLI’s cmdlet coverage continues to grow, you can simplify some of those commands. For example, checking virtual switch security settings became much easier with the introduction of the Get-SecurityPolicy
and Get-VDSecurityPolicy
cmdlets in PowerCLI 5.5 (Release 2 and Release 1, respectively). And the accompanying Set-SecurityPolicy
and Get-VDSecurityPolicy
cmdlets provide a means by which to manage security settings.
The vNetwork vSwitch/vPortgroup assessment commands in the Hardening Guide are a bit more involved than necessary (thanks to the new *SecurityPolicy
cmdlets), and the Guide provides no remediation commands. Therefore, Listings 13-6 and 13-7 offer ways in which to check and set the security settings. These guidelines deal with the Forged Transmits, MAC Address Changes, and Promiscuous Mode security settings of virtual switches and virtual port groups.
Listing 13-6 addresses vSphere standard switches and port groups for the Hardening Guidelines with IDs of vNetwork.reject-forged-transmit
, vNetwork.reject-mac-changes
, and vNetwork.reject-promiscuous-mode
. Listing 13-7 addresses vSphere distributed switches and port groups for the Hardening Guidelines with IDs of vNetwork.reject-forged-transmit-dvportgroup
, vNetwork.reject-mac-changes-dvportgroup
, and vNetwork.reject-promiscuous-mode-dvportgroup
.
Listing 13-6: Reporting and updating standard switch/port group security policies
## Assess in more straightforward manner than in the hardening guide:
Get-VMHost | Get-VirtualSwitch -Standard | Get-SecurityPolicy
VirtualSwitch AllowPromiscuous ForgedTransmits MacChanges
------------- ---------------- --------------- ----------
vSwitch0 False True True
vSwitch1 False True True
Get-VMHost | Get-VirtualSwitch -Standard | Get-VirtualPortGroup |
Get-SecurityPolicy
VirtualPortGroup AllowPromiscuous ForgedTransmits MacChanges
---------------- ---------------- --------------- ----------
MgmtNetwork False True True
dNet0 False True True
vESXiNet0 True True True
## remediate
Get-VMHost | Get-VirtualSwitch -Standard | Get-SecurityPolicy |
Set-SecurityPolicy -AllowPromiscuous:$false -ForgedTransmits:$false `
-MacChanges:$false
VirtualSwitch AllowPromiscuous ForgedTransmits MacChanges
------------- ---------------- --------------- ----------
vSwitch0 False False False
vSwitch1 False False False
Get-VMHost | Get-VirtualSwitch -Standard | Get-VirtualPortGroup |
Get-SecurityPolicy | Set-SecurityPolicy -AllowPromiscuousInherited:$true `
-ForgedTransmitsInherited:$true -MacChangesInherited:$true
VirtualPortGroup AllowPromiscuous ForgedTransmits MacChanges
---------------- ---------------- --------------- ----------
MgmtNetwork False False False
dNet0 False False False
vESXiNet0 False False False
Notice that we set the explicit security policies to the desired setting on the virtual switches, and then updated the security policies of the virtual port groups on these virtual switches to inherit the settings from the virtual switches’ configurations. You see the same technique in Listing 13-7, but it uses the VMware.VimAutomation.Vds
PowerCLI module for dealing with virtual distributed networking components.
Listing 13-7: Reporting and updating distributed switch/port group security policies
Import-Module VMware.VimAutomation.Vds
## Assess in more straightforward manner than in the hardening guide:
Get-VDSwitch | Get-VDSecurityPolicy
VDSwitch AllowPromiscuous MacChanges ForgedTransmits
-------- ---------------- ---------- ---------------
vDSw-Dev0 True True False
vDSw-Prod1 False True True
Get-VDSwitch | Get-VDPortgroup | Get-VDSecurityPolicy
VDPortgroup AllowPromiscuous MacChanges ForgedTransmits
----------- ---------------- ---------- ---------------
dvPG01 True False False
vMotion True False True
## remediate
Get-VDSwitch | Get-VDSecurityPolicy | Set-VDSecurityPolicy `
-AllowPromiscuous:$false -MacChanges:$false -ForgedTransmits:$false
VDSwitch AllowPromiscuous MacChanges ForgedTransmits
-------- ---------------- ---------- ---------------
vDSw-Dev0 False False False
vDSw-Prod1 False False False
Get-VDSwitch | Get-VDPortgroup | Get-VDSecurityPolicy | Set-VDSecurityPolicy `
-AllowPromiscuousInherited:$true -ForgedTransmitsInherited:$true `
-MacChangesInherited:$true
VDPortgroup AllowPromiscuous MacChanges ForgedTransmits
----------- ---------------- ---------- ---------------
dvPG01 False False False
vMotion False False False
The next vNetwork guideline that needs some PowerCLI help is Guideline ID vNetwork.limit-network-healthcheck
. The Hardening Guide recommends, “Enable VDS network healthcheck only if you need it.” Because there is no cmdlet for directly checking and managing these healthcheck
options, we created the function shown in Listing 13-8.
Listing 13-8: Assessing and remediating network healthcheck
options
function Assert-HardeningGuideVNetworkHealthChk {
<# .Description
Function to assess and/or remediate the VMware Hardening Guide item with
Guideline ID "vNetwork.limit-network-healthcheck" (which do not have
PowerCLI assessment/remediation code in the guide). The guideline
recommends disabling these health check items, since, as the guide states,
"once enabled, the healthcheck packets contain information on host#,
vds# port#, which an attacker would find useful"
.Example
Assert-HardeningGuideVNetworkHealthChk
Report on the current values of the health check configuration items for
all virtual distributed switches
.Example
Get-VDSwitch myVDSwitch0, myVDSwitch1 |
Assert-HardeningGuideVNetworkHealthChk -Remediate:$true
Disable any enabled health check setting on vDSwitches, as the hardening
guide recommends
.Outputs
If remediating, the VMware.VimAutomation.Vds.Impl.V1.VmwareVDSwitchImpl
object for each VDSwitch updated. If just reporting, the
Selected.VMware.Vim.VMwareDVSVlanMtuHealthCheckConfig and
Selected.VMware.Vim.VMwareDVSTeamingHealthCheckConfig object for each
VDSwitch involved
#>
[CmdletBinding(SupportsShouldProcess=$true)]
Param(
## Virtual distributed switch to assess/remediate. If none specified, will
# act on all vDSwitches in the VIServer to which current session is
# connected
[parameter(ValueFromPipeline=$true)][PSObject[]]$VDSwitch,
## Switch: Remediate the entity? Default is $false, which causes function to
# only report
[switch]$Remediate = $false
)
begin {
## the desired value for the HealthCheckConfig properties
$bDesiredConfigValue = $false
}
process {
$arrVDSwitchOfInterest = if ($PsBoundParameters.ContainsKey("VDSwitch")) {
if ($VDSwitch.GetType().Name -eq "VmwareVDSwitchImpl") {$VDSwitch}
else {Get-VDSwitch -Name $VDSwitch}
} else {Get-VDSwitch}
## get the HealthCheckConfig information for the VDSwitch(es)
$arrHealthCheckConfigInfo = $arrVDSwitchOfInterest | Foreach-Object {
$oThisVDSwitch = $_
$oThisVDSwitch.ExtensionData.Config.HealthCheckConfig |
Select-Object @{n="VDSwitch"; e={$oThisVDSwitch}},
@{n="HealthCheckConfigType"; e={$_.GetType().Name}},
@{n="Enabled"; e={$_.Enable}},
@{n="IsSuggestedValue"; e={$bDesiredConfigValue -eq $_.Enable}}
}
if ($Remediate) {
## get just the VDSwitches who have a healthcheckconfig value that is
# not the suggested value
$arrVDSwitchToRemediate = $arrHealthCheckConfigInfo |
Where-Object {$_.IsSuggestedValue -eq $false} |
Select-Object -Unique -ExpandProperty VDSwitch
## make the healthCheckConfig items to use for updating the
# DVSHealthCheckConfig
$arrDVSHealthCheckConfigItems =
"VMware.Vim.VMwareDVSTeamingHealthCheckConfig",
"VMware.Vim.VMwareDVSVlanMtuHealthCheckConfig" |
Foreach-Object {
New-Object -TypeName $_ -Property @{Enable = $bDesiredConfigValue}
}
if (($arrVDSwitchToRemediate | Measure-Object).Count -eq 0) {
Write-Verbose "All VDSwitches specified have the suggested values for
their Health Check configurations"}
else {
$arrVDSwitchToRemediate | Foreach-Object {
$oThisVDSwitch = $_
if ($PsCmdlet.ShouldProcess($oThisVDSwitch.Name,
"Disable Health Checking")) {
try {
$oTaskId =
$oThisVDSwitch.ExtensionData.UpdateDVSHealthCheckConfig_Task(`
$arrDVSHealthCheckConfigItems)
## wait for the task; this task should return nothing, but
# sending to Out-Null for cleanliness
Wait-Task -Task (Get-Task -Id $oTaskId) | Out-Null
$oTask_final = Get-Task -Id $oTaskId
## if the task succeeded, return the newly updated VDSwitch
if ($oTask_final.State -eq "Success") {
Get-VDSwitch -Id $oThisVDSwitch.Id
}
else {
Write-Error "Problem setting Health Check Config on VDSwitch
'$($oThisVDSwitch.Name)'. The reconfiguration task result:
'$($oTask_final.Result)'. And, the task error:
'$($oTask_final.ExtensionData.Info.Error)'"
}
} catch {Write-Error "Problem initiating the configuration task for
VDSwitch '$($oThisVDSwitch.Name)'"}
}
}
}
}
else {
## else just return the health check config info items
$arrHealthCheckConfigInfo
}
}
}
Here are a pair of examples of using the Assert-HardeningGuideVNetworkHealthChk
function from Listing 13-8, first to assess the vNetwork healthcheck
settings, and then to remediate the settings:
## Assess
Assert-HardeningGuideVNetworkHealthChk
VDSwitch HealthCheckConfigType Enabled IsSuggestedValue
-------- --------------------- ------- ----------------
vDSwitch0 VMwareDVSVlanMtuHealthCheckConfig True False
vDSwitch0 VMwareDVSTeamingHealthCheckConfig True False
vDSwitch1 VMwareDVSVlanMtuHealthCheckConfig False True
vDSwitch1 VMwareDVSTeamingHealthCheckConfig False True
## remediate; returns the updated vSwitch object after updating the Health
# Check config
Assert-HardeningGuideVNetworkHealthChk -Remediate
Name NumPorts Mtu Version Vendor
---- -------- --- ------- ------
vDSwitch0 256 9000 6.0.0 VMware, Inc.
## next assessment shows all is well; so good!
Assert-HardeningGuideVNetworkHealthChk
VDSwitch HealthCheckConfigType Enabled IsSuggestedValue
-------- --------------------- ------- ----------------
vDSwitch0 VMwareDVSVlanMtuHealthCheckConfig False True
vDSwitch0 VMwareDVSTeamingHealthCheckConfig False True
vDSwitch1 VMwareDVSVlanMtuHealthCheckConfig False True
vDSwitch1 VMwareDVSTeamingHealthCheckConfig False True
As with the other advanced functions in this chapter, the function in Listing 13-8 supports ShouldProcess
, which allows you to use it in what if mode with the -WhatIf
parameter.
Another vNetwork guideline for which we need to make some PowerCLI assessment and remediation commands is Guideline ID vNetwork.restrict-port-level-overrides
. Here, the Hardening Guide advises, “Restrict port-level configuration overrides on VDS.” PowerCLI growth provides us with a way to check and set the vSphere distributed port group port-level overrides, in the form of the cmdlets Get-VDPortGroupOverridePolicy
and Set-VDPortGroupOverridePolicy
, both available since PowerCLI 5.5 Release 1. The Set-VDPortGroupOverridePolicy
cmdlet from PowerCLI 6.0 allows you to set five of the nine DVPortgroupPolicy
and VMwareDVSPortgroupPolicy
OverrideAllowed
properties. The PowerCLI Help shows how to use the cmdlet.
To add a little more value, we created an advanced function (see Listing 13-9) that you can use to assess and/or remediate all nine of the vSphere distributed port group (vDPortgroup) override configurations for this guideline.
Listing 13-9: Assessing and remediating vDPortgroup override options
function Assert-HardeningGuideVNetworkPortOverride {
<# .Description
Function to assess and/or remediate the VMware Hardening Guide item with
Guideline ID "vNetwork.restrict-port-level-overrides" (which do not have
PowerCLI assessment/remediation code in the guide). This guideline is to,
"Restrict port-level configuration overrides on VDS". This function will
assess/update the VDPortgroup policy's OverrideAllowed properties
.Example
Assert-HardeningGuideVNetworkPortOverride
Assess the OverrideAllowed settings for all VDPortgroups
.Example
Get-VDSwitch myVDSwitch0 | Get-VDPortgroup |
Assert-HardeningGuideVNetworkPortOverride -Remediate:$true
Get the VDPortgroups of the given VDSwitch, and remediate the
OverrideAllowed settings for each per the hardening guide
.Outputs
If remediating, the VMware.VimAutomation.Vds.Impl.V1.VmwareVDPortgroupImpl
object for each VDPortgroup updated. If just reporting, a PSCustomObject
for each VDPortgroup with information about the given override policies,
and whether the VDPortgroup config is that as suggested in the hardening
guide
#>
[CmdletBinding(SupportsShouldProcess=$true)]
Param(
## Virtual distributed portgroup to assess/remediate. If none specified,
# will act on all VDPortgroups in the VIServer to which current session
# is connected
[parameter(ValueFromPipeline=$true)][PSObject[]]$VDPortgroup,
## Switch: Remediate the entity? Default is $false, which causes function
# to only report
[switch]$Remediate = $false
)
begin {
## the desired value for each of the OverrideAllowed properties
$bDesiredConfigValue = $false
## the names of the "*OverrideAllowed" properties to assess/update
$arrOverridePropNames = Write-Output BlockOverrideAllowed,
VendorConfigOverrideAllowed,VlanOverrideAllowed,ipfixOverrideAllowed,
trafficFilterOverrideAllowed,NetworkResourcePoolOverrideAllowed,
SecurityPolicyOverrideAllowed,ShapingOverrideAllowed,
UplinkTeamingOverrideAllowed
}
process {
$arrVDPGOfInterest = if ($PsBoundParameters.ContainsKey("VDPortgroup")) {
if ($VDPortgroup.GetType().Name -eq "VmwareVDPortgroupImpl") {
$VDPortgroup
}
else {Get-VDPortgroup -Name $VDPortgroup}
} else {Get-VDPortgroup}
## make some objects with the VDPortgroups' Policy values, and note if they
# are all the suggested value
$arrVDPGPolicyInfo = $arrVDPGOfInterest | Foreach-Object {
$oDVSPGPolicy_thisPG = $_.Extensiondata.Config.Policy
New-Object -Type PSObject -Property ([ordered]@{
VDPortgroup = $_
VDSwitch = $_.VDSwitch
## are all of the given properties of the policy of this PG set to the
# desired config value?
HasSuggestedValues = $bDesiredConfigValue -eq ($arrOverridePropNames |
Foreach-Object {$oDVSPGPolicy_thisPG.$_} | Select-Object -Unique)
BlockPorts = $oDVSPGPolicy_thisPG.BlockOverrideAllowed
TrafficShaping = $oDVSPGPolicy_thisPG.ShapingOverrideAllowed
VendorConfiguration = $oDVSPGPolicy_thisPG.VendorConfigOverrideAllowed
VLAN = $oDVSPGPolicy_thisPG.VlanOverrideAllowed
UplinkTeaming = $oDVSPGPolicy_thisPG.UplinkTeamingOverrideAllowed
ResourceAllocation =
$oDVSPGPolicy_thisPG.NetworkResourcePoolOverrideAllowed
SecurityPolicy = $oDVSPGPolicy_thisPG.SecurityPolicyOverrideAllowed
NetFlow = $oDVSPGPolicy_thisPG.ipfixOverrideAllowed
TrafficFilteringMarking =
$oDVSPGPolicy_thisPG.trafficFilterOverrideAllowed
})
}
if ($Remediate) {
## get just the VDPortgroups who have an OverrideAllowed value that is
# not the suggested value
$arrVDPGToRemediate = $arrVDPGPolicyInfo | Where-Object {
$_.HasSuggestedValues -eq $false} |
Select-Object -Unique -ExpandProperty VDPortgroup
if (($arrVDPGToRemediate | Measure-Object).Count -eq 0) {Write-Verbose
"All VDPortgroups specified have the suggested values for their
OverrideAllowed policies"}
else {
$arrVDPGToRemediate | Foreach-Object {
$oThisVDPG = $_
if ($PsCmdlet.ShouldProcess($oThisVDPG.Name,
"Reconfigure OverrideAllowed policies to '$bDesiredConfigValue'"
)) {
$oDVSPGPolicy_thisPG = $oThisVDPG.Extensiondata.Config.Policy
$arrOverridePropNames | Foreach-Object {$oDVSPGPolicy_thisPG.$_ =
$bDesiredConfigValue}
## make a new DVPGConfigSpec with just the configVersion and policy
# properties set, so as not to deal with other settings
$oNewDVPGConfigSpec = New-Object -TypeName
VMware.Vim.DVPortGroupConfigSpec -Property `
@{configVersion = $_.ExtensionData.Config.ConfigVersion; policy =
$oDVSPGPolicy_thisPG}
try {
$oTaskId = $oThisVDPG.ExtensionData.ReconfigureDVPortgroup_Task(
$oNewDVPGConfigSpec)
## wait for the task; this task should return nothing, but
# sending to Out-Null for cleanliness
Wait-Task -Task (Get-Task -Id $oTaskId) | Out-Null
$oTask_final = Get-Task -Id $oTaskId
## if the task succeeded, return the newly updated VDPG
if ($oTask_final.State -eq "Success") {Get-VDPortgroup -Id `
$oThisVDPG.Id}
else {Write-Error "Problem reconfiguring VDPortgroup
'$($oThisVDPG.Name)'. The reconfiguration task result:
'$($oTask_final.Result)'. And, the task error:
'$($oTask_final.ExtensionData.Info.Error)'"}
} catch {Write-Error "Problem initiating the reconfiguration task
for VDPortgroup '$($oThisVDPG.Name)'"}
}
}
}
}
else {
## else just return the VDPG policy info objects
$arrVDPGPolicyInfo
}
}
}
The following snippets give examples of how to use Assert-HardeningGuideVNetworkPortOverride
from Listing 13-9 to assess and remediate the vDPortgroup override settings:
## Assess
Assert-HardeningGuideVNetworkPortOverride
VDPortgroup : vDS-Primary-DVUplinks-160
VDSwitch : vDSwitch0
HasSuggestedValues : False
BlockPorts : True
TrafficShaping : True
VendorConfiguration : False
VLAN : False
UplinkTeaming : True
ResourceAllocation : True
SecurityPolicy : True
NetFlow : False
TrafficFilteringMarking : False
VDPortgroup : vDS-jcpwp0
VDSwitch : vDSwitch0
HasSuggestedValues : False
BlockPorts : False
TrafficShaping : False
VendorConfiguration : False
VLAN : True
UplinkTeaming : True
ResourceAllocation : False
SecurityPolicy : False
NetFlow : False
TrafficFilteringMarking : False
## Remediate; returns the updated vDPortgroup objects after reconfig
Assert-HardeningGuideVNetworkPortOverride -Remediate -Verbose
VERBOSE: Performing the operation "Reconfigure OverrideAllowed policies to
'False'" on target "vDS-Primary-DVUplinks-160".
VERBOSE: Performing the operation "Reconfigure OverrideAllowed policies to
'False'" on target "vDS-jcpwp0".
Name NumPorts PortBinding
---- -------- -----------
vDS-Primary-DVUplinks-160 4 Static
vDS-jcpwp0 64 Static
## next assessment shows all is well; great!
Assert-HardeningGuideVNetworkPortOverride
VDPortgroup : vDS-Primary-DVUplinks-160
VDSwitch : vDSwitch0
HasSuggestedValues : True
BlockPorts : False
TrafficShaping : False
VendorConfiguration : False
VLAN : False
UplinkTeaming : False
ResourceAllocation : False
SecurityPolicy : False
NetFlow : False
TrafficFilteringMarking : False
VDPortgroup : vDS-jcpwp0
VDSwitch : vDSwitch0
HasSuggestedValues : True
BlockPorts : False
TrafficShaping : False
VendorConfiguration : False
VLAN : False
UplinkTeaming : False
ResourceAllocation : False
SecurityPolicy : False
NetFlow : False
TrafficFilteringMarking : False
As you can see, the two vDPortgroup
s involved each had some settings that allowed various overrides. The remediation task updated the settings and returned the updated vDPortgroup
objects. Rerunning the assessment returned information objects that show that each vDPortgroup
now has the suggested values. The Assert-HardeningGuideVNetworkPortOverride
function in Listing 13-9 assesses and remediates all vDPortgroup
s in the connected vCenter by default. You can focus this scope by using the -VDPortgroup
parameter.
Again, as VMware has gone the route of more secure default settings in vSphere products, the number of items of security concern has dropped. There is but one remaining vCenter.*
programmatic guideline in the 6.0 version of the Hardening Guide, and the guide includes the PowerCLI commands for assessment and remediation of that recommended setting.
As for other vCenter considerations, you should follow relevant common security best practices for your vCenter Server if it is running in a Windows-based OS, such as antivirus, antimalware, and security-related OS configurations. These practices are outside the scope of this book. For vCenter running as the vCenter Server Appliance (VCSA), VMware already applies hardening recommendations to the appliance—hurray for VMware!
In this chapter, we looked at the security hardening guidelines from the Hardening Guide, focusing on the ones for which the Guide does not provide assessment and/or remediation PowerCLI automation. We provided a command, code block, or function for you to use to close the security hole. Now your hosts, your VMs, and your vNetworks will be that much better prepared for the security threats that they face.
Next improvement: Instead of running these detection and remediation scripts separately for each of the guidelines, we’d like to see a module or tool that leverages the assessment and remediation code to run as a whole—say, something schedulable that reports each time period on the security “hardness” of your environment, like Alan Renouf’s vCheck-vSphere
(https://github.com/alanrenouf/vCheck-vSphere) but with a security focus (we could call it vSecCheck
).
While more desirable, creating such a tool is beyond the scope of this chapter. Let us know on the book’s web page (see www.wiley.com/go/vmwarevspherepowercli2e) if there is interest in such a framework, and we will try to oblige. Or, start such a project, make it open (on GitHub perhaps), and let everyone know so that we all can contribute to your project!
Enjoy making your vSphere environment more secure with the help of PowerCLI.