Skip to content

RUN-3540: Security Fix: Disable external extension loading by default. - #592

Merged
fdevans merged 6 commits into
mainfrom
RUN-3540
Jan 13, 2026
Merged

RUN-3540: Security Fix: Disable external extension loading by default.#592
fdevans merged 6 commits into
mainfrom
RUN-3540

Conversation

@fdevans

@fdevans fdevans commented Jan 6, 2026

Copy link
Copy Markdown
Contributor

Security Vulnerability Fix

Addresses RUN-3540 - Arbitrary Code Execution vulnerability in Rundeck CLI extension loading mechanism.

Vulnerability Summary

The Rundeck CLI contains an extension loading mechanism that can lead to arbitrary code execution. By controlling the RD_EXT_DIR environment variable, an attacker can cause the rd tool to load malicious JAR files, executing code through static initializers without user interaction.

The vulnerability exists because:

  1. rd checks for RD_EXT_DIR environment variable at startup
  2. Lists all *.jar files in the specified directory
  3. Loads these JARs into a URLClassLoader
  4. Uses Java ServiceLoader to find extension implementations
  5. Static initializer blocks execute immediately when classes are loaded

Current Fix (Implemented in this PR)

Changes:

  • Changed RD_EXT_DISABLED default from false to true in Main.java line 199
  • Extension directory loading is now disabled by default
  • Users who need this feature must explicitly set RD_EXT_DISABLED=false
  • Updated CHANGELOG.md for version 2.0.10
  • Added test coverage in MainSpec.groovy
  • Updated .gitignore to exclude build artifacts (bin/ directories)

Impact:

  • ✅ Secure by default - prevents arbitrary code execution
  • ✅ Minimal code change (one line)
  • ✅ Uses existing feature flag
  • ⚠️ Breaking change for users currently using external extensions (if any exist)
  • ⚠️ Double-negative naming convention (RD_EXT_DISABLED=false to enable)

To re-enable external extensions:

export RD_EXT_DISABLED=false

Alternative More Aggressive Approach (For Discussion)

We could implement a more forceful "soft break" approach that requires explicit opt-in:

Proposed changes:

  1. Keep extension loading disabled by default
  2. Introduce a NEW explicit opt-in flag: RD_ALLOW_EXTERNAL_EXTENSIONS=true
  3. Ignore RD_EXT_DISABLED=false (or deprecate it)
  4. Require the new positive-affirmation flag to enable

Benefits of alternative approach:

  • ✅ Clearer intent - users must consciously enable a security-sensitive feature
  • ✅ Positive flag naming (set something to true to enable, not false)
  • ✅ Prevents accidental re-enabling via old documentation/scripts
  • ✅ Makes security implications more obvious to users
  • ⚠️ More code changes required
  • ⚠️ Potentially more disruptive to existing users (though unlikely many exist)

Example implementation:

private static void loadExtensionJars(ConfigSource config) {
    // Explicit opt-in required for security
    if (!config.getBool("RD_ALLOW_EXTERNAL_EXTENSIONS", false)) {
        return;
    }
    // ... rest of loading logic
}

To enable (alternative approach):

export RD_ALLOW_EXTERNAL_EXTENSIONS=true
export RD_EXT_DIR=/path/to/extensions

Testing

  • ✅ Unit tests added and passing
  • ✅ Gradle build successful
  • ✅ Existing bundled extensions (like rd acl) continue to work normally

Future Considerations (Major Release)

Per Greg Schueler's recommendation: In a future major release, the loadExtensionJars method should be completely removed along with all external extension directory loading functionality, as this feature is no longer actively used.

Files Changed

  • rd-cli-tool/src/main/java/org/rundeck/client/tool/Main.java - Changed default value
  • CHANGELOG.md - Added version 2.0.10 with security fix note
  • rd-cli-tool/src/test/groovy/org/rundeck/client/tool/MainSpec.groovy - New test file
  • .gitignore - Added bin/ directories

Questions for Review

  1. Should we proceed with the current minimal fix, or implement the more aggressive opt-in approach?
  2. Should we add a warning message when someone sets RD_EXT_DISABLED=false to alert them of security implications?
  3. Should we document this more prominently in the README or security documentation?

- Changed RD_EXT_DISABLED default from false to true in Main.java
- Extension directory loading now disabled by default to prevent arbitrary code execution
- Users must explicitly set RD_EXT_DISABLED=false to enable external extensions
- Updated CHANGELOG.md for version 2.0.10
- Added MainSpec test for Main class constants
- Updated .gitignore to exclude bin/ directories

Addresses security vulnerability where malicious JAR files could be loaded
via RD_EXT_DIR environment variable, executing code through static initializers
without user interaction.
@fdevans fdevans changed the title Security Fix: Disable external extension loading by default (RUN-3540) RUN-3540: Security Fix: Disable external extension loading by default. Jan 6, 2026
Comment thread CHANGELOG.md Outdated
Comment thread rd-cli-tool/src/test/groovy/org/rundeck/client/tool/MainSpec.groovy Outdated
fdevans and others added 5 commits January 12, 2026 14:32
Co-authored-by: Greg Schueler <greg.schueler@gmail.com>
The test only verified that string constants equal themselves, which provides no value. The actual security behavior is covered by integration tests.
Updates packaging submodule from 8077ed3 to 662b44a, which includes:
- GPG key rotation (commit 5174e38)
- New pubring.gpg with updated signing key
- Archived old key as pubring20250228.gpg

This should resolve the GPG signature verification failures in CI/CD.
The upgrade test installs version 2.0.9 which was signed with the old GPG key
before the recent key rotation. Import the archived key (pubring20250228.gpg)
to allow verification of the 2.0.9 package signature during the upgrade test.
@fdevans
fdevans requested a review from gschueler January 12, 2026 23:06
@fdevans

fdevans commented Jan 12, 2026

Copy link
Copy Markdown
Contributor Author

Removed un-necessary test and fixed build CI issues related to GPG key rotation.

@fdevans
fdevans merged commit 4ad5a79 into main Jan 13, 2026
16 checks passed
@fdevans
fdevans deleted the RUN-3540 branch January 13, 2026 16:53
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.

2 participants