NIFI-13297 Removed Kerberos SPENGO Authentication

This closes #8879

Signed-off-by: Joseph Witt <joewitt@apache.org>
This commit is contained in:
exceptionfactory 2024-05-24 15:58:36 -05:00 committed by Joseph Witt
parent de11b6c43e
commit 43cc2b4aaa
No known key found for this signature in database
GPG Key ID: 9093BF854F811A1A
23 changed files with 14 additions and 506 deletions

View File

@ -281,9 +281,6 @@ public class NiFiProperties extends ApplicationProperties {
public static final String KERBEROS_KRB5_FILE = "nifi.kerberos.krb5.file";
public static final String KERBEROS_SERVICE_PRINCIPAL = "nifi.kerberos.service.principal";
public static final String KERBEROS_SERVICE_KEYTAB_LOCATION = "nifi.kerberos.service.keytab.location";
public static final String KERBEROS_SPNEGO_PRINCIPAL = "nifi.kerberos.spnego.principal";
public static final String KERBEROS_SPNEGO_KEYTAB_LOCATION = "nifi.kerberos.spnego.keytab.location";
public static final String KERBEROS_AUTHENTICATION_EXPIRATION = "nifi.kerberos.spnego.authentication.expiration";
// state management
public static final String STATE_MANAGEMENT_CONFIG_FILE = "nifi.state.management.configuration.file";
@ -982,43 +979,6 @@ public class NiFiProperties extends ApplicationProperties {
}
}
public String getKerberosSpnegoPrincipal() {
final String spengoPrincipal = getProperty(KERBEROS_SPNEGO_PRINCIPAL);
if (!StringUtils.isBlank(spengoPrincipal)) {
return spengoPrincipal.trim();
} else {
return null;
}
}
public String getKerberosSpnegoKeytabLocation() {
final String keytabLocation = getProperty(KERBEROS_SPNEGO_KEYTAB_LOCATION);
if (!StringUtils.isBlank(keytabLocation)) {
return keytabLocation.trim();
} else {
return null;
}
}
public String getKerberosAuthenticationExpiration() {
final String authenticationExpirationString = getProperty(KERBEROS_AUTHENTICATION_EXPIRATION, DEFAULT_KERBEROS_AUTHENTICATION_EXPIRATION);
if (!StringUtils.isBlank(authenticationExpirationString)) {
return authenticationExpirationString.trim();
} else {
return null;
}
}
/**
* Returns true if the Kerberos service principal and keytab location
* properties are populated.
*
* @return true if Kerberos service support is enabled
*/
public boolean isKerberosSpnegoSupportEnabled() {
return !StringUtils.isBlank(getKerberosSpnegoPrincipal()) && !StringUtils.isBlank(getKerberosSpnegoKeytabLocation());
}
/**
* Returns true if the login identity provider has been configured.
*
@ -1402,7 +1362,6 @@ public class NiFiProperties extends ApplicationProperties {
*/
public boolean isClientAuthRequiredForRestApi() {
return !isLoginIdentityProviderEnabled()
&& !isKerberosSpnegoSupportEnabled()
&& !isOidcEnabled()
&& !isKnoxSsoEnabled()
&& !isSamlEnabled()

View File

@ -3176,57 +3176,6 @@ link:https://nginx.org/[Nginx] supports session affinity in the upstream module
link:https://nginx.org/en/docs/http/ngx_http_upstream_module.html#sticky[sticky] directive. The *sticky* directive
supports different strategies, including *cookie* and *route* options.
[[kerberos_service]]
== Kerberos Service
NiFi can be configured to use Kerberos SPNEGO (or "Kerberos Service") for authentication. In this scenario, users will hit the REST endpoint `/access/kerberos` and the server will respond with a `401` status code and the challenge response header `WWW-Authenticate: Negotiate`. This communicates to the browser to use the GSS-API and load the user's Kerberos ticket and provide it as a Base64-encoded header value in the subsequent request. It will be of the form `Authorization: Negotiate YII...`. NiFi will attempt to validate this ticket with the KDC. If it is successful, the user's _principal_ will be returned as the identity, and the flow will follow login/credential authentication, in that a JWT will be issued in the response to prevent the unnecessary overhead of Kerberos authentication on every subsequent request. If the ticket cannot be validated, it will return with the appropriate error response code. The user will then be able to provide their Kerberos credentials to the login form if the `KerberosLoginIdentityProvider` has been configured. See <<kerberos_login_identity_provider>> login identity provider for more details.
NiFi will only respond to Kerberos SPNEGO negotiation over an HTTPS connection, as unsecured requests are never authenticated.
The following properties must be set in _nifi.properties_ to enable Kerberos service authentication.
|====
|*Property*|*Required*|*Description*
|`Service Principal`|true|The service principal used by NiFi to communicate with the KDC
|`Keytab Location`|true|The file path to the keytab containing the service principal
|====
See <<kerberos_properties>> for complete documentation.
[[kerberos_service_notes]]
=== Notes
* Kerberos is case-sensitive in many places and the error messages (or lack thereof) may not be sufficiently explanatory. Check the case sensitivity of the service principal in your configuration files. Convention is `HTTP/fully.qualified.domain@REALM`.
* Browsers have varying levels of restriction when dealing with SPNEGO negotiations. Some will provide the local Kerberos ticket to any domain that requests it, while others explicitly specify the trusted domains in advance via an allow list. See link:http://docs.spring.io/autorepo/docs/spring-security-kerberos/1.0.2.BUILD-SNAPSHOT/reference/htmlsingle/#browserspnegoconfig[Spring Security Kerberos - Reference Documentation: Appendix E. Configure browsers for SPNEGO Negotiation^] for common browsers.
* Some browsers (legacy IE) do not support recent encryption algorithms such as AES, and are restricted to legacy algorithms (DES). This should be noted when generating keytabs.
* The KDC must be configured and a service principal defined for NiFi and a keytab exported. Comprehensive instructions for Kerberos server configuration and administration are beyond the scope of this document (see link:http://web.mit.edu/kerberos/krb5-current/doc/admin/index.html[MIT Kerberos Admin Guide^]), but an example is below:
Adding a service principal for a server at `nifi.nifi.apache.org` and exporting the keytab from the KDC:
....
root@kdc:/etc/krb5kdc# kadmin.local
Authenticating as principal admin/admin@NIFI.APACHE.ORG with password.
kadmin.local: listprincs
K/M@NIFI.APACHE.ORG
admin/admin@NIFI.APACHE.ORG
...
kadmin.local: addprinc -randkey HTTP/nifi.nifi.apache.org
WARNING: no policy specified for HTTP/nifi.nifi.apache.org@NIFI.APACHE.ORG; defaulting to no policy
Principal "HTTP/nifi.nifi.apache.org@NIFI.APACHE.ORG" created.
kadmin.local: ktadd -k /http-nifi.keytab HTTP/nifi.nifi.apache.org
Entry for principal HTTP/nifi.nifi.apache.org with kvno 2, encryption type des3-cbc-sha1 added to keytab WRFILE:/http-nifi.keytab.
Entry for principal HTTP/nifi.nifi.apache.org with kvno 2, encryption type des-cbc-crc added to keytab WRFILE:/http-nifi.keytab.
kadmin.local: listprincs
HTTP/nifi.nifi.apache.org@NIFI.APACHE.ORG
K/M@NIFI.APACHE.ORG
admin/admin@NIFI.APACHE.ORG
...
kadmin.local: q
root@kdc:~# ll /http*
-rw------- 1 root root 162 Mar 14 21:43 /http-nifi.keytab
root@kdc:~#
....
[[analytics_framework]]
== Analytics Framework
NiFi has an internal analytics framework which can be enabled to predict back pressure occurrence, given the configured settings for threshold on a queue. The model used by default for prediction is an ordinary least squares (OLS) linear regression. It uses recent observations from a queue (either number of objects or content size over time) and calculates a regression line for that data. The line's equation is then used to determine the next value that will be reached within a given time interval (e.g. number of objects in queue in the next 5 minutes). Below is an example graph of the linear regression model for Queue/Object Count over time which is used for predictions:
@ -4142,18 +4091,13 @@ Changing this property *requires* setting `jute.maxbuffer` on ZooKeeper servers.
|====
|*Property*|*Description*
|`nifi.kerberos.krb5.file`*|The location of the krb5 file, if used. It is blank by default. At this time, only a single krb5 file is allowed to
be specified per NiFi instance, so this property is configured here to support SPNEGO and service principals rather than in individual Processors.
be specified per NiFi instance, so this property is configured here to support service principals rather than in individual Processors.
If necessary the krb5 file can support multiple realms.
Example: `/etc/krb5.conf`
|`nifi.kerberos.service.principal`*|The name of the NiFi Kerberos service principal, if used. It is blank by default. Note that this property is for NiFi to authenticate as a client other systems.
Example: `nifi/nifi.example.com` or `nifi/nifi.example.com@EXAMPLE.COM`
|`nifi.kerberos.service.keytab.location`*|The file path of the NiFi Kerberos keytab, if used. It is blank by default. Note that this property is for NiFi to authenticate as a client other systems.
Example: `/etc/nifi.keytab`
|`nifi.kerberos.spnego.principal`*|The name of the NiFi Kerberos service principal, if used. It is blank by default. Note that this property is used to authenticate NiFi users.
Example: `HTTP/nifi.example.com` or `HTTP/nifi.example.com@EXAMPLE.COM`
|`nifi.kerberos.spnego.keytab.location`*|The file path of the NiFi Kerberos keytab, if used. It is blank by default. Note that this property is used to authenticate NiFi users.
Example: `/etc/http-nifi.keytab`
|`nifi.kerberos.spengo.authentication.expiration`*|The expiration duration of a successful Kerberos user authentication, if used. The default value is `12 hours`.
|====
[[analytics_properties]]

View File

@ -177,8 +177,3 @@ nifi.kerberos.krb5.file=
# kerberos service principal #
nifi.kerberos.service.principal=
nifi.kerberos.service.keytab.location=
# kerberos spnego principal #
nifi.kerberos.spnego.principal=
nifi.kerberos.spnego.keytab.location=
nifi.kerberos.spnego.authentication.expiration=12 hours

View File

@ -177,8 +177,3 @@ nifi.kerberos.krb5.file=
# kerberos service principal #
nifi.kerberos.service.principal=
nifi.kerberos.service.keytab.location=
# kerberos spnego principal #
nifi.kerberos.spnego.principal=
nifi.kerberos.spnego.keytab.location=
nifi.kerberos.spnego.authentication.expiration=12 hours

View File

@ -241,9 +241,6 @@
<nifi.kerberos.krb5.file> </nifi.kerberos.krb5.file>
<nifi.kerberos.service.principal />
<nifi.kerberos.service.keytab.location />
<nifi.kerberos.spnego.principal />
<nifi.kerberos.spnego.keytab.location />
<nifi.kerberos.spnego.authentication.expiration>12 hours</nifi.kerberos.spnego.authentication.expiration>
<!-- nifi.properties: analytics properties -->
<nifi.analytics.predict.enabled>false</nifi.analytics.predict.enabled>

View File

@ -321,11 +321,6 @@ nifi.kerberos.krb5.file=${nifi.kerberos.krb5.file}
nifi.kerberos.service.principal=${nifi.kerberos.service.principal}
nifi.kerberos.service.keytab.location=${nifi.kerberos.service.keytab.location}
# kerberos spnego principal #
nifi.kerberos.spnego.principal=${nifi.kerberos.spnego.principal}
nifi.kerberos.spnego.keytab.location=${nifi.kerberos.spnego.keytab.location}
nifi.kerberos.spnego.authentication.expiration=${nifi.kerberos.spnego.authentication.expiration}
# analytics properties #
nifi.analytics.predict.enabled=${nifi.analytics.predict.enabled}
nifi.analytics.predict.interval=${nifi.analytics.predict.interval}

View File

@ -556,12 +556,6 @@
<version>2.0.0-SNAPSHOT</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.security.kerberos</groupId>
<artifactId>spring-security-kerberos-core</artifactId>
<version>1.0.1.RELEASE</version>
<scope>provided</scope> <!-- expected to be provided by parent classloader -->
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>

View File

@ -23,7 +23,6 @@ import java.time.Instant;
import java.util.Collections;
import java.util.Optional;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.media.Content;
@ -57,7 +56,6 @@ import org.apache.nifi.authorization.user.NiFiUser;
import org.apache.nifi.authorization.user.NiFiUserDetails;
import org.apache.nifi.authorization.user.NiFiUserUtils;
import org.apache.nifi.authorization.util.IdentityMappingUtil;
import org.apache.nifi.util.FormatUtils;
import org.apache.nifi.web.api.dto.AccessConfigurationDTO;
import org.apache.nifi.web.api.dto.AccessStatusDTO;
import org.apache.nifi.web.api.dto.AccessTokenExpirationDTO;
@ -71,7 +69,6 @@ import org.apache.nifi.web.security.UntrustedProxyException;
import org.apache.nifi.web.security.cookie.ApplicationCookieName;
import org.apache.nifi.web.security.jwt.provider.BearerTokenProvider;
import org.apache.nifi.web.security.jwt.revocation.JwtLogoutListener;
import org.apache.nifi.web.security.kerberos.KerberosService;
import org.apache.nifi.web.security.knox.KnoxService;
import org.apache.nifi.web.security.logout.LogoutRequest;
import org.apache.nifi.web.security.logout.LogoutRequestManager;
@ -112,7 +109,6 @@ public class AccessResource extends ApplicationResource {
private BearerTokenProvider bearerTokenProvider;
private BearerTokenResolver bearerTokenResolver;
private KnoxService knoxService;
private KerberosService kerberosService;
private LogoutRequestManager logoutRequestManager;
/**
@ -296,80 +292,6 @@ public class AccessResource extends ApplicationResource {
return generateOkResponse(entity).build();
}
/**
* Creates a token for accessing the REST API via Kerberos ticket exchange / SPNEGO negotiation.
*
* @param httpServletRequest the servlet request
* @return A JWT (string)
*/
@POST
@Consumes(MediaType.TEXT_PLAIN)
@Produces(MediaType.TEXT_PLAIN)
@Path("/kerberos")
@Operation(
summary = "Creates a token for accessing the REST API via Kerberos ticket exchange / SPNEGO negotiation",
description = "The token returned is formatted as a JSON Web Token (JWT). The token is base64 encoded and comprised of three parts. The header, " +
"the body, and the signature. The expiration of the token is a contained within the body. The token can be used in the Authorization header " +
"in the format 'Authorization: Bearer <token>'. It is also stored in the browser as a cookie.",
responses = @ApiResponse(content = @Content(schema = @Schema(implementation = String.class)))
)
@ApiResponses(
value = {
@ApiResponse(responseCode = "400", description = "NiFi was unable to complete the request because it was invalid. The request should not be retried without modification."),
@ApiResponse(
responseCode = "401", description = "NiFi was unable to complete the request because it did not contain a valid Kerberos " +
"ticket in the Authorization header. Retry this request after initializing a ticket with kinit and " +
"ensuring your browser is configured to support SPNEGO."
),
@ApiResponse(responseCode = "409", description = "The request was valid but NiFi was not in the appropriate state to process it."),
@ApiResponse(responseCode = "500", description = "Unable to create access token because an unexpected error occurred.")
}
)
public Response createAccessTokenFromTicket(@Context final HttpServletRequest httpServletRequest, @Context final HttpServletResponse httpServletResponse) {
// only support access tokens when communicating over HTTPS
if (!httpServletRequest.isSecure()) {
throw new AuthenticationNotSupportedException("Access tokens are only issued over HTTPS.");
}
// If Kerberos Service Principal and keytab location not configured, throws exception
if (!properties.isKerberosSpnegoSupportEnabled() || kerberosService == null) {
final String message = "Kerberos ticket login not supported by this NiFi.";
logger.debug(message);
return Response.status(Response.Status.CONFLICT).entity(message).build();
}
String authorizationHeaderValue = httpServletRequest.getHeader(KerberosService.AUTHORIZATION_HEADER_NAME);
if (!kerberosService.isValidKerberosHeader(authorizationHeaderValue)) {
return generateNotAuthorizedResponse().header(KerberosService.AUTHENTICATION_CHALLENGE_HEADER_NAME, KerberosService.AUTHORIZATION_NEGOTIATE).build();
} else {
try {
// attempt to authenticate
Authentication authentication = kerberosService.validateKerberosTicket(httpServletRequest);
if (authentication == null) {
throw new IllegalArgumentException("Request is not HTTPS or Kerberos ticket missing or malformed");
}
final String expirationFromProperties = properties.getKerberosAuthenticationExpiration();
final long expirationDuration = Math.round(FormatUtils.getPreciseTimeDuration(expirationFromProperties, TimeUnit.MILLISECONDS));
final Instant expiration = Instant.now().plusMillis(expirationDuration);
final String rawIdentity = authentication.getName();
final String mappedIdentity = IdentityMappingUtil.mapIdentity(rawIdentity, IdentityMappingUtil.getIdentityMappings(properties));
final LoginAuthenticationToken loginAuthenticationToken = new LoginAuthenticationToken(mappedIdentity, expiration, Collections.emptySet());
final String token = bearerTokenProvider.getBearerToken(loginAuthenticationToken);
final URI uri = URI.create(generateResourceUri("access", "kerberos"));
setBearerToken(httpServletResponse, token);
return generateCreatedResponse(uri, token).build();
} catch (final AuthenticationException e) {
throw new AccessDeniedException(e.getMessage(), e);
}
}
}
/**
* Creates a token for accessing the REST API via username/password stored as a cookie in the browser.
*
@ -608,10 +530,6 @@ public class AccessResource extends ApplicationResource {
this.jwtLogoutListener = jwtLogoutListener;
}
public void setKerberosService(KerberosService kerberosService) {
this.kerberosService = kerberosService;
}
public void setX509AuthenticationProvider(X509AuthenticationProvider x509AuthenticationProvider) {
this.x509AuthenticationProvider = x509AuthenticationProvider;
}

View File

@ -616,7 +616,6 @@
<property name="jwtLogoutListener" ref="jwtLogoutListener"/>
<property name="bearerTokenProvider" ref="bearerTokenProvider"/>
<property name="bearerTokenResolver" ref="bearerTokenResolver"/>
<property name="kerberosService" ref="kerberosService"/>
<property name="properties" ref="nifiProperties"/>
<property name="clusterCoordinator" ref="clusterCoordinator"/>
<property name="requestReplicator" ref="requestReplicator" />

View File

@ -163,6 +163,3 @@ nifi.zookeeper.root.node=${nifi.zookeeper.root.node}
nifi.kerberos.krb5.file=${nifi.kerberos.krb5.file}
nifi.kerberos.service.principal=${nifi.kerberos.service.principal}
nifi.kerberos.service.keytab.location=${nifi.kerberos.service.keytab.location}
nifi.kerberos.spnego.principal=${nifi.kerberos.spnego.principal}
nifi.kerberos.spnego.keytab.location=${nifi.kerberos.spnego.keytab.location}
nifi.kerberos.spnego.authentication.expiration=${nifi.kerberos.spnego.authentication.expiration}

View File

@ -250,10 +250,6 @@
<artifactId>commons-codec</artifactId>
<version>${org.apache.commons.codec.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.security.kerberos</groupId>
<artifactId>spring-security-kerberos-core</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-oauth2-resource-server</artifactId>

View File

@ -37,7 +37,6 @@ import org.springframework.security.authentication.AuthenticationManager;
ClientRegistrationConfiguration.class,
JwtAuthenticationSecurityConfiguration.class,
JwtDecoderConfiguration.class,
KerberosAuthenticationSecurityConfiguration.class,
KnoxAuthenticationSecurityConfiguration.class,
OidcSecurityConfiguration.class,
SamlAuthenticationSecurityConfiguration.class,

View File

@ -1,46 +0,0 @@
/*
* 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.configuration;
import org.apache.nifi.util.NiFiProperties;
import org.apache.nifi.web.security.kerberos.KerberosService;
import org.apache.nifi.web.security.spring.KerberosServiceFactoryBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* Kerberos Configuration for Authentication Security
*/
@Configuration
public class KerberosAuthenticationSecurityConfiguration {
private final NiFiProperties niFiProperties;
@Autowired
public KerberosAuthenticationSecurityConfiguration(
final NiFiProperties niFiProperties
) {
this.niFiProperties = niFiProperties;
}
@Bean
public KerberosService kerberosService() throws Exception {
final KerberosServiceFactoryBean kerberosServiceFactoryBean = new KerberosServiceFactoryBean();
kerberosServiceFactoryBean.setProperties(niFiProperties);
return kerberosServiceFactoryBean.getObject();
}
}

View File

@ -115,7 +115,6 @@ public class WebSecurityConfiguration {
"/access",
"/access/config",
"/access/token",
"/access/kerberos",
"/access/knox/callback",
"/access/knox/request",
"/access/logout/complete",

View File

@ -1,32 +0,0 @@
/*
* 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.kerberos;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
/* Potential refactoring is documented in NIFI-1637 */
public class AlternateKerberosUserDetailsService implements UserDetailsService {
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
return new User(username, "notUsed", true, true, true, true, AuthorityUtils.createAuthorityList("ROLE_USER"));
}
}

View File

@ -1,77 +0,0 @@
/*
* 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.kerberos;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.authentication.AuthenticationDetailsSource;
import org.springframework.security.core.Authentication;
import org.springframework.security.kerberos.authentication.KerberosServiceAuthenticationProvider;
import org.springframework.security.kerberos.authentication.KerberosServiceRequestToken;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import jakarta.servlet.http.HttpServletRequest;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
/**
*
*/
public class KerberosService {
private static final Logger logger = LoggerFactory.getLogger(KerberosService.class);
public static final String AUTHORIZATION_HEADER_NAME = "Authorization";
public static final String AUTHENTICATION_CHALLENGE_HEADER_NAME = "WWW-Authenticate";
public static final String AUTHORIZATION_NEGOTIATE = "Negotiate";
private static final Base64.Decoder decoder = Base64.getDecoder();
private KerberosServiceAuthenticationProvider kerberosServiceAuthenticationProvider;
private final AuthenticationDetailsSource<HttpServletRequest, ?> authenticationDetailsSource = new WebAuthenticationDetailsSource();
public void setKerberosServiceAuthenticationProvider(KerberosServiceAuthenticationProvider kerberosServiceAuthenticationProvider) {
this.kerberosServiceAuthenticationProvider = kerberosServiceAuthenticationProvider;
}
public Authentication validateKerberosTicket(HttpServletRequest request) {
// Only support Kerberos login when running securely
if (!request.isSecure()) {
return null;
}
String header = request.getHeader(AUTHORIZATION_HEADER_NAME);
if (isValidKerberosHeader(header)) {
if (logger.isDebugEnabled()) {
logger.debug("Received Negotiate Header for request " + request.getRequestURL() + ": " + header);
}
byte[] base64Token = header.substring(header.indexOf(" ") + 1).getBytes(StandardCharsets.UTF_8);
byte[] kerberosTicket = decoder.decode(base64Token);
KerberosServiceRequestToken authenticationRequest = new KerberosServiceRequestToken(kerberosTicket);
authenticationRequest.setDetails(authenticationDetailsSource.buildDetails(request));
return kerberosServiceAuthenticationProvider.authenticate(authenticationRequest);
} else {
return null;
}
}
public boolean isValidKerberosHeader(String header) {
return header != null && (header.startsWith("Negotiate ") || header.startsWith("Kerberos "));
}
}

View File

@ -1,86 +0,0 @@
/*
* 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.spring;
import org.apache.nifi.util.NiFiProperties;
import org.apache.nifi.web.security.kerberos.AlternateKerberosUserDetailsService;
import org.apache.nifi.web.security.kerberos.KerberosService;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.core.io.FileSystemResource;
import org.springframework.security.kerberos.authentication.KerberosServiceAuthenticationProvider;
import org.springframework.security.kerberos.authentication.KerberosTicketValidator;
import org.springframework.security.kerberos.authentication.sun.GlobalSunJaasKerberosConfig;
import org.springframework.security.kerberos.authentication.sun.SunJaasKerberosTicketValidator;
import java.io.File;
public class KerberosServiceFactoryBean implements FactoryBean<KerberosService> {
private KerberosService kerberosService = null;
private NiFiProperties properties = null;
@Override
public KerberosService getObject() throws Exception {
if (kerberosService == null && properties.isKerberosSpnegoSupportEnabled()) {
final File krb5ConfigFile = properties.getKerberosConfigurationFile();
if (krb5ConfigFile != null) {
final GlobalSunJaasKerberosConfig krb5Config = new GlobalSunJaasKerberosConfig();
krb5Config.setKrbConfLocation(krb5ConfigFile.getAbsolutePath());
krb5Config.afterPropertiesSet();
}
kerberosService = new KerberosService();
kerberosService.setKerberosServiceAuthenticationProvider(createKerberosServiceAuthenticationProvider());
}
return kerberosService;
}
@Override
public Class<?> getObjectType() {
return KerberosService.class;
}
@Override
public boolean isSingleton() {
return true;
}
public void setProperties(NiFiProperties properties) {
this.properties = properties;
}
private KerberosServiceAuthenticationProvider createKerberosServiceAuthenticationProvider() throws Exception {
KerberosServiceAuthenticationProvider kerberosServiceAuthenticationProvider = new KerberosServiceAuthenticationProvider();
kerberosServiceAuthenticationProvider.setTicketValidator(createTicketValidator());
kerberosServiceAuthenticationProvider.setUserDetailsService(createAlternateKerberosUserDetailsService());
kerberosServiceAuthenticationProvider.afterPropertiesSet();
return kerberosServiceAuthenticationProvider;
}
private AlternateKerberosUserDetailsService createAlternateKerberosUserDetailsService() {
return new AlternateKerberosUserDetailsService();
}
private KerberosTicketValidator createTicketValidator() throws Exception {
SunJaasKerberosTicketValidator ticketValidator = new SunJaasKerberosTicketValidator();
ticketValidator.setServicePrincipal(properties.getKerberosSpnegoPrincipal());
ticketValidator.setKeyTabLocation(new FileSystemResource(properties.getKerberosSpnegoKeytabLocation()));
ticketValidator.afterPropertiesSet();
return ticketValidator;
}
}

View File

@ -115,7 +115,6 @@
accessTokenExpiration: '../nifi-api/access/token/expiration',
currentUser: '../nifi-api/flow/current-user',
controllerBulletins: '../nifi-api/flow/controller/bulletins',
kerberos: '../nifi-api/access/kerberos',
revision: '../nifi-api/flow/revision',
banners: '../nifi-api/flow/banners'
}
@ -913,7 +912,7 @@
* Initialize NiFi.
*/
init: function () {
// attempt kerberos/oidc/saml authentication
// attempt oidc/saml authentication
var ticketExchange = $.Deferred(function (deferred) {
var successfulAuthentication = function (jwt) {
// Use Expiration from JWT for tracking authentication status
@ -928,27 +927,19 @@
deferred.resolve();
} else {
$.ajax({
type: 'POST',
url: config.urls.kerberos,
dataType: 'text'
}).done(function (jwt) {
successfulAuthentication(jwt);
type: 'GET',
url: config.urls.accessTokenExpiration,
dataType: 'json'
}).done(function (accessTokenExpirationEntity) {
var accessTokenExpiration = accessTokenExpirationEntity.accessTokenExpiration;
// Convert ISO 8601 string to session expiration in seconds
var expiration = Date.parse(accessTokenExpiration.expiration);
var expirationSeconds = expiration / 1000;
var sessionExpiration = Math.round(expirationSeconds);
nfAuthorizationStorage.setToken(sessionExpiration);
deferred.resolve();
}).fail(function () {
$.ajax({
type: 'GET',
url: config.urls.accessTokenExpiration,
dataType: 'json'
}).done(function (accessTokenExpirationEntity) {
var accessTokenExpiration = accessTokenExpirationEntity.accessTokenExpiration;
// Convert ISO 8601 string to session expiration in seconds
var expiration = Date.parse(accessTokenExpiration.expiration);
var expirationSeconds = expiration / 1000;
var sessionExpiration = Math.round(expirationSeconds);
nfAuthorizationStorage.setToken(sessionExpiration);
deferred.resolve();
}).fail(function () {
deferred.reject();
});
deferred.reject();
});
}
}).promise();

View File

@ -269,11 +269,6 @@
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.security.kerberos</groupId>
<artifactId>spring-security-kerberos-core</artifactId>
<version>1.0.1.RELEASE</version>
</dependency>
<!-- Override OpenSAML to version 4 for Spring Security SAML -->
<dependency>
<groupId>org.opensaml</groupId>

View File

@ -248,8 +248,3 @@ nifi.kerberos.krb5.file=
# kerberos service principal #
nifi.kerberos.service.principal=
nifi.kerberos.service.keytab.location=
# kerberos spnego principal #
nifi.kerberos.spnego.principal=
nifi.kerberos.spnego.keytab.location=
nifi.kerberos.spnego.authentication.expiration=12 hours

View File

@ -248,8 +248,3 @@ nifi.kerberos.krb5.file=
# kerberos service principal #
nifi.kerberos.service.principal=
nifi.kerberos.service.keytab.location=
# kerberos spnego principal #
nifi.kerberos.spnego.principal=
nifi.kerberos.spnego.keytab.location=
nifi.kerberos.spnego.authentication.expiration=12 hours

View File

@ -249,8 +249,3 @@ nifi.kerberos.krb5.file=
# kerberos service principal #
nifi.kerberos.service.principal=
nifi.kerberos.service.keytab.location=
# kerberos spnego principal #
nifi.kerberos.spnego.principal=
nifi.kerberos.spnego.keytab.location=
nifi.kerberos.spnego.authentication.expiration=12 hours

View File

@ -253,12 +253,3 @@ nifi.kerberos.krb5.file=
# kerberos service principal #
nifi.kerberos.service.principal=
nifi.kerberos.service.keytab.location=
# kerberos spnego principal #
nifi.kerberos.spnego.principal=
nifi.kerberos.spnego.keytab.location=
nifi.kerberos.spnego.authentication.expiration=12 hours
# external properties files for variable registry
# supports a comma delimited list of file locations
nifi.variable.registry.properties=