Skip to content

SOLR-8442: Make security easier WIP - #5107

Draft
epugh wants to merge 25 commits into
apache:mainfrom
epugh:SOLR-8442
Draft

epugh wants to merge 25 commits into
apache:mainfrom
epugh:SOLR-8442

Conversation

@epugh

@epugh epugh commented Oct 12, 2026 •

Copy link
Copy Markdown
Contributor

WIP exploration for SOLR-8442 and related security usability work. This work is intended to be split into smaller, focused PRs once I get some community feedback.

Description

Make it easier to configure and try Solr security from the command line. This branch adds a guided bin/solr start -e secure-setup example that combines HTTPS, HTTP authentication and authorization, and ZooKeeper access control in a two-node SolrCloud cluster using embedded ZooKeeper.

Solution

  • Add a bin/solr ssl command that imports PEM certificates and private keys into JKS or PKCS12 keystores, builds a truststore, updates the include file, and optionally sets the SolrCloud urlScheme property. Support encrypted private keys and password files. Thanks @janhoy for the suggestion.
  • Add the Unix/macOS-only secure-setup example set up! Generate a local self-signed certificate, show the generated file locations, and prompt the user to verify HTTPS with a v2 curl request before configuring HTTP accounts. Unfortunantly osx/linux only as I couldn't test effectively on windows
    • Start two HTTPS nodes sharing the first node's embedded ZooKeeper. Create a fresh techproducts collection with two shards placed across the nodes, index sample documents as the index user, and exercise distributed updates and queries with security enabled. Preserve existing collections and reuse saved credentials and TLS configuration on restart.
    • Separate administrative and document access: the administrator has superadmin and admin; index has indexing and search roles; search has the search role. Use demonstration passwords by default, offer password customization, and retry invalid wizard input using the shared password validation.
    • Allow anonymous health and metrics requests while keeping document and administrative APIs protected. Explain the generated security.json, permission order, and v2 monitoring checks in the tutorial.
    • Configure ZooKeeper digest authentication and ACLs with separate administrator and read-only accounts. Save HTTP and ZooKeeper credentials in private properties files, print both paths together, and show an authenticated ZooKeeper CLI example when setup finishes.
  • Add --zk-credentials to the ZooKeeper CLI commands in both CLI implementations, independently of HTTP --credentials. Preserve configured ACL providers when supplying explicit connection credentials. Trying to get away from the weird hardcoded settings in solr.in.sh approach for a cli tool!
  • Add the securing-Solr tutorial and update the SSL, ZooKeeper, generated CLI, and control-script reference documentation. Keep walkthrough instructions in the tutorial and command behavior in the reference.

Significant implementation, documentation, and test work was assisted by OpenAI Codex.

Tests

  • Focused secure-example tests for both CLI implementations, including default/custom passwords, credential reuse, authorization, and communication between nodes.
  • ./gradlew iTest --tests test_secure_example.bats: HTTPS verification guidance, two-node setup and restart, anonymous monitoring, HTTP roles, and ZooKeeper ACLs.
  • ./gradlew iTest --tests test_ssl.bats: all seven tests pass, covering HTTPS, Basic Authentication, hostname checks, client truststores, required/optional mTLS, and keystore reload.

Checklist

  • I have reviewed the guidelines for How to Contribute and my code conforms to the standards described there to the best of my ability.
  • I have created a Jira issue and added the issue ID to my pull request title.
  • I have given Solr maintainers access to contribute to my PR branch.
  • I have developed this patch against the main branch.
  • I have run ./gradlew check.
  • I have added tests for my changes.
  • I have added documentation for the Reference Guide.
  • I have added a changelog entry for my change.

@github-actions github-actions Bot added documentation Improvements or additions to documentation start-scripts tests cat:cli labels Oct 12, 2026
@epugh epugh changed the title Solr 8442 SOLR-8442: Make security easier WIP Oct 12, 2026
@epugh

epugh commented Oct 29, 2026

Copy link
Copy Markdown
Contributor Author

@janhoy you gave me some feedback I think, but now I can't find it ;-(

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I wonder jhow much of this can be pulled out and be it's own refactoring? Like this guy..

.required()
.desc("Name of the example to launch, one of: cloud, techproducts, schemaless, films.")
.desc(
"Name of the example to launch, one of: cloud, techproducts, secure-setup, schemaless, films.")

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

do we need to be smart and not show secure-setup on windows?

paramLabel = "NAME",
description =
"Name of the example to launch, one of: cloud, techproducts, schemaless, films.")
"Name of the example to launch, one of: cloud, techproducts, secure-setup, schemaless, films.")

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

also needs to be aware of windows!

changePasswords
? promptPassword(input, "Password for zk-readonly [SolrRocks]: ", "zk-readonly")
: "SolrRocks";
secureZk.create(zkAdminPassword, zkReadonlyPassword);

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

i wonder if this should be it's won bin/solr auth tool? or bin/solr zk auth tool call? Probably bin/solr auth?

Map<String, Object> publicMonitoring = new HashMap<>();
publicMonitoring.put("name", "public-monitoring");
publicMonitoring.put("collection", null);
publicMonitoring.put("path", List.of("/____v2/node/health", "/____v2/metrics"));

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

ugh, I hate teh ____v2 here!

String zkPassword = zkCredentials.getProperty("zkDigestPassword");
echo("List ZooKeeper data with TLS and digest authentication:");
echo(
"bin/solr zk ls / --zk-tls-ca-file '"

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

are we missing --zk-tls ??


private void configureSecureExampleSSL(Scanner input, StartSolrParams start, boolean noPrompt)
throws Exception {
echo("Generating sample PEM certificate and key files. OpenSSL must be available on PATH.");

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

we could be all java!

("techproducts".equals(params.example()) || "secure-setup".equals(params.example()))
? "-Dsolr.modules=clustering,extraction,langid,ltr,scripting,sql -Dsolr.ltr.enabled=true -Dsolr.clustering.enabled=true"
: "";
if ("secure-setup".equals(params.example())) {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

i thought we had a constant we added so we didn't need "secure-setup"?

"Unsupported example "
+ exampleType
+ "! Please choose one of: cloud, schemaless, techproducts, or films");
+ "! Please choose one of: cloud, schemaless, techproducts, secure-setup, or films");

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

also need to filter on windows. I think we show the list like three times!

import java.util.Properties;

/** Persists HTTP credentials so the secure example can restart without password prompts. */
final class SecureExampleCredentials {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

this feels like step one towards "solr.properties" ;-)

"[req]\ndistinguished_name=dn\n[dn]\n[v3]\nsubjectAltName=DNS:localhost,IP:127.0.0.1\n");
run(
List.of(
"openssl",

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

could be all java! don't we run openssl elsewhere too?

}

Path credentialsFile() {
return home.resolve("zk-credentials.properties");

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I think this should just be solr.properties

picocli.CommandLine commandLine = new picocli.CommandLine(new SolrCLI());
propagateCommandSettings(commandLine);
exit(commandLine.execute(stripEmptyLeadingArg(args)));
return commandLine.execute(stripEmptyLeadingArg(args));

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

interesting, hope this doesn't break something else!

private Path keystore;

@CommandLine.Option(
names = "--store-type",

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

do we need two types of store?

}
String type = params.storeType().toUpperCase(Locale.ROOT);
if (!type.equals("JKS") && !type.equals("PKCS12")) {
throw new IllegalArgumentException("--store-type must be JKS or PKCS12.");

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

okay, "PKCS12" is apparently better, and also the future... JKS is deprecated.. so let's not sue it anymore.

trust.setCertificateEntry("solr-" + i, chain[i]);
}
String original = Files.readString(include);
Map<String, String> settings = new LinkedHashMap<>();

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

this is jsut so messy!

@CommandLine.Option(
names = {"-e", "--example"},
description = "Run an example: cloud, techproducts, schemaless, films")
description = "Run an example: cloud, techproducts, secure-setup, schemaless, films")

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

and one more example ;-)

assertTrue(enabled.contains(sslSetting));
assertTrue(
enabled.contains(
Constants.WINDOWS ? "set SOLR_AUTH_TYPE=basic" : "SOLR_AUTH_TYPE='basic'"));

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

ugh, yeah, come on .toml or .yaml or even .properties

dir.resolve("openssl.cnf"),
"[req]\ndistinguished_name=dn\n[dn]\n[v3]\nsubjectAltName=DNS:localhost,IP:127.0.0.1\n");
try {
Process process =

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

is this how we test these things?

(
cd "$ssl_dir"
rm -f solr-ssl.keystore.p12 solr-ssl.pem
keytool -genkeypair -alias solr-ssl -keyalg RSA -keysize 2048 -keypass secret -storepass secret -validity 9999 -keystore solr-ssl.keystore.p12 -storetype PKCS12 -ext SAN=DNS:localhost,IP:127.0.0.1 -dname "CN=localhost, OU=Organizational Unit, O=Organization, L=Location, ST=State, C=Country"

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

what is the relationship between keytool and openssl? Is one better? Why did we delete this in vaour of openssl?

@nick-boss-tech

nick-boss-tech commented Nov 2, 2026 •

Copy link
Copy Markdown
Contributor

🤖 AI text below 🤖 (reviewed by Nick Shanin)

Prepared fixes for the November 2 review, integrated with your current head: compare 7d193dac0f...F4

The branch merges your head 7d193dac0f with the security-review fixes (both histories preserved, no rebase) and adds four commits on top: the repair itself, the generated start-page synchronization, the removal of the two scoped changelog fragments, and the credentials-option declaration described below. The credentials option needed work at both ends: bin/solr rejected it as unsupported, and RunExampleTool read the value without declaring the option, so both Java parsers rejected it as unknown. The branch teaches the shell script to forward --credentials and -u to example starts as separate arguments, declares the option in RunExampleTool for both parsers and wires the picocli value through, and rejects missing option values and credentials on a plain start before anything starts. The ZooKeeper client-address selection moved back above the logging guard so the log-call validator accepts it again. The secure-example BATS fixture now also covers the credential aliases, password rotation through the API, and a wrong-CA rejection followed by a correct-CA success. The generated CLI start page is synchronized at the head. No changelog fragment rides along; the PR's entry is yours to write.

Verification on Linux at 7aa3f77
  • Above the gated head 7e531b083182366f8b6e7f00a3781ce3cbb713de, this head adds the fragment removal described above and the credentials-option declaration (RunExampleTool plus its parse test). At this head the new parse test fails without the declaration and passes with it, the Error Prone compile and tidy pass, and on a rebuilt distribution start -e secure-setup --credentials solr:SolrRocks gets past argument parsing into the example wizard in both parser modes. Example starts cannot complete on that host for an unrelated environment reason: its Java localhost connections are intercepted, so the tool's port probe always reports the port as used. The runs below are from the gated head.
  • :solr:solr-ref-guide:generateCliDocs leaves the tree clean; :solr:core:compileTestJava and :solr:core:validateLogCalls pass; tidy is clean.
  • With the security manager: SecureExampleCredentialsTest 5 of 5, SecureExampleZkTest 3 of 3, CLIUtilsTest 10 of 10, AuthToolTest and AuthToolPicocliTest 3 of 3 each; SSLToolTest and IncludeFileEditorTest pass with their usual external-process skips under the manager.
  • Without the security manager: SSLToolTest 9 of 9 with no skips, IncludeFileEditorTest 7 of 7, and both secure-setup example methods pass in each parser class.
  • Fail-before: with only SSLTool.java and SecureExampleCredentials.java restored from your head, the three defect tests fail with their original signatures (client-auth settings not preserved; the six-character PEM password rejection; an unusable administrator accepted). The same tests pass at this head.
  • Root check -x test stops at :solr:webapp:verifyUiWasm on a checksum mismatch for the prebuilt UI bundle. The same task fails at your head with the same expected checksum; the downloaded bytes differed between the two runs here, so this is not caused by the branch.

One limit: the packaging BATS suites (test_ssl and test_secure_example in both parser modes) have not been run on this combined head yet, so the end-to-end shell credential flow and the ZK TLS example are verified by the unit suites above, not by BATS. If the No value present startup failure from CI recurs there, the fixture now starts examples with --verbose so the stack is captured. Separately, per the guide, private trust anchors still need restoring after a truststore regeneration.

To take the branch onto your PR head (your head is an ancestor, so this fast-forwards):

git switch SOLR-8442
git fetch https://github.com/nick-boss-tech/solr.git solr-8442-security-review-r2-submit
git merge --ff-only 7aa3f77bed263728a13f361d9f371bf16937672c

You're welcome to take the commits or adapt the changes.

Nick Shanin directed the work and takes responsibility for this contribution.

@epugh

epugh commented Nov 2, 2026

Copy link
Copy Markdown
Contributor Author

The branch merges your head 7d193dac0f with the security-review fixes (both histories preserved, no rebase) and adds three commits on top: the repair itself, the generated start-page synchronization, and the removal of the two scoped changelog fragments. RunExampleTool already accepts --credentials; bin/solr rejected the option as unsupported, so the repair teaches the shell script to forward --credentials and -u to example starts as separate arguments,

interesting... So, I actually don't have a good sense of if our examples actually work well if you run jthem twice... generally in development I'l do a gradlew clean dev just to reset thigns. I think, for simpliciity just at this point, I'll make it so it explicit that if you do run an example twice, it just steps on things.

so bin/solr start -e secure-setup would just step over any previous bin/solr start -e secure-setup. We really probably should verify that if you run

bin/solr start -e techproducts

#and then

bin/solr start -e films

that process still works... I actually wou'ndt be surprise if you got werid shit, I've never tested that flow.. interested in poking at that?

@epugh

epugh commented Nov 4, 2026

Copy link
Copy Markdown
Contributor Author

I had a long chat with @Gerlowski and I wanted to capture some notes:

  • Much easier to have a bin/solr start -e secure that does everything, but doestn' provide extra tooling around certs and sll that users can use. (i.e, don't expose the current bin/solr ssl tooling, jsut hide it.
  • Onboard with using bouncycastle for all java instead of openssl
  • a vote for either .yaml or .toml over our current solr.in.sh and solr.in.cmd files!
  • requiring solr.in.sh is fine for bin/solr commands that aren't about starting/sotpping solr.. that are potentialyly client side.
  • i think he is onboard with creating a basic security.json pattern???
  • "most teams have SREs that do all the ssl stuff" ;-). Sigh.

@Gerlowski

Copy link
Copy Markdown

Hi! I think you've mentioned the wrong account — I'm not involved in Solr and don't recall any conversation about this. You probably meant @gerlowskija (Jason Gerlowski). Cheers!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants