You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucene.apache.org by ep...@apache.org on 2021/02/27 14:24:51 UTC
[lucene-solr] branch branch_8x updated: SOLR-15194: relax
requirements and allow http urls. (#2430)
This is an automated email from the ASF dual-hosted git repository.
epugh pushed a commit to branch branch_8x
in repository https://gitbox.apache.org/repos/asf/lucene-solr.git
The following commit(s) were added to refs/heads/branch_8x by this push:
new d9416d1 SOLR-15194: relax requirements and allow http urls. (#2430)
d9416d1 is described below
commit d9416d10c2946d0d462f73acdc55e71a56f29d31
Author: Eric Pugh <ep...@opensourceconnections.com>
AuthorDate: Sat Feb 27 09:13:51 2021 -0500
SOLR-15194: relax requirements and allow http urls. (#2430)
Relax the need for https urls for JWT IDP's if you pass in solr.auth.jwt.allowOutboundHttp=true system property.
---
solr/CHANGES.txt | 2 +
.../org/apache/solr/security/JWTIssuerConfig.java | 29 ++++++++--
.../apache/solr/security/JWTIssuerConfigTest.java | 65 ++++++++++++++++++----
.../src/jwt-authentication-plugin.adoc | 4 ++
4 files changed, 82 insertions(+), 18 deletions(-)
diff --git a/solr/CHANGES.txt b/solr/CHANGES.txt
index 29598fe..cc5028e 100644
--- a/solr/CHANGES.txt
+++ b/solr/CHANGES.txt
@@ -35,6 +35,8 @@ Improvements
* SOLR-15038: Add elevateOnlyDocsMatchingQuery and collectElevatedDocsWhenCollapsing parameters to query elevation.
(Dennis Berger, Tobias Kässmann via Bruno Roustant)
+* SOLR-15194: Allow Solr to make outbound non SSL calls to a JWT IDP via -Dsolr.auth.jwt.allowOutboundHttp=true property. (Eric Pugh)
+
Optimizations
---------------------
* SOLR-15079: Block Collapse - Faster collapse code when groups are co-located via Block Join style nested doc indexing.
diff --git a/solr/core/src/java/org/apache/solr/security/JWTIssuerConfig.java b/solr/core/src/java/org/apache/solr/security/JWTIssuerConfig.java
index 65ff808..7f53f4d 100644
--- a/solr/core/src/java/org/apache/solr/security/JWTIssuerConfig.java
+++ b/solr/core/src/java/org/apache/solr/security/JWTIssuerConfig.java
@@ -68,6 +68,9 @@ public class JWTIssuerConfig {
private WellKnownDiscoveryConfig wellKnownDiscoveryConfig;
private String clientId;
private String authorizationEndpoint;
+
+ public static boolean ALLOW_OUTBOUND_HTTP = Boolean.parseBoolean(System.getProperty("solr.auth.jwt.allowOutboundHttp", "false"));
+ public static final String ALLOW_OUTBOUND_HTTP_ERR_MSG = "HTTPS required for IDP communication. Please use SSL or start your nodes with -Dsolr.auth.jwt.allowOutboundHttp=true to allow HTTP for test purposes.";
/**
* Create config for further configuration with setters, builder style.
@@ -349,13 +352,16 @@ public class JWTIssuerConfig {
this.jwkCacheDuration = jwkCacheDuration;
this.refreshReprieveThreshold = refreshReprieveThreshold;
}
-
+
+ /**
+ * While the class name is HttpsJwks, it actually works with plain http formatted url as well.
+ * @param url the Url to connect to for JWK details.
+ */
private HttpsJwks create(String url) {
try {
URL jwksUrl = new URL(url);
- if (!"https".equalsIgnoreCase(jwksUrl.getProtocol())) {
- throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, PARAM_JWKS_URL + " must use HTTPS");
- }
+ checkAllowOutboundHttpConnections(PARAM_JWKS_URL, jwksUrl);
+
} catch (MalformedURLException e) {
throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, "Url " + url + " configured in " + PARAM_JWKS_URL + " is not a valid URL");
}
@@ -384,9 +390,11 @@ public class JWTIssuerConfig {
public static WellKnownDiscoveryConfig parse(String urlString) {
try {
URL url = new URL(urlString);
- if (!Arrays.asList("https", "file").contains(url.getProtocol())) {
- throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "Well-known config URL must be HTTPS or file");
+ if (!Arrays.asList("https", "file", "http").contains(url.getProtocol())) {
+ throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "Well-known config URL must be one of HTTPS or HTTP or file");
}
+ checkAllowOutboundHttpConnections(PARAM_WELL_KNOWN_URL, url);
+
return parse(url.openStream());
} catch (MalformedURLException e) {
throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, "Well-known config URL " + urlString + " is malformed", e);
@@ -435,4 +443,13 @@ public class JWTIssuerConfig {
return (List<String>) securityConf.get("response_types_supported");
}
}
+
+ public static void checkAllowOutboundHttpConnections(String parameterName, URL url) {
+ if ("http".equalsIgnoreCase(url.getProtocol())) {
+ if (!ALLOW_OUTBOUND_HTTP) {
+ throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, parameterName + " is using http protocol. " + ALLOW_OUTBOUND_HTTP_ERR_MSG);
+ }
+ }
+ }
+
}
diff --git a/solr/core/src/test/org/apache/solr/security/JWTIssuerConfigTest.java b/solr/core/src/test/org/apache/solr/security/JWTIssuerConfigTest.java
index 3388552..7623f98 100644
--- a/solr/core/src/test/org/apache/solr/security/JWTIssuerConfigTest.java
+++ b/solr/core/src/test/org/apache/solr/security/JWTIssuerConfigTest.java
@@ -17,6 +17,9 @@
package org.apache.solr.security;
+import static org.apache.solr.SolrTestCaseJ4.TEST_PATH;
+import static org.apache.solr.security.JWTAuthPluginTest.testJwk;
+
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
@@ -28,24 +31,22 @@ import java.util.List;
import java.util.Map;
import org.apache.commons.lang3.StringUtils;
+import org.apache.solr.SolrTestCase;
import org.apache.solr.common.SolrException;
import org.jose4j.jwk.JsonWebKeySet;
+import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.noggit.JSONUtil;
-import static org.apache.solr.SolrTestCaseJ4.TEST_PATH;
-import static org.apache.solr.security.JWTAuthPluginTest.testJwk;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-
-public class JWTIssuerConfigTest {
+public class JWTIssuerConfigTest extends SolrTestCase {
private JWTIssuerConfig testIssuer;
private Map<String, Object> testIssuerConfigMap;
private String testIssuerJson;
@Before
public void setUp() throws Exception {
+ super.setUp();
testIssuer = new JWTIssuerConfig("name")
.setJwksUrl("https://issuer/path")
.setIss("issuer")
@@ -65,6 +66,12 @@ public class JWTIssuerConfigTest {
" \"iss\":\"issuer\",\n" +
" \"authorizationEndpoint\":\"https://issuer/authz\"}";
}
+
+ @After
+ public void tearDown() throws Exception {
+ super.tearDown();
+ JWTIssuerConfig.ALLOW_OUTBOUND_HTTP = false;
+ }
@Test
public void parseConfigMap() {
@@ -126,6 +133,26 @@ public class JWTIssuerConfigTest {
}
@Test
+ public void jwksUrlwithHttpBehaviors() {
+
+ HashMap<String, Object> issuerConfigMap = new HashMap<>();
+ issuerConfigMap.put("name", "myName");
+ issuerConfigMap.put("iss", "myIss");
+ issuerConfigMap.put("jwksUrl", "http://host/jwk");
+
+ JWTIssuerConfig issuerConfig = new JWTIssuerConfig(issuerConfigMap);
+
+ SolrException e = expectThrows(SolrException.class, () -> issuerConfig.getHttpsJwks());
+ assertEquals(400, e.code());
+ assertEquals("jwksUrl is using http protocol. HTTPS required for IDP communication. Please use SSL or start your nodes with -Dsolr.auth.jwt.allowOutboundHttp=true to allow HTTP for test purposes.", e.getMessage());
+
+ JWTIssuerConfig.ALLOW_OUTBOUND_HTTP = true;
+
+ assertEquals(1, issuerConfig.getHttpsJwks().size());
+ assertEquals("http://host/jwk", issuerConfig.getHttpsJwks().get(0).getLocation());
+ }
+
+ @Test
public void wellKnownConfigFromInputstream() throws IOException {
Path configJson = TEST_PATH().resolve("security").resolve("jwt_well-known-config.json");
JWTIssuerConfig.WellKnownDiscoveryConfig config = JWTIssuerConfig.WellKnownDiscoveryConfig.parse(Files.newInputStream(configJson));
@@ -144,13 +171,27 @@ public class JWTIssuerConfigTest {
assertEquals(Arrays.asList("code", "code id_token", "code token", "code id_token token", "token", "id_token", "id_token token"), config.getResponseTypesSupported());
}
- @Test(expected = SolrException.class)
- public void wellKnownConfigNotHttps() {
- JWTIssuerConfig.WellKnownDiscoveryConfig.parse("http://127.0.0.1:45678/.well-known/config");
- }
+ @Test
+ public void wellKnownConfigWithHttpBehaviors() {
+ SolrException e = expectThrows(SolrException.class, () -> JWTIssuerConfig.WellKnownDiscoveryConfig.parse("http://127.0.0.1:45678/.well-known/config"));
+ assertEquals(400, e.code());
+ assertEquals("wellKnownUrl is using http protocol. HTTPS required for IDP communication. Please use SSL or start your nodes with -Dsolr.auth.jwt.allowOutboundHttp=true to allow HTTP for test purposes.", e.getMessage());
+
+ JWTIssuerConfig.ALLOW_OUTBOUND_HTTP = true;
+
+ e = expectThrows(SolrException.class, () -> JWTIssuerConfig.WellKnownDiscoveryConfig.parse("http://127.0.0.1:45678/.well-known/config"));
+ assertEquals(500, e.code());
+ // We open a connection in the code path to a server that doesn't exist, which causes this. Should really be mocked.
+ assertEquals("Well-known config could not be read from url http://127.0.0.1:45678/.well-known/config", e.getMessage());
+
+
- @Test(expected = SolrException.class)
+ }
+
+ @Test
public void wellKnownConfigNotReachable() {
- JWTIssuerConfig.WellKnownDiscoveryConfig.parse("https://127.0.0.1:45678/.well-known/config");
+ SolrException e = expectThrows(SolrException.class, () -> JWTIssuerConfig.WellKnownDiscoveryConfig.parse("https://127.0.0.1:45678/.well-known/config"));
+ assertEquals(500, e.code());
+ assertEquals("Well-known config could not be read from url https://127.0.0.1:45678/.well-known/config", e.getMessage());
}
}
\ No newline at end of file
diff --git a/solr/solr-ref-guide/src/jwt-authentication-plugin.adoc b/solr/solr-ref-guide/src/jwt-authentication-plugin.adoc
index 2d96c0c..9742924 100644
--- a/solr/solr-ref-guide/src/jwt-authentication-plugin.adoc
+++ b/solr/solr-ref-guide/src/jwt-authentication-plugin.adoc
@@ -162,6 +162,10 @@ Let's comment on this config:
<12> Configure the audience claim. A token's 'aud' claim must match 'aud' for one of the configured issuers.
<13> This issuer is auto configured through discovery, so 'iss' and JWK settings are not required
+=== Using non SSL URLs
+In production environments you should always use SSL protected HTTPS connections, otherwise you open yourself up to attacks.
+However, in development, it may be useful to use regular http urls, and bypass the
+security check that Solr performs. To support this you can set the environment variable `solr.auth.jwt.allowOutboundHttp=true`.
== Editing JWT Authentication Plugin Configuration