CloudTadaInsights

Securing ESXi Connections: Using Non-Root Users with vCenter Server

Securing ESXi Connections: Using Non-Root Users with vCenter Server

Why Use Non-Root Users for ESXi Management?

Using the root account for ESXi host management in Data Center Virtualization (DCV) environments creates several security and operational challenges:

Security Concerns

  1. Shared Account Risks: The root user is a well-known administrative account that poses audit and security risks when shared across multiple systems.

  2. Audit Difficulties: In vCenter Server (VCSA) or Cloud Foundation (SDDC) environments, using shared root accounts creates difficulties for IT audits and accountability.

  3. Over-Privileged Access: The root user has full administrative control of ESXi and serves as the connection method for adding hosts to vCenter, Cloud Foundation, backup solutions, and storage arrays.

  4. Operational Dependencies: IT security processes often require changing root passwords on scheduled intervals, which affects VM control, host connections in VCSA/SDDC, backup solutions, and storage connections.

Best Practice Approach

  • Implement dedicated administrative accounts for ESXi-vCenter connections
  • Reduce dependency on root account credentials
  • Enhance security posture and audit compliance

Method 1: Local User Account Configuration

Prerequisites and Understanding

In ESXi 7.0+, additional authorization configuration is required when adding ESXi hosts to vCenter Server with non-root users. Without proper configuration, you may encounter authentication errors such as "Cannot complete login due to incorrect username or password."

Step-by-Step Configuration Process

Step 1: Create the Automation Script

Create a Python script named esxi_non_root_setup.py with the following code:

PYTHON
#!/usr/bin/python
import atexit
import argparse
import getpass
import ssl
import sys

from pyVmomi import vim, vmodl
from pyVim.connect import SmartConnect, Disconnect

def get_args():
    parser = argparse.ArgumentParser(description='Configure ESXi for non-root user and Lockdown Mode')
    parser.add_argument('-s', '--host', required=True, help='IP of the ESXi host')
    parser.add_argument('-u', '--user', required=True, help='Root user to connect initially')
    parser.add_argument('-n', '--new_user', required=True, help='Name of the new user to create (e.g., svc_vcenter)')
    parser.add_argument('-p', '--new_pass', required=True, help='Password for the new user')
    
    args = parser.parse_args()
    # Securely enter root password
    args.root_pass = getpass.getpass(prompt='Enter root password: ')
    return args

def create_user_with_minimal_privileges(content, username, password):
    """
    Create user, new role (minimal privileges) and assign permissions.
    """
    auth_manager = content.authorizationManager
    user_manager = content.user_manager

    # 1. Create user
    print(f"[*] Creating user: {username}...")
    try:
        user_manager.CreateUser(username, password, None)
        print(f"[+] User {username} has been created.")
    except vmodl.fault.AlreadyExists:
        print(f"[!] User {username} already exists. Skipping user creation.")
    except Exception as e:
        print(f"[!] Error creating user: {e}")
        sys.exit(1)

    # 2. Create new Role with minimum privileges (Minimal Privileges)
    # vCenter requires this permission to create the vpxuser and manage the host.
    role_name = "vCenterAdminMinimal"
    
    # List of required privileges (Important: Host.Local.ManageUserGroups)
    required_privileges = [
        'Host.Config.*',           # Configure host
        'Host.Cim.*',              # Hardware management (CIM)
        'Host.Inventory.*',        # Manage inventory
        'Host.Local.ManageUserGroups', # REQUIRED: To create vpxuser
        'System.Read',
        'System.Write'
    ]
    
    # Check if role already exists
    role_id = None
    for role in auth_manager.role_list:
        if role.name == role_name:
            role_id = role.roleId
            print(f"[!] Role '{role_name}' already exists. Using existing role.")
            break
    
    if role_id is None:
        print(f"[*] Creating new Role: {role_name}...")
        try:
            # Map string privilege to object privilege id
            priv_ids = []
            avail_privs = auth_manager.privilege_list
            priv_dict = {p.name: p.privId for p in avail_privs}
            
            for p in required_privileges:
                if p in priv_dict:
                    priv_ids.append(priv_dict[p])
                elif '*' in p:
                    # Handle wildcard (e.g., Host.Config.*)
                    base = p.replace('*', '')
                    for key in priv_dict:
                        if key.startswith(base):
                            priv_ids.append(priv_dict[key])

            auth_manager.AddRole(role_name, priv_ids)
            role_id = next(r.roleId for r in auth_manager.role_list if r.name == role_name)
            print(f"[+] Role '{role_name}' has been created with {len(priv_ids)} privileges.")
        except Exception as e:
            print(f"[!] Error creating role: {e}")
            sys.exit(1)

    # 3. Assign Role to User on Root Folder
    print(f"[*] Assigning permissions to user {username}...")
    perm = vim.Permission()
    perm.principal = username
    perm.group = False
    perm.roleId = role_id
    perm.propagate = True
    
    try:
        auth_manager.SetEntityPermissions(content.rootFolder, [perm])
        print(f"[+] Permissions assigned successfully.")
    except Exception as e:
        print(f"[!] Error assigning permissions: {e}")
        sys.exit(1)

def configure_lockdown_mode(host_system, username):
    """
    Configure Lockdown Mode and add user to Exception List.
    """
    print("[*] Configuring Lockdown Mode...")
    
    # Get Access Manager
    host_config_manager = host_system.configManager
    access_manager = host_config_manager.hostAccessManager
    
    # Get current exception list
    # Note: You might need to change user to domain\user if using domain
    current_exceptions = list(access_manager.exceptionUsers) if access_manager.exceptionUsers else []
    
    if username not in current_exceptions:
        current_exceptions.append(username)
        print(f"[*] Adding user {username} to the exception list...")
    
    # Update Exception List before enabling Lockdown (If not, the user might be blocked immediately)
    try:
        access_manager.UpdateLockdownExceptions(current_exceptions)
        print("[+] Updated Exception List.")
    except Exception as e:
        print(f"[!] Warning: Could not update exception list: {e}")
        print("[!] If Lockdown is enabled, the user might be blocked. Please check manually.")

    # Enable Lockdown Mode (lockdownNormal)
    try:
        # State: lockdownNormal (Normal), lockdownStrict (Strict)
        access_manager.ChangeLockdownMode("lockdownNormal")
        print("[+] Enabled Lockdown Mode (lockdownNormal).")
        print("[+] Root account has been locked directly.")
    except Exception as e:
        print(f"[!] Error enabling Lockdown Mode: {e}")

def main():
    args = get_args()
    
    # SSL context ignoring verify (for Lab environment)
    context = ssl.SSLContext(ssl.PROTOCOL_TLSv1_2)
    context.verify_mode = ssl.CERT_NONE

    # Connect to ESXi using ROOT
    try:
        si = SmartConnect(host=args.host, user=args.user, pwd=args.root_pass, sslContext=context)
        atexit.register(Disconnect, si)
        content = si.RetrieveContent()
        
        # Get Host System object (Get first host in the list)
        host_system = content.rootFolder.childEntity[0].hostFolder.childEntity[0].hostFolder.childEntity[0].host[0]
        
        # 1. Create User & Role
        create_user_with_minimal_privileges(content, args.new_user, args.new_pass)
        
        # 2. Configure Lockdown Mode
        configure_lockdown_mode(host_system, args.new_user)
        
        print("\n" + "="*50)
        print("SUCCESS! System has been configured securely.")
        print("Please use the information below to add host to vCenter:")
        print(f"Host IP: {args.host}")
        print(f"Username: {args.new_user}")
        print(f"Password: {args.new_pass}")
        print("="*50)

    except vmodl.MethodFault as e:
        print(f"[!] vAPI/VMware Error: {e.msg}")
        sys.exit(1)
    except Exception as e:
        print(f"[!] Undefined Error: {e}")
        sys.exit(1)

if __name__ == "__main__":
    main()

Step 2: Upload and Execute the Script

  1. Upload the script to your ESXi host using SSH tools like PuTTY or WinSCP to the /tmp directory.

  2. Connect to ESXi via SSH as root user.

  3. Execute the script with the following command:

    BASH
    python /tmp/esxi_non_root_setup.py -s [ESXI_HOST_IP] -u root -n [NEW_USERNAME] -p [NEW_PASSWORD]
  4. Provide the root password when prompted.

Step 3: Verify Configuration

After successful execution, verify that:

  • The new user has been created with appropriate permissions
  • Lockdown mode is properly configured
  • The new user can connect to ESXi from vCenter

Step 4: Add ESXi Host to vCenter

Use the new non-root account credentials when adding the ESXi host to vCenter Server through the vSphere Client.

Method 2: Active Directory User Configuration

Prerequisites

  • ESXi host must be joined to Active Directory domain
  • AD user must be created and assigned to appropriate groups

Step-by-Step Configuration

Step 1: Create AD User and Group

On your Active Directory server:

  1. Create a new user account (e.g., svc-vcenter-esxi)
  2. Create or use an existing group (e.g., ESX Admins)
  3. Add the new user to the ESX Admins group

Step 2: Domain Join ESXi Host

Join the ESXi host to your Active Directory domain using the vSphere Client or ESXi Shell.

Step 3: Configure AD Group Permissions

The ESX Admins group from Active Directory will be automatically assigned administrative privileges on the ESXi host.

Step 4: Add Host Using AD Credentials

When adding the ESXi host to vCenter, use the AD user credentials in the format:

Security Considerations and Best Practices

Account Management

  • Service Account Naming: Use descriptive names like svc-vcenter-esxi or system-esxi-connection
  • Password Policy: Implement strong passwords (8-16+ characters with complexity)
  • Account Type: Use service accounts, not personal accounts
  • Password Rotation: Consider whether to enable periodic password changes based on security requirements

Privilege Management

  • Principle of Least Privilege: Grant only necessary permissions
  • Regular Reviews: Periodically audit account permissions
  • Monitoring: Implement logging for account usage

Lockdown Mode Benefits

  • Enhanced Security: Prevents direct root access
  • Audit Trail: Better accountability for administrative actions
  • Compliance: Helps meet security compliance requirements

Troubleshooting Common Issues

Authentication Failures

  • Verify user account exists and is enabled
  • Check that required permissions are assigned
  • Ensure account is not locked or expired

Lockdown Mode Issues

  • Confirm the user is in the lockdown exception list
  • Verify the user has appropriate administrative privileges
  • Check that the vpxuser account can be created

Connection Problems

  • Validate network connectivity between systems
  • Check firewall rules for required ports
  • Verify time synchronization between systems

Operational Considerations

Host Disconnection vs. Removal

Understanding the difference between disconnecting and removing a host from vCenter:

  • Disconnect: Suspends monitoring and management but keeps host and VMs in inventory. Performance metrics are preserved.
  • Remove: Removes host and VMs from inventory completely. Historical performance data is lost.

Credential Management

When using the root password to add a host to vCenter, the system establishes connection credentials that maintain communication between ESXi and vCenter, even if the ESXi root password is later changed.

Summary

Using non-root users for ESXi-vCenter connections provides significant security and operational benefits:

  1. Enhanced Security: Reduces risk of shared account compromises
  2. Better Auditability: Clear accountability for administrative actions
  3. Operational Resilience: Reduces dependency on root password changes
  4. Compliance: Helps meet security compliance requirements

Of the two methods, using Active Directory users is generally recommended as it provides centralized account management and security controls separate from ESXi systems, secured with AD encryption and policies.

Following these practices will help ensure secure and compliant DCV deployments while maintaining operational efficiency.

References

  1. Adding ESXi 7 to vCenter Server with Non-Root User
  2. VMware vSphere Security Documentation
  3. Active Directory Integration Best Practices for vSphere

You might also like

Browse all articles
Series

Security Best Practices in VMware Environments

Comprehensive guide to security best practices in VMware environments, covering ESXi hardening, vCenter security, network security, and compliance.

#VMware#Security#Hardening
Series

Introduction to Virtualization and VMware

An introduction to virtualization concepts and VMware products, covering the fundamentals of virtualization technology and VMware's role in the industry.

#VMware#Virtualization#ESXi
Series

Installation and Setup of VMware Workstation/ESXi

Complete guide to installing and setting up VMware Workstation and ESXi, including system requirements, installation procedures, and initial configuration.

#VMware#ESXi#Workstation
Series

Creating and Managing Virtual Machines

Learn how to create, configure, and manage virtual machines in VMware environments, including VM creation, resource allocation, and lifecycle management.

#VMware#Virtual Machine#VM Creation