FreeIPA
Draft version. Content is hallucinated. Do not use!
interface

System Accounts

Service authentication for applications without Kerberos support using dedicated system accounts

Overview

System accounts, introduced in FreeIPA 4.13.0, provide a secure way for applications and services to authenticate to FreeIPA when Kerberos authentication is not available. These specialized LDAP objects enable non-human service authentication while maintaining FreeIPA’s security model and access control framework.

Unlike traditional user accounts, system accounts are designed exclusively for programmatic access and don’t represent actual users or require POSIX attributes.

Why System Accounts?

Legacy Application Support

Many enterprise applications lack native Kerberos support but need to interact with FreeIPA’s LDAP directory. System accounts provide these applications with authentication credentials without creating full user accounts.

Common scenarios include:

  • Database management tools querying LDAP for user information
  • Legacy authentication systems performing password validation
  • Monitoring tools collecting identity data
  • Third-party integrations requiring directory access

External Password Management

Organizations using external password management systems (like enterprise password vaults or synchronization tools) need controlled access to modify user passwords without triggering FreeIPA’s password policy reset mechanisms.

System accounts with privileged password change capabilities solve this by:

  • Allowing password updates without forcing user password changes at next login
  • Maintaining audit trails of password modifications
  • Enabling integration with external credential management systems
  • Supporting password synchronization across multiple platforms

Key Differences from Regular Users

System accounts are fundamentally different from standard user accounts:

FeatureSystem AccountRegular User
PurposeService authenticationHuman authentication
Object Classesaccount, simpleSecurityObjectinetOrgPerson, posixAccount
AuthenticationLDAP bind with passwordKerberos + optional password
POSIX AttributesNone (no UID/GID)Required for Unix access
Login CapabilityCannot login to systemsCan login to enrolled hosts
IdentificationLDAP DN onlyUsername, UID, email, etc.

Authentication Mechanism

System accounts authenticate using LDAP simple bind:

DN: uid=my-app,cn=sysaccounts,cn=etc,dc=example,dc=com
Password: stored in userPassword attribute

Applications perform LDAP bind operations:

import ldap

# Initialize connection
conn = ldap.initialize('ldaps://ipa.example.com')

# Bind as system account
conn.simple_bind_s(
    'uid=my-app,cn=sysaccounts,cn=etc,dc=example,dc=com',
    'system-account-password'
)

# Perform LDAP operations
result = conn.search_s(
    'cn=users,cn=accounts,dc=example,dc=com',
    ldap.SCOPE_SUBTREE,
    '(objectClass=person)'
)

Privileged System Accounts

System accounts can be designated as “privileged” to enable password management capabilities:

Standard System Account

  • Read-only access to directory information
  • Cannot modify user passwords
  • Limited to search and bind operations

Privileged System Account

  • Can modify user passwords
  • Password changes don’t trigger “change on next login”
  • Ideal for password synchronization systems
  • Requires explicit permission grants

Management Commands

Creating System Accounts

# Create basic system account
ipa sysaccount-add app-reader \
  --desc="Application for reading user data"

# Create privileged system account with random password
ipa sysaccount-add password-sync \
  --desc="Password synchronization service" \
  --privileged=true \
  --random

# Create with specific password
ipa sysaccount-add monitoring \
  --desc="Monitoring system" \
  --password

The --random flag generates a secure random password, which is displayed once during creation.

Managing System Accounts

# List all system accounts
ipa sysaccount-find

# Show account details
ipa sysaccount-show app-reader

# Modify account description
ipa sysaccount-mod app-reader \
  --desc="Updated description"

# Change password
ipa sysaccount-mod app-reader --password

# Disable account (prevent LDAP bind)
ipa sysaccount-disable app-reader

# Re-enable account
ipa sysaccount-enable app-reader

# Delete account
ipa sysaccount-del app-reader

Managing Privileged Status

# Grant privileged password change capability
ipa sysaccount-policy password-sync \
  --privileged=true

# Revoke privileged status
ipa sysaccount-policy password-sync \
  --privileged=false

# View privileged accounts
ipa sysaccount-find --privileged=true

Role-Based Access Control

System accounts integrate with FreeIPA’s RBAC system through roles and privileges:

Basic Workflow

# 1. Create custom privilege
ipa privilege-add 'App User Read Privilege' \
  --desc="Read user information"

# 2. Add permissions to privilege
ipa privilege-add-permission 'App User Read Privilege' \
  --permissions='System: Read Users'

# 3. Create role
ipa role-add 'Application Reader Role' \
  --desc="Role for read-only applications"

# 4. Add privilege to role
ipa role-add-privilege 'Application Reader Role' \
  --privileges='App User Read Privilege'

# 5. Assign system account to role
ipa role-add-member 'Application Reader Role' \
  --sysaccounts=app-reader

Password Management Privileges

For privileged password changes:

# Create password management privilege
ipa privilege-add 'Password Sync Privilege' \
  --desc="Change user passwords without reset"

# Add password change permission
ipa privilege-add-permission 'Password Sync Privilege' \
  --permissions='System: Change User password'

# Create role
ipa role-add 'Password Sync Role'

# Add privilege to role
ipa role-add-privilege 'Password Sync Role' \
  --privileges='Password Sync Privilege'

# Assign privileged system account to role
ipa role-add-member 'Password Sync Role' \
  --sysaccounts=password-sync

Use Cases

Password Synchronization

Synchronize passwords between FreeIPA and external systems:

#!/usr/bin/env python3
import ldap
import ldap.modlist as modlist

# System account credentials
SYSACCOUNT_DN = 'uid=password-sync,cn=sysaccounts,cn=etc,dc=example,dc=com'
SYSACCOUNT_PW = 'secure-password'

def sync_password(username, new_password):
    """Update user password without forcing reset"""

    # Connect and bind
    conn = ldap.initialize('ldaps://ipa.example.com')
    conn.simple_bind_s(SYSACCOUNT_DN, SYSACCOUNT_PW)

    # Find user DN
    user_dn = f'uid={username},cn=users,cn=accounts,dc=example,dc=com'

    # Modify password
    mod_attrs = [(ldap.MOD_REPLACE, 'userPassword', new_password.encode())]
    conn.modify_s(user_dn, mod_attrs)

    print(f"Password updated for {username}")
    conn.unbind()

# Sync password from external system
sync_password('jsmith', 'NewSecurePassword123!')

Legacy Application Integration

Authenticate legacy applications to LDAP:

# Create system account for legacy app
ipa sysaccount-add legacy-app \
  --desc="Legacy HR application" \
  --random

# Grant read-only access to user data
ipa role-add 'Legacy App Reader'
ipa role-add-privilege 'Legacy App Reader' \
  --privileges='User Administrators'
ipa role-add-member 'Legacy App Reader' \
  --sysaccounts=legacy-app

Application configuration:

# legacy-app.conf
ldap_uri = ldaps://ipa.example.com
ldap_bind_dn = uid=legacy-app,cn=sysaccounts,cn=etc,dc=example,dc=com
ldap_bind_password = [password from ipa sysaccount-add]
ldap_search_base = cn=users,cn=accounts,dc=example,dc=com

Monitoring and Automation

Create read-only access for monitoring systems:

# Create monitoring account
ipa sysaccount-add nagios \
  --desc="Nagios monitoring system" \
  --random

# Grant search permissions only
ipa role-add 'Monitoring Role'
ipa privilege-add 'Monitoring Privilege'
ipa privilege-add-permission 'Monitoring Privilege' \
  --permissions='System: Read Users' \
  --permissions='System: Read Groups' \
  --permissions='System: Read Hosts'
ipa role-add-privilege 'Monitoring Role' \
  --privileges='Monitoring Privilege'
ipa role-add-member 'Monitoring Role' \
  --sysaccounts=nagios

Monitoring script:

#!/bin/bash
# Check FreeIPA user count

SYSACCOUNT_DN="uid=nagios,cn=sysaccounts,cn=etc,dc=example,dc=com"
SYSACCOUNT_PW="monitoring-password"

USER_COUNT=$(ldapsearch -x -H ldaps://ipa.example.com \
  -D "$SYSACCOUNT_DN" \
  -w "$SYSACCOUNT_PW" \
  -b "cn=users,cn=accounts,dc=example,dc=com" \
  "(objectClass=person)" dn | grep -c "^dn:")

echo "Total users: $USER_COUNT"

if [ $USER_COUNT -lt 1 ]; then
  echo "CRITICAL: No users found"
  exit 2
fi

DevOps Automation

Automate user provisioning with system accounts:

#!/usr/bin/env python3
"""
Automated user onboarding script using system account
"""
import ldap
import ldap.modlist as modlist
import json

SYSACCOUNT_DN = 'uid=provisioning,cn=sysaccounts,cn=etc,dc=example,dc=com'
SYSACCOUNT_PW = 'automation-password'

def create_user_ldap(user_data):
    """Create user via LDAP using system account"""

    conn = ldap.initialize('ldaps://ipa.example.com')
    conn.simple_bind_s(SYSACCOUNT_DN, SYSACCOUNT_PW)

    dn = f"uid={user_data['username']},cn=users,cn=accounts,dc=example,dc=com"

    attrs = {
        'objectClass': [b'inetOrgPerson', b'person'],
        'cn': [user_data['fullname'].encode()],
        'sn': [user_data['lastname'].encode()],
        'givenName': [user_data['firstname'].encode()],
        'mail': [user_data['email'].encode()],
        'uid': [user_data['username'].encode()],
    }

    ldif = modlist.addModlist(attrs)
    conn.add_s(dn, ldif)

    print(f"Created user: {user_data['username']}")
    conn.unbind()

# Read from onboarding system
with open('new_users.json') as f:
    users = json.load(f)
    for user in users:
        create_user_ldap(user)

Multi-Server Considerations

Important: The privileged system accounts configuration is not replicated across FreeIPA servers. Each server maintains its own list of privileged accounts.

Managing Across Replicas

Update privileged status on all servers:

# List all IPA servers
ipa server-find --pkey-only

# Update on specific server
ipa sysaccount-policy password-sync \
  --privileged=true \
  --force-server=ipa01.example.com

ipa sysaccount-policy password-sync \
  --privileged=true \
  --force-server=ipa02.example.com

ipa sysaccount-policy password-sync \
  --privileged=true \
  --force-server=ipa03.example.com

Automation script:

#!/bin/bash
# Synchronize system account policy across all servers

ACCOUNT="password-sync"

# Get all servers
SERVERS=$(ipa server-find --pkey-only | grep "Server name:" | awk '{print $3}')

for server in $SERVERS; do
  echo "Updating policy on $server..."
  ipa sysaccount-policy "$ACCOUNT" \
    --privileged=true \
    --force-server="$server"
done

echo "Policy synchronized across all servers"

Security Best Practices

1. Use Strong Passwords

Generate secure random passwords:

# Always use --random for production
ipa sysaccount-add production-app --random

# Store password securely (don't save in shell history)
ipa sysaccount-add app-name --password
# Enter password interactively

2. Principle of Least Privilege

Grant minimum necessary permissions:

# Bad: Assigning admin privileges
ipa role-add-member 'Security Architect' --sysaccounts=app

# Good: Create specific role with limited permissions
ipa privilege-add 'App Specific Privilege'
ipa privilege-add-permission 'App Specific Privilege' \
  --permissions='System: Read Users'
ipa role-add 'App Specific Role'
ipa role-add-privilege 'App Specific Role' \
  --privileges='App Specific Privilege'
ipa role-add-member 'App Specific Role' --sysaccounts=app

3. Regular Audits

Monitor system account usage:

# List all system accounts
ipa sysaccount-find --all

# Find privileged accounts
ipa sysaccount-find --privileged=true

# Review roles and privileges
ipa role-show 'App Role' --all

4. Disable Unused Accounts

Don’t delete immediately; disable first:

# Disable instead of delete
ipa sysaccount-disable old-app

# Monitor for issues, then delete
ipa sysaccount-del old-app

5. Secure Credential Storage

Store passwords securely in applications:

# Use environment variables (not hardcoded)
export LDAP_BIND_PASSWORD="$(cat /secure/path/password)"

# Or use secret management systems
# - HashiCorp Vault
# - Kubernetes Secrets
# - AWS Secrets Manager

6. Monitor Authentication

Track system account binds:

# Enable LDAP access logging
ldapmodify -x -D "cn=Directory Manager" -W << EOF
dn: cn=config
changetype: modify
replace: nsslapd-accesslog-level
nsslapd-accesslog-level: 256
EOF

# Monitor access logs
tail -f /var/log/dirsrv/slapd-*/access | grep "cn=sysaccounts"

Troubleshooting

Cannot Bind as System Account

# Verify account exists
ipa sysaccount-show my-app

# Check if disabled
ipa sysaccount-show my-app | grep "Account disabled"

# Enable if needed
ipa sysaccount-enable my-app

# Test bind
ldapwhoami -x -H ldaps://ipa.example.com \
  -D "uid=my-app,cn=sysaccounts,cn=etc,dc=example,dc=com" \
  -W

Password Change Not Working

# Verify account is privileged
ipa sysaccount-find --privileged=true | grep my-app

# Check privileged status on current server
ipa sysaccount-policy my-app

# Verify permissions
ipa role-show 'My App Role' --all

# Ensure 'System: Change User password' permission granted

Permission Denied

# Check role membership
ipa role-show 'App Role' --all

# Verify privilege includes needed permissions
ipa privilege-show 'App Privilege' --all

# Test with IPA CLI using system account (if possible)
kinit admin
ipa user-show testuser

Administrative Permissions

Default FreeIPA roles with system account management:

  • Security Architect: Full system account administration
    • Add/modify/remove system accounts
    • Configure privileged status
    • Manage system account roles

Custom delegation:

# Create help desk role for basic management
ipa privilege-add 'System Account Help Desk'
ipa privilege-add-permission 'System Account Help Desk' \
  --permissions='System: Add System Accounts' \
  --permissions='System: Modify System Accounts' \
  --permissions='System: Remove System Accounts'

ipa role-add 'System Account Operators'
ipa role-add-privilege 'System Account Operators' \
  --privileges='System Account Help Desk'

ipa role-add-member 'System Account Operators' \
  --users=helpdesk_staff

Migration from Service Accounts

If migrating from generic service accounts to system accounts:

# 1. Create system account
ipa sysaccount-add new-system-app \
  --desc="Migrated from service user" \
  --random

# 2. Grant equivalent permissions via roles
ipa role-add-member 'Appropriate Role' \
  --sysaccounts=new-system-app

# 3. Update application configuration
# Change DN from:
#   uid=serviceuser,cn=users,cn=accounts,dc=example,dc=com
# To:
#   uid=new-system-app,cn=sysaccounts,cn=etc,dc=example,dc=com

# 4. Test thoroughly in non-production

# 5. Disable old service user
ipa user-disable serviceuser

# 6. Monitor and delete after confirmation
ipa user-del serviceuser --preserve

Limitations

No Kerberos Support

System accounts cannot obtain Kerberos tickets:

# This will fail
kinit uid=my-app,cn=sysaccounts,cn=etc,dc=example,dc=com

# System accounts use LDAP bind only

No POSIX Attributes

Cannot be used for system login:

# System account has no UID/GID
ipa sysaccount-show my-app
# No uidNumber or gidNumber

# Cannot SSH or login to systems
ssh my-app@server.example.com  # Will fail

Per-Server Configuration

Privileged status requires manual synchronization:

  • Not automatically replicated
  • Must update each server individually
  • Use --force-server or SSH to each replica
  • Consider automation scripts for consistency

Getting Started

Quick start for creating your first system account:

# 1. Create system account
ipa sysaccount-add my-first-app \
  --desc="My first application" \
  --random

# Save the displayed password securely!

# 2. Create role and privilege
ipa privilege-add 'App Read Privilege'
ipa privilege-add-permission 'App Read Privilege' \
  --permissions='System: Read Users'

ipa role-add 'App Reader Role'
ipa role-add-privilege 'App Reader Role' \
  --privileges='App Read Privilege'

# 3. Assign system account to role
ipa role-add-member 'App Reader Role' \
  --sysaccounts=my-first-app

# 4. Test LDAP bind
ldapwhoami -x -H ldaps://ipa.example.com \
  -D "uid=my-first-app,cn=sysaccounts,cn=etc,dc=example,dc=com" \
  -W

# 5. Test search
ldapsearch -x -H ldaps://ipa.example.com \
  -D "uid=my-first-app,cn=sysaccounts,cn=etc,dc=example,dc=com" \
  -W \
  -b "cn=users,cn=accounts,dc=example,dc=com" \
  "(uid=admin)"

Conclusion

System accounts provide a secure, purpose-built solution for application authentication in FreeIPA environments. By separating service authentication from user accounts and integrating with FreeIPA’s RBAC system, they enable secure application integration while maintaining security and auditability.

Key benefits:

  • Purpose-built for application authentication
  • No POSIX overhead for non-login services
  • Granular permission control via RBAC
  • Privileged password management capabilities
  • Clear separation from user accounts
  • Full audit trail of operations

Start using system accounts today to improve security and simplify application integration with your FreeIPA infrastructure!

Resources