Overview

Part 4 of the Identity series tests FreeIPA password policies in practice: global policy, group policy, minimum length, character classes, history, minimum lifetime, password expiration, admin reset, failed login counters, account lockout, unlock and account expiration.

All CLI commands and outputs are rendered as selectable text and code blocks. Screenshots are used only for the FreeIPA Web UI.

https://ipa01.example.test/ipa/ui/

FreeIPA Web UI after authenticated login. Screenshot from the isolated FreeIPA C2 lab with trusted lab CA.

Lab Setup

bash
Server: ipa01.example.test
Client: client01.example.test
Domain: example.test
Realm: EXAMPLE.TEST
FreeIPA: 4.13.3
SSSD: 2.13.1
SELinux: Enforcing

Which Password Policy FreeIPA Uses

FreeIPA has a global password policy and can apply additional group-specific policies. The lab first captured the real defaults.

bash
ipa pwpolicy-show
ipa pwpolicy-find
bash
Group: global_policy
Max lifetime (days): 90
Min lifetime (hours): 1
History size: 0
Character classes: 0
Min length: 8
Max failures: 6
Failure reset interval: 60
Lockout duration: 600
Grace login limit: -1
https://ipa01.example.test/ipa/ui/

FreeIPA password policy overview with global_policy and policy-users. Screenshot from the isolated FreeIPA C2 lab with trusted lab CA.

https://ipa01.example.test/ipa/ui/

Global FreeIPA password policy. Screenshot from the isolated FreeIPA C2 lab with trusted lab CA.

Group-specific Password Policy

The lab created the synthetic policy-users group and attached a group-specific policy. Priority controls which policy applies when a user belongs to multiple policy groups.

bash
ipa group-add policy-users --desc='C2 password policy users'
ipa pwpolicy-add policy-users \
--priority=10 \
--minlength=12 \
--minclasses=2 \
--history=2 \
--minlife=0 \
--maxlife=1 \
--maxfail=3 \
--failinterval=30 \
--lockouttime=60
bash
Group: policy-users
Max lifetime (days): 1
Min lifetime (hours): 0
History size: 2
Character classes: 2
Min length: 12
Priority: 10
Max failures: 3
Failure reset interval: 30
Lockout duration: 60
Grace login limit: -1
https://ipa01.example.test/ipa/ui/

Group-specific FreeIPA password policy policy-users. Screenshot from the isolated FreeIPA C2 lab with trusted lab CA.

https://ipa01.example.test/ipa/ui/

FreeIPA group policy-users with test users. Screenshot from the isolated FreeIPA C2 lab with trusted lab CA.

Minimum Length and Character Classes

A deliberately invalid password was rejected. This output comes from the real kinit password-change flow on the client.

bash
Password expired. You must change it now.
Password change rejected: Password does not contain enough character classes
Password not changed.

Password History

After two valid changes, the lab attempted to reuse an earlier password. FreeIPA rejected the reuse.

bash
Password change rejected: New password was used previously. Please choose a different password.

Minimum Lifetime

With minlife set to 1, an immediate user-initiated password change was blocked. The lab then restored minlife to 0 for the remaining tests.

bash
Password change rejected: Current password's minimum life has not expired
Password not changed.

Password Expiration

The lab forced policy-expired into the past. The next kinit required a password change and then issued a Kerberos ticket.

bash
ipa user-mod policy-expired --password-expiration=20000101000000Z
bash
Password expired. You must change it now.
Ticket cache: KCM:1001
Default principal: policy-expired@EXAMPLE.TEST
https://ipa01.example.test/ipa/ui/

FreeIPA user policy-expired for password expiration. Screenshot from the isolated FreeIPA C2 lab with trusted lab CA.

Administrator Reset

An admin reset with ipa passwd was tested separately. The next user login had to change the temporary password.

bash
ipa passwd policy-reset
https://ipa01.example.test/ipa/ui/

FreeIPA user policy-reset after admin reset. Screenshot from the isolated FreeIPA C2 lab with trusted lab CA.

Failed Logins and Account Lockout

The lockout test separates two phases: failed attempts below the threshold, then three consecutive failures until lockout.

bash
attempt_1_rc=1
attempt_2_rc=1
valid_before_threshold_rc=0
attempt_3_rc=1
attempt_4_rc=1
attempt_5_rc=1
valid_after_threshold_rc=1
bash
Account disabled: False
Server: ipa01.example.test
Failed logins: 3
Last failed authentication: 20260919225833Z
https://ipa01.example.test/ipa/ui/

FreeIPA user policy-lock after lockout test and recovery. Screenshot from the isolated FreeIPA C2 lab with trusted lab CA.

Unlocking the Account

The locked user was recovered with ipa user-unlock. After unlock, kinit and SSH worked again.

bash
ipa user-unlock policy-lock
kinit policy-lock
ssh policy-lock@client01.example.test

Account Expiration

Account expiration was tested independently from password expiration. Identity lookup still worked, but kinit and SSH were denied.

bash
ipa user-mod policy-account-expired --principal-expiration=20000101000000Z
bash
policy-account-expired:*:40800010:40800010:Policy AccountExpired:/home/policy-account-expired:/bin/bash
uid=40800010(policy-account-expired) gid=40800010(policy-account-expired) groups=40800010(policy-account-expired),40800003(policy-users)
kinit: Client's entry in database has expired while getting initial credentials
ssh_rc=5
https://ipa01.example.test/ipa/ui/

FreeIPA user policy-account-expired with account expiration. Screenshot from the isolated FreeIPA C2 lab with trusted lab CA.

Disabled vs Locked vs Password Expired vs Account Expired

Disabled user

Identity lookup
Yes
kinit
Credentials revoked
SSH
Failure
Recovery
ipa user-enable

Locked user

Identity lookup
Yes
kinit
Failure after threshold
SSH
Failure in final run
Recovery
ipa user-unlock or lockout expiry

Password expired

Identity lookup
Yes
kinit
Password change required
SSH
Works after change
Recovery
User change or admin reset

Account expired

Identity lookup
Yes
kinit
Entry expired
SSH
Failure
Recovery
Adjust principal expiration
https://ipa01.example.test/ipa/ui/

Disabled FreeIPA user policy-disabled. Screenshot from the isolated FreeIPA C2 lab with trusted lab CA.

What the Linux Client Sees

The client was tested with SSSD, Kerberos and SSH. The important distinction: identity resolution through getent or id is not the same as permitted authentication.

bash
getent passwd policy-valid
id policy-valid
kinit policy-valid
klist
ssh policy-valid@client01.example.test
sssctl user-checks policy-valid
journalctl -u sssd --no-pager
https://ipa01.example.test/ipa/ui/

FreeIPA user policy-valid with applied password policy. Screenshot from the isolated FreeIPA C2 lab with trusted lab CA.

Result Check

Global policy

Check
pwpolicy-show/find
Expected result
Defaults visible
Status
PASS

Group policy

Check
policy-users
Expected result
Group policy applies
Status
PASS

Minimum length

Check
invalid change
Expected result
Policy violation is rejected
Status
PASS

History

Check
reuse password
Expected result
Reuse is rejected
Status
PASS

Expiration

Check
expire password
Expected result
kinit forces change
Status
PASS

Admin reset

Check
ipa passwd
Expected result
User must change password
Status
PASS

Lockout

Check
failed attempts
Expected result
User is locked
Status
PASS

Recovery

Check
ipa user-unlock
Expected result
Login works again
Status
PASS

Account expiration

Check
expired principal
Expected result
kinit/SSH fail
Status
PASS

Web UI

Check
trusted-CA login
Expected result
screenshots authenticated
Status
PASS

CLI

Check
code/text instead of terminal images
Expected result
copyable evidence
Status
PASS

Limits of This Lab

  • The Web UI screenshots used a documented lab-port workaround because the host could not bind privileged TCP/443.

  • The policy values are intentionally short and synthetic so expiration and lockout can be reproduced.

  • Production policies need coordinated helpdesk, recovery, MFA and emergency processes.

Identity Management with FreeIPA and Keycloak

Previous part: managing FreeIPA users and groups. We only link a next part once it is published.