User Manager Guide
Configure user accounts, privileges, and LDAP authentication on OpenBMC.
Table of Contents
- Overview
- Setup & Configuration
- Local User Management
- Privilege Roles
- IPMI User Management
- LDAP Configuration
- Active Directory
- Account Policies
- Multi-Factor Authentication (MFA)
- Session Management
- Troubleshooting
- Deep Dive
- Examples
- References
Overview
OpenBMC user management is handled by phosphor-user-manager, providing local accounts, role-based access control, and LDAP integration.
---
title: User Manager Architecture
---
flowchart TB
subgraph access["Access Methods"]
direction TB
redfish["Redfish API"]
ipmi["IPMI Users"]
ssh["SSH Login"]
webui["WebUI Login"]
end
access --> usermgr
subgraph usermgr["phosphor-user-manager"]
direction TB
local["Local Accounts<br/>/etc/passwd"]
ldap["LDAP/AD Config<br/>nslcd/sssd"]
priv["Privilege<br/>Management"]
end
usermgr --> pam["PAM<br/>(Pluggable Authentication Modules)"]
ASCII-art version (for comparison)
┌─────────────────────────────────────────────────────────────────┐
│ User Manager Architecture │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────────────────────────────────────┐│
│ │ Access Methods ││
│ │ ││
│ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ ││
│ │ │ Redfish │ │ IPMI │ │ SSH │ │ WebUI │ ││
│ │ │ API │ │ Users │ │ Login │ │ Login │ ││
│ │ └────┬─────┘ └────┬─────┘ └────┬─────┘ └────┬─────┘ ││
│ └────────┼─────────────┼─────────────┼─────────────┼──────────┘│
│ └─────────────┴──────┬──────┴─────────────┘ │
│ │ │
│ ┌─────────────────────────────┴───────────────────────────────┐│
│ │ phosphor-user-manager ││
│ │ ││
│ │ ┌────────────────┐ ┌────────────────┐ ┌──────────────┐ ││
│ │ │ Local Accounts │ │ LDAP/AD Config │ │ Privilege │ ││
│ │ │ /etc/passwd │ │ nslcd/sssd │ │ Management │ ││
│ │ └────────────────┘ └────────────────┘ └──────────────┘ ││
│ └──────────────────────────┬──────────────────────────────────┘│
│ │ │
│ ┌──────────────────────────┴──────────────────────────────────┐│
│ │ PAM ││
│ │ (Pluggable Authentication Modules) ││
│ └─────────────────────────────────────────────────────────────┘│
└─────────────────────────────────────────────────────────────────┘
Setup & Configuration
Build-Time Configuration (Yocto)
# In your machine .conf or local.conf
# Include user management
IMAGE_INSTALL:append = " \
phosphor-user-manager \
"
# For LDAP support
IMAGE_INSTALL:append = " \
nss-pam-ldapd \
pam-plugin-ldap \
"
# Configure Meson options
EXTRA_OEMESON:pn-phosphor-user-manager = " \
-Dldap=enabled \
"
Configuration Files
| Config File | Install Path | Description |
|---|---|---|
| PAM auth config | /etc/pam.d/common-auth |
Authentication module stack |
| PAM password config | /etc/pam.d/common-password |
Password change rules |
| PAM account config | /etc/pam.d/common-account |
Account validation |
| LDAP client config | /etc/nslcd.conf |
nslcd LDAP client settings |
| LDAP NSS config | /etc/nsswitch.conf |
Name service switch (adds ldap) |
| D-Bus policy | /etc/dbus-1/system.d/phosphor-user-manager.conf |
D-Bus access control |
Deploying Custom PAM Configuration via Yocto
To customize PAM authentication (e.g., add stricter password policies or modify the auth module order), create a bbappend for the pam recipe:
# meta-myplatform/recipes-phosphor/users/phosphor-user-manager/
# └── files/
# │ └── common-auth
# │ └── common-password
# └── phosphor-user-manager_%.bbappend
cat > phosphor-user-manager_%.bbappend << 'EOF'
FILESEXTRAPATHS:prepend := "${THISDIR}/files:"
SRC_URI += " \
file://common-auth \
file://common-password \
"
do_install:append() {
# Deploy custom PAM configuration
install -d ${D}${sysconfdir}/pam.d
install -m 0644 ${WORKDIR}/common-auth ${D}${sysconfdir}/pam.d/
install -m 0644 ${WORKDIR}/common-password ${D}${sysconfdir}/pam.d/
}
EOF
Example common-auth with lockout policy:
# /etc/pam.d/common-auth
auth required pam_tally2.so deny=5 unlock_time=300 onerr=fail
auth sufficient pam_unix.so nullok try_first_pass
auth sufficient pam_ldap.so use_first_pass
auth required pam_deny.so
LDAP Configuration Persistence
LDAP configuration set via Redfish/D-Bus is persisted to:
# LDAP config persistence (managed by phosphor-user-manager)
/var/lib/phosphor-user-manager/ldap.conf
# nslcd runtime config (generated from D-Bus settings)
/etc/nslcd.conf
To bake default LDAP settings into the image:
# meta-myplatform/recipes-phosphor/users/nss-pam-ldapd/
# └── files/
# │ └── nslcd.conf
# └── nss-pam-ldapd_%.bbappend
cat > nss-pam-ldapd_%.bbappend << 'EOF'
FILESEXTRAPATHS:prepend := "${THISDIR}/files:"
SRC_URI += "file://nslcd.conf"
do_install:append() {
install -m 0600 ${WORKDIR}/nslcd.conf ${D}${sysconfdir}/
}
EOF
Runtime Configuration
# Check user manager service
systemctl status phosphor-user-manager
# View logs
journalctl -u phosphor-user-manager -f
# User configuration files
cat /etc/passwd
cat /etc/group
# Verify PAM config
cat /etc/pam.d/common-auth
# Verify LDAP client config
cat /etc/nslcd.conf
Local User Management
Default Users
| Username | Default Password | Role |
|---|---|---|
| root | 0penBmc | Administrator |
Create User via Redfish
# Create new user
curl -k -u root:0penBmc -X POST \
-H "Content-Type: application/json" \
-d '{
"UserName": "operator1",
"Password": "SecurePass123!",
"RoleId": "Operator",
"Enabled": true
}' \
https://localhost/redfish/v1/AccountService/Accounts
Create User via D-Bus
# Create user
busctl call xyz.openbmc_project.User.Manager \
/xyz/openbmc_project/user \
xyz.openbmc_project.User.Manager \
CreateUser ssasbs \
"newuser" \
3 "ipmi" "redfish" "ssh" \
"priv-admin" \
true
Modify User
# Change password via Redfish
curl -k -u root:0penBmc -X PATCH \
-H "Content-Type: application/json" \
-d '{"Password": "NewPassword123!"}' \
https://localhost/redfish/v1/AccountService/Accounts/operator1
# Change role
curl -k -u root:0penBmc -X PATCH \
-H "Content-Type: application/json" \
-d '{"RoleId": "Administrator"}' \
https://localhost/redfish/v1/AccountService/Accounts/operator1
# Enable/disable user
curl -k -u root:0penBmc -X PATCH \
-H "Content-Type: application/json" \
-d '{"Enabled": false}' \
https://localhost/redfish/v1/AccountService/Accounts/operator1
Delete User
# Via Redfish
curl -k -u root:0penBmc -X DELETE \
https://localhost/redfish/v1/AccountService/Accounts/operator1
# Via IPMI
ipmitool -I lanplus -H bmc-ip -U root -P 0penBmc user set name 3 ""
Privilege Roles
Available Roles
| Role | Redfish | IPMI Priv | Description |
|---|---|---|---|
| Administrator | Administrator | 4 (admin) | Full access |
| Operator | Operator | 3 (operator) | Operations, no user mgmt |
| ReadOnly | ReadOnly | 2 (user) | Read-only access |
| Callback | Callback | 1 | Limited callback access |
Role Permissions
# View available roles
curl -k -u root:0penBmc \
https://localhost/redfish/v1/AccountService/Roles
# Get role details
curl -k -u root:0penBmc \
https://localhost/redfish/v1/AccountService/Roles/Operator
D-Bus Privilege Mapping
# View user privileges
busctl get-property xyz.openbmc_project.User.Manager \
/xyz/openbmc_project/user/newuser \
xyz.openbmc_project.User.Attributes \
UserPrivilege
IPMI User Management
Configure IPMI Users
# List users
ipmitool user list
# Create user
ipmitool user set name 3 operator1
ipmitool user set password 3 Password123!
ipmitool user priv 3 3 1 # operator privilege, channel 1
ipmitool user enable 3
# Disable user
ipmitool user disable 3
# Delete user
ipmitool user set name 3 ""
IPMI User Privileges
| Level | Name | Description |
|---|---|---|
| 1 | Callback | Minimal access |
| 2 | User | Read sensors, view logs |
| 3 | Operator | Power control, sensor config |
| 4 | Administrator | Full access |
| 5 | OEM | Vendor-specific |
LDAP Configuration
Enable LDAP Authentication
# Configure LDAP via Redfish
curl -k -u root:0penBmc -X PATCH \
-H "Content-Type: application/json" \
-d '{
"LDAP": {
"ServiceEnabled": true,
"ServiceAddresses": ["ldap://ldap.example.com"],
"Authentication": {
"AuthenticationType": "UsernameAndPassword",
"Username": "cn=admin,dc=example,dc=com",
"Password": "ldappassword"
},
"LDAPService": {
"SearchSettings": {
"BaseDistinguishedNames": ["dc=example,dc=com"],
"UsernameAttribute": "uid",
"GroupsAttribute": "memberOf"
}
}
}
}' \
https://localhost/redfish/v1/AccountService
LDAP Role Mapping
# Map LDAP group to role
curl -k -u root:0penBmc -X POST \
-H "Content-Type: application/json" \
-d '{
"RemoteGroup": "cn=admins,ou=groups,dc=example,dc=com",
"LocalRole": "Administrator"
}' \
https://localhost/redfish/v1/AccountService/LDAP/RemoteRoleMapping
D-Bus LDAP Configuration
# Create LDAP config
busctl call xyz.openbmc_project.User.Manager \
/xyz/openbmc_project/user/ldap \
xyz.openbmc_project.User.Ldap.Create \
Create sssssa{ss} \
"ldap://ldap.example.com" \
"cn=admin,dc=example,dc=com" \
"dc=example,dc=com" \
"uid" \
"OpenLDAP" \
0
Test LDAP Connection
# Test authentication
ldapsearch -x -H ldap://ldap.example.com \
-D "cn=admin,dc=example,dc=com" \
-w password \
-b "dc=example,dc=com" \
"(uid=testuser)"
Active Directory
Configure AD Authentication
# Configure Active Directory
curl -k -u root:0penBmc -X PATCH \
-H "Content-Type: application/json" \
-d '{
"ActiveDirectory": {
"ServiceEnabled": true,
"ServiceAddresses": ["ldap://ad.example.com"],
"Authentication": {
"AuthenticationType": "UsernameAndPassword",
"Username": "admin@example.com",
"Password": "adpassword"
},
"LDAPService": {
"SearchSettings": {
"BaseDistinguishedNames": ["DC=example,DC=com"],
"UsernameAttribute": "sAMAccountName",
"GroupsAttribute": "memberOf"
}
}
}
}' \
https://localhost/redfish/v1/AccountService
Account Policies
Configure Lockout
# Set account lockout policy
curl -k -u root:0penBmc -X PATCH \
-H "Content-Type: application/json" \
-d '{
"AccountLockoutThreshold": 5,
"AccountLockoutDuration": 300,
"AccountLockoutCounterResetAfter": 60
}' \
https://localhost/redfish/v1/AccountService
Password Policy
# Set minimum password length
curl -k -u root:0penBmc -X PATCH \
-H "Content-Type: application/json" \
-d '{
"MinPasswordLength": 12
}' \
https://localhost/redfish/v1/AccountService
Multi-Factor Authentication (MFA)
Beyond passwords, LDAP, and Active Directory, phosphor-user-manager supports multi-factor authentication (MFA) using TOTP (Time-based One-Time Password), which is compatible with Google Authenticator and equivalent authenticator apps. When MFA is enabled for a user, login requires both the account password and a current one-time passcode (OTP).
How TOTP Enrollment Works
- Secret generation — enabling TOTP for a user runs the
google-authenticatorutility to produce a per-user secret. OpenBMC invokes it non-interactively in time-based mode (google-authenticator -s <path> -u -W -Q NONE -t -f -d -C), so no QR code or interactive prompt is generated. - Provisional secret — the new secret is first written to a temporary file
(a
.google_authenticator.tmpunder the user’s home) and is not yet active. - Verification makes it permanent — the user must submit one valid OTP. Only
after that OTP verifies successfully is the secret promoted (renamed) to its
live location,
~/.google_authenticator. A failed verification — or disabling MFA — removes the temporary secret, so a half-finished enrollment never locks the account.
OTP Verification
One-time passcodes are checked through a dedicated PAM service, mfa_pam
(installed to /etc/pam.d/mfa_pam). The internal Totp::verify(username, token)
helper opens a PAM conversation against that service, answers its
Verification code: prompt with the supplied token, and reports success only when
PAM accepts the code. Isolating OTP checks in their own PAM stack lets MFA layer
on top of the existing common-auth password flow without modifying it.
MFA State and D-Bus
MFA configuration lives on the xyz.openbmc_project.User.Manager service, exposed
through the xyz.openbmc_project.User.MultiFactorAuthConfiguration interface —
whose Enabled property selects the active MFA type (for example
GoogleAuthenticator or None) — and is mirrored to the Redfish AccountService.
The selected MFA type and related state are persisted as JSON (via the
manager’s JsonSerializer), so the setting survives a BMC reboot.
# Inspect the user manager object and its MFA-related interfaces
busctl introspect xyz.openbmc_project.User.Manager \
/xyz/openbmc_project/user
Per-User Bypass
For temporary exceptions — a break-glass account, or a user who has lost their
authenticator — phosphor-user-manager supports a per-user bypass protocol.
Bypassing a protocol (such as GoogleAuthenticator) exempts that user from the
corresponding factor and clears their stored secret. The bypass selection is
itself persisted as JSON, so it too survives a reboot.
MFA secrets are sensitive. A user’s
~/.google_authenticatorsecret is the shared key for their second factor. It is stored with restricted permissions under the user’s home directory and must never be logged, exported, or copied off the BMC — treat it with the same care as a private key or password hash.
Session Management
View Active Sessions
# List sessions
curl -k -u root:0penBmc \
https://localhost/redfish/v1/SessionService/Sessions
Terminate Session
# Delete specific session
curl -k -u root:0penBmc -X DELETE \
https://localhost/redfish/v1/SessionService/Sessions/session_id
Session Timeout
# Configure session timeout
curl -k -u root:0penBmc -X PATCH \
-H "Content-Type: application/json" \
-d '{"SessionTimeout": 1800}' \
https://localhost/redfish/v1/SessionService
Troubleshooting
Login Failures
# Check PAM logs
journalctl | grep pam
# Check user exists
cat /etc/passwd | grep username
# Verify user enabled
busctl get-property xyz.openbmc_project.User.Manager \
/xyz/openbmc_project/user/username \
xyz.openbmc_project.User.Attributes \
UserEnabled
LDAP Issues
# Check LDAP service
systemctl status nslcd
# Test LDAP bind
ldapwhoami -x -H ldap://ldap.example.com \
-D "uid=user,dc=example,dc=com" -W
# Check LDAP configuration
busctl introspect xyz.openbmc_project.User.Manager \
/xyz/openbmc_project/user/ldap
Account Locked
# Check lockout status (via PAM tally)
pam_tally2 --user=username
# Reset lockout
pam_tally2 --user=username --reset
Deep Dive
Advanced implementation details for user management developers.
PAM Integration
OpenBMC uses PAM (Pluggable Authentication Modules) for authentication:
┌─────────────────────────────────────────────────────────────────────────┐
│ PAM Authentication Flow │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ Login Request (Redfish/IPMI/SSH) │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ PAM Stack │ │
│ │ /etc/pam.d/common-auth │ │
│ └────────────────────────────┬────────────────────────────────────┘ │
│ │ │
│ ┌─────────────────────┼─────────────────────┐ │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ ┌────────────┐ ┌────────────┐ ┌────────────┐ │
│ │ pam_unix │ │ pam_ldap │ │ pam_radius │ │
│ │ (local) │ │ (LDAP/AD) │ │ (RADIUS) │ │
│ └─────┬──────┘ └─────┬──────┘ └─────┬──────┘ │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ /etc/shadow LDAP Server RADIUS Server │
│ │
│ PAM Configuration (/etc/pam.d/common-auth): │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ auth sufficient pam_unix.so nullok try_first_pass │ │
│ │ auth sufficient pam_ldap.so use_first_pass │ │
│ │ auth required pam_deny.so │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────┘
PAM module flow:
sufficient: If module succeeds, skip remaining; if fails, continuerequired: Must pass, but continue checking other modulesrequisite: Must pass, fail immediately if not
LDAP Authentication Flow
┌─────────────────────────────────────────────────────────────────────────┐
│ LDAP Authentication Sequence │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ BMC (phosphor-user-manager) LDAP Server │
│ │ │ │
│ │ 1. Bind (service account) │ │
│ │─────────────────────────────────────▶│ │
│ │◀─────────────────────────────────────│ Bind success │
│ │ │ │
│ │ 2. Search (find user DN) │ │
│ │ filter: (uid=username) │ │
│ │─────────────────────────────────────▶│ │
│ │◀─────────────────────────────────────│ User DN returned │
│ │ │ │
│ │ 3. Bind (user DN + password) │ │
│ │─────────────────────────────────────▶│ │
│ │◀─────────────────────────────────────│ Auth success/fail │
│ │ │ │
│ │ 4. Search (get group membership) │ │
│ │ filter: (member=userDN) │ │
│ │─────────────────────────────────────▶│ │
│ │◀─────────────────────────────────────│ Group list │
│ │ │ │
│ │
│ Group to Privilege Mapping: │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ LDAP Group │ OpenBMC Role │ IPMI Privilege │ │
│ ├─────────────────────┼───────────────────┼───────────────────────┤ │
│ │ cn=bmc-admins │ Administrator │ 4 (Administrator) │ │
│ │ cn=bmc-operators │ Operator │ 3 (Operator) │ │
│ │ cn=bmc-users │ User │ 2 (User) │ │
│ │ cn=bmc-readonly │ ReadOnly │ 1 (Callback) │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────┘
Password Hashing
┌─────────────────────────────────────────────────────────────────────────┐
│ Password Storage │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ /etc/shadow format: │
│ username:$6$salt$hash:lastchange:min:max:warn:inactive:expire: │
│ │
│ Hash Types: │
│ ├── $1$ = MD5 (deprecated, insecure) │
│ ├── $5$ = SHA-256 │
│ └── $6$ = SHA-512 (default, recommended) │
│ │
│ Password Change Flow: │
│ ┌──────────────────────────────────────────────────────────────────┐ │
│ │ 1. Redfish PATCH /AccountService/Accounts/{id} │ │
│ │ { "Password": "newPassword" } │ │
│ │ │ │
│ │ 2. bmcweb calls D-Bus: │ │
│ │ xyz.openbmc_project.User.Manager.RenameUser() or │ │
│ │ org.freedesktop.Accounts.User.SetPassword() │ │
│ │ │ │
│ │ 3. phosphor-user-manager: │ │
│ │ - Validates password policy (length, complexity) │ │
│ │ - Calls pam_chauthtok() to update /etc/shadow │ │
│ │ - Emits PropertiesChanged signal │ │
│ └──────────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────┘
D-Bus Permission Enforcement
┌─────────────────────────────────────────────────────────────────────────┐
│ D-Bus Access Control │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ /etc/dbus-1/system.d/ policy files control D-Bus access: │
│ │
│ Example: phosphor-user-manager.conf │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ <policy user="root"> │ │
│ │ <allow own="xyz.openbmc_project.User.Manager"/> │ │
│ │ <allow send_destination="xyz.openbmc_project.User.Manager"/> │ │
│ │ </policy> │ │
│ │ │ │
│ │ <policy context="default"> │ │
│ │ <deny send_destination="xyz.openbmc_project.User.Manager" │ │
│ │ send_interface="xyz.openbmc_project.User.Manager"/> │ │
│ │ <allow send_destination="xyz.openbmc_project.User.Manager" │ │
│ │ send_interface="org.freedesktop.DBus.Properties" │ │
│ │ send_member="Get"/> │ │
│ │ </policy> │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
│ Privilege Enforcement in bmcweb: │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ // Route with privilege requirement │ │
│ │ BMCWEB_ROUTE(app, "/redfish/v1/AccountService/Accounts/<str>/") │ │
│ │ .privileges(redfish::privileges::patchAccount) │ │
│ │ .methods(boost::beast::http::verb::patch)(handlePatch); │ │
│ │ │ │
│ │ // privileges::patchAccount requires ConfigureUsers │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────┘
Source Code Reference
Key implementation files in phosphor-user-manager:
| File | Description |
|---|---|
user_mgr.cpp |
Main user manager implementation |
users.cpp |
Individual user object handling |
ldap_config.cpp |
LDAP configuration management |
ldap_mapper.cpp |
LDAP group to privilege mapping |
shadowlock.cpp |
/etc/shadow file locking |
phosphor-ldap-conf.cpp |
LDAP config daemon |
Examples
Working examples are available in the examples/user-manager directory:
user-management.sh- User CRUD operations scriptldap-config.json- LDAP configuration example
References
Tested on: OpenBMC master, QEMU romulus