mirror of https://github.com/apache/nifi.git
NIFI-7799: Relogin with Kerberos on connect exception in DBCPConnectionPool (#4519)
This commit is contained in:
parent
9370571131
commit
7e145142e1
|
@ -0,0 +1,44 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
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.
|
||||
-->
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>org.apache.nifi</groupId>
|
||||
<artifactId>nifi-extension-utils</artifactId>
|
||||
<version>1.13.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>nifi-kerberos-test-utils</artifactId>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.apache.nifi</groupId>
|
||||
<artifactId>nifi-api</artifactId>
|
||||
<version>1.13.0-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.nifi</groupId>
|
||||
<artifactId>nifi-utils</artifactId>
|
||||
<version>1.13.0-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.nifi</groupId>
|
||||
<artifactId>nifi-kerberos-credentials-service-api</artifactId>
|
||||
<version>1.13.0-SNAPSHOT</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
|
@ -0,0 +1,85 @@
|
|||
/*
|
||||
* 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.kerberos;
|
||||
|
||||
import org.apache.nifi.annotation.lifecycle.OnEnabled;
|
||||
import org.apache.nifi.components.PropertyDescriptor;
|
||||
import org.apache.nifi.controller.AbstractControllerService;
|
||||
import org.apache.nifi.controller.ConfigurationContext;
|
||||
import org.apache.nifi.expression.ExpressionLanguageScope;
|
||||
import org.apache.nifi.processor.util.StandardValidators;
|
||||
import org.apache.nifi.reporting.InitializationException;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class MockKerberosCredentialsService extends AbstractControllerService implements KerberosCredentialsService {
|
||||
|
||||
public static String DEFAULT_KEYTAB = "src/test/resources/fake.keytab";
|
||||
public static String DEFAULT_PRINCIPAL = "test@REALM.COM";
|
||||
|
||||
private volatile String keytab = DEFAULT_KEYTAB;
|
||||
private volatile String principal = DEFAULT_PRINCIPAL;
|
||||
|
||||
public static final PropertyDescriptor PRINCIPAL = new PropertyDescriptor.Builder()
|
||||
.name("Kerberos Principal")
|
||||
.description("Kerberos principal to authenticate as. Requires nifi.kerberos.krb5.file to be set in your nifi.properties")
|
||||
.addValidator(StandardValidators.NON_BLANK_VALIDATOR)
|
||||
.expressionLanguageSupported(ExpressionLanguageScope.VARIABLE_REGISTRY)
|
||||
.required(true)
|
||||
.build();
|
||||
|
||||
public static final PropertyDescriptor KEYTAB = new PropertyDescriptor.Builder()
|
||||
.name("Kerberos Keytab")
|
||||
.description("Kerberos keytab associated with the principal. Requires nifi.kerberos.krb5.file to be set in your nifi.properties")
|
||||
.addValidator(StandardValidators.FILE_EXISTS_VALIDATOR)
|
||||
.expressionLanguageSupported(ExpressionLanguageScope.VARIABLE_REGISTRY)
|
||||
.required(true)
|
||||
.build();
|
||||
|
||||
public MockKerberosCredentialsService() {
|
||||
}
|
||||
|
||||
@OnEnabled
|
||||
public void onConfigured(final ConfigurationContext context) throws InitializationException {
|
||||
keytab = context.getProperty(KEYTAB).getValue();
|
||||
principal = context.getProperty(PRINCIPAL).getValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getKeytab() {
|
||||
return keytab;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPrincipal() {
|
||||
return principal;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<PropertyDescriptor> getSupportedPropertyDescriptors() {
|
||||
final List<PropertyDescriptor> properties = new ArrayList<>(2);
|
||||
properties.add(KEYTAB);
|
||||
properties.add(PRINCIPAL);
|
||||
return properties;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getIdentifier() {
|
||||
return "kcs";
|
||||
}
|
||||
}
|
|
@ -36,6 +36,7 @@
|
|||
<module>nifi-database-test-utils</module>
|
||||
<module>nifi-service-utils</module>
|
||||
<module>nifi-prometheus-utils</module>
|
||||
<module>nifi-kerberos-test-utils</module>
|
||||
</modules>
|
||||
|
||||
</project>
|
||||
|
|
|
@ -147,6 +147,12 @@
|
|||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.nifi</groupId>
|
||||
<artifactId>nifi-kerberos-test-utils</artifactId>
|
||||
<version>1.13.0-SNAPSHOT</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<build>
|
||||
<plugins>
|
||||
|
|
|
@ -48,15 +48,11 @@ import org.apache.hive.streaming.StubSerializationError;
|
|||
import org.apache.hive.streaming.StubStreamingIOFailure;
|
||||
import org.apache.hive.streaming.StubTransactionError;
|
||||
import org.apache.nifi.avro.AvroTypeUtil;
|
||||
import org.apache.nifi.components.PropertyDescriptor;
|
||||
import org.apache.nifi.components.ValidationContext;
|
||||
import org.apache.nifi.components.ValidationResult;
|
||||
import org.apache.nifi.controller.ControllerService;
|
||||
import org.apache.nifi.controller.ControllerServiceInitializationContext;
|
||||
import org.apache.nifi.hadoop.SecurityUtil;
|
||||
import org.apache.nifi.json.JsonRecordSetWriter;
|
||||
import org.apache.nifi.json.JsonTreeReader;
|
||||
import org.apache.nifi.kerberos.KerberosCredentialsService;
|
||||
import org.apache.nifi.kerberos.MockKerberosCredentialsService;
|
||||
import org.apache.nifi.logging.ComponentLog;
|
||||
import org.apache.nifi.processor.exception.ProcessException;
|
||||
import org.apache.nifi.reporting.InitializationException;
|
||||
|
@ -96,7 +92,6 @@ import java.sql.Timestamp;
|
|||
import java.time.Instant;
|
||||
import java.util.Arrays;
|
||||
import java.util.Calendar;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
|
@ -293,6 +288,8 @@ public class TestPutHive3Streaming {
|
|||
KerberosCredentialsService kcs = new MockKerberosCredentialsService();
|
||||
runner.addControllerService("kcs", kcs);
|
||||
runner.setProperty(KERBEROS_CREDENTIALS_SERVICE, "kcs");
|
||||
runner.setProperty(kcs, MockKerberosCredentialsService.PRINCIPAL, "test");
|
||||
runner.setProperty(kcs, MockKerberosCredentialsService.KEYTAB, "src/test/resources/core-site-security.xml");
|
||||
runner.enableControllerService(kcs);
|
||||
ugi = mock(UserGroupInformation.class);
|
||||
when(hiveConfigurator.authenticate(eq(hiveConf), any(KerberosUser.class))).thenReturn(ugi);
|
||||
|
@ -313,8 +310,10 @@ public class TestPutHive3Streaming {
|
|||
runner.setProperty(PutHive3Streaming.HIVE_CONFIGURATION_RESOURCES, "src/test/resources/core-site-security.xml, src/test/resources/hive-site-security.xml");
|
||||
|
||||
hiveConf.set(SecurityUtil.HADOOP_SECURITY_AUTHENTICATION, SecurityUtil.KERBEROS);
|
||||
KerberosCredentialsService kcs = new MockKerberosCredentialsService(null, null);
|
||||
KerberosCredentialsService kcs = new MockKerberosCredentialsService();
|
||||
runner.addControllerService("kcs", kcs);
|
||||
runner.setProperty(kcs, MockKerberosCredentialsService.PRINCIPAL, "test");
|
||||
runner.setProperty(kcs, MockKerberosCredentialsService.KEYTAB, "src/test/resources/core-site-security.xml");
|
||||
runner.setProperty(KERBEROS_CREDENTIALS_SERVICE, "kcs");
|
||||
runner.enableControllerService(kcs);
|
||||
runner.assertNotValid();
|
||||
|
@ -1321,58 +1320,4 @@ public class TestPutHive3Streaming {
|
|||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static class MockKerberosCredentialsService implements KerberosCredentialsService, ControllerService {
|
||||
|
||||
private String keytab = "src/test/resources/fake.keytab";
|
||||
private String principal = "test@REALM.COM";
|
||||
|
||||
public MockKerberosCredentialsService() {
|
||||
}
|
||||
|
||||
public MockKerberosCredentialsService(String keytab, String principal) {
|
||||
this.keytab = keytab;
|
||||
this.principal = principal;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getKeytab() {
|
||||
return keytab;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPrincipal() {
|
||||
return principal;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize(ControllerServiceInitializationContext context) throws InitializationException {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<ValidationResult> validate(ValidationContext context) {
|
||||
return Collections.EMPTY_LIST;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PropertyDescriptor getPropertyDescriptor(String name) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPropertyModified(PropertyDescriptor descriptor, String oldValue, String newValue) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<PropertyDescriptor> getPropertyDescriptors() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getIdentifier() {
|
||||
return "kcs";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -114,9 +114,28 @@
|
|||
<version>1.4.192</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.nifi</groupId>
|
||||
<artifactId>nifi-kerberos-test-utils</artifactId>
|
||||
<version>1.13.0-SNAPSHOT</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.hamcrest</groupId>
|
||||
<artifactId>hamcrest-all</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.rat</groupId>
|
||||
<artifactId>apache-rat-plugin</artifactId>
|
||||
<configuration>
|
||||
<excludes combine.children="append">
|
||||
<exclude>src/test/resources/fake.keytab</exclude>
|
||||
</excludes>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
|
@ -513,6 +513,15 @@ public class DBCPConnectionPool extends AbstractControllerService implements DBC
|
|||
}
|
||||
return con;
|
||||
} catch (final SQLException e) {
|
||||
// If using Kerberos, attempt to re-login
|
||||
if (kerberosUser != null) {
|
||||
try {
|
||||
getLogger().info("Error getting connection, performing Kerberos re-login");
|
||||
kerberosUser.login();
|
||||
} catch (LoginException le) {
|
||||
throw new ProcessException("Unable to authenticate Kerberos principal", le);
|
||||
}
|
||||
}
|
||||
throw new ProcessException(e);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,6 +17,8 @@
|
|||
package org.apache.nifi.dbcp;
|
||||
|
||||
import org.apache.derby.drda.NetworkServerControl;
|
||||
import org.apache.nifi.kerberos.KerberosCredentialsService;
|
||||
import org.apache.nifi.kerberos.MockKerberosCredentialsService;
|
||||
import org.apache.nifi.processor.exception.ProcessException;
|
||||
import org.apache.nifi.reporting.InitializationException;
|
||||
import org.apache.nifi.util.TestRunner;
|
||||
|
@ -304,6 +306,42 @@ public class DBCPServiceTest {
|
|||
connection.close(); // return to pool
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that we relogin to Kerberos if a ConnectException occurs during getConnection().
|
||||
*/
|
||||
@Test(expected = ProcessException.class)
|
||||
public void testConnectExceptionCausesKerberosRelogin() throws InitializationException, SQLException {
|
||||
final TestRunner runner = TestRunners.newTestRunner(TestProcessor.class);
|
||||
final DBCPConnectionPool service = new DBCPConnectionPool();
|
||||
runner.addControllerService("test-good1", service);
|
||||
|
||||
final KerberosCredentialsService kerberosCredentialsService = new MockKerberosCredentialsService();
|
||||
runner.addControllerService("kcs", kerberosCredentialsService);
|
||||
runner.setProperty(kerberosCredentialsService, MockKerberosCredentialsService.PRINCIPAL, "bad@PRINCIPAL.COM");
|
||||
runner.setProperty(kerberosCredentialsService, MockKerberosCredentialsService.KEYTAB, "src/test/resources/fake.keytab");
|
||||
runner.enableControllerService(kerberosCredentialsService);
|
||||
|
||||
// set fake Derby database connection url
|
||||
runner.setProperty(service, DBCPConnectionPool.DATABASE_URL, "jdbc:derby://localhost:1527/NoDB");
|
||||
runner.setProperty(service, DBCPConnectionPool.DB_USER, "tester");
|
||||
runner.setProperty(service, DBCPConnectionPool.DB_PASSWORD, "testerp");
|
||||
// Use the client driver here rather than the embedded one, as it will generate a ConnectException for the test
|
||||
runner.setProperty(service, DBCPConnectionPool.DB_DRIVERNAME, "org.apache.derby.jdbc.ClientDriver");
|
||||
runner.setProperty(service, DBCPConnectionPool.KERBEROS_CREDENTIALS_SERVICE, "kcs");
|
||||
|
||||
try {
|
||||
runner.enableControllerService(service);
|
||||
} catch (AssertionError ae) {
|
||||
// Ignore, this happens because it tries to do the initial Kerberos login
|
||||
}
|
||||
|
||||
runner.assertValid(service);
|
||||
final DBCPService dbcpService = (DBCPService) runner.getProcessContext().getControllerServiceLookup().getControllerService("test-good1");
|
||||
Assert.assertNotNull(dbcpService);
|
||||
|
||||
dbcpService.getConnection();
|
||||
}
|
||||
|
||||
@Rule
|
||||
public ExpectedException exception = ExpectedException.none();
|
||||
|
||||
|
|
Loading…
Reference in New Issue