Linux operating systems use the root user as the full authority account, and all administrative users know this account exists.
In Data Center Virtualization (DCV) environments such as vCenter Server (VCSA) or Cloud Foundation (SDDC), using shared root accounts is a mistake because it creates difficulties for IT audits.
The root user not only has full administrative control (AFC) of ESXi but also serves as the connection for Add Host to Inventory > Host Cluster of vCenter, Cloud Foundation, Veeam Backup & Replication (VBR), Commvault Server (CS), NAS storage that needs to connect and mount data stores to ESXi, etc.
IT security processes often include changing passwords for the root account at scheduled times or unexpectedly, all of which affect VM control, host connections in the Inventory of VCSA or SDDC, VBR, CS, NAS, etc.
In summary:
This is a security issue for DCV systems.
The goal to limit the use of root users with administrative roles is obvious.
Replacing with a different Admin account that maintains the connection between ESXi and VCSA/SDDC and storage arrays is the recommended approach.
In ESXi 7.0, users are required to configure additional authorization to be used for adding the ESXi host to vCenter Server.
When attempting to add ESXi version 7.0+ hosts to vCenter Server with a custom non-root user, the following error was received:
"System Error!" and "Status" is "Cannot complete login due to incorrect username or password."
ESXi Connection
ESXi Authentication Error Details
Steps to resolve this issue:
Step 1.
New users are not created by this script; we must follow the procedure to create new users and assign administrative privileges as applied to previous ESXi versions.
ESXi Host Successfully Added to vCenter Inventory
Changing Root Password on ESXi Host
Root Access Status After Password Change
Step 2:
Prepare the attached script (in the file) "esxi_new_admin.py" with the following .py 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 3. Use PuTTY/WINSCP to open SSH and upload the script esxi_new_admin.py to the '/' directory on the ESXi host.
Use SSH to connect to the ESXi host as root, see Using ESXi Shell in ESXi 5.x, 6.x and 7.x.
ESXi Shell Connection Interface
Run the uploaded script by executing the command:
TEXT
python /esxi_new_admin.py
ESXi LocalOS User Configuration Process
When prompted by the script, provide the new username and password for the "root" user.
For example, here User5 is the user we manually created as part of the LocalOS group on ESXi.
Step 4. Verify Admin Access Rights to ESXi for the New User:
Root User Demoted to Read-Only Access Rights
Test adding the host to the VCSA inventory using the new user:
AD User Admin Privileges Configuration Screen
Successfully added the host using the new user.
AD User Login Process in vCenter
Now change the ESXi root password to demonstrate that the root user does not affect the new user with admin rights.
Example: Reduce privileges of the root user on the ESXi host
Adding Host to vCenter with AD User Interface
Or
Change the password of the root user
Successfully Added Host to VCSA Inventory Confirmation
VMware Security Implementation Summary
After successfully changing the root user password, we will log out from the new user and log back in using the root user with the old password
Of the two methods above, using AD User is the best approach because it provides security and is separate from ESXi and is secured with MS AD-DC encryption.
Passwords should be at least 8-16 characters long with uppercase, lowercase, numbers, and special characters.
Account names should be in the form "SystemAccountServices" and should not have periodic password changes ("Password age") enabled, or be used for personal administration ("Personal account"), but should only be used to maintain connections between ESXi and VCSA/SDDC, etc.
Wishing you success and safety in configuring and operating secure DCV deployments!
From the famous virtualization book "Mastering vSphere 6.7"
Configuring and Managing vSphere 6.7
Chapter 5
When you enter the root password to add the host to the vCenter, the password is used to establish a connection with the host and to install the vCenter agent. The process sets different credentials that maintain the communication and authentication between ESXi and vCenter, even if ESXi's root password is changed.
Disconnecting a host from vCenter Server
Once ESXi is connected to vCenter Server, you can always disconnect or remove the host later on. It's important to understand that disconnecting the host from vCenter Server is different from removing the host. Disconnecting a managed host from vCenter Server doesn't remove ESXi from the VCenter Inventory as well as the VMs registered on the host. When the managed host is disconnected, vCenter Server suspends monitoring and management activities for that host. If you disconnect a host and then connect it again, all the performance metrics will be kept.
It's a different story if you remove the host from the vCenter Server. Removing a managed host from the vCenter Server means the host and its VMs are removed from the vCenter Inventory. If you remove a host from the inventory and then add it again, it will be considered a new object and no historical performance metrics will be available.
Setting up the infrastructure for PostgreSQL High Availability with Patroni and etcd, including hardware requirements, network configuration, firewall, SSH keys, and time synchronization.
Essential security features and hardening measures for PostgreSQL HA cluster deployment with Patroni, etcd, and PgBouncer. Follow this guide for production security best practices.
Understanding Patroni and etcd for PostgreSQL High Availability, including DCS, Raft consensus algorithm, leader election, and split-brain prevention mechanisms.
Deep dive into PostgreSQL Streaming Replication, covering WAL mechanisms, synchronous vs asynchronous replication, replication slots, and hands-on lab setup.