VMware ESXi Host Configuration with Ansible – (Security Hardening)

By | March 8, 2022

Ransomware is on everyone’s mind or at least it should be… 

Please be aware this is not a prescription to keep you from being hacked, but rather an improvement of your security posture specific to vSphere.

Attackers are constantly looking for weaknesses in your IT environment to exploit for malicious purposes. To help minimize a potential breach in your environment, you should ensure that your operating systems are configured according to established security standards and industry best practices – and that they remain that way. 

The vSphere Security Configuration Guide (SCG) 7 is the baseline for security hardening of VMware vSphere itself, and the core of VMware security best practices. The VMware vSphere Security Hardening Guide gives recommended guidance for vSphere Administrators looking to protect their infrastructure.

One way to ensure that your VMware ESXi hosts have a consistent and repeatable configuration; is with the use of Ansible. The ansible playbook can be run against a host to apply a desired configuration, run on a weekly basis to check for configuration drift, and reapply desired state. The true power is running the ansible playbook against multiple hosts and confirming the configuration is the same on all hosts in minutes.

My very good friend Sean has been the inspiration of my Ansible playbooks, you can build and expand the contents of your Ansible playbook, simply by adding or removing ‘tasks’, to enhance and automate additional steps. Make sure to check out his blog Madlabber

RedHat Ansible

The Red Hat Ansible website has various modules, along with examples on how to configure and manage your VMware environment using Ansible playbooks.  You just need to find what you are trying to accomplish, copy / modify the sample and make it your own.

ESXi MANAGE SERVICES

The “community.vmware.vmware_host_service_manager” module can be used to manage (start or stop) services on VMware ESXi hosts.

With the use of a variables file (vars.yml), I can easily make changes globally that all my playbooks can use.

To enable or disable SSH, you just need to modify the ‘tsm_state’ on the vars.yml from ‘present’ to ‘absent’. 

Now you can start to see some of the power of running this against the hosts in your environment to configure / validate the state of SSH.

  - name: Add ESXi host for SSH access
    add_host:
      name: '{{ esxi_address }}'
      group: "esx"
      ansible_user: '{{ esxi_username }}'
      ansible_password: '{{ esxi_password }}'
      ansible_ssh_common_args: '-o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no'
  - name: Enable SSH (TSM-SSH)
    community.vmware.vmware_host_service_manager:
      <<: *esxi_login
      esxi_hostname: '{{ esxi_address }}'
      service_name: TSM-SSH
      service_policy: '{{ tsm_policy }}'
      state: '{{ tsm_state }}'
    delegate_to: localhost
  - name: Enable ESX Shell (TSM)
  - name: Enable ESX Shell (TSM)
    community.vmware.vmware_host_service_manager:
      <<: *esxi_login
      esxi_hostname: '{{ esxi_address }}'
      service_name: TSM
      service_policy: '{{ tsm_policy }}'
      state: '{{ tsm_state }}'
    delegate_to: localhost

ESXi Security Configuration Tasks

The “community.vmware.vmware_host_config_manager” module can be used to configure advanced settings on VMware ESXi hosts.

I have chosen most of the ESXi configuration options from the vSphere Security Configuration Guide (SCG) 7 along with other best practices to enforce VMware ESXi security configurations across my environment.

  - name: Set Advanced Options
    community.vmware.vmware_host_config_manager:
      <<: *esxi_login
      esxi_hostname: '{{ esxi_address }}'
      options:
        "UserVars.ESXiShellInteractiveTimeOut": 900
        "UserVars.ESXiShellTimeOut": 900
        "UserVars.DcuiTimeOut": 600
        "Security.AccountLockFailures": 5
        "Security.AccountUnlockTime": 900
        "Security.PasswordQualityControl": "similar=deny retry=3 min=disabled,disabled,disabled,disabled,15"
        "UserVars.SuppressShellWarning": 1 
        "Mem.ShareForceSalting": 0
        "Misc.BlueScreenTimeout": 60
        "Config.HostAgent.plugins.solo.enableMob": false 

Here are some additional settings for those using NetApp NFS

  - name: Set Advanced Option NFS NetAPP VSC Values
    community.vmware.vmware_host_config_manager:
      <<: *esxi_login
      esxi_hostname: '{{ esxi_address }}'
      options:
        "Net.TcpipHeapSize": 32
        "Net.TcpipHeapMax": 1536
        "NFS.MaxVolumes": 256
        "NFS41.MaxVolumes": 256
        "NFS.MaxQueueDepth": 128
        "NFS.HeartbeatMaxFailures": 10
        "NFS.HeartbeatFrequency": 12
        "NFS.HeartbeatTimeout": 5
        "Disk.QFullSampleSize": 32
        "Disk.QFullThreshold": 8
    delegate_to: localhost

ESXi DNS / NTP Configuration Tasks

The “community.vmware.vmware_host_dns” module can be used to configure DNS settings on VMware ESXi hosts.

  - name: Configure ESXi hostname and upstream DNS servers
    community.vmware.vmware_host_dns:
      <<: *esxi_login
      domain: '{{ domain_name }}'
      type: static 
      dns_servers:
      - '{{ upstream_dns1 }}'
      - '{{ upstream_dns2 }}'
    delegate_to: localhost

The “community.vmware.vmware_host_ntp” module can be used to configure NTP settings on VMware ESXi hosts.

  - name: Set NTP servers for an ESXi Host # Configure Host NTP Settings 
    community.vmware.vmware_host_ntp:
      <<: *esxi_login
      esxi_hostname: '{{ esxi_hostname }}'
      state: present
      ntp_servers:
        - '{{ upstream_ntp1 }}'
        - '{{ upstream_ntp2 }}'
    delegate_to: localhost
  - name: Start ntpd service setting for all ESXi Host in given Cluster # Enable  NTP Service
    community.vmware.vmware_host_service_manager:
      <<: *esxi_login
      esxi_hostname: '{{ esxi_hostname }}'
      service_name: ntpd
      service_policy: on
      state: present
    delegate_to: localhost

Ansible 01_ESX_Config.YML Playbook

All the above snippets together form the 01_ESX_Config Ansible Playbook to help enforce the VMware ESXi Security Configurations, hardening and other common configurations.

--- 
- hosts: localhost 
  name: ESXi Configuration 
  gather_facts: false
  vars:
    esxi_login: &esxi_login
      hostname: '{{ esxi_address }}'  
      username: '{{ esxi_username }}'
      password: '{{ esxi_password }}'   
      validate_certs: no 
    tsm_policy: on
    tsm_state: present
  vars_files: 
    vars.yml
  tasks: 
  - name: Add ESXi host for SSH access
    add_host:
      name: '{{ esxi_address }}'
      group: "esx"
      ansible_user: '{{ esxi_username }}'
      ansible_password: '{{ esxi_password }}'
      ansible_ssh_common_args: '-o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no'
  - name: Enable SSH (TSM-SSH)
    community.vmware.vmware_host_service_manager:
      <<: *esxi_login
      esxi_hostname: '{{ esxi_address }}'
      service_name: TSM-SSH
      service_policy: '{{ tsm_policy }}'
      state: '{{ tsm_state }}'
    delegate_to: localhost
  - name: Enable ESX Shell (TSM)
    community.vmware.vmware_host_service_manager:
      <<: *esxi_login
      esxi_hostname: '{{ esxi_address }}'
      service_name: TSM
      service_policy: '{{ tsm_policy }}'
      state: '{{ tsm_state }}'
    delegate_to: localhost
  - name: Set Advanced Options
    community.vmware.vmware_host_config_manager:
      <<: *esxi_login
      esxi_hostname: '{{ esxi_address }}'
      options:
        "UserVars.ESXiShellInteractiveTimeOut": 900
        "UserVars.ESXiShellTimeOut": 900
        "UserVars.DcuiTimeOut": 600
        "Security.AccountLockFailures": 5
        "Security.AccountUnlockTime": 900
        "Security.PasswordQualityControl": "similar=deny retry=3 min=disabled,disabled,disabled,disabled,15"
        "UserVars.SuppressShellWarning": 1 
        "Mem.ShareForceSalting": 0
        "Misc.BlueScreenTimeout": 60
        "Config.HostAgent.plugins.solo.enableMob": false 
    delegate_to: localhost
  - name: Set Advanced Option NFS NetAPP VSC Values
    community.vmware.vmware_host_config_manager:
      <<: *esxi_login
      esxi_hostname: '{{ esxi_address }}'
      options:
        "Net.TcpipHeapSize": 32
        "Net.TcpipHeapMax": 1536
        "NFS.MaxVolumes": 256
        "NFS41.MaxVolumes": 256
        "NFS.MaxQueueDepth": 128
        "NFS.HeartbeatMaxFailures": 10
        "NFS.HeartbeatFrequency": 12
        "NFS.HeartbeatTimeout": 5
        "Disk.QFullSampleSize": 32
        "Disk.QFullThreshold": 8
    delegate_to: localhost
  - name: Manage Firewall Rules
    community.vmware.vmware_host_firewall_manager:
      <<: *esxi_login
      esxi_hostname: '{{ esxi_address }}'      
      rules:
        - name: remoteSerialPort 
          enabled: true
          allowed_host:
            all_ip: true
    delegate_to: localhost
  - name: Configure ESXi hostname and upstream DNS servers
    community.vmware.vmware_host_dns:
      <<: *esxi_login
      domain: '{{ domain_name }}'
      type: static 
      dns_servers:
      - '{{ upstream_dns1 }}'
      - '{{ upstream_dns2 }}'
    delegate_to: localhost
  - name: Set NTP servers for an ESXi Host # Configure Host NTP Settings 
    community.vmware.vmware_host_ntp:
      <<: *esxi_login
      esxi_hostname: '{{ esxi_hostname }}'
      state: present
      ntp_servers:
        - '{{ upstream_ntp1 }}'
        - '{{ upstream_ntp2 }}'
    delegate_to: localhost
  - name: Start ntpd service setting for all ESXi Host in given Cluster # Enable  NTP Service
    community.vmware.vmware_host_service_manager:
      <<: *esxi_login
      esxi_hostname: '{{ esxi_hostname }}'
      service_name: ntpd
      service_policy: on
      state: present
    delegate_to: localhost
  - name: Copy VAAI vib to esx host
    copy:
      src: '{{ files_path }}/{{ vaai_plugin }}'
      dest: '/vmfs/volumes/{{ esxi_local_datastore }}/NetAppNasPlugin.vib'
    delegate_to: '{{ esxi_address }}'
  - name: Install the VAAI vib 
    shell: 'esxcli software vib install -v /vmfs/volumes/{{ esxi_local_datastore }}/NetAppNasPlugin.vib'
    args:
      creates: /bootbank/netappna.v00
    ignore_errors: yes
    delegate_to: '{{ esxi_address }}'
    register: installvib
  - name: Reboot-Host
    vmware_host_powerstate:
      <<: *esxi_login
      esxi_hostname: '{{ esxi_address }}'   
      state: reboot-host
      force: yes
    delegate_to: localhost
    when: installvib.changed
  - name: Wait for Host Reboot
    wait_for:
      port: 443
      host: '{{ esxi_address }}'
      delay: 120
      timeout: 300
    connection: local
    when: installvib.changed

Leave a Reply

Your email address will not be published. Required fields are marked *

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