This is an automated email from the ASF dual-hosted git repository. rlevas pushed a commit to branch trunk in repository https://gitbox.apache.org/repos/asf/ambari.git
The following commit(s) were added to refs/heads/trunk by this push: new ceaf5cf [AMBARI-23921] If Kerberos is enabled, then stack upgrade prerequisite check should ensure the KDC admin credential is persisted ceaf5cf is described below commit ceaf5cfb1b79a973724bbe13596213555c5c1893 Author: Robert Levas <rle...@hortonworks.com> AuthorDate: Mon May 21 22:30:19 2018 -0400 [AMBARI-23921] If Kerberos is enabled, then stack upgrade prerequisite check should ensure the KDC admin credential is persisted --- .../ambari/server/checks/CheckDescription.java | 13 ++ .../KerberosAdminPersistedCredentialCheck.java | 104 ++++++++++ .../KerberosAdminPersistedCredentialCheckTest.java | 229 +++++++++++++++++++++ 3 files changed, 346 insertions(+) diff --git a/ambari-server/src/main/java/org/apache/ambari/server/checks/CheckDescription.java b/ambari-server/src/main/java/org/apache/ambari/server/checks/CheckDescription.java index 4ef5d50..76b8e23 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/checks/CheckDescription.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/checks/CheckDescription.java @@ -379,6 +379,19 @@ public class CheckDescription { "Hence need to migrate existing data to newer formats post upgrade. " + "To migrate existing data, Kindly refer and follow Apache Atlas documentation for 1.0 release.").build()); + public static CheckDescription KERBEROS_ADMIN_CREDENTIAL_CHECK = new CheckDescription("KERBEROS_ADMIN_CREDENTIAL_CHECK", + PrereqCheckType.CLUSTER, + "The KDC administrator credentials need to be stored in Ambari persisted credential store.", + new ImmutableMap.Builder<String, String>() + .put(KerberosAdminPersistedCredentialCheck.KEY_PERSISTED_STORE_NOT_CONFIGURED, + "Ambari's credential store has not been configured. " + + "This is needed so the KDC administrator credential may be stored long enough to ensure it will be around if needed during the upgrade process.") + .put(KerberosAdminPersistedCredentialCheck.KEY_CREDENTIAL_NOT_STORED, + "The KDC administrator credential has not been stored in the persisted credential store. " + + "Visit the Kerberos administrator page to set the credential. " + + "This is needed so the KDC administrator credential may be stored long enough to ensure it will be around if needed during the upgrade process.") + .build()); + private String m_name; private PrereqCheckType m_type; private String m_description; diff --git a/ambari-server/src/main/java/org/apache/ambari/server/checks/KerberosAdminPersistedCredentialCheck.java b/ambari-server/src/main/java/org/apache/ambari/server/checks/KerberosAdminPersistedCredentialCheck.java new file mode 100644 index 0000000..1e5821d --- /dev/null +++ b/ambari-server/src/main/java/org/apache/ambari/server/checks/KerberosAdminPersistedCredentialCheck.java @@ -0,0 +1,104 @@ +/* + * 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.ambari.server.checks; + +import java.util.Collections; +import java.util.Set; + +import org.apache.ambari.server.AmbariException; +import org.apache.ambari.server.controller.KerberosHelper; +import org.apache.ambari.server.controller.PrereqCheckRequest; +import org.apache.ambari.server.security.encryption.CredentialStoreService; +import org.apache.ambari.server.security.encryption.CredentialStoreType; +import org.apache.ambari.server.state.Cluster; +import org.apache.ambari.server.state.SecurityType; +import org.apache.ambari.server.state.stack.PrereqCheckStatus; +import org.apache.ambari.server.state.stack.PrerequisiteCheck; +import org.apache.ambari.server.state.stack.upgrade.UpgradeType; + +import com.google.inject.Inject; + +/** + * The {@link KerberosAdminPersistedCredentialCheck} class is used to check that the Kerberos + * administrator credentials are stored in the persisted credential store when Kerberos is enabled. + * <p> + * This is needed so that if Kerberos principals and/or keytab files need to be created during a stack + * upgrade, the KDC administrator credentials are guaranteed to be available. If the temporary store + * is used, the credential may have expired before needed. + */ +@UpgradeCheck( + group = UpgradeCheckGroup.KERBEROS, + required = {UpgradeType.ROLLING, UpgradeType.NON_ROLLING, UpgradeType.HOST_ORDERED}) +public class KerberosAdminPersistedCredentialCheck extends AbstractCheckDescriptor { + + public static final String KEY_PERSISTED_STORE_NOT_CONFIGURED = "persisted_store_no_configured"; + + public static final String KEY_CREDENTIAL_NOT_STORED = "admin_credential_not_stored"; + + @Inject + private CredentialStoreService credentialStoreService; + + /** + * Constructor. + */ + @Inject + public KerberosAdminPersistedCredentialCheck() { + super(CheckDescription.KERBEROS_ADMIN_CREDENTIAL_CHECK); + } + + /** + * {@inheritDoc} + */ + @Override + public Set<String> getApplicableServices() { + return Collections.emptySet(); + } + + /** + * {@inheritDoc} + */ + @Override + public void perform(PrerequisiteCheck prerequisiteCheck, PrereqCheckRequest request) throws AmbariException { + + final String clusterName = request.getClusterName(); + final Cluster cluster = clustersProvider.get().getCluster(clusterName); + + // Perform the check only if Kerberos is enabled + if (cluster.getSecurityType() != SecurityType.KERBEROS) { + return; + } + + // Perform the check only if Ambari is managing the Kerberos identities + if (!"true".equalsIgnoreCase(getProperty(request, "kerberos-env", "manage_identities"))) { + return; + } + + if (!credentialStoreService.isInitialized(CredentialStoreType.PERSISTED)) { + // The persisted store is not available + prerequisiteCheck.setFailReason(getFailReason(KEY_PERSISTED_STORE_NOT_CONFIGURED, prerequisiteCheck, request)); + prerequisiteCheck.setStatus(PrereqCheckStatus.FAIL); + prerequisiteCheck.getFailedOn().add(request.getClusterName()); + } else if (credentialStoreService.getCredential(clusterName, KerberosHelper.KDC_ADMINISTRATOR_CREDENTIAL_ALIAS, CredentialStoreType.PERSISTED) == null) { + // The KDC administrator credential has not been stored in the persisted credential store + prerequisiteCheck.setFailReason(getFailReason(KEY_CREDENTIAL_NOT_STORED, prerequisiteCheck, request)); + prerequisiteCheck.setStatus(PrereqCheckStatus.FAIL); + prerequisiteCheck.getFailedOn().add(request.getClusterName()); + } + + } +} diff --git a/ambari-server/src/test/java/org/apache/ambari/server/checks/KerberosAdminPersistedCredentialCheckTest.java b/ambari-server/src/test/java/org/apache/ambari/server/checks/KerberosAdminPersistedCredentialCheckTest.java new file mode 100644 index 0000000..46184ec --- /dev/null +++ b/ambari-server/src/test/java/org/apache/ambari/server/checks/KerberosAdminPersistedCredentialCheckTest.java @@ -0,0 +1,229 @@ +/* + * 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.ambari.server.checks; + +import static org.easymock.EasyMock.expect; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +import javax.persistence.EntityManager; + +import org.apache.ambari.server.actionmanager.ActionDBAccessor; +import org.apache.ambari.server.actionmanager.ActionDBAccessorImpl; +import org.apache.ambari.server.actionmanager.ActionManager; +import org.apache.ambari.server.actionmanager.HostRoleCommandFactory; +import org.apache.ambari.server.actionmanager.HostRoleCommandFactoryImpl; +import org.apache.ambari.server.actionmanager.StageFactory; +import org.apache.ambari.server.api.services.AmbariMetaInfo; +import org.apache.ambari.server.audit.AuditLogger; +import org.apache.ambari.server.controller.AbstractRootServiceResponseFactory; +import org.apache.ambari.server.controller.AmbariCustomCommandExecutionHelper; +import org.apache.ambari.server.controller.AmbariManagementController; +import org.apache.ambari.server.controller.KerberosHelper; +import org.apache.ambari.server.controller.PrereqCheckRequest; +import org.apache.ambari.server.controller.RootServiceResponseFactory; +import org.apache.ambari.server.hooks.HookService; +import org.apache.ambari.server.hooks.users.UserHookService; +import org.apache.ambari.server.metadata.CachedRoleCommandOrderProvider; +import org.apache.ambari.server.metadata.RoleCommandOrderProvider; +import org.apache.ambari.server.orm.DBAccessor; +import org.apache.ambari.server.orm.dao.ArtifactDAO; +import org.apache.ambari.server.orm.dao.HostRoleCommandDAO; +import org.apache.ambari.server.scheduler.ExecutionScheduler; +import org.apache.ambari.server.scheduler.ExecutionSchedulerImpl; +import org.apache.ambari.server.security.SecurityHelper; +import org.apache.ambari.server.security.credential.Credential; +import org.apache.ambari.server.security.encryption.CredentialStoreService; +import org.apache.ambari.server.security.encryption.CredentialStoreType; +import org.apache.ambari.server.stack.StackManagerFactory; +import org.apache.ambari.server.state.Cluster; +import org.apache.ambari.server.state.Clusters; +import org.apache.ambari.server.state.Config; +import org.apache.ambari.server.state.ConfigHelper; +import org.apache.ambari.server.state.DesiredConfig; +import org.apache.ambari.server.state.SecurityType; +import org.apache.ambari.server.state.ServiceComponentHostFactory; +import org.apache.ambari.server.state.stack.OsFamily; +import org.apache.ambari.server.state.stack.PrereqCheckStatus; +import org.apache.ambari.server.state.stack.PrerequisiteCheck; +import org.apache.ambari.server.state.stack.UpgradePack; +import org.apache.ambari.server.testutils.PartialNiceMockBinder; +import org.apache.ambari.server.topology.PersistedState; +import org.apache.ambari.server.topology.PersistedStateImpl; +import org.easymock.EasyMockSupport; +import org.junit.Assert; +import org.junit.Test; +import org.springframework.security.crypto.password.PasswordEncoder; + +import com.google.inject.AbstractModule; +import com.google.inject.Guice; +import com.google.inject.Injector; +import com.google.inject.Provider; + +public class KerberosAdminPersistedCredentialCheckTest extends EasyMockSupport { + + @Test + public void testMissingCredentialStoreKerberosEnabledManagingIdentities() throws Exception { + PrerequisiteCheck result = executeCheck(true, true, false, false); + Assert.assertEquals(PrereqCheckStatus.FAIL, result.getStatus()); + Assert.assertTrue(result.getFailReason().startsWith("Ambari's credential store has not been configured.")); + } + + @Test + public void testMissingCredentialStoreKerberosEnabledNotManagingIdentities() throws Exception { + PrerequisiteCheck result = executeCheck(true, false, false, false); + Assert.assertEquals(PrereqCheckStatus.PASS, result.getStatus()); + } + + @Test + public void testMissingCredentialStoreKerberosNotEnabled() throws Exception { + PrerequisiteCheck result = executeCheck(false, false, false, false); + Assert.assertEquals(PrereqCheckStatus.PASS, result.getStatus()); + } + + @Test + public void testMissingCredentialKerberosEnabledManagingIdentities() throws Exception { + PrerequisiteCheck result = executeCheck(true, true, true, false); + Assert.assertEquals(PrereqCheckStatus.FAIL, result.getStatus()); + Assert.assertTrue(result.getFailReason().startsWith("The KDC administrator credential has not been stored in the persisted credential store.")); + } + + @Test + public void testMissingCredentialKerberosEnabledNotManagingIdentities() throws Exception { + PrerequisiteCheck result = executeCheck(true, false, true, false); + Assert.assertEquals(PrereqCheckStatus.PASS, result.getStatus()); + } + + @Test + public void testMissingCredentialKerberosNotEnabled() throws Exception { + PrerequisiteCheck result = executeCheck(false, true, true, false); + Assert.assertEquals(PrereqCheckStatus.PASS, result.getStatus()); + } + + @Test + public void testCredentialsSetKerberosNotEnabled() throws Exception { + PrerequisiteCheck result = executeCheck(false, false, true, true); + Assert.assertEquals(PrereqCheckStatus.PASS, result.getStatus()); + } + + @Test + public void testCredentialsSetKerberosEnabledNotManagingIdentities() throws Exception { + PrerequisiteCheck result = executeCheck(true, false, true, true); + Assert.assertEquals(PrereqCheckStatus.PASS, result.getStatus()); + } + + @Test + public void testCredentialsSetKerberosEnabledManagingIdentities() throws Exception { + PrerequisiteCheck result = executeCheck(true, true, true, true); + Assert.assertEquals(PrereqCheckStatus.PASS, result.getStatus()); + } + + private PrerequisiteCheck executeCheck(boolean kerberosEnabled, boolean manageIdentities, boolean credentialStoreInitialized, boolean credentialSet) throws Exception { + + String clusterName = "c1"; + + Map<String, String> checkProperties = new HashMap<>(); + + UpgradePack.PrerequisiteCheckConfig prerequisiteCheckConfig = createMock(UpgradePack.PrerequisiteCheckConfig.class); + expect(prerequisiteCheckConfig.getCheckProperties(KerberosAdminPersistedCredentialCheck.class.getName())).andReturn(checkProperties).anyTimes(); + + PrerequisiteCheck prerequisiteCheck = new PrerequisiteCheck(null, null); + PrereqCheckRequest request = new PrereqCheckRequest(clusterName); + request.setPrerequisiteCheckConfig(prerequisiteCheckConfig); + + DesiredConfig desiredKerberosEnv = createMock(DesiredConfig.class); + expect(desiredKerberosEnv.getTag()).andReturn("tag").anyTimes(); + + Map<String, DesiredConfig> desiredConfigs = Collections.singletonMap("kerberos-env", desiredKerberosEnv); + + Config kerberosEnv = createMock(Config.class); + expect(kerberosEnv.getProperties()).andReturn(Collections.singletonMap("manage_identities", manageIdentities ? "true" : "false")).anyTimes(); + + Cluster cluster = createMock(Cluster.class); + expect(cluster.getSecurityType()).andReturn(kerberosEnabled ? SecurityType.KERBEROS : SecurityType.NONE).anyTimes(); + expect(cluster.getDesiredConfigs()).andReturn(desiredConfigs).anyTimes(); + expect(cluster.getConfig("kerberos-env", "tag")).andReturn(kerberosEnv).anyTimes(); + + Clusters clusters = createMock(Clusters.class); + expect(clusters.getCluster(clusterName)).andReturn(cluster).anyTimes(); + + Credential credential = createMock(Credential.class); + + Injector injector = getInjector(); + CredentialStoreService credentialStoreProvider = injector.getInstance(CredentialStoreService.class); + expect(credentialStoreProvider.isInitialized(CredentialStoreType.PERSISTED)).andReturn(credentialStoreInitialized).anyTimes(); + expect(credentialStoreProvider.getCredential(clusterName, KerberosHelper.KDC_ADMINISTRATOR_CREDENTIAL_ALIAS, CredentialStoreType.PERSISTED)).andReturn(credentialSet ? credential : null).anyTimes(); + + Provider<Clusters> clustersProvider = () -> clusters; + + replayAll(); + + injector.getInstance(AmbariMetaInfo.class).init(); + + KerberosAdminPersistedCredentialCheck check = new KerberosAdminPersistedCredentialCheck(); + injector.injectMembers(check); + + check.clustersProvider = clustersProvider; + check.perform(prerequisiteCheck, request); + + verifyAll(); + + return prerequisiteCheck; + } + + + Injector getInjector() { + return Guice.createInjector(new AbstractModule() { + + @Override + protected void configure() { + PartialNiceMockBinder.newBuilder().addActionDBAccessorConfigsBindings().addFactoriesInstallBinding() + .build().configure(binder()); + + bind(ExecutionScheduler.class).toInstance(createNiceMock(ExecutionSchedulerImpl.class)); + bind(EntityManager.class).toInstance(createNiceMock(EntityManager.class)); + bind(DBAccessor.class).toInstance(createNiceMock(DBAccessor.class)); + bind(OsFamily.class).toInstance(createNiceMock(OsFamily.class)); + bind(HostRoleCommandDAO.class).toInstance(createNiceMock(HostRoleCommandDAO.class)); + bind(HostRoleCommandFactory.class).toInstance(createNiceMock(HostRoleCommandFactoryImpl.class)); + bind(ActionDBAccessor.class).to(ActionDBAccessorImpl.class); + bind(AbstractRootServiceResponseFactory.class).to(RootServiceResponseFactory.class); + bind(ServiceComponentHostFactory.class).toInstance(createNiceMock(ServiceComponentHostFactory.class)); + bind(PasswordEncoder.class).toInstance(createNiceMock(PasswordEncoder.class)); + bind(HookService.class).to(UserHookService.class); + bind(PersistedState.class).to(PersistedStateImpl.class); + bind(SecurityHelper.class).toInstance(createNiceMock(SecurityHelper.class)); + bind(AmbariCustomCommandExecutionHelper.class).toInstance(createNiceMock(AmbariCustomCommandExecutionHelper.class)); + bind(AmbariManagementController.class).toInstance(createNiceMock(AmbariManagementController.class)); + bind(AmbariMetaInfo.class).toInstance(createNiceMock(AmbariMetaInfo.class)); + bind(ActionManager.class).toInstance(createNiceMock(ActionManager.class)); + bind(StageFactory.class).toInstance(createNiceMock(StageFactory.class)); + bind(Clusters.class).toInstance(createNiceMock(Clusters.class)); + bind(ConfigHelper.class).toInstance(createNiceMock(ConfigHelper.class)); + bind(StackManagerFactory.class).toInstance(createNiceMock(StackManagerFactory.class)); + bind(AuditLogger.class).toInstance(createNiceMock(AuditLogger.class)); + bind(ArtifactDAO.class).toInstance(createNiceMock(ArtifactDAO.class)); + bind(RoleCommandOrderProvider.class).to(CachedRoleCommandOrderProvider.class); + + bind(CredentialStoreService.class).toInstance(createMock(CredentialStoreService.class)); + } + }); + } +} -- To stop receiving notification emails like this one, please contact rle...@apache.org.