Add Local Password History with pam_pwhistory
You will configure Linux-PAM to remember recent local passwords and reject a reuse during a password change. The examples target Ubuntu's libpam-modules package, version 1.5.3-5ubuntu5.7 on the machine used for this guide. Allow about 15 minutes, including a test and a recovery check.
The route
Jump straight to the step you need, or tick off Done means at the end.
This is a security-sensitive change to the password stack. Keep an existing root shell or console session open before editing PAM, and make a dated backup first. A malformed PAM file can prevent authentication, even though an already logged-in shell continues to work.
1. Check the installed module and current stack
Run these read-only checks as an ordinary user:
$ dpkg-query -W -f='${Package} ${Version}\n' libpam-modules:amd64
libpam-modules:amd64 1.5.3-5ubuntu5.7
$ ls -l /lib/x86_64-linux-gnu/security/pam_pwhistory.so
$ grep -n '^[[:space:]]*password' /etc/pam.d/common-password
The module is a PAM password module, not a command that you run directly. On this Ubuntu installation, common-password contains the shared password stack and currently uses pam_unix. Your service may include a different stack, so inspect the file that actually handles the password change before editing it.
Checkpoint: identify the existing pam_unix.so password line. The history line belongs immediately before it, so that pam_pwhistory can obtain the new token and pam_unix can reuse it.
2. Choose the history policy
The preferred configuration file is /etc/security/pwhistory.conf. It uses one option per line in name = value form; comments start with #, and whitespace around the equals sign is ignored. The installed defaults remember 10 passwords, prompt once, and store the history in /etc/security/opasswd.
For a five-password history, edit the file as root:
$ sudoedit /etc/security/pwhistory.conf
Set or uncomment these values, keeping the other comments if they help future maintenance:
remember = 5
retry = 1
file = /etc/security/opasswd
Do not add debug as a routine setting. It sends module diagnostics to syslog and can expose more operational detail than a normal password policy needs. Add enforce_for_root only when you have deliberately decided that root password changes must obey the same history. Without it, the history check is not enforced for root.
Checkpoint: verify the file as root without printing the password history:
$ sudo sed -n '1,160p' /etc/security/pwhistory.conf
$ sudo stat -c '%U:%G %a %n' /etc/security/opasswd
The history file contains password-history data and should remain protected. Do not copy it into a ticket, paste it into a chat, or use a temporary path such as /tmp/opasswd for a production policy.
3. Add the module before pam_unix
Back up the PAM file, then open it with elevated privileges:
$ sudo cp --preserve=all /etc/pam.d/common-password /etc/pam.d/common-password.$(date +%Y%m%d-%H%M%S).bak
$ sudoedit /etc/pam.d/common-password
Add this line immediately before the existing pam_unix.so password line:
password required pam_pwhistory.so use_authtok
For the stack installed here, the result should look like this around the password modules:
password required pam_pwhistory.so use_authtok
password [success=1 default=ignore] pam_unix.so obscure yescrypt
password requisite pam_deny.so
password required pam_permit.so
use_authtok tells the module to use the new password supplied by an earlier password module. If your stack already has a password-quality module before pam_pwhistory, follow the local stack's ordering carefully and do not duplicate an unrelated example from another distribution. The module provides only the password type.
Do not put use_authtok on the history line when no earlier module supplies the token. Conversely, do not remove use_authtok from the following pam_unix line in the documented two-module arrangement, or the stack can ask for the new password twice.
4. Test a real password change safely
First check that the edited lines are present and the history file is still protected:
$ grep -n -A2 -B1 'pam_pwhistory' /etc/pam.d/common-password
$ sudo stat -c '%U:%G %a %n' /etc/security/opasswd
root:root 600 /etc/security/opasswd
Use a disposable non-root test account if your system policy permits it. The following changes state and requires elevated privileges:
$ sudo useradd --create-home pam-history-test
$ sudo passwd pam-history-test
Give the test account an initial password, then run sudo passwd pam-history-test again and try that same password as the new value. The second change should be rejected as a password reuse. Choose a fresh test password to confirm that a permitted change succeeds.
Do not use a real user's password in a test transcript. Remove the disposable account after testing, only if you created it for this purpose:
$ sudo userdel --remove pam-history-test
This deletion removes the test account and its home directory. It does not remove the shared history entries for other accounts. If the account was not disposable, stop and do not run that command.
5. Recover if authentication behaves unexpectedly
If a password change fails unexpectedly, record the exact PAM error and inspect the system log through your normal log-management process. A return of PAM_MAXTRIES means the password was rejected too often; PAM_AUTHTOK_ERR means no usable new password was set; PAM_USER_UNKNOWN means the account was not known to PAM; and PAM_IGNORE indicates disabled history.
If the edited stack causes broader authentication trouble, use the root shell or console you kept open. Restore the backup you created, then check the file before closing that recovery session:
$ sudo cp --preserve=all /etc/pam.d/common-password.YYYYMMDD-HHMMSS.bak /etc/pam.d/common-password
$ sudo grep -n '^[[:space:]]*password' /etc/pam.d/common-password
Replace YYYYMMDD-HHMMSS with the actual backup suffix. Do not guess if several backups exist. List them first with ls -1t /etc/pam.d/common-password.*.bak. The same restore approach applies to pwhistory.conf if a policy edit needs to be undone, provided you made a backup before changing it.
Done means
pam_pwhistory.sois installed and the relevant PAM stack has been identified./etc/security/pwhistory.confsets an intentional history count and protected history path.- The history module is before
pam_unix, with token handling appropriate to the surrounding stack. - A disposable account rejected a recently used password and accepted a new one, where testing was permitted.
- A dated PAM backup remains available, and the recovery shell has not been closed until the change is trusted.