You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@solr.apache.org by ja...@apache.org on 2023/01/13 13:21:24 UTC
[solr] branch branch_9x updated: SOLR-16616: JWTAuthPlugin: Read trusted X509 certificates from multi files (#1284)
This is an automated email from the ASF dual-hosted git repository.
janhoy pushed a commit to branch branch_9x
in repository https://gitbox.apache.org/repos/asf/solr.git
The following commit(s) were added to refs/heads/branch_9x by this push:
new e604e837795 SOLR-16616: JWTAuthPlugin: Read trusted X509 certificates from multi files (#1284)
e604e837795 is described below
commit e604e8377953a6e724c9840cdb703696f43287d8
Author: Jan Høydahl <ja...@users.noreply.github.com>
AuthorDate: Fri Jan 13 14:10:22 2023 +0100
SOLR-16616: JWTAuthPlugin: Read trusted X509 certificates from multi files (#1284)
---
solr/CHANGES.txt | 2 +
.../apache/solr/security/jwt/JWTAuthPlugin.java | 72 +++++++++++++++-------
.../solr/security/jwt/JWTAuthPluginTest.java | 35 +++++++++++
.../pages/jwt-authentication-plugin.adoc | 4 +-
4 files changed, 89 insertions(+), 24 deletions(-)
diff --git a/solr/CHANGES.txt b/solr/CHANGES.txt
index cc1547e22b8..f90dcb75d4a 100644
--- a/solr/CHANGES.txt
+++ b/solr/CHANGES.txt
@@ -90,6 +90,8 @@ Improvements
* SOLR-10470: Introduce Builder setter for parallelCacheRefreshes on cloud SolrClients. Deprecated
direct setter setParallelCacheRefreshes on cloud SolrClients. (Eric Pugh, David Smiley, Alex Deparvu)
+* SOLR-16616: JWTAuthPlugin: Read trusted X509 certificates from multiple files (janhoy)
+
* SOLR-15787: FileSystemConfigSetService: implement the abstraction completely. It could be useful
for putting ConfigSets on a shared file system. (Nazerke Seidan, David Smiley)
diff --git a/solr/modules/jwt-auth/src/java/org/apache/solr/security/jwt/JWTAuthPlugin.java b/solr/modules/jwt-auth/src/java/org/apache/solr/security/jwt/JWTAuthPlugin.java
index 6c85d72ba46..9af08d6a655 100644
--- a/solr/modules/jwt-auth/src/java/org/apache/solr/security/jwt/JWTAuthPlugin.java
+++ b/solr/modules/jwt-auth/src/java/org/apache/solr/security/jwt/JWTAuthPlugin.java
@@ -17,7 +17,6 @@
package org.apache.solr.security.jwt;
import java.io.IOException;
-import java.io.InputStream;
import java.lang.invoke.MethodHandles;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
@@ -78,7 +77,7 @@ import org.jose4j.lang.JoseException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-/** Authenticaion plugin that finds logged in user by validating the signature of a JWT token */
+/** Authentication plugin that finds logged in user by validating the signature of a JWT token */
public class JWTAuthPlugin extends AuthenticationPlugin
implements SpecProvider, ConfigEditablePlugin {
private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
@@ -211,11 +210,10 @@ public class JWTAuthPlugin extends AuthenticationPlugin
requiredScopes = Arrays.asList(requiredScopesStr.split("\\s+"));
}
- // Parse custom IDP SSL Cert from either path or string
- InputStream trustedCertsStream = null;
- String trustedCertsFile = (String) pluginConfig.get(PARAM_TRUSTED_CERTS_FILE);
+ // Parse custom IDP SSL Cert from either path, list of paths or plaintext string
+ Object trustedCertsFileObj = pluginConfig.get(PARAM_TRUSTED_CERTS_FILE);
String trustedCerts = (String) pluginConfig.get(PARAM_TRUSTED_CERTS);
- if (trustedCertsFile != null && trustedCerts != null) {
+ if (trustedCertsFileObj != null && trustedCerts != null) {
throw new SolrException(
SolrException.ErrorCode.SERVER_ERROR,
"Found both "
@@ -224,25 +222,13 @@ public class JWTAuthPlugin extends AuthenticationPlugin
+ PARAM_TRUSTED_CERTS
+ ", please use only one");
}
- if (trustedCertsFile != null) {
- try {
- Path trustedCertsPath = Paths.get(trustedCertsFile);
- if (coreContainer != null) {
- coreContainer.assertPathAllowed(trustedCertsPath);
- }
- trustedCertsStream = Files.newInputStream(trustedCertsPath);
- log.info("Reading trustedCerts from file {}", trustedCertsFile);
- } catch (IOException e) {
- throw new SolrException(
- SolrException.ErrorCode.SERVER_ERROR, "Failed to read file " + trustedCertsFile, e);
- }
+ if (trustedCertsFileObj != null) {
+ trustedSslCerts = readSslCertsFromFileOrList(trustedCertsFileObj);
}
if (trustedCerts != null) {
log.info("Reading trustedCerts PEM from configuration string");
- trustedCertsStream = IOUtils.toInputStream(trustedCerts, StandardCharsets.UTF_8);
- }
- if (trustedCertsStream != null) {
- trustedSslCerts = CryptoKeys.parseX509Certs(trustedCertsStream);
+ trustedSslCerts =
+ CryptoKeys.parseX509Certs(IOUtils.toInputStream(trustedCerts, StandardCharsets.UTF_8));
}
long jwkCacheDuration =
@@ -297,6 +283,48 @@ public class JWTAuthPlugin extends AuthenticationPlugin
lastInitTime = Instant.now();
}
+ /**
+ * Given a configuration object of a file name or list of file names, read X509 certificates from
+ * each file
+ */
+ @SuppressWarnings("unchecked")
+ Collection<X509Certificate> readSslCertsFromFileOrList(Object trustedCertsFileObj) {
+ Collection<X509Certificate> certs = new HashSet<>();
+ List<String> trustedCertsFileList;
+ if (trustedCertsFileObj instanceof String) {
+ trustedCertsFileList = List.of((String) trustedCertsFileObj);
+ } else if (trustedCertsFileObj instanceof List) {
+ trustedCertsFileList = (List<String>) trustedCertsFileObj;
+ } else {
+ throw new SolrException(
+ SolrException.ErrorCode.SERVER_ERROR, "trustedCertsFile is neither a String or List");
+ }
+ log.info("Reading trustedCerts from file(s) {}", trustedCertsFileList);
+ trustedCertsFileList.forEach(
+ f -> {
+ try {
+ certs.addAll(parseCertsFromFile(f));
+ } catch (IOException e) {
+ throw new SolrException(
+ SolrException.ErrorCode.SERVER_ERROR, "Failed to read file " + f, e);
+ }
+ });
+ return certs;
+ }
+
+ /**
+ * Given a filename string, validate the file and then read any X509 certificates from it
+ *
+ * @return list of certificates found in file
+ */
+ Collection<? extends X509Certificate> parseCertsFromFile(String certFileName) throws IOException {
+ Path certFilePath = Paths.get(certFileName);
+ if (coreContainer != null) {
+ coreContainer.assertPathAllowed(certFilePath);
+ }
+ return CryptoKeys.parseX509Certs(Files.newInputStream(certFilePath));
+ }
+
@SuppressWarnings("unchecked")
private Optional<JWTIssuerConfig> parseIssuerFromTopLevelConfig(Map<String, Object> conf) {
try {
diff --git a/solr/modules/jwt-auth/src/test/org/apache/solr/security/jwt/JWTAuthPluginTest.java b/solr/modules/jwt-auth/src/test/org/apache/solr/security/jwt/JWTAuthPluginTest.java
index 9fa5e09d252..9c5bd7144f8 100644
--- a/solr/modules/jwt-auth/src/test/org/apache/solr/security/jwt/JWTAuthPluginTest.java
+++ b/solr/modules/jwt-auth/src/test/org/apache/solr/security/jwt/JWTAuthPluginTest.java
@@ -665,4 +665,39 @@ public class JWTAuthPluginTest extends SolrTestCaseJ4 {
assertEquals(
2, CryptoKeys.parseX509Certs(IOUtils.toInputStream(cert, StandardCharsets.UTF_8)).size());
}
+
+ @Test
+ public void readSslCertsFromFileOrList() {
+ String pemWithTwoCerts =
+ JWT_TEST_PATH()
+ .resolve("security")
+ .resolve("jwt_plugin_idp_cert.pem")
+ .toAbsolutePath()
+ .toString();
+ String pemWithOneCert =
+ JWT_TEST_PATH()
+ .resolve("security")
+ .resolve("jwt_plugin_idp_wrongcert.pem")
+ .toAbsolutePath()
+ .toString();
+ assertEquals(
+ 3, plugin.readSslCertsFromFileOrList(List.of(pemWithTwoCerts, pemWithOneCert)).size());
+ }
+
+ @Test(expected = SolrException.class)
+ public void readSslCertsFromFileNotFound() {
+ String pemFilePath = JWT_TEST_PATH().resolve("not_exist.pem").toAbsolutePath().toString();
+ plugin.readSslCertsFromFileOrList(pemFilePath);
+ }
+
+ @Test
+ public void parseCertsFromFile() throws IOException {
+ String pemFilePath =
+ JWT_TEST_PATH()
+ .resolve("security")
+ .resolve("jwt_plugin_idp_cert.pem")
+ .toAbsolutePath()
+ .toString();
+ assertEquals(2, plugin.parseCertsFromFile(pemFilePath).size());
+ }
}
diff --git a/solr/solr-ref-guide/modules/deployment-guide/pages/jwt-authentication-plugin.adoc b/solr/solr-ref-guide/modules/deployment-guide/pages/jwt-authentication-plugin.adoc
index 8bd7962ea1d..ec79558548e 100644
--- a/solr/solr-ref-guide/modules/deployment-guide/pages/jwt-authentication-plugin.adoc
+++ b/solr/solr-ref-guide/modules/deployment-guide/pages/jwt-authentication-plugin.adoc
@@ -64,7 +64,7 @@ claimsMatch ; JSON object of claims (key) that must match a regular exp
adminUiScope ; Define what scope is requested when logging in from Admin UI ; If not defined, the first scope from `scope` parameter is used
redirectUris ; Valid location(s) for redirect after external authentication. Takes a string or array of strings. Must be the base URL of Solr, e.g., https://solr1.example.com:8983/solr/ and must match the list of redirect URIs registered with the Identity Provider beforehand. ; Defaults to empty list, i.e., any node is assumed to be a valid redirect target.
trustedCerts ; One or more X.509 SSL certificates in plaintext PEM or PKCS#7 formats, that should be trusted when talking to IdPs. Newlines must be replaced with `\n`. See paragraph <<Trusting the IdP server>> for more about its usage. ; Defaults to Java truststore
-trustedCertsFile ; Path to a file of type PEM, DER or PKCS#7, containing one or more X.509 SSL certificates that should be trusted when talking to IdPs. See paragraph <<Trusting the IdP server>> for more about its usage. ; Defaults to Java truststore
+trustedCertsFile ; Path to a file of type PEM, DER or PKCS#7, containing one or more X.509 SSL certificates that should be trusted when talking to IdPs. Can also be an array of file paths. See paragraph <<Trusting the IdP server>> for more about its usage. ; Defaults to Java truststore
issuers ; List of issuers (Identity providers) to support. See section <<issuer-configuration,Issuer configuration>> for configuration syntax ;
|===
@@ -193,7 +193,7 @@ This is both more secure and also lets you trust self-signed certificates.
It also has the benefit of working even if Solr is not started in SSL mode.
Please configure either the `trustedCerts` or `trustedCertsFile` option.
-Configuring both will cause an error.
+Configuring both will cause an error. If `trustedCertsFile` is an array of strings, Solr will parse certificates from all files.
=== Multiple Authentication Schemes