November 30, 2022·6 min read·engineeringnixnixosauthenticationmigration

NixOS Raccoon starts a migration away from weak password hashes

NixOS 22.11 replaces glibc's password-hashing implementation with libxcrypt. Its activation checks give existing accounts a migration period, while the hash format and user-management policy determine what must change.

Replacing a password-hashing library does not replace the hashes already stored for existing accounts. That distinction is the operational issue in NixOS 22.11's move to libxcrypt. The Raccoon release notes describe the replacement for glibc's crypt implementation and deprecate algorithms that libxcrypt does not classify as strong. Existing weak hashes need renewal before support is removed in the next release.

The 22.11 release tag is dated November 30, matching the versioned notes. The tagged source provides a concrete account of the migration as it stands today. New algorithm support and the handling of existing credentials are related changes, but they operate on different things.

I value NixOS because configuration can describe the intended system explicitly. Password records show where that model meets persistent state. Updating the software that verifies a record does not supply the original password needed to generate a different record.

A stored hash identifies its hashing scheme

The libxcrypt crypt manual defines the operation as irreversible hashing of a passphrase for storage. Its setting argument selects the method and supplies parameters, including a salt. The salt allows equal passphrases to have different stored hashes.

The algorithm configuration in libxcrypt 4.4.28 records the prefixes associated with those methods. Yescrypt uses $y$, scrypt uses $7$, and sha512crypt uses $6$. These prefixes identify the method to the implementation that interprets the stored string.

That makes a hash more than an opaque sequence of password-derived characters. It carries information needed to select a verification method. A system can recognize that a stored record uses an old scheme without knowing the account's passphrase. It cannot infer that passphrase merely from recognizing the scheme.

Consequently, changing a prefix is not a migration. A string beginning with the yescrypt prefix must contain a result generated according to that method. Relabeling an older result would give the verifier inconsistent information about how it was produced. The release notes call for creating new hashes, not editing the labels on existing ones.

Strong is an explicit library classification

The algorithm file explains its STRONG flag as the methods considered suitable for newly hashed passwords. Building libxcrypt with its strong-method selection enables that set. A separate DEFAULT flag identifies methods eligible to become the default when salt generation receives no explicit prefix.

Those flags have different purposes. A supported method can be strong without being the selected default. A method with historical compatibility support need not be appropriate for creating a new password record. The release's deprecation policy refers to the strong classification, so it is more precise than assuming every method accepted by the library has the same status.

In the referenced configuration, sha512crypt has both STRONG and DEFAULT flags. Its $6$ records therefore remain in the classified set used by this migration. The file does not give sha256crypt's $5$ method the same strong flag. Similar algorithm names should not replace reading the actual classification.

Yescrypt is the first enabled-default candidate in the file's ordering, and the release notes recommend the yescrypt result produced by mkpasswd for declaratively configured passwords. That does not imply every password-changing interface necessarily selects yescrypt. An interface can supply its own method setting.

The PAM configuration in the release, for example, explicitly supplies sha512 to its Unix password-management module. This is a useful distinction between library capability, library default selection, and an application's explicit configuration. The migration should be explained through the path actually used to create the record.

Activation checks inspect the resulting account state

The NixOS user-management module adds a hash-check activation script after user activation. It reads /etc/shadow, identifies hash strings beginning with a dollar sign, and compares their prefixes with the accepted set. Accounts using deprecated formats receive a warning.

The allowed prefixes correspond to the selected strong schemes, including the yescrypt variants, scrypt, supported bcrypt forms, and sha512crypt. This is a format-classification check. It does not attempt to recover a password or judge whether a particular user's chosen passphrase is difficult to guess.

Running after user activation matters because the script inspects the account state present on the machine. The source configuration alone may not describe every current password when ordinary user-management commands are allowed to change them. Checking the resulting records makes the warning applicable to that state.

The release notes tell administrators to rebuild and activate after migration to see whether deprecated-hash warnings remain. The warning is part of the transition period. It identifies a future compatibility problem while the current release still provides the opportunity to renew the affected credentials.

This is useful migration design because the check describes the affected accounts. It does not merely announce that some users somewhere may have old records. An operator can relate the diagnostic to an actual account and to the mechanism that owns its password state.

The script's predicate is also narrower than a complete audit of authentication. It selects dollar-prefixed hash strings. The user-management module separately recognizes values representing disabled password login, including ! and !!, as well as the unset marker *. These account states do not describe the same operation as verifying a password hash. Treating every non-yescrypt field as an old password would confuse disabled authentication with a stored verifier. Reading the activation predicate and the account option definitions together gives a more accurate explanation of what the diagnostic examines. The supported-hash classification remains the relevant rule for the password records it identifies.

Mutable and declarative users require different updates

The user-management module documents the effect of users.mutableUsers. With mutable users, configured password values are applied when the user is first created, after which ordinary user-management commands can change the password. With immutable users, the declared password options govern the password state.

That distinction determines where a lasting migration must happen. The release notes recommend resetting interactively managed passwords with passwd. For a declarative hashedPassword, they recommend running mkpasswd and replacing the configured hash with the generated result.

mkpasswd

This is the interactive generation command named by the release notes. Its output belongs in the appropriate managed hash setting after generation. The command should receive the actual passphrase through its normal interactive input, not a fictitious example hash copied from an article.

The distinction also prevents a misleading success. An interactive password change is not necessarily the lasting source of truth for an immutable-user configuration. If activation reapplies the declaration, the configured record remains the value that must be updated. A migration has to change the state owner, rather than merely change whichever file is easiest to reach temporarily.

My preference is to make that ownership visible in the configuration. A hash stored in a declaration and a password maintained interactively are different operational policies. NixOS documents both, so the migration can follow the policy the machine actually uses.

Hash handling is different from storing a clear-text password

The same module offers separate options for hashed and clear-text passwords. Its documentation warns that clear-text password options expose their contents through the world-readable Nix store and are intended only for public accounts. Switching to such an option is not a substitute for generating the required hash.

The distinction is particularly relevant when a configuration repository describes account state. A declarative interface still needs a suitable representation for the value it stores. The release's instruction to generate a new hash preserves that distinction between the passphrase and the verifier stored for authentication.

Nor does upgrading the library establish that every account has migrated. The new verifier can support stronger schemes while existing records continue to use deprecated ones. The activation warning and account-management policy together identify which records require replacement and where those replacements must be made.

Raccoon also updates the Nix package manager to 2.11.0 and changes other parts of the system, but password migration has its own lifecycle. Its important result is a transition in which the system can identify old hash formats before their planned removal. A completed migration leaves each affected account with a supported record maintained through the same source of truth that will govern its next activation.