|
| 1 | +--- |
| 2 | +layout: default |
| 3 | +title: Harden VMs in vSphere |
| 4 | +--- |
| 5 | + |
| 6 | +# Introduction |
| 7 | + |
| 8 | +This tutorial will walk you through executing functions in response to vSphere events. |
| 9 | +_Note:_ Ensure you have followed the [Quickstart]({{ '/documentation/front/quickstart' | relative_url }}) guide and have a working Dispatch Virtual Appliance. |
| 10 | + |
| 11 | +Our goal is to execute a PowerShell function called hardenVM, which will apply additional hardening configuration on every virtual machine created in vSphere. |
| 12 | + |
| 13 | +# Create Seed Images |
| 14 | + |
| 15 | +Dispatch is bundled with a set of seed images for multiple languages to get you started easily. If you followed the guide, you should |
| 16 | +have a set of images created in your Dispatch VM. Execute the following command to check: |
| 17 | + |
| 18 | +```bash |
| 19 | +$ dispatch get images |
| 20 | + NAME | URL | BASEIMAGE | STATUS | CREATED DATE |
| 21 | +----------------------------------------------------------------------------- |
| 22 | + java | dispatch/f23f029e... | java-base | READY | ... |
| 23 | + nodejs | dispatch/6f04f67d... | nodejs-base | READY | ... |
| 24 | + powershell | dispatch/edcbdda8... | powershell-base | READY | ... |
| 25 | + python3 | dispatch/1937b329... | python3-base | READY | ... |
| 26 | +``` |
| 27 | + |
| 28 | +If the list is empty, you can create the seed images using the following command: |
| 29 | + |
| 30 | +```bash |
| 31 | +$ dispatch create seed-images |
| 32 | +Created BaseImage: nodejs-base |
| 33 | +Created BaseImage: python3-base |
| 34 | +Created BaseImage: powershell-base |
| 35 | +Created BaseImage: java-base |
| 36 | +Created Image: nodejs |
| 37 | +Created Image: python3 |
| 38 | +Created Image: powershell |
| 39 | +Created Image: java |
| 40 | +``` |
| 41 | + |
| 42 | +**Note**: You need to wait a short while for the images to be in the `READY` state. You can always check the status using the `dispatch get images` command. |
| 43 | + |
| 44 | +# Create a PowerShell image with PowerCLI dependency |
| 45 | + |
| 46 | +Our function needs a library that knows how to talk to vSphere. We will use `PowerCLI` SDK to do that. To bring the dependency for our function, we will prepare a dedicated image. |
| 47 | + |
| 48 | +1. Create a file named `deps.ps1` with PowerCLI dependency: |
| 49 | + |
| 50 | +```bash |
| 51 | +cat << EOF > deps.psd1 |
| 52 | +@{ |
| 53 | + 'VMware.PowerCLI' = 'latest' |
| 54 | +} |
| 55 | +EOF |
| 56 | +``` |
| 57 | + |
| 58 | +2. create a new image using PowerShell base image: |
| 59 | + |
| 60 | +``` |
| 61 | +$ dispatch create image powershell-powercli powershell-base --runtime-deps deps.psd1 |
| 62 | +Created image: powershell-powercli |
| 63 | +``` |
| 64 | + |
| 65 | +# Create a secret with vSphere credentials |
| 66 | + |
| 67 | +Our function also needs to know how to connect to vSphere, and what credentials to use. In order to do that, we will create a Dispatch secret. |
| 68 | + |
| 69 | +1. Create a JSON file with secret contents: |
| 70 | + |
| 71 | +```bash |
| 72 | +cat << EOF > vsphere.json |
| 73 | +{ |
| 74 | + "host": "<vCenter host URL, e.g. https://vcenter.example.com>", |
| 75 | + "username": "<username>", |
| 76 | + "password": "<password>" |
| 77 | + "vcenterurl": "<full vCenter URL, e.g. username:[email protected]:443>" |
| 78 | +} |
| 79 | +EOF |
| 80 | +``` |
| 81 | + |
| 82 | +2. Create a dispatch secret called `vsphere`: |
| 83 | +``` |
| 84 | +$ dispatch create secret vsphere vsphere.json |
| 85 | +Created secret: vsphere |
| 86 | +``` |
| 87 | + |
| 88 | +# Create a function |
| 89 | + |
| 90 | +We will use [`ApplyHardening` function](https://github.com/vmware/PowerCLI-Example-Scripts/blob/cb9e57e185a268559747fdb679299499dea816b5/Modules/apply-hardening/apply-hardening.psm1) |
| 91 | +from PowerCLI-Example-Scripts repo. We slightly modify it to include the entrypoint handler (comments removed for brevity. |
| 92 | + |
| 93 | +1. Create function file with following contents: |
| 94 | +```powershell |
| 95 | +Import-Module PowerCLI.ViCore |
| 96 | +
|
| 97 | +function Apply-Hardening { |
| 98 | + [CmdletBinding()] |
| 99 | + param( |
| 100 | + [Parameter(Mandatory=$true, |
| 101 | + ValueFromPipeline=$True, |
| 102 | + Position=0)] |
| 103 | + [VMware.VimAutomation.ViCore.Impl.V1.Inventory.InventoryItemImpl[]] |
| 104 | + $VMs |
| 105 | + ) |
| 106 | + Process { |
| 107 | + $ExtraOptions = @{ |
| 108 | + "isolation.tools.diskShrink.disable"="true"; |
| 109 | + "isolation.tools.diskWiper.disable"="true"; |
| 110 | + "isolation.tools.copy.disable"="true"; |
| 111 | + "isolation.tools.paste.disable"="true"; |
| 112 | + "isolation.tools.dnd.disable"="true"; |
| 113 | + "isolation.tools.setGUIOptions.enable"="false"; |
| 114 | + "log.keepOld"="10"; |
| 115 | + "log.rotateSize"="100000" |
| 116 | + "RemoteDisplay.maxConnections"="2"; |
| 117 | + "RemoteDisplay.vnc.enabled"="false"; |
| 118 | + |
| 119 | + } |
| 120 | + if ($DebugPreference -eq "Inquire") { |
| 121 | + Write-Output "VM Hardening Options:" |
| 122 | + $ExtraOptions | Format-Table -AutoSize |
| 123 | + } |
| 124 | + |
| 125 | + $VMConfigSpec = New-Object VMware.Vim.VirtualMachineConfigSpec |
| 126 | + |
| 127 | + Foreach ($Option in $ExtraOptions.GetEnumerator()) { |
| 128 | + $OptionValue = New-Object VMware.Vim.optionvalue |
| 129 | + $OptionValue.Key = $Option.Key |
| 130 | + $OptionValue.Value = $Option.Value |
| 131 | + $VMConfigSpec.extraconfig += $OptionValue |
| 132 | + } |
| 133 | + ForEach ($VM in $VMs){ |
| 134 | + $VMv = Get-VM $VM | Get-View |
| 135 | + $state = $VMv.Summary.Runtime.PowerState |
| 136 | + Write-Output "...Starting Reconfiguring VM: $VM " |
| 137 | + $TaskConf = ($VMv).ReconfigVM_Task($VMConfigSpec) |
| 138 | + if ($state -eq "poweredOn") { |
| 139 | + Write-Output "...Migrating VM: $VM " |
| 140 | + $TaskMig = $VMv.MigrateVM_Task($null, $_.Runtime.Host, 'highPriority', $null) |
| 141 | + } |
| 142 | + } |
| 143 | + } |
| 144 | +} |
| 145 | + |
| 146 | +function handle($context, $payload) { |
| 147 | + [void](Set-PowerCLIConfiguration -InvalidCertificateAction ignore -Confirm:$false) |
| 148 | +
|
| 149 | + $username = $context.secrets.username |
| 150 | + $password = $context.secrets.password |
| 151 | + $hostname = $context.secrets.host |
| 152 | + $vmName = $payload.metadata.vm_name |
| 153 | +
|
| 154 | + # Connect to vSphere |
| 155 | + Write-Host "Checking VC Connection is active" |
| 156 | + if (-not $global:defaultviservers) { |
| 157 | + Write-Host "Connecting to $hostname" |
| 158 | + $server = connect-viserver -server $hostname -User $username -Password $password |
| 159 | + } else { |
| 160 | + Write-Host "Already connected to $hostname" |
| 161 | + } |
| 162 | +
|
| 163 | + Write-Host "Get Virtual Machine By name" |
| 164 | + $vm = Get-VM -Name $vmName |
| 165 | +
|
| 166 | + Write-Host "Security Harden our VM" |
| 167 | + $vm | Apply-Hardening |
| 168 | + |
| 169 | + return "success" |
| 170 | +} |
| 171 | +``` |
| 172 | + |
| 173 | +2. Create Dispatch function using previously created image and secret (assuming the function file is called hardenvm.ps1): |
| 174 | +``` |
| 175 | +$ dispatch create function harden-vm hardenvm.ps1 --image=powershell-powercli --secret vsphere |
| 176 | +Created function: harden-vm |
| 177 | +``` |
| 178 | + |
| 179 | +If you have a VM in your vSphere already, you can test the function before wiring it to vSphere events by running it manually: |
| 180 | + |
| 181 | +``` |
| 182 | +$ dispatch exec harden-vm --wait --input '{"metadata": {"vm_name": "myvm"}}' | jq -r '.output' |
| 183 | +[ |
| 184 | + "...Starting Reconfiguring VM: myvm ", |
| 185 | + "success" |
| 186 | +] |
| 187 | +``` |
| 188 | + |
| 189 | +Replace `myvm` with the name of your vm. |
| 190 | + |
| 191 | +# Create an event driver |
| 192 | +In order to receive vSphere events we need to create an event driver. |
| 193 | + |
| 194 | +1. Create a vcenter event driver type which registers the docker image implementing the driver: |
| 195 | +``` |
| 196 | +$ dispatch create event-driver-type vcenter dispatchframework/dispatch-events-vcenter:solo-auth |
| 197 | +Created event driver type: vcenter |
| 198 | +``` |
| 199 | + |
| 200 | +2. Create the driver using previously created secret: |
| 201 | +``` |
| 202 | +$ dispatch create event-driver vcenter --secret vsphere --name vcenter |
| 203 | +Created event driver: vcenter |
| 204 | +``` |
| 205 | + |
| 206 | +# Wiring function and event - subscription |
| 207 | + |
| 208 | +The final step is to wire the function and event together. to do that, we create a subscription. We will use vsphere event |
| 209 | +`VMDeployedEvent`, which is emitted when VM finishes deploying: |
| 210 | + |
| 211 | +``` |
| 212 | +$ dispatch create subscription --event-type vm.deployed harden-vm --name harden_deployed |
| 213 | +created subscription: harden_deployed |
| 214 | +``` |
| 215 | + |
| 216 | +Now, when you create a VM in vSphere, after a while you should see that the `harden-vm` function is executed, and in vSphere UI |
| 217 | +yo can see that VM has now extra configured applied to it! |
| 218 | + |
| 219 | +{:width="600px"} |
| 220 | + |
| 221 | + |
| 222 | + |
| 223 | + |
0 commit comments