mirror of https://github.com/apache/lucene.git
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.
This commit is contained in:
parent
6348c284fd
commit
d4fb023756
|
@ -217,7 +217,7 @@ Bug Fixes
|
|||
* SOLR-14546: Fix for a relatively hard to hit issue in OverseerTaskProcessor that could lead to out of order execution
|
||||
of Collection API tasks competing for a lock (Ilan Ginzburg).
|
||||
|
||||
* SOLR-15162: Allow readOnly parameter to be used with v2 modify collection command (Eric Pugh)
|
||||
* SOLR-15162: Allow readOnly parameter to be used with v2 modify collection command (Eric Pugh)
|
||||
|
||||
================== 8.9.0 ==================
|
||||
|
||||
|
@ -250,6 +250,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.
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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() {
|
||||
|
@ -125,6 +132,26 @@ public class JWTIssuerConfigTest {
|
|||
assertEquals("https://host/jwk", issuerConfig.getJwksUrls().get(0));
|
||||
}
|
||||
|
||||
@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");
|
||||
|
@ -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());
|
||||
}
|
||||
}
|
|
@ -161,6 +161,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
|
||||
|
||||
|
|
Loading…
Reference in New Issue