You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nifi.apache.org by ex...@apache.org on 2022/02/18 20:22:39 UTC
[nifi] branch main updated: NIFI-7333 Added OIDC trust store strategy property
This is an automated email from the ASF dual-hosted git repository.
exceptionfactory pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/nifi.git
The following commit(s) were added to refs/heads/main by this push:
new 7ef2fd2 NIFI-7333 Added OIDC trust store strategy property
7ef2fd2 is described below
commit 7ef2fd2986461b632e398b7ddff47db153c3c0e5
Author: Nathan Gough <th...@gmail.com>
AuthorDate: Mon Feb 7 21:46:46 2022 -0500
NIFI-7333 Added OIDC trust store strategy property
This closes #5753
Signed-off-by: David Handermann <ex...@apache.org>
---
.../java/org/apache/nifi/util/NiFiProperties.java | 6 +++
.../src/main/asciidoc/administration-guide.adoc | 1 +
.../nifi-framework/nifi-resources/pom.xml | 1 +
.../src/main/resources/conf/nifi.properties | 1 +
.../apache/nifi/web/api/OIDCAccessResource.java | 49 ++++++++++++------
.../oidc/StandardOidcIdentityProvider.java | 59 +++++++++++++++++-----
.../nifi/web/security/oidc/TruststoreStrategy.java | 33 ++++++++++++
7 files changed, 122 insertions(+), 28 deletions(-)
diff --git a/nifi-commons/nifi-properties/src/main/java/org/apache/nifi/util/NiFiProperties.java b/nifi-commons/nifi-properties/src/main/java/org/apache/nifi/util/NiFiProperties.java
index 3ed2801..6533dec 100644
--- a/nifi-commons/nifi-properties/src/main/java/org/apache/nifi/util/NiFiProperties.java
+++ b/nifi-commons/nifi-properties/src/main/java/org/apache/nifi/util/NiFiProperties.java
@@ -188,6 +188,7 @@ public class NiFiProperties extends ApplicationProperties {
public static final String SECURITY_USER_OIDC_READ_TIMEOUT = "nifi.security.user.oidc.read.timeout";
public static final String SECURITY_USER_OIDC_CLIENT_ID = "nifi.security.user.oidc.client.id";
public static final String SECURITY_USER_OIDC_CLIENT_SECRET = "nifi.security.user.oidc.client.secret";
+ public static final String SECURITY_USER_OIDC_TRUSTSTORE_STRATEGY = "nifi.security.user.oidc.truststore.strategy";
public static final String SECURITY_USER_OIDC_PREFERRED_JWSALGORITHM = "nifi.security.user.oidc.preferred.jwsalgorithm";
public static final String SECURITY_USER_OIDC_ADDITIONAL_SCOPES = "nifi.security.user.oidc.additional.scopes";
public static final String SECURITY_USER_OIDC_CLAIM_IDENTIFYING_USER = "nifi.security.user.oidc.claim.identifying.user";
@@ -369,6 +370,7 @@ public class NiFiProperties extends ApplicationProperties {
public static final String DEFAULT_FLOW_CONFIGURATION_ARCHIVE_MAX_STORAGE = "500 MB";
public static final String DEFAULT_SECURITY_USER_OIDC_CONNECT_TIMEOUT = "5 secs";
public static final String DEFAULT_SECURITY_USER_OIDC_READ_TIMEOUT = "5 secs";
+ public static final String DEFAULT_SECURITY_USER_OIDC_TRUSTSTORE_STRATEGY = "JDK";
public static final String DEFAULT_SECURITY_USER_SAML_METADATA_SIGNING_ENABLED = "false";
public static final String DEFAULT_SECURITY_USER_SAML_REQUEST_SIGNING_ENABLED = "false";
public static final String DEFAULT_SECURITY_USER_SAML_WANT_ASSERTIONS_SIGNED = "true";
@@ -1121,6 +1123,10 @@ public class NiFiProperties extends ApplicationProperties {
}
}
+ public String getOidcClientTruststoreStrategy() {
+ return getProperty(SECURITY_USER_OIDC_TRUSTSTORE_STRATEGY, DEFAULT_SECURITY_USER_OIDC_TRUSTSTORE_STRATEGY);
+ }
+
public boolean shouldSendServerVersion() {
return Boolean.parseBoolean(getProperty(WEB_SHOULD_SEND_SERVER_VERSION, DEFAULT_WEB_SHOULD_SEND_SERVER_VERSION));
}
diff --git a/nifi-docs/src/main/asciidoc/administration-guide.adoc b/nifi-docs/src/main/asciidoc/administration-guide.adoc
index b13961f..6501c27 100644
--- a/nifi-docs/src/main/asciidoc/administration-guide.adoc
+++ b/nifi-docs/src/main/asciidoc/administration-guide.adoc
@@ -448,6 +448,7 @@ JSON Web Key (JWK) provided through the jwks_uri in the metadata found at the di
|`nifi.security.user.oidc.additional.scopes` | Comma separated scopes that are sent to OpenId Connect Provider in addition to `openid` and `email`.
|`nifi.security.user.oidc.claim.identifying.user` | Claim that identifies the user to be logged in; default is `email`. May need to be requested via the `nifi.security.user.oidc.additional.scopes` before usage.
|`nifi.security.user.oidc.fallback.claims.identifying.user` | Comma separated possible fallback claims used to identify the user in case `nifi.security.user.oidc.claim.identifying.user` claim is not present for the login user.
+|`nifi.security.user.oidc.truststore.strategy` | If value is `NIFI`, use the NiFi truststore when connecting to the OIDC service, otherwise if value is `JDK` use Java's default `cacerts` truststore. The default value is `JDK`.
|==================================================================================================================================================
[[saml]]
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-resources/pom.xml b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-resources/pom.xml
index 7e9747c..df08858 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-resources/pom.xml
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-resources/pom.xml
@@ -169,6 +169,7 @@
<nifi.security.user.oidc.additional.scopes />
<nifi.security.user.oidc.claim.identifying.user />
<nifi.security.user.oidc.fallback.claims.identifying.user />
+ <nifi.security.user.oidc.truststore.strategy>JDK</nifi.security.user.oidc.truststore.strategy>
<!-- nifi.properties: apache knox -->
<nifi.security.user.knox.url />
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-resources/src/main/resources/conf/nifi.properties b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-resources/src/main/resources/conf/nifi.properties
index 0b90803..f260928 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-resources/src/main/resources/conf/nifi.properties
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-resources/src/main/resources/conf/nifi.properties
@@ -200,6 +200,7 @@ nifi.security.user.oidc.preferred.jwsalgorithm=${nifi.security.user.oidc.preferr
nifi.security.user.oidc.additional.scopes=${nifi.security.user.oidc.additional.scopes}
nifi.security.user.oidc.claim.identifying.user=${nifi.security.user.oidc.claim.identifying.user}
nifi.security.user.oidc.fallback.claims.identifying.user=${nifi.security.user.oidc.fallback.claims.identifying.user}
+nifi.security.user.oidc.truststore.strategy=${nifi.security.user.oidc.truststore.strategy}
# Apache Knox SSO Properties #
nifi.security.user.knox.url=${nifi.security.user.knox.url}
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/OIDCAccessResource.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/OIDCAccessResource.java
index cc952a7..89abed0 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/OIDCAccessResource.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/OIDCAccessResource.java
@@ -38,16 +38,21 @@ import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.message.BasicNameValuePair;
import org.apache.nifi.authentication.exception.AuthenticationNotSupportedException;
import org.apache.nifi.authorization.user.NiFiUserUtils;
+import org.apache.nifi.security.util.SslContextFactory;
+import org.apache.nifi.security.util.StandardTlsConfiguration;
+import org.apache.nifi.security.util.TlsConfiguration;
+import org.apache.nifi.security.util.TlsException;
import org.apache.nifi.util.NiFiProperties;
import org.apache.nifi.web.api.cookie.ApplicationCookieName;
import org.apache.nifi.web.security.jwt.provider.BearerTokenProvider;
import org.apache.nifi.web.security.oidc.OIDCEndpoints;
import org.apache.nifi.web.security.oidc.OidcService;
+import org.apache.nifi.web.security.oidc.TruststoreStrategy;
import org.apache.nifi.web.security.token.LoginAuthenticationToken;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import javax.annotation.PreDestroy;
+import javax.net.ssl.SSLContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.ws.rs.Consumes;
@@ -89,19 +94,9 @@ public class OIDCAccessResource extends ApplicationResource {
private OidcService oidcService;
private BearerTokenProvider bearerTokenProvider;
- private final CloseableHttpClient httpClient;
public OIDCAccessResource() {
- RequestConfig config = RequestConfig.custom()
- .setConnectTimeout(msTimeout)
- .setConnectionRequestTimeout(msTimeout)
- .setSocketTimeout(msTimeout)
- .build();
- httpClient = HttpClientBuilder
- .create()
- .setDefaultRequestConfig(config)
- .build();
}
@GET
@@ -437,7 +432,7 @@ public class OIDCAccessResource extends ApplicationResource {
* @throws IOException exceptional case for communication error with the OpenId Connect Provider
*/
private void revokeEndpointRequest(@Context HttpServletResponse httpServletResponse, String accessToken, URI revokeEndpoint) throws IOException {
-
+ final CloseableHttpClient httpClient = getHttpClient();
HttpPost httpPost = new HttpPost(revokeEndpoint);
List<NameValuePair> params = new ArrayList<>();
@@ -455,12 +450,27 @@ public class OIDCAccessResource extends ApplicationResource {
logger.error("There was an error logging out of the OpenId Connect Provider. " +
"Response status: " + response.getStatusLine().getStatusCode());
}
+ } finally {
+ httpClient.close();
}
}
- @PreDestroy
- public void closeClient() throws IOException {
- httpClient.close();
+ private CloseableHttpClient getHttpClient() {
+ RequestConfig config = RequestConfig.custom()
+ .setConnectTimeout(msTimeout)
+ .setConnectionRequestTimeout(msTimeout)
+ .setSocketTimeout(msTimeout)
+ .build();
+
+ HttpClientBuilder builder = HttpClientBuilder
+ .create()
+ .setDefaultRequestConfig(config);
+
+ if (TruststoreStrategy.NIFI.name().equals(properties.getOidcClientTruststoreStrategy())) {
+ builder.setSSLContext(getSslContext());
+ }
+
+ return builder.build();
}
private AuthenticationResponse parseOidcResponse(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, boolean isLogin) throws Exception {
@@ -515,6 +525,15 @@ public class OIDCAccessResource extends ApplicationResource {
}
}
+ private SSLContext getSslContext() {
+ TlsConfiguration tlsConfiguration = StandardTlsConfiguration.fromNiFiProperties(properties);
+ try {
+ return SslContextFactory.createSslContext(tlsConfiguration);
+ } catch (TlsException e) {
+ throw new RuntimeException("Unable to establish an SSL context for OIDC access resource from nifi.properties", e);
+ }
+ }
+
private String getForwardPageTitle(boolean isLogin) {
return isLogin ? ApplicationResource.LOGIN_ERROR_TITLE : ApplicationResource.LOGOUT_ERROR_TITLE;
}
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/oidc/StandardOidcIdentityProvider.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/oidc/StandardOidcIdentityProvider.java
index eaadd55..09fa79e 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/oidc/StandardOidcIdentityProvider.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/oidc/StandardOidcIdentityProvider.java
@@ -53,6 +53,20 @@ import com.nimbusds.openid.connect.sdk.token.OIDCTokens;
import com.nimbusds.openid.connect.sdk.validators.AccessTokenValidator;
import com.nimbusds.openid.connect.sdk.validators.IDTokenValidator;
import com.nimbusds.openid.connect.sdk.validators.InvalidHashException;
+import net.minidev.json.JSONObject;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.nifi.authentication.exception.IdentityAccessException;
+import org.apache.nifi.security.util.SslContextFactory;
+import org.apache.nifi.security.util.StandardTlsConfiguration;
+import org.apache.nifi.security.util.TlsConfiguration;
+import org.apache.nifi.security.util.TlsException;
+import org.apache.nifi.util.FormatUtils;
+import org.apache.nifi.util.NiFiProperties;
+import org.apache.nifi.web.security.token.LoginAuthenticationToken;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.net.ssl.SSLContext;
import java.io.IOException;
import java.net.URI;
import java.net.URL;
@@ -63,14 +77,6 @@ import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
-import net.minidev.json.JSONObject;
-import org.apache.commons.lang3.StringUtils;
-import org.apache.nifi.authentication.exception.IdentityAccessException;
-import org.apache.nifi.util.FormatUtils;
-import org.apache.nifi.util.NiFiProperties;
-import org.apache.nifi.web.security.token.LoginAuthenticationToken;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
/**
@@ -88,6 +94,7 @@ public class StandardOidcIdentityProvider implements OidcIdentityProvider {
private IDTokenValidator tokenValidator;
private ClientID clientId;
private Secret clientSecret;
+ private SSLContext sslContext;
/**
* Creates a new StandardOidcIdentityProvider.
@@ -110,6 +117,11 @@ public class StandardOidcIdentityProvider implements OidcIdentityProvider {
return;
}
+ // Set up trust store SSLContext
+ if (TruststoreStrategy.NIFI.name().equals(properties.getOidcClientTruststoreStrategy())) {
+ setSslContext();
+ }
+
validateOIDCConfiguration();
try {
@@ -122,6 +134,15 @@ public class StandardOidcIdentityProvider implements OidcIdentityProvider {
validateOIDCProviderMetadata();
}
+ private void setSslContext() {
+ TlsConfiguration tlsConfiguration = StandardTlsConfiguration.fromNiFiProperties(properties);
+ try {
+ this.sslContext = SslContextFactory.createSslContext(tlsConfiguration);
+ } catch (TlsException e) {
+ throw new RuntimeException("Unable to establish an SSL context for OIDC identity provider from nifi.properties", e);
+ }
+ }
+
/**
* Validates the retrieved OIDC provider metadata.
*/
@@ -166,7 +187,7 @@ public class StandardOidcIdentityProvider implements OidcIdentityProvider {
} else if (JWSAlgorithm.HS256.equals(preferredJwsAlgorithm) || JWSAlgorithm.HS384.equals(preferredJwsAlgorithm) || JWSAlgorithm.HS512.equals(preferredJwsAlgorithm)) {
tokenValidator = new IDTokenValidator(oidcProviderMetadata.getIssuer(), clientId, preferredJwsAlgorithm, clientSecret);
} else {
- final ResourceRetriever retriever = new DefaultResourceRetriever(oidcConnectTimeout, oidcReadTimeout);
+ final ResourceRetriever retriever = getResourceRetriever();
tokenValidator = new IDTokenValidator(oidcProviderMetadata.getIssuer(), clientId, preferredJwsAlgorithm, oidcProviderMetadata.getJWKSetURI().toURL(), retriever);
}
} catch (final Exception e) {
@@ -245,9 +266,7 @@ public class StandardOidcIdentityProvider implements OidcIdentityProvider {
private OIDCProviderMetadata retrieveOidcProviderMetadata(final String discoveryUri) throws IOException, ParseException {
final URL url = new URL(discoveryUri);
final HTTPRequest httpRequest = new HTTPRequest(HTTPRequest.Method.GET, url);
- httpRequest.setConnectTimeout(oidcConnectTimeout);
- httpRequest.setReadTimeout(oidcReadTimeout);
-
+ setHttpRequestProperties(httpRequest);
final HTTPResponse httpResponse = httpRequest.send();
if (httpResponse.getStatusCode() != 200) {
@@ -485,12 +504,26 @@ public class StandardOidcIdentityProvider implements OidcIdentityProvider {
}
private HTTPRequest formHTTPRequest(Request request) {
- final HTTPRequest httpRequest = request.toHTTPRequest();
+ return setHttpRequestProperties(request.toHTTPRequest());
+ }
+
+ private HTTPRequest setHttpRequestProperties(final HTTPRequest httpRequest) {
httpRequest.setConnectTimeout(oidcConnectTimeout);
httpRequest.setReadTimeout(oidcReadTimeout);
+ if (TruststoreStrategy.NIFI.name().equals(properties.getOidcClientTruststoreStrategy())) {
+ httpRequest.setSSLSocketFactory(sslContext.getSocketFactory());
+ }
return httpRequest;
}
+ private ResourceRetriever getResourceRetriever() {
+ if (TruststoreStrategy.NIFI.name().equals(properties.getOidcClientTruststoreStrategy())) {
+ return new DefaultResourceRetriever(oidcConnectTimeout, oidcReadTimeout, 0, true, sslContext.getSocketFactory());
+ } else {
+ return new DefaultResourceRetriever(oidcConnectTimeout, oidcReadTimeout);
+ }
+ }
+
private ClientAuthentication createClientAuthentication() {
final ClientAuthentication clientAuthentication;
List<ClientAuthenticationMethod> authMethods = oidcProviderMetadata.getTokenEndpointAuthMethods();
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/oidc/TruststoreStrategy.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/oidc/TruststoreStrategy.java
new file mode 100644
index 0000000..43e1606
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/oidc/TruststoreStrategy.java
@@ -0,0 +1,33 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.nifi.web.security.oidc;
+
+/**
+ * Indicates which truststore should be used when creating an HttpClient for an https URL.
+ */
+public enum TruststoreStrategy {
+
+ /**
+ * Use the JDK truststore.
+ */
+ JDK,
+
+ /**
+ * Use NiFi's truststore specified in nifi.properties.
+ */
+ NIFI;
+}