Skip to content

OF-3305: Abort Blowfish PBKDF2 migration when security.xml is not persistable#3385

Open
viv wants to merge 2 commits into
igniterealtime:mainfrom
surevine:OF-3305/abort-migration-when-security-xml-not-persistable
Open

OF-3305: Abort Blowfish PBKDF2 migration when security.xml is not persistable#3385
viv wants to merge 2 commits into
igniterealtime:mainfrom
surevine:OF-3305/abort-migration-when-security-xml-not-persistable

Conversation

@viv

@viv viv commented Jun 7, 2026

Copy link
Copy Markdown
Member

Problem

The Blowfish SHA1 to PBKDF2 migration (OF-3075) re-encrypts the database properties
with a PBKDF2 key derived from a freshly generated salt, and stores that salt and the
kdf=pbkdf2 flag in conf/security.xml.

When conf/security.xml cannot be loaded or written (it is missing, empty, corrupt,
or not writable), JiveGlobals falls back to a non-persisting, in-memory-only
XMLProperties instance. In that state:

  • getBlowfishSalt() generates a salt and calls setProperty(), which cannot save
    (IllegalStateException: No file specified). The error is logged and the boolean
    result is ignored.
  • The migration re-encrypts and commits the database properties regardless.
  • setBlowfishKdf() likewise fails to persist, and the tool reports success.

After a restart, security.xml is still unusable, so the server defaults to the
sha1 KDF with no salt and can no longer decrypt the PBKDF2-encrypted values. Among
those values are the certificate-store passwords (*.keypass / *.trustpass), which
Openfire stores as encrypted database properties. With them unreadable, the keystores
fail to load (UnrecoverableKeyException: Password verification failed), including the
WEBADMIN store used by the admin console, so the admin console cannot start its HTTPS
listener. On a server that reaches the admin console over HTTPS, this locks the
administrator out (in the original report there was no admin listener on either port,
and the server logged "admin console not started due to configuration settings"). The
randomly generated salt is unrecoverable, so the re-encrypted data can be recovered
only from a pre-migration database backup.

See OF-3305, ADR-004, ADR-005.

Follow-up to #3067 (OF-3075), which added this migration. Reported on the forums: https://discourse.igniterealtime.org/t/admin-gui-not-starting-after-migration-to-pbkdf2/96500

Fix

Fail fast, before any irreversible change, if the security configuration cannot be
persisted:

  • Add XMLProperties.isPersistable() (true only when backed by a file).
  • Add JiveGlobals.isSecurityPropertiesPersistable().
  • JiveGlobals.migrateXMLPropertiesFromSHA1ToPBKDF2() throws IllegalStateException
    if security properties are not persistable, before deriving any key or touching the
    database. The servlet runs this before any database work and reports that no
    database changes were made.
  • BlowfishMigrationServlet.doPost() adds a pre-flight check (after the CSRF, backup,
    and clustering gates) that blocks migration and shows a clear admin message
    (security.blowfish.migration.error.security-xml-not-writable).
  • Correct the load-failure log message in JiveGlobals.loadSecurityProperties() to
    name security.xml rather than openfire.xml.

The check runs at the very start of the migration, before any key derivation or
database write, so an unwritable security.xml is reported to the administrator as a
clear, actionable error and the database is never modified.

Testing

  • XMLPropertiesTest: isPersistable() returns false for a non-persisted instance and
    true for a file-backed one.
  • BlowfishMigrationServletTest: the migration throws with a security.xml message
    when not persistable; doPost() sets the new error key and redirects without
    attempting migration. Existing migration and clustering tests pass.
  • Full xmppserver suite green via mvn clean verify: 1911 tests, 0 failures, 0 errors.

…able

When conf/security.xml cannot be loaded or written (missing, empty, corrupt
or not writable), JiveGlobals falls back to a non-persisting, in-memory-only
properties object. The SHA1-to-PBKDF2 migration would generate a PBKDF2 salt,
re-encrypt and commit the database properties, and report success, while the
salt and the kdf=pbkdf2 flag were silently discarded. After a restart the
server defaulted back to SHA1 with no salt and could no longer decrypt the
re-encrypted values (including the keystore passwords, which are stored as
encrypted database properties), so an HTTPS-only admin console failed to
start. The randomly generated salt is unrecoverable, leaving the data
readable only from a pre-migration backup.

Fail fast before any irreversible change:
- Add XMLProperties.isPersistable() and JiveGlobals.isSecurityPropertiesPersistable().
- migrateXMLPropertiesFromSHA1ToPBKDF2() now throws before deriving any key
  or touching the database when security properties are not persistable. The
  servlet runs this before database work and reports no database changes were made.
- BlowfishMigrationServlet.doPost() adds a pre-flight check that blocks
  migration with a clear admin message before any changes.

getBlowfishSalt() and setBlowfishKdf() are intentionally left unchanged (no
global throw): they are also used by legitimate non-persisted scenarios
(including the unit-test environment), so the targeted guard prevents the
dangerous path without altering unrelated behaviour.

Also corrects a misleading log message in loadSecurityProperties() that
reported the openfire.xml path when it was actually security.xml that failed
to load, which sent investigators to the wrong file during diagnosis.

See: OF-3305, ADR-004
@coderabbitai

coderabbitai Bot commented Jun 7, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 202bf739-cf7c-4b9b-8f3f-9730f0efeb2d

📥 Commits

Reviewing files that changed from the base of the PR and between c84322b and 10545ba.

📒 Files selected for processing (2)
  • xmppserver/src/test/java/org/jivesoftware/admin/servlet/BlowfishMigrationServletTest.java
  • xmppserver/src/test/java/org/jivesoftware/util/XMLPropertiesTest.java
🚧 Files skipped from review as they are similar to previous changes (2)
  • xmppserver/src/test/java/org/jivesoftware/util/XMLPropertiesTest.java
  • xmppserver/src/test/java/org/jivesoftware/admin/servlet/BlowfishMigrationServletTest.java

📝 Walkthrough

Walkthrough

This PR addresses OF-3305 by preventing unsafe Blowfish-to-PBKDF2 migration when conf/security.xml cannot be persisted. The change introduces a persistability detection mechanism: XMLProperties.isPersistable() checks whether an instance is backed by a file, and JiveGlobals.isSecurityPropertiesPersistable() exposes this check for security properties. Migration preconditions in both JiveGlobals and BlowfishMigrationServlet now fail fast if the security configuration file cannot be written, preventing irreversible re-encryption that would become unreadable after restart. A localized error message guides administrators to repair the file before retry, and ADR-004 documents the safety constraint.

Suggested reviewers

  • akrherz
🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description check ✅ Passed The PR description clearly and comprehensively explains the problem (Blowfish SHA1 to PBKDF2 migration data loss when security.xml is not persistable), the fix (fail-fast checks before any irreversible changes), and testing approach.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (2)
xmppserver/src/test/java/org/jivesoftware/admin/servlet/BlowfishMigrationServletTest.java (2)

586-615: 💤 Low value

Consider consistent test naming convention.

The new test uses should_X_when_Y naming while existing tests follow testDoPost_X convention. While both styles are valid, consistency within a file improves readability.

Optional: align with existing convention
-public void should_blockMigrationAndReportError_when_securityXmlNotPersistable() throws Exception {
+public void testDoPost_SecurityXmlNotPersistable_BlocksMigration() throws Exception {
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@xmppserver/src/test/java/org/jivesoftware/admin/servlet/BlowfishMigrationServletTest.java`
around lines 586 - 615, The test method
should_blockMigrationAndReportError_when_securityXmlNotPersistable in
BlowfishMigrationServletTest uses a different naming style than the rest of the
file; rename it to follow the existing testDoPost_* convention (e.g.,
testDoPost_securityXmlNotPersistable_blocksMigration) and update any references
to the method (test runner will pick it up by reflection), keeping the
assertions and calls to servlet.doPost(request, response), ClusterManager mocks,
and verify(...) expectations unchanged.

623-635: 💤 Low value

Consider consistent test naming convention.

Same naming convention inconsistency as the previous test method.

Optional: align with existing convention
-public void should_throwWithSecurityXmlMessage_when_migratingWhileNotPersistable() {
+public void testMigrateXMLPropertiesFromSHA1ToPBKDF2_SecurityXmlNotPersistable_ThrowsIllegalStateException() {
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@xmppserver/src/test/java/org/jivesoftware/admin/servlet/BlowfishMigrationServletTest.java`
around lines 623 - 635, The test method name uses underscores and is
inconsistent with the project's test naming convention; rename the method
should_throwWithSecurityXmlMessage_when_migratingWhileNotPersistable to match
the existing convention used across BlowfishMigrationServletTest (e.g.,
camelCase like
shouldThrowWithSecurityXmlMessageWhenMigratingWhileNotPersistable), update any
references in the test class, and keep the method body and assertions
(JiveGlobals.setBlowfishKdf and the assertThrows on
JiveGlobals.migrateXMLPropertiesFromSHA1ToPBKDF2) unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In
`@xmppserver/src/test/java/org/jivesoftware/admin/servlet/BlowfishMigrationServletTest.java`:
- Around line 586-615: The test method
should_blockMigrationAndReportError_when_securityXmlNotPersistable in
BlowfishMigrationServletTest uses a different naming style than the rest of the
file; rename it to follow the existing testDoPost_* convention (e.g.,
testDoPost_securityXmlNotPersistable_blocksMigration) and update any references
to the method (test runner will pick it up by reflection), keeping the
assertions and calls to servlet.doPost(request, response), ClusterManager mocks,
and verify(...) expectations unchanged.
- Around line 623-635: The test method name uses underscores and is inconsistent
with the project's test naming convention; rename the method
should_throwWithSecurityXmlMessage_when_migratingWhileNotPersistable to match
the existing convention used across BlowfishMigrationServletTest (e.g.,
camelCase like
shouldThrowWithSecurityXmlMessageWhenMigratingWhileNotPersistable), update any
references in the test class, and keep the method body and assertions
(JiveGlobals.setBlowfishKdf and the assertThrows on
JiveGlobals.migrateXMLPropertiesFromSHA1ToPBKDF2) unchanged.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: dfa6a439-bd7b-4f54-91d2-ee6ee0b1bfea

📥 Commits

Reviewing files that changed from the base of the PR and between 75c25b0 and c84322b.

📒 Files selected for processing (7)
  • doc/adr/004-manual-blowfish-pbkdf2-migration.md
  • i18n/src/main/resources/openfire_i18n.properties
  • xmppserver/src/main/java/org/jivesoftware/admin/servlet/BlowfishMigrationServlet.java
  • xmppserver/src/main/java/org/jivesoftware/util/JiveGlobals.java
  • xmppserver/src/main/java/org/jivesoftware/util/XMLProperties.java
  • xmppserver/src/test/java/org/jivesoftware/admin/servlet/BlowfishMigrationServletTest.java
  • xmppserver/src/test/java/org/jivesoftware/util/XMLPropertiesTest.java

@viv viv requested a review from guusdk June 7, 2026 12:55
The four tests added for OF-3305 used a should_X_when_Y style, whereas the
existing tests in BlowfishMigrationServletTest and XMLPropertiesTest use the
testXxx convention. Rename them to match, for consistency within each file.
No behavioural change.

See: OF-3305
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant