madrob commented on a change in pull request #139:
URL: https://github.com/apache/solr/pull/139#discussion_r645835911



##########
File path: solr/solr-ref-guide/src/jwt-authentication-plugin.adoc
##########
@@ -158,12 +161,18 @@ Let's comment on this config:
 <11> Configure the issuer id. Will be used for validating tokens. A token's 
'iss' claim must match one of the configured issuer IDs.
 <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
+<14> Provides SSL certificates to trust to support self-signed SSL in the 
issuers.
 
 === 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`.
 
+=== Trusting the IdP server
+All communication with issuer server (IdP) is done over HTTPS. If the issuer 
uses a self signed SSL certificate, the connection will fail since the plugin 
enforces verification of SSL certificates on outbound traffic. By default, the 
root certificates present in Java's TrustStore (default or custom provided) are 
consulted. If you do not want to place the IdP certificates in Java's 
TrustStore, you can configure this plugin with either `trustedCertsFile` or 
`trustedCerts` options to provide a set of certificates to use when talking to 
IdP. This has the extra benefit of also working even if Solr is not started in 
SSL mode.
+
+Please configure either `trustedCerts` or `trustedCertsFile`, not both.

Review comment:
       Stronger language here, indicating that it will fail if both are 
configured.

##########
File path: 
solr/core/src/test/org/apache/solr/security/JWTAuthPluginIntegrationTest.java
##########
@@ -127,13 +135,40 @@ public void tearDown() throws Exception {
     super.tearDown();
   }
 
-  @Test(expected = IOException.class)
-  public void infoRequestWithoutToken() throws Exception {
-    get(baseUrl + "/admin/info/system", null);
+  @Test
+  public void extractCertificateFromPem() throws IOException {

Review comment:
       Can we have a test where the pen file or directly supplied string is 
improperly formatted? I'd like to see what kind of error message users can 
expect in that case (and maybe we should document that, because I suspect it 
will be cryptic otherwise)

##########
File path: 
solr/core/src/java/org/apache/solr/security/JWTVerificationkeyResolver.java
##########
@@ -110,7 +112,13 @@ public Key resolveKey(JsonWebSignature jws, 
List<JsonWebStructure> nestingContex
       if (issuerConfig.usesHttpsJwk()) {
         keysSource = "[" + String.join(", ", issuerConfig.getJwksUrls()) + "]";
         for (HttpsJwks hjwks : issuerConfig.getHttpsJwks()) {
-          jsonWebKeys.addAll(hjwks.getJsonWebKeys());
+          try {
+            jsonWebKeys.addAll(hjwks.getJsonWebKeys());
+          } catch (SSLHandshakeException e) {
+            log.warn("Failed to talk SSL with {}, do you have the correct SSL 
certificate configured?", hjwks.getLocation());
+            throw new SolrException(SolrException.ErrorCode.SERVER_ERROR,
+                "SSL error when validating auth token. Make sure correct 
certificates are configured.", e);

Review comment:
       Generally not a fan of both logging and throwing, as that likely means 
that we log twice.

##########
File path: solr/core/src/java/org/apache/solr/security/JWTAuthPlugin.java
##########
@@ -155,8 +175,38 @@ public void init(Map<String, Object> pluginConfig) {
       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);
+    String trustedCerts = (String) pluginConfig.get(PARAM_TRUSTED_CERTS);
+    if (trustedCertsFile != null && trustedCerts != null) {
+      throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, "Found 
both " + PARAM_TRUSTED_CERTS_FILE + " and " + 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 (trustedCerts != null) {
+      trustedCertsStream = IOUtils.toInputStream(trustedCerts, 
StandardCharsets.UTF_8);

Review comment:
       I'm amazed that this doesn't exist in the JDK. We have StringStream in 
ContentStreamBase that is similar, maybe we can create something for the common 
need.

##########
File path: 
solr/core/src/test/org/apache/solr/security/JWTAuthPluginIntegrationTest.java
##########
@@ -70,54 +89,43 @@
   protected static final int NUM_SERVERS = 2;
   protected static final int NUM_SHARDS = 2;
   protected static final int REPLICATION_FACTOR = 1;
+  private static String mockOAuthToken;
+  private static Path pemFilePath;
+  private static Path wrongPemFilePath;
   private final String COLLECTION = "jwtColl";
-  private String jwtTestToken;
+  private static String jwtStaticTestToken;
   private String baseUrl;
-  private JsonWebSignature jws;
-  private String jwtTokenWrongSignature;
+  private static JsonWebSignature jws;
+  private static String jwtTokenWrongSignature;
 
-  @Override
-  @Before
-  public void setUp() throws Exception {
-    super.setUp();
-    
-    configureCluster(NUM_SERVERS)// nodes
-        
.withSecurityJson(TEST_PATH().resolve("security").resolve("jwt_plugin_jwk_security.json"))
-        .addConfig("conf1", 
TEST_PATH().resolve("configsets").resolve("cloud-minimal").resolve("conf"))
-        .withDefaultClusterProperty("useLegacyReplicaAssignment", "false")
-        .configure();
-    baseUrl = cluster.getRandomJetty(random()).getBaseUrl().toString();
+  private static MockOAuth2Server mockOAuth2Server;
 
-    String jwkJSON = "{\n" +
-        "  \"kty\": \"RSA\",\n" +
-        "  \"d\": 
\"i6pyv2z3o-MlYytWsOr3IE1olu2RXZBzjPRBNgWAP1TlLNaphHEvH5aHhe_CtBAastgFFMuP29CFhaL3_tGczkvWJkSveZQN2AHWHgRShKgoSVMspkhOt3Ghha4CvpnZ9BnQzVHnaBnHDTTTfVgXz7P1ZNBhQY4URG61DKIF-JSSClyh1xKuMoJX0lILXDYGGcjVTZL_hci4IXPPTpOJHV51-pxuO7WU5M9252UYoiYyCJ56ai8N49aKIMsqhdGuO4aWUwsGIW4oQpjtce5eEojCprYl-9rDhTwLAFoBtjy6LvkqlR2Ae5dKZYpStljBjK8PJrBvWZjXAEMDdQ8PuQ\",\n"
 +
-        "  \"e\": \"AQAB\",\n" +
-        "  \"use\": \"sig\",\n" +
-        "  \"kid\": \"test\",\n" +
-        "  \"alg\": \"RS256\",\n" +
-        "  \"n\": 
\"jeyrvOaZrmKWjyNXt0myAc_pJ1hNt3aRupExJEx1ewPaL9J9HFgSCjMrYxCB1ETO1NDyZ3nSgjZis-jHHDqBxBjRdq_t1E2rkGFaYbxAyKt220Pwgme_SFTB9MXVrFQGkKyjmQeVmOmV6zM3KK8uMdKQJ4aoKmwBcF5Zg7EZdDcKOFgpgva1Jq-FlEsaJ2xrYDYo3KnGcOHIt9_0NQeLsqZbeWYLxYni7uROFncXYV5FhSJCeR4A_rrbwlaCydGxE0ToC_9HNYibUHlkJjqyUhAgORCbNS8JLCJH8NUi5sDdIawK9GTSyvsJXZ-QHqo4cMUuxWV5AJtaRGghuMUfqQ\"\n"
 +
-        "}";
+  @BeforeClass
+  public static void beforeClass() throws Exception {
+    // Setup an OAuth2 mock server with SSL
+    Path p12Cert = 
SolrTestCaseJ4.TEST_PATH().resolve("security").resolve("jwt_plugin_idp_certs.p12");
+    pemFilePath = 
SolrTestCaseJ4.TEST_PATH().resolve("security").resolve("jwt_plugin_idp_cert.pem");
+    wrongPemFilePath = 
SolrTestCaseJ4.TEST_PATH().resolve("security").resolve("jwt_plugin_idp_wrongcert.pem");
 
-    PublicJsonWebKey jwk = RsaJsonWebKey.Factory.newPublicJwk(jwkJSON);
-    JwtClaims claims = JWTAuthPluginTest.generateClaims();
-    jws = new JsonWebSignature();
-    jws.setPayload(claims.toJson());
-    jws.setKey(jwk.getPrivateKey());
-    jws.setKeyIdHeaderValue(jwk.getKeyId());
-    jws.setAlgorithmHeaderValue(AlgorithmIdentifiers.RSA_USING_SHA256);
+    mockOAuth2Server = createMockOAuthServer(p12Cert,"secret");
+    mockOAuth2Server.start();
+    mockOAuthToken = mockOAuth2Server.issueToken("default",
+        "myClientId",
+        new DefaultOAuth2TokenCallback()).serialize();
+    initStaticJwt();
+  }
 
-    jwtTestToken = jws.getCompactSerialization();
-    
-    PublicJsonWebKey jwk2 = RsaJwkGenerator.generateJwk(2048);
-    jwk2.setKeyId("k2");
-    JsonWebSignature jws2 = new JsonWebSignature();
-    jws2.setPayload(claims.toJson());
-    jws2.setKey(jwk2.getPrivateKey());
-    jws2.setKeyIdHeaderValue(jwk2.getKeyId());
-    jws2.setAlgorithmHeaderValue(AlgorithmIdentifiers.RSA_USING_SHA256);
-    jwtTokenWrongSignature = jws2.getCompactSerialization();
+  @AfterClass
+  public static void afterClass() throws Exception {
+    if (mockOAuth2Server != null) {
+      mockOAuth2Server.shutdown();
+    }
+  }
 
-    cluster.waitForAllNodes(10);
+  @Override
+  @Before
+  public void setUp() throws Exception {
+    super.setUp();

Review comment:
       if all it does it call super, then we don't need this method.

##########
File path: solr/solr-ref-guide/src/jwt-authentication-plugin.adoc
##########
@@ -158,12 +161,18 @@ Let's comment on this config:
 <11> Configure the issuer id. Will be used for validating tokens. A token's 
'iss' claim must match one of the configured issuer IDs.
 <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
+<14> Provides SSL certificates to trust to support self-signed SSL in the 
issuers.
 
 === 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`.
 
+=== Trusting the IdP server
+All communication with issuer server (IdP) is done over HTTPS. If the issuer 
uses a self signed SSL certificate, the connection will fail since the plugin 
enforces verification of SSL certificates on outbound traffic. By default, the 
root certificates present in Java's TrustStore (default or custom provided) are 
consulted. If you do not want to place the IdP certificates in Java's 
TrustStore, you can configure this plugin with either `trustedCertsFile` or 
`trustedCerts` options to provide a set of certificates to use when talking to 
IdP. This has the extra benefit of also working even if Solr is not started in 
SSL mode.

Review comment:
       I like this comment.




-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
[email protected]



---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to