This is an automated email from the ASF dual-hosted git repository.
chungen0126 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/ozone.git
The following commit(s) were added to refs/heads/master by this push:
new a57788ea4cd HDDS-12250. Speed up TestSecureOzoneCluster (#10834)
a57788ea4cd is described below
commit a57788ea4cda64c2130ce6087a96835f201dd0ca
Author: Chia-Yi Chiu <[email protected]>
AuthorDate: Thu Jul 23 11:19:39 2026 +0900
HDDS-12250. Speed up TestSecureOzoneCluster (#10834)
Co-authored-by: Claude Opus 4.8 <[email protected]>
---
.../apache/hadoop/ozone/TestDelegationToken.java | 66 +-
.../hadoop/ozone/TestSecureOzoneCluster.java | 1009 ++++++++------------
2 files changed, 486 insertions(+), 589 deletions(-)
diff --git
a/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/TestDelegationToken.java
b/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/TestDelegationToken.java
index 1092517abce..444b6c313fa 100644
---
a/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/TestDelegationToken.java
+++
b/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/TestDelegationToken.java
@@ -35,12 +35,14 @@
import static org.apache.hadoop.net.ServerSocketUtil.getPort;
import static org.apache.hadoop.ozone.OzoneConfigKeys.OZONE_ADMINISTRATORS;
import static
org.apache.hadoop.ozone.OzoneConfigKeys.OZONE_SECURITY_ENABLED_KEY;
+import static
org.apache.hadoop.ozone.om.OMConfigKeys.DELEGATION_TOKEN_MAX_LIFETIME_KEY;
import static
org.apache.hadoop.ozone.om.OMConfigKeys.OZONE_OM_HTTP_KERBEROS_KEYTAB_FILE;
import static
org.apache.hadoop.ozone.om.OMConfigKeys.OZONE_OM_HTTP_KERBEROS_PRINCIPAL_KEY;
import static
org.apache.hadoop.ozone.om.OMConfigKeys.OZONE_OM_KERBEROS_KEYTAB_FILE_KEY;
import static
org.apache.hadoop.ozone.om.OMConfigKeys.OZONE_OM_KERBEROS_PRINCIPAL_KEY;
import static
org.apache.hadoop.ozone.om.exceptions.OMException.ResultCodes.INVALID_AUTH_METHOD;
import static
org.apache.hadoop.ozone.om.exceptions.OMException.ResultCodes.TOKEN_ERROR_OTHER;
+import static
org.apache.hadoop.ozone.om.exceptions.OMException.ResultCodes.TOKEN_EXPIRED;
import static
org.apache.hadoop.ozone.om.exceptions.OMException.ResultCodes.VOLUME_NOT_FOUND;
import static
org.apache.hadoop.security.UserGroupInformation.AuthenticationMethod.KERBEROS;
import static org.assertj.core.api.Assertions.assertThat;
@@ -262,7 +264,9 @@ private void initSCM() throws IOException {
* 3. Client can authenticate using token.
* 4. Delegation token renewal without Kerberos auth fails.
* 5. Test success of token cancellation.
- * 5. Test failure of token cancellation.
+ * 6. Test failure of token cancellation.
+ * 7. Test delegation token renewal failures (expired maxDate, non-matching
+ * renewer, tampered token).
*/
@ParameterizedTest
@MethodSource("options")
@@ -277,6 +281,10 @@ public void testDelegationToken(boolean useIp) throws
Exception {
GenericTestUtils.setLogLevel(Server.class, INFO);
SecurityUtil.setTokenServiceUseIp(useIp);
+ // Generous token lifetime so the renewal-failure cases below do not race
+ // token expiry.
+ conf.setLong(DELEGATION_TOKEN_MAX_LIFETIME_KEY, 60 * 1000L);
+
// Setup secure OM for start
setupOm(conf);
@@ -398,12 +406,68 @@ public void testDelegationToken(boolean useIp) throws
Exception {
assertEquals(TOKEN_ERROR_OTHER, ex.getResult());
assertThat(ex.getMessage()).contains("Cancel delegation token failed");
assertThat(logs.getOutput()).contains("Auth failed for");
+
+ // Case 7: Delegation token renewal failures.
+ assertDelegationTokenRenewalFailures(ugi, omLogs);
} finally {
om.stop();
om.join();
}
}
+ /**
+ * Exercises the delegation token renewal failure paths against the running
+ * OM. Reconnects via Kerberos and seeds a fresh token, then verifies renewal
+ * fails for an expired maxDate, a non-matching renewer, and a tampered
token.
+ */
+ private void assertDelegationTokenRenewalFailures(
+ UserGroupInformation ugi, LogCapturer omLogs) throws Exception {
+ omClient.close();
+ UserGroupInformation.setLoginUser(ugi);
+ omClient = new OzoneManagerProtocolClientSideTranslatorPB(
+ OmTransportFactory.create(conf, ugi, null),
+ RandomStringUtils.secure().nextAscii(5));
+ Token<OzoneTokenIdentifier> seedToken =
+ omClient.getDelegationToken(new Text("om"));
+
+ // 1. When token maxExpiryTime exceeds (maxDate in the past).
+ OzoneTokenIdentifier expiredId = OzoneTokenIdentifier.readProtoBuf(
+ seedToken.getIdentifier());
+ expiredId.setMaxDate(System.currentTimeMillis() - 1000);
+ Token<OzoneTokenIdentifier> expiredToken = new Token<>(
+ expiredId.getBytes(), seedToken.getPassword(), seedToken.getKind(),
+ seedToken.getService());
+ OMException ex = assertThrows(OMException.class,
+ () -> omClient.renewDelegationToken(expiredToken));
+ assertEquals(TOKEN_EXPIRED, ex.getResult());
+ omLogs.clearOutput();
+
+ // 2. When renewer doesn't match (implicitly covers when renewer is
+ // null or empty).
+ Token<OzoneTokenIdentifier> token2 = omClient.getDelegationToken(
+ new Text("randomService"));
+ assertNotNull(token2);
+ ex = assertThrows(OMException.class,
+ () -> omClient.renewDelegationToken(token2));
+ assertThat(ex).hasMessageContaining("Delegation token renewal failed");
+ assertThat(omLogs.getOutput()).contains(" with non-matching renewer
randomService");
+ omLogs.clearOutput();
+
+ // 3. Tampered token that can't be found in cache.
+ OzoneTokenIdentifier tokenId = OzoneTokenIdentifier.readProtoBuf(
+ seedToken.getIdentifier());
+ tokenId.setRenewer(new Text("om"));
+ tokenId.setMaxDate(System.currentTimeMillis() * 2);
+ Token<OzoneTokenIdentifier> tamperedToken = new Token<>(
+ tokenId.getBytes(), token2.getPassword(), token2.getKind(),
+ token2.getService());
+ ex = assertThrows(OMException.class,
+ () -> omClient.renewDelegationToken(tamperedToken));
+ assertThat(ex).hasMessageContaining("Delegation token renewal failed");
+ assertThat(omLogs.getOutput()).contains("can't be found in cache");
+ omLogs.clearOutput();
+ }
+
private void generateKeyPair() throws Exception {
SecurityConfig securityConfig = new SecurityConfig(conf);
HDDSKeyGenerator keyGenerator = new HDDSKeyGenerator(securityConfig);
diff --git
a/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/TestSecureOzoneCluster.java
b/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/TestSecureOzoneCluster.java
index 386ba7c8240..bb9fb9f6180 100644
---
a/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/TestSecureOzoneCluster.java
+++
b/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/TestSecureOzoneCluster.java
@@ -44,7 +44,6 @@
import static
org.apache.hadoop.ozone.OzoneConfigKeys.OZONE_CLIENT_FAILOVER_MAX_ATTEMPTS_KEY;
import static
org.apache.hadoop.ozone.OzoneConfigKeys.OZONE_SECURITY_ENABLED_KEY;
import static org.apache.hadoop.ozone.OzoneConsts.SCM_SUB_CA;
-import static
org.apache.hadoop.ozone.om.OMConfigKeys.DELEGATION_TOKEN_MAX_LIFETIME_KEY;
import static org.apache.hadoop.ozone.om.OMConfigKeys.OZONE_OM_ADDRESS_KEY;
import static
org.apache.hadoop.ozone.om.OMConfigKeys.OZONE_OM_HTTP_KERBEROS_KEYTAB_FILE;
import static
org.apache.hadoop.ozone.om.OMConfigKeys.OZONE_OM_HTTP_KERBEROS_PRINCIPAL_KEY;
@@ -52,7 +51,6 @@
import static
org.apache.hadoop.ozone.om.OMConfigKeys.OZONE_OM_KERBEROS_PRINCIPAL_KEY;
import static
org.apache.hadoop.ozone.om.OMConfigKeys.OZONE_OM_S3_GPRC_SERVER_ENABLED;
import static org.apache.hadoop.ozone.om.OMConfigKeys.OZONE_OM_TRANSPORT_CLASS;
-import static
org.apache.hadoop.ozone.om.exceptions.OMException.ResultCodes.TOKEN_EXPIRED;
import static
org.apache.hadoop.ozone.om.exceptions.OMException.ResultCodes.USER_MISMATCH;
import static
org.apache.hadoop.security.UserGroupInformation.AuthenticationMethod.KERBEROS;
import static org.apache.ozone.test.GenericTestUtils.PortAllocator.getFreePort;
@@ -102,12 +100,10 @@
import
org.apache.hadoop.hdds.protocol.proto.SCMSecurityProtocolProtos.SCMGetCertResponseProto;
import
org.apache.hadoop.hdds.protocolPB.SCMSecurityProtocolClientSideTranslatorPB;
import org.apache.hadoop.hdds.scm.HddsTestUtils;
-import org.apache.hadoop.hdds.scm.ScmConfig;
import org.apache.hadoop.hdds.scm.ScmInfo;
import org.apache.hadoop.hdds.scm.client.ScmTopologyClient;
import org.apache.hadoop.hdds.scm.protocol.ScmBlockLocationProtocol;
import org.apache.hadoop.hdds.scm.protocol.StorageContainerLocationProtocol;
-import org.apache.hadoop.hdds.scm.server.SCMHTTPServerConfig;
import org.apache.hadoop.hdds.scm.server.SCMStorageConfig;
import org.apache.hadoop.hdds.scm.server.StorageContainerManager;
import org.apache.hadoop.hdds.security.SecurityConfig;
@@ -128,7 +124,6 @@
import org.apache.hadoop.hdds.utils.HAUtils;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.ipc_.Client;
-import org.apache.hadoop.ipc_.Server;
import org.apache.hadoop.metrics2.lib.DefaultMetricsSystem;
import org.apache.hadoop.minikdc.MiniKdc;
import org.apache.hadoop.ozone.client.OzoneClient;
@@ -163,7 +158,9 @@
import org.apache.ozone.test.tag.Unhealthy;
import org.apache.ratis.protocol.ClientId;
import org.apache.ratis.util.ExitUtils;
+import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
@@ -177,38 +174,64 @@ final class TestSecureOzoneCluster {
private static final String COMPONENT = "om";
private static final String OM_CERT_SERIAL_ID = "9879877970576";
+ private static final String CANNOT_AUTHENTICATE_MESSAGE = "Client cannot
authenticate via:[KERBEROS]";
private static final Logger LOG = LoggerFactory
.getLogger(TestSecureOzoneCluster.class);
+ private static final int CERT_GRACE_TIME_MS = 10 * 1000; // 10s
+ private static final int DELEGATION_TOKEN_MAX_TIME_MS = 9 * 1000; // 9s
@TempDir
- private File tempDir;
+ private static File workDir;
+ private static MiniKdc miniKdc;
+ private static OzoneConfiguration kdcConf;
+ private static File scmKeytab;
+ private static File spnegoKeytab;
+ private static File omKeytab;
+ private static File testUserKeytab;
+ private static String testUserPrincipal;
+ private static String host;
+ private static String realm;
- private MiniKdc miniKdc;
+ @TempDir
+ private File tempDir;
private OzoneConfiguration conf;
- private File workDir;
- private File scmKeytab;
- private File spnegoKeytab;
- private File omKeyTab;
- private File testUserKeytab;
- private String testUserPrincipal;
private StorageContainerManager scm;
private ScmBlockLocationProtocol scmBlockClient;
private OzoneManager om;
private HddsProtos.OzoneManagerDetailsProto omInfo;
- private String host;
private String clusterId;
private String scmId;
private String omId;
private OzoneManagerProtocolClientSideTranslatorPB omClient;
private KeyPair keyPair;
private Path omMetaDirPath;
- private int certGraceTime = 10 * 1000; // 10s
- private int delegationTokenMaxTime = 9 * 1000; // 9s
+
+ @BeforeAll
+ static void setupKdc() throws Exception {
+ ExitUtils.disableSystemExit();
+ DefaultMetricsSystem.setMiniClusterMode(true);
+ host = InetAddress.getLocalHost().getCanonicalHostName().toLowerCase();
+ startMiniKdc();
+ realm = miniKdc.getRealm();
+ testUserPrincipal = "test@" + realm;
+ scmKeytab = new File(workDir, "scm.keytab");
+ spnegoKeytab = new File(workDir, "http.keytab");
+ omKeytab = new File(workDir, "om.keytab");
+ testUserKeytab = new File(workDir, "testuser.keytab");
+ kdcConf = new OzoneConfiguration();
+ setSecureConfig(kdcConf);
+ createCredentialsInKDC(kdcConf);
+ }
+
+ @AfterAll
+ static void tearDownKdc() {
+ stopMiniKdc();
+ }
@BeforeEach
void init() {
try {
- conf = new OzoneConfiguration();
+ conf = new OzoneConfiguration(kdcConf);
conf.set(OZONE_SCM_CLIENT_ADDRESS_KEY, "localhost");
conf.setInt(OZONE_SCM_CLIENT_PORT_KEY, getFreePort());
@@ -220,34 +243,25 @@ void init() {
conf.set(OZONE_OM_ADDRESS_KEY,
InetAddress.getLocalHost().getCanonicalHostName() + ":" +
getFreePort());
- DefaultMetricsSystem.setMiniClusterMode(true);
- ExitUtils.disableSystemExit();
final String path = tempDir.getAbsolutePath();
omMetaDirPath = Paths.get(path, "om-meta");
conf.set(OZONE_METADATA_DIRS, omMetaDirPath.toString());
- conf.setBoolean(OZONE_SECURITY_ENABLED_KEY, true);
- conf.set(HADOOP_SECURITY_AUTHENTICATION, KERBEROS.name());
+
conf.set(HDDS_X509_CA_ROTATION_CHECK_INTERNAL,
- Duration.ofMillis(certGraceTime - 1000).toString());
+ Duration.ofMillis(CERT_GRACE_TIME_MS - 1000).toString());
conf.set(HDDS_X509_RENEW_GRACE_DURATION,
- Duration.ofMillis(certGraceTime).toString());
+ Duration.ofMillis(CERT_GRACE_TIME_MS).toString());
conf.setBoolean(HDDS_X509_GRACE_DURATION_TOKEN_CHECKS_ENABLED, false);
- conf.set(HDDS_X509_CA_ROTATION_CHECK_INTERNAL,
- Duration.ofMillis(certGraceTime - 1000).toString());
conf.set(HDDS_X509_CA_ROTATION_ACK_TIMEOUT,
- Duration.ofMillis(certGraceTime - 1000).toString());
+ Duration.ofMillis(CERT_GRACE_TIME_MS - 1000).toString());
conf.setLong(OMConfigKeys.DELEGATION_TOKEN_MAX_LIFETIME_KEY,
- delegationTokenMaxTime);
+ DELEGATION_TOKEN_MAX_TIME_MS);
- workDir = new File(tempDir, "workdir");
clusterId = UUID.randomUUID().toString();
scmId = UUID.randomUUID().toString();
omId = UUID.randomUUID().toString();
scmBlockClient = new ScmBlockLocationTestingClient(null, null, 0);
- startMiniKdc();
- setSecureConfig();
- createCredentialsInKDC();
generateKeyPair();
omInfo = OzoneManager.getOmDetailsProto(conf, omId);
} catch (Exception e) {
@@ -258,182 +272,245 @@ void init() {
@AfterEach
void stop() throws Exception {
try {
- stopMiniKdc();
if (scm != null) {
scm.stop();
scm.join();
}
- if (om != null) {
- om.stop();
- om.join();
- }
- IOUtils.closeQuietly(omClient);
} catch (Exception e) {
LOG.error("Failed to stop TestSecureOzoneCluster", e);
+ } finally {
+ IOUtils.closeQuietly(om);
+ IOUtils.closeQuietly(omClient);
}
}
- private void createCredentialsInKDC() throws Exception {
- ScmConfig scmConfig = conf.getObject(ScmConfig.class);
- SCMHTTPServerConfig httpServerConfig =
- conf.getObject(SCMHTTPServerConfig.class);
- createPrincipal(scmKeytab, scmConfig.getKerberosPrincipal());
- createPrincipal(spnegoKeytab, httpServerConfig.getKerberosPrincipal());
+ private static void createCredentialsInKDC(OzoneConfiguration conf) throws
Exception {
+ createPrincipal(scmKeytab, conf.get(HDDS_SCM_KERBEROS_PRINCIPAL_KEY));
+ createPrincipal(spnegoKeytab,
conf.get(HDDS_SCM_HTTP_KERBEROS_PRINCIPAL_KEY));
+ createPrincipal(omKeytab, conf.get(OZONE_OM_KERBEROS_PRINCIPAL_KEY));
createPrincipal(testUserKeytab, testUserPrincipal);
- createPrincipal(omKeyTab,
- conf.get(OZONE_OM_KERBEROS_PRINCIPAL_KEY));
}
- private void createPrincipal(File keytab, String... principal)
+ private static void createPrincipal(File keytab, String... principal)
throws Exception {
miniKdc.createPrincipal(keytab, principal);
}
- private void startMiniKdc() throws Exception {
+ private static void startMiniKdc() throws Exception {
Properties securityProperties = MiniKdc.createConf();
miniKdc = new MiniKdc(securityProperties, workDir);
miniKdc.start();
}
- private void stopMiniKdc() {
- miniKdc.stop();
+ private static void stopMiniKdc() {
+ if (miniKdc != null) {
+ miniKdc.stop();
+ }
}
- private void setSecureConfig() throws IOException {
+ private static void setSecureConfig(OzoneConfiguration conf) throws
IOException {
conf.setBoolean(OZONE_SECURITY_ENABLED_KEY, true);
- host = InetAddress.getLocalHost().getCanonicalHostName()
- .toLowerCase();
-
conf.set(HADOOP_SECURITY_AUTHENTICATION, "kerberos");
String curUser = UserGroupInformation.getCurrentUser().getUserName();
conf.set(OZONE_ADMINISTRATORS, curUser);
- String realm = miniKdc.getRealm();
String hostAndRealm = host + "@" + realm;
conf.set(HDDS_SCM_KERBEROS_PRINCIPAL_KEY, "scm/" + hostAndRealm);
conf.set(HDDS_SCM_HTTP_KERBEROS_PRINCIPAL_KEY, "HTTP_SCM/" + hostAndRealm);
conf.set(OZONE_OM_KERBEROS_PRINCIPAL_KEY, "om/" + hostAndRealm);
conf.set(OZONE_OM_HTTP_KERBEROS_PRINCIPAL_KEY, "HTTP_OM/" + hostAndRealm);
- scmKeytab = new File(workDir, "scm.keytab");
- spnegoKeytab = new File(workDir, "http.keytab");
- omKeyTab = new File(workDir, "om.keytab");
- testUserKeytab = new File(workDir, "testuser.keytab");
- testUserPrincipal = "test@" + realm;
-
- conf.set(HDDS_SCM_KERBEROS_KEYTAB_FILE_KEY,
- scmKeytab.getAbsolutePath());
- conf.set(HDDS_SCM_HTTP_KERBEROS_KEYTAB_FILE_KEY,
- spnegoKeytab.getAbsolutePath());
- conf.set(OZONE_OM_KERBEROS_KEYTAB_FILE_KEY,
- omKeyTab.getAbsolutePath());
- conf.set(OZONE_OM_HTTP_KERBEROS_KEYTAB_FILE,
- spnegoKeytab.getAbsolutePath());
+ conf.set(HDDS_SCM_KERBEROS_KEYTAB_FILE_KEY, scmKeytab.getAbsolutePath());
+ conf.set(HDDS_SCM_HTTP_KERBEROS_KEYTAB_FILE_KEY,
spnegoKeytab.getAbsolutePath());
+ conf.set(OZONE_OM_KERBEROS_KEYTAB_FILE_KEY, omKeytab.getAbsolutePath());
+ conf.set(OZONE_OM_HTTP_KERBEROS_KEYTAB_FILE,
spnegoKeytab.getAbsolutePath());
}
+ /**
+ * Exercises a secure SCM and OM sharing a single SCM instance to avoid
paying
+ * the SCM startup cost twice. Covers SCM startup and cert trust chain, SCM
+ * security/admin access control, and secure OM initialization (the
+ * delegation-token/secret-key config-validation failure, the login success
+ * case, the Kerberos failure case, and re-initialization of an
+ * already-initialized OM).
+ */
@Test
- void testSecureScmStartupSuccess() throws Exception {
-
+ void testSecureScmAndOmStartupAndAccessControl() throws Exception {
initSCM();
scm = HddsTestUtils.getScmSimple(conf);
- //Reads the SCM Info from SCM instance
- try {
- scm.start();
- ScmInfo scmInfo = scm.getClientProtocolServer().getScmInfo();
- assertEquals(clusterId, scmInfo.getClusterId());
- assertEquals(scmId, scmInfo.getScmId());
- assertEquals(2, scm.getScmCertificateClient().getTrustChain().size());
- } finally {
- if (scm != null) {
- scm.stop();
- }
+ scm.start();
+
+ // SCM startup and access control.
+ assertScmExposesClusterInfoAndTrustChain();
+ assertScmSecurityProtocolAllowsKerberosUser();
+ assertScmSecurityProtocolRejectsNonKerberosUser();
+ assertScmAdminProtocolDeniesNonAdminUser();
+ assertScmAdminProtocolRejectsNonKerberosUser();
+
+ // Secure OM initialization against the shared SCM.
+ assertSecureOmInitFailsWithInvalidTokenAndSecretKeyConfig();
+ assertSecureOmInitSucceeds();
+ assertSecureOmInitFailsWithNonExistentPrincipal();
+ assertSecureOmInitWhenAlreadyInitialized();
+ }
+
+ /** SCM starts up and exposes its cluster info and cert trust chain. */
+ private void assertScmExposesClusterInfoAndTrustChain() throws IOException {
+ ScmInfo scmInfo = scm.getClientProtocolServer().getScmInfo();
+ assertEquals(clusterId, scmInfo.getClusterId());
+ assertEquals(scmId, scmInfo.getScmId());
+ assertEquals(2, scm.getScmCertificateClient().getTrustChain().size());
+ }
+
+ /** SCM security protocol - user with Kerberos credentials succeeds. */
+ private void assertScmSecurityProtocolAllowsKerberosUser() throws Exception {
+ UserGroupInformation ugi =
+ UserGroupInformation.loginUserFromKeytabAndReturnUGI(
+ testUserPrincipal, testUserKeytab.getCanonicalPath());
+ ugi.setAuthenticationMethod(KERBEROS);
+ try (SCMSecurityProtocolClientSideTranslatorPB securityClient =
+ getScmSecurityClient(conf, ugi)) {
+ assertNotNull(securityClient);
+ String caCert = securityClient.getCACertificate();
+ assertNotNull(caCert);
+ // Get some random certificate, used serial id 100 which will be
+ // unavailable as our serial id is time stamp. Serial id 1 is root CA,
+ // and it is persisted in DB.
+ SCMSecurityException securityException = assertThrows(
+ SCMSecurityException.class,
+ () -> securityClient.getCertificate("100"));
+ assertThat(securityException)
+ .hasMessageContaining("Certificate not found");
}
}
- @Test
- void testSCMSecurityProtocol() throws Exception {
+ /** SCM security protocol - user without Kerberos credentials fails. */
+ private void assertScmSecurityProtocolRejectsNonKerberosUser() throws
Exception {
+ UserGroupInformation ugi = UserGroupInformation.createRemoteUser("test");
+ ugi.setAuthenticationMethod(AuthMethod.TOKEN);
+ try (SCMSecurityProtocolClientSideTranslatorPB securityClient =
+ getScmSecurityClient(conf, ugi)) {
+ IOException ioException = assertThrows(IOException.class,
+ securityClient::getCACertificate);
+
assertThat(ioException).hasMessageContaining(CANNOT_AUTHENTICATE_MESSAGE);
+ ioException = assertThrows(IOException.class,
+ () -> securityClient.getCertificate("1"));
+
assertThat(ioException).hasMessageContaining(CANNOT_AUTHENTICATE_MESSAGE);
+ }
+ }
- initSCM();
- scm = HddsTestUtils.getScmSimple(conf);
- //Reads the SCM Info from SCM instance
- try {
- scm.start();
+ /** SCM admin protocol - authenticated non-admin user is denied. */
+ private void assertScmAdminProtocolDeniesNonAdminUser() throws IOException {
+ UserGroupInformation ugi =
UserGroupInformation.loginUserFromKeytabAndReturnUGI(
+ testUserPrincipal, testUserKeytab.getCanonicalPath());
+ StorageContainerLocationProtocol scmRpcClient =
+ HAUtils.getScmContainerClient(conf, ugi);
+ IOException adminException = assertThrows(IOException.class,
+ scmRpcClient::forceExitSafeMode);
+ assertThat(adminException).hasMessageContaining("Access denied");
+ }
- // Case 1: User with Kerberos credentials should succeed.
- UserGroupInformation ugi =
- UserGroupInformation.loginUserFromKeytabAndReturnUGI(
- testUserPrincipal, testUserKeytab.getCanonicalPath());
- ugi.setAuthenticationMethod(KERBEROS);
- try (SCMSecurityProtocolClientSideTranslatorPB securityClient =
- getScmSecurityClient(conf, ugi)) {
- assertNotNull(securityClient);
- String caCert = securityClient.getCACertificate();
- assertNotNull(caCert);
- // Get some random certificate, used serial id 100 which will be
- // unavailable as our serial id is time stamp. Serial id 1 is root CA,
- // and it is persisted in DB.
- SCMSecurityException securityException = assertThrows(
- SCMSecurityException.class,
- () -> securityClient.getCertificate("100"));
- assertThat(securityException)
- .hasMessageContaining("Certificate not found");
- }
+ /** SCM admin protocol - user without Kerberos credentials fails. */
+ private void assertScmAdminProtocolRejectsNonKerberosUser() throws
IOException {
+ UserGroupInformation ugi = UserGroupInformation.createRemoteUser("test");
+ ugi.setAuthenticationMethod(AuthMethod.TOKEN);
+ StorageContainerLocationProtocol scmRpcClient =
+ HAUtils.getScmContainerClient(conf, ugi);
+ IOException adminException = assertThrows(IOException.class,
+ scmRpcClient::forceExitSafeMode);
+ assertThat(adminException)
+ .hasMessageContaining(CANNOT_AUTHENTICATE_MESSAGE);
+ }
- // Case 2: User without Kerberos credentials should fail.
- ugi = UserGroupInformation.createRemoteUser("test");
- ugi.setAuthenticationMethod(AuthMethod.TOKEN);
- try (SCMSecurityProtocolClientSideTranslatorPB securityClient =
- getScmSecurityClient(conf, ugi)) {
-
- String cannotAuthMessage = "Client cannot authenticate via:[KERBEROS]";
- IOException ioException = assertThrows(IOException.class,
- securityClient::getCACertificate);
- assertThat(ioException).hasMessageContaining(cannotAuthMessage);
- ioException = assertThrows(IOException.class,
- () -> securityClient.getCertificate("1"));
- assertThat(ioException).hasMessageContaining(cannotAuthMessage);
- }
- } finally {
- if (scm != null) {
- scm.stop();
- }
- }
+ /**
+ * Secure OM initialization fails when the delegation token and secret key
+ * configuration don't meet requirement.
+ */
+ private void assertSecureOmInitFailsWithInvalidTokenAndSecretKeyConfig() {
+ conf.setTimeDuration(HDDS_SECRET_KEY_EXPIRY_DURATION, 7, TimeUnit.DAYS);
+ conf.setTimeDuration(OMConfigKeys.DELEGATION_TOKEN_MAX_LIFETIME_KEY, 7,
TimeUnit.DAYS);
+ IllegalArgumentException exception = assertThrows(
+ IllegalArgumentException.class, () -> setupOm(conf));
+ assertThat(exception.getMessage()).contains("Secret key expiry duration
hdds.secret.key.expiry.duration " +
+ "should be greater than value of
(ozone.manager.delegation.token.max-lifetime + " +
+ "ozone.manager.delegation.remover.scan.interval +
hdds.secret.key.rotate.duration");
+ // Restore valid durations so the remaining cases can start OM.
+ conf.unset(HDDS_SECRET_KEY_EXPIRY_DURATION);
+ conf.setLong(OMConfigKeys.DELEGATION_TOKEN_MAX_LIFETIME_KEY,
DELEGATION_TOKEN_MAX_TIME_MS);
}
- @Test
- void testAdminAccessControlException() throws Exception {
- initSCM();
- scm = HddsTestUtils.getScmSimple(conf);
- //Reads the SCM Info from SCM instance
- try {
- scm.start();
+ /** Secure OM initialization succeeds. */
+ private void assertSecureOmInitSucceeds() throws Exception {
+ LogCapturer logs = LogCapturer.captureLogs(OzoneManager.class);
+ GenericTestUtils.setLogLevel(OzoneManager.class, INFO);
+ setupOm(conf);
+ assertThrows(Exception.class, om::start);
+ assertThat(logs.getOutput()).contains("Ozone Manager login successful");
+ logs.clearOutput();
+ stopOm();
+ }
- //case 1: Run admin command with non-admin user.
- UserGroupInformation ugi =
- UserGroupInformation.loginUserFromKeytabAndReturnUGI(
- testUserPrincipal, testUserKeytab.getCanonicalPath());
- StorageContainerLocationProtocol scmRpcClient =
- HAUtils.getScmContainerClient(conf, ugi);
- IOException ioException = assertThrows(IOException.class,
- scmRpcClient::forceExitSafeMode);
- assertThat(ioException).hasMessageContaining("Access denied");
+ /**
+ * Secure OM initialization fails at Kerberos login for a non-existent
+ * principal. Storage is already initialized by
+ * {@link #assertSecureOmInitSucceeds()}, and createOm with a non-existent
+ * principal fails at Kerberos login before any storage check or RPC bind, so
+ * no additional setup is required.
+ */
+ private void assertSecureOmInitFailsWithNonExistentPrincipal() {
+ conf.set(OZONE_OM_KERBEROS_PRINCIPAL_KEY,
+ "[email protected]");
+ testCommonKerberosFailures(() -> OzoneManager.createOm(conf));
+ }
- // Case 2: User without Kerberos credentials should fail.
- ugi = UserGroupInformation.createRemoteUser("test");
- ugi.setAuthenticationMethod(AuthMethod.TOKEN);
- scmRpcClient =
- HAUtils.getScmContainerClient(conf, ugi);
+ /**
+ * Secure OM can be re-initialized when it is already initialized.
+ * The failure cases above left a non-existent principal and an invalid auth
+ * method on conf; restore the valid values before re-initializing. A fresh
+ * RPC port is also required: {@link #assertSecureOmInitSucceeds()}'s start()
+ * failed before starting the RPC server, so its listener selector loop never
+ * ran and stop() cannot release the bound port.
+ */
+ private void assertSecureOmInitWhenAlreadyInitialized() throws Exception {
+ conf.set(HADOOP_SECURITY_AUTHENTICATION, "kerberos");
+ conf.set(OZONE_OM_KERBEROS_PRINCIPAL_KEY, "om/" + host + "@" + realm);
+ conf.set(OZONE_OM_ADDRESS_KEY,
+ InetAddress.getLocalHost().getCanonicalHostName() + ":" +
getFreePort());
+ LogCapturer omLogs = LogCapturer.captureLogs(OMCertificateClient.class);
+ omLogs.clearOutput();
+ conf.setBoolean(OZONE_SECURITY_ENABLED_KEY, false);
+ OMStorage omStore = new OMStorage(conf);
+ initializeOmStorage(omStore);
+ OzoneManager.setTestSecureOmFlag(true);
+ om = OzoneManager.createOm(conf);
- String cannotAuthMessage = "Client cannot authenticate via:[KERBEROS]";
- ioException = assertThrows(IOException.class,
- scmRpcClient::forceExitSafeMode);
- assertThat(ioException).hasMessageContaining(cannotAuthMessage);
- } finally {
- if (scm != null) {
- scm.stop();
- }
- }
+ assertNull(om.getCertificateClient());
+ String logOutput = omLogs.getOutput();
+ assertThat(logOutput)
+ .doesNotContain("Init response: GETCERT");
+ assertThat(logOutput)
+ .doesNotContain("Successfully stored SCM signed certificate");
+
+ stopOm();
+
+ conf.setBoolean(OZONE_SECURITY_ENABLED_KEY, true);
+ conf.setBoolean(OZONE_OM_S3_GPRC_SERVER_ENABLED, true);
+ conf.set(OZONE_OM_ADDRESS_KEY,
+ InetAddress.getLocalHost().getCanonicalHostName() + ":" +
getFreePort());
+
+ OzoneManager.omInit(conf);
+ om = OzoneManager.createOm(conf);
+
+ assertNotNull(om.getCertificateClient());
+ assertNotNull(om.getCertificateClient().getPublicKey());
+ assertNotNull(om.getCertificateClient().getPrivateKey());
+ assertNotNull(om.getCertificateClient().getCertificate());
+ assertThat(omLogs.getOutput())
+ .contains("Init response: GETCERT")
+ .contains("Successfully stored OM signed certificate");
+ X509Certificate certificate = om.getCertificateClient().getCertificate();
+ validateCertificate(certificate);
}
private void initSCM() throws IOException {
@@ -504,97 +581,19 @@ private void testCommonKerberosFailures(Callable<?> test)
{
.hasMessageContaining("KERBEROS_SSL authentication method not");
}
- /**
- * Tests the secure om Initialization Failure.
- */
@Test
- void testSecureOMInitializationFailure() throws Exception {
- initSCM();
- // Create a secure SCM instance as om client will connect to it
- scm = HddsTestUtils.getScmSimple(conf);
- try {
- scm.start();
- setupOm(conf);
- conf.set(OZONE_OM_KERBEROS_PRINCIPAL_KEY,
- "[email protected]");
- testCommonKerberosFailures(() -> OzoneManager.createOm(conf));
- } finally {
- if (scm != null) {
- scm.stop();
- }
- }
- }
-
- /**
- * Tests the secure om Initialization Failure due to delegation token and
secret key configuration don't meet
- * requirement.
- */
- @Test
- void testSecureOMDelegationTokenSecretManagerInitializationFailure() throws
Exception {
- initSCM();
- // Create a secure SCM instance as om client will connect to it
- scm = HddsTestUtils.getScmSimple(conf);
- try {
- scm.start();
- conf.setTimeDuration(HDDS_SECRET_KEY_EXPIRY_DURATION, 7, TimeUnit.DAYS);
- conf.setTimeDuration(OMConfigKeys.DELEGATION_TOKEN_MAX_LIFETIME_KEY, 7,
TimeUnit.DAYS);
- IllegalArgumentException exception = assertThrows(
- IllegalArgumentException.class, () -> setupOm(conf));
- assertThat(exception.getMessage()).contains("Secret key expiry duration
hdds.secret.key.expiry.duration " +
- "should be greater than value of
(ozone.manager.delegation.token.max-lifetime + " +
- "ozone.manager.delegation.remover.scan.interval +
hdds.secret.key.rotate.duration");
- } finally {
- if (scm != null) {
- scm.stop();
- }
- }
- }
-
- /**
- * Tests the secure om Initialization success.
- */
- @Test
- void testSecureOmInitializationSuccess() throws Exception {
+ void testAccessControlExceptionOnClient() throws Exception {
initSCM();
// Create a secure SCM instance as om client will connect to it
scm = HddsTestUtils.getScmSimple(conf);
- LogCapturer logs = LogCapturer.captureLogs(OzoneManager.class);
- GenericTestUtils.setLogLevel(OzoneManager.class, INFO);
-
- try {
- scm.start();
- setupOm(conf);
- om.start();
- } catch (Exception ex) {
- // Expects timeout failure from scmClient in om but om user login via
- // kerberos should succeed.
- assertThat(logs.getOutput()).contains("Ozone Manager login successful");
- } finally {
- if (scm != null) {
- scm.stop();
- }
- }
- }
+ scm.start();
- @Test
- void testAccessControlExceptionOnClient() throws Exception {
- initSCM();
- LogCapturer logs = LogCapturer.captureLogs(OzoneManager.class);
- GenericTestUtils.setLogLevel(OzoneManager.class, INFO);
- try {
- // Create a secure SCM instance as om client will connect to it
- scm = HddsTestUtils.getScmSimple(conf);
- scm.start();
+ setupOm(conf);
+ om.setCertClient(new CertificateClientTestImpl(conf));
+ om.setScmTopologyClient(new ScmTopologyClient(scmBlockClient));
+ om.start();
- setupOm(conf);
- om.setCertClient(new CertificateClientTestImpl(conf));
- om.setScmTopologyClient(new ScmTopologyClient(scmBlockClient));
- om.start();
- } catch (Exception ex) {
- // Expects timeout failure from scmClient in om but om user login via
- // kerberos should succeed.
- assertThat(logs.getOutput()).contains("Ozone Manager login successful");
- }
+ // positive case (happy-path)
UserGroupInformation ugi =
UserGroupInformation.loginUserFromKeytabAndReturnUGI(
testUserPrincipal, testUserKeytab.getCanonicalPath());
@@ -609,6 +608,7 @@ void testAccessControlExceptionOnClient() throws Exception {
.setAdminName("admin")
.build());
+ // negative (an unauthenticated client gets rejected)
ugi = UserGroupInformation.createUserForTesting(
"testuser1", new String[] {"test"});
@@ -618,7 +618,7 @@ void testAccessControlExceptionOnClient() throws Exception {
ClientId.randomId().toString());
String exMessage = "org.apache.hadoop.security.AccessControlException: " +
"Client cannot authenticate via:[TOKEN, KERBEROS]";
- logs = LogCapturer.captureLogs(Client.class);
+ LogCapturer logs = LogCapturer.captureLogs(Client.class);
IOException ioException = assertThrows(IOException.class,
() -> unsecureClient.listAllVolumes(null, null, 0));
assertThat(ioException).hasMessageContaining(exMessage);
@@ -634,304 +634,145 @@ private void generateKeyPair() throws Exception {
keyStorage.storeKeyPair(keyPair);
}
- /**
- * Tests delegation token renewal.
- */
- @Test
- void testDelegationTokenRenewal() throws Exception {
- GenericTestUtils.setLogLevel(Server.class, INFO);
- LogCapturer omLogs = LogCapturer.captureLogs(OzoneManager.class);
-
- // Setup SCM
- initSCM();
- scm = HddsTestUtils.getScmSimple(conf);
- try {
- // Start SCM
- scm.start();
-
- // Setup secure OM for start. Generous token lifetime so the renewer and
- // tampered-token cases below do not race token expiry.
- conf.setLong(DELEGATION_TOKEN_MAX_LIFETIME_KEY, 60 * 1000L);
- setupOm(conf);
- OzoneManager.setTestSecureOmFlag(true);
- om.setCertClient(new CertificateClientTestImpl(conf));
- om.setScmTopologyClient(new ScmTopologyClient(scmBlockClient));
- om.start();
-
- UserGroupInformation ugi = UserGroupInformation.getCurrentUser();
-
- // Get first OM client which will authenticate via Kerberos
- omClient = new OzoneManagerProtocolClientSideTranslatorPB(
- OmTransportFactory.create(conf, ugi, null),
- RandomStringUtils.secure().nextAscii(5));
-
- // Since client is already connected get a delegation token
- Token<OzoneTokenIdentifier> token = omClient.getDelegationToken(
- new Text("om"));
-
- // Check if token is of right kind and renewer is running om instance
- assertNotNull(token);
- assertEquals("OzoneToken", token.getKind().toString());
- assertEquals(SecurityUtil.buildTokenService(
- om.getNodeDetails().getRpcAddress()).toString(),
- token.getService().toString());
-
- // Renew delegation token
- long expiryTime = omClient.renewDelegationToken(token);
- assertThat(expiryTime).isGreaterThan(0);
- omLogs.clearOutput();
-
- // Test failure of delegation renewal
- // 1. When token maxExpiryTime exceeds (maxDate in the past)
- OzoneTokenIdentifier expiredId = OzoneTokenIdentifier.readProtoBuf(
- token.getIdentifier());
- expiredId.setMaxDate(System.currentTimeMillis() - 1000);
- Token<OzoneTokenIdentifier> expiredToken = new Token<>(
- expiredId.getBytes(), token.getPassword(), token.getKind(),
- token.getService());
- OMException ex = assertThrows(OMException.class,
- () -> omClient.renewDelegationToken(expiredToken));
- assertEquals(TOKEN_EXPIRED, ex.getResult());
- omLogs.clearOutput();
-
- // 2. When renewer doesn't match (implicitly covers when renewer is
- // null or empty )
- Token<OzoneTokenIdentifier> token2 = omClient.getDelegationToken(
- new Text("randomService"));
- assertNotNull(token2);
- ex = assertThrows(OMException.class,
- () -> omClient.renewDelegationToken(token2));
- assertThat(ex).hasMessageContaining("Delegation token renewal failed");
- assertThat(omLogs.getOutput()).contains(" with non-matching renewer
randomService");
- omLogs.clearOutput();
-
- // 3. Test tampered token
- OzoneTokenIdentifier tokenId = OzoneTokenIdentifier.readProtoBuf(
- token.getIdentifier());
- tokenId.setRenewer(new Text("om"));
- tokenId.setMaxDate(System.currentTimeMillis() * 2);
- Token<OzoneTokenIdentifier> tamperedToken = new Token<>(
- tokenId.getBytes(), token2.getPassword(), token2.getKind(),
- token2.getService());
- ex = assertThrows(OMException.class,
- () -> omClient.renewDelegationToken(tamperedToken));
- assertThat(ex).hasMessageContaining("Delegation token renewal failed");
- assertThat(omLogs.getOutput()).contains("can't be found in cache");
- omLogs.clearOutput();
- } finally {
- if (scm != null) {
- scm.stop();
- }
- IOUtils.closeQuietly(om);
- }
- }
-
private void setupOm(OzoneConfiguration config) throws Exception {
OMStorage omStore = new OMStorage(config);
- omStore.setClusterId(clusterId);
- omStore.setOmCertSerialId(OM_CERT_SERIAL_ID);
- // writes the version file properties
- omStore.initialize();
+ if (omStore.getState() != Storage.StorageState.INITIALIZED) {
+ omStore.setClusterId(clusterId);
+ omStore.setOmCertSerialId(OM_CERT_SERIAL_ID);
+ // writes the version file properties
+ omStore.initialize();
+ }
OzoneManager.setTestSecureOmFlag(true);
om = OzoneManager.createOm(config);
}
- @Test
- @Flaky("HDDS-9349")
- void testGetSetRevokeS3Secret() throws Exception {
- initSCM();
- try {
- scm = HddsTestUtils.getScmSimple(conf);
- scm.start();
-
- // Setup secure OM for start
- setupOm(conf);
- // Start OM
- om.setCertClient(new CertificateClientTestImpl(conf));
- om.setScmTopologyClient(new ScmTopologyClient(scmBlockClient));
- om.start();
- UserGroupInformation ugi = UserGroupInformation.getCurrentUser();
- String username = ugi.getUserName();
-
- // Get first OM client which will authenticate via Kerberos
- omClient = new OzoneManagerProtocolClientSideTranslatorPB(
- OmTransportFactory.create(conf, ugi, null),
- RandomStringUtils.secure().nextAscii(5));
-
- // Creates a secret since it does not exist
- S3SecretValue attempt1 = omClient.getS3Secret(username);
-
- // A second getS3Secret on the same username should throw exception
- try {
- omClient.getS3Secret(username);
- } catch (OMException omEx) {
- assertEquals(OMException.ResultCodes.S3_SECRET_ALREADY_EXISTS,
- omEx.getResult());
- }
-
- // Revoke the existing secret
- omClient.revokeS3Secret(username);
-
- // Set secret should fail since the accessId is revoked
- final String secretKeySet = "somesecret1";
- try {
- omClient.setS3Secret(username, secretKeySet);
- } catch (OMException omEx) {
- assertEquals(OMException.ResultCodes.ACCESS_ID_NOT_FOUND,
- omEx.getResult());
- }
-
- // Get a new secret
- S3SecretValue attempt3 = omClient.getS3Secret(username);
-
- // secret should differ because it has been revoked previously
- assertNotEquals(attempt3.getAwsSecret(), attempt1.getAwsSecret());
-
- // accessKey is still the same because it is derived from username
- assertEquals(attempt3.getAwsAccessKey(), attempt1.getAwsAccessKey());
-
- // Admin can set secret for any user
- S3SecretValue attempt4 = omClient.setS3Secret(username, secretKeySet);
- assertEquals(secretKeySet, attempt4.getAwsSecret());
-
- // A second getS3Secret on the same username should throw exception
- try {
- omClient.getS3Secret(username);
- } catch (OMException omEx) {
- assertEquals(OMException.ResultCodes.S3_SECRET_ALREADY_EXISTS,
- omEx.getResult());
- }
-
- // Clean up
- omClient.revokeS3Secret(username);
-
- // Admin can get and revoke other users' secrets
- // omClient's ugi is current user, which is added as an OM admin
- omClient.getS3Secret("HADOOP/ALICE");
- omClient.revokeS3Secret("HADOOP/ALICE");
-
- // testUser is not an admin
- final UserGroupInformation ugiNonAdmin =
- UserGroupInformation.loginUserFromKeytabAndReturnUGI(
- testUserPrincipal, testUserKeytab.getCanonicalPath());
- final OzoneManagerProtocolClientSideTranslatorPB omClientNonAdmin =
- new OzoneManagerProtocolClientSideTranslatorPB(
- OmTransportFactory.create(conf, ugiNonAdmin, null),
- RandomStringUtils.secure().nextAscii(5));
-
- OMException omException = assertThrows(OMException.class,
- () -> omClientNonAdmin.getS3Secret("HADOOP/JOHN"));
- assertSame(USER_MISMATCH, omException.getResult());
- omException = assertThrows(OMException.class,
- () -> omClientNonAdmin.revokeS3Secret("HADOOP/DOE"));
- assertSame(USER_MISMATCH, omException.getResult());
-
- } finally {
- if (scm != null) {
- scm.stop();
+ private void stopOm() {
+ if (om != null) {
+ if (om.stop()) {
+ om.join();
}
- IOUtils.closeQuietly(om);
+ om = null;
}
}
- /**
- * Tests functionality to init secure OM when it is already initialized.
- */
@Test
- void testSecureOmReInit() throws Exception {
- LogCapturer omLogs = LogCapturer.captureLogs(OMCertificateClient.class);
- omLogs.clearOutput();
-
+ @Flaky("HDDS-9349")
+ void testGetSetRevokeS3Secret() throws Exception {
initSCM();
- try {
- scm = HddsTestUtils.getScmSimple(conf);
- scm.start();
- conf.setBoolean(OZONE_SECURITY_ENABLED_KEY, false);
- OMStorage omStore = new OMStorage(conf);
- initializeOmStorage(omStore);
- OzoneManager.setTestSecureOmFlag(true);
- om = OzoneManager.createOm(conf);
-
- assertNull(om.getCertificateClient());
- String logOutput = omLogs.getOutput();
- assertThat(logOutput)
- .doesNotContain("Init response: GETCERT");
- assertThat(logOutput)
- .doesNotContain("Successfully stored SCM signed certificate");
-
- if (om.stop()) {
- om.join();
- }
-
- conf.setBoolean(OZONE_SECURITY_ENABLED_KEY, true);
- conf.setBoolean(OZONE_OM_S3_GPRC_SERVER_ENABLED, true);
- conf.set(OZONE_OM_ADDRESS_KEY,
- InetAddress.getLocalHost().getCanonicalHostName() + ":" +
getFreePort());
-
- OzoneManager.omInit(conf);
- om = OzoneManager.createOm(conf);
-
- assertNotNull(om.getCertificateClient());
- assertNotNull(om.getCertificateClient().getPublicKey());
- assertNotNull(om.getCertificateClient().getPrivateKey());
- assertNotNull(om.getCertificateClient().getCertificate());
- assertThat(omLogs.getOutput())
- .contains("Init response: GETCERT")
- .contains("Successfully stored OM signed certificate");
- X509Certificate certificate = om.getCertificateClient().getCertificate();
- validateCertificate(certificate);
-
- } finally {
- if (scm != null) {
- scm.stop();
- }
- }
+ scm = HddsTestUtils.getScmSimple(conf);
+ scm.start();
+
+ // Setup secure OM for start
+ setupOm(conf);
+ // Start OM
+ om.setCertClient(new CertificateClientTestImpl(conf));
+ om.setScmTopologyClient(new ScmTopologyClient(scmBlockClient));
+ om.start();
+ UserGroupInformation ugi = UserGroupInformation.getCurrentUser();
+ String username = ugi.getUserName();
+
+ // Get first OM client which will authenticate via Kerberos
+ omClient = new OzoneManagerProtocolClientSideTranslatorPB(
+ OmTransportFactory.create(conf, ugi, null),
+ RandomStringUtils.secure().nextAscii(5));
+
+ // Creates a secret since it does not exist
+ S3SecretValue attempt1 = omClient.getS3Secret(username);
+
+ // A second getS3Secret on the same username should throw exception
+ OMException omEx = assertThrows(OMException.class,
+ () -> omClient.getS3Secret(username));
+ assertEquals(OMException.ResultCodes.S3_SECRET_ALREADY_EXISTS,
+ omEx.getResult());
+
+ // Revoke the existing secret
+ omClient.revokeS3Secret(username);
+
+ // Set secret should fail since the accessId is revoked
+ final String secretKeySet = "somesecret1";
+ omEx = assertThrows(OMException.class,
+ () -> omClient.setS3Secret(username, secretKeySet));
+ assertEquals(OMException.ResultCodes.ACCESS_ID_NOT_FOUND,
+ omEx.getResult());
+
+ // Get a new secret
+ S3SecretValue attempt3 = omClient.getS3Secret(username);
+
+ // secret should differ because it has been revoked previously
+ assertNotEquals(attempt3.getAwsSecret(), attempt1.getAwsSecret());
+
+ // accessKey is still the same because it is derived from username
+ assertEquals(attempt3.getAwsAccessKey(), attempt1.getAwsAccessKey());
+
+ // Admin can set secret for any user
+ S3SecretValue attempt4 = omClient.setS3Secret(username, secretKeySet);
+ assertEquals(secretKeySet, attempt4.getAwsSecret());
+
+ // A second getS3Secret on the same username should throw exception
+ omEx = assertThrows(OMException.class,
+ () -> omClient.getS3Secret(username));
+ assertEquals(OMException.ResultCodes.S3_SECRET_ALREADY_EXISTS,
+ omEx.getResult());
+
+ // Clean up
+ omClient.revokeS3Secret(username);
+
+ // Admin can get and revoke other users' secrets
+ // omClient's ugi is current user, which is added as an OM admin
+ omClient.getS3Secret("HADOOP/ALICE");
+ omClient.revokeS3Secret("HADOOP/ALICE");
+
+ // testUser is not an admin
+ final UserGroupInformation ugiNonAdmin =
+ UserGroupInformation.loginUserFromKeytabAndReturnUGI(
+ testUserPrincipal, testUserKeytab.getCanonicalPath());
+ final OzoneManagerProtocolClientSideTranslatorPB omClientNonAdmin =
+ new OzoneManagerProtocolClientSideTranslatorPB(
+ OmTransportFactory.create(conf, ugiNonAdmin, null),
+ RandomStringUtils.secure().nextAscii(5));
+
+ OMException omException = assertThrows(OMException.class,
+ () -> omClientNonAdmin.getS3Secret("HADOOP/JOHN"));
+ assertSame(USER_MISMATCH, omException.getResult());
+ omException = assertThrows(OMException.class,
+ () -> omClientNonAdmin.revokeS3Secret("HADOOP/DOE"));
+ assertSame(USER_MISMATCH, omException.getResult());
}
/**
* Test functionality to get SCM signed certificate for OM.
*/
@Test
- void testSecureOmInitSuccess() throws Exception {
+ void testSecureOmGetsScmSignedCertificate() throws Exception {
LogCapturer omLogs =
LogCapturer.captureLogs(OMCertificateClient.class);
omLogs.clearOutput();
initSCM();
- try {
- scm = HddsTestUtils.getScmSimple(conf);
- scm.start();
-
- OMStorage omStore = new OMStorage(conf);
- initializeOmStorage(omStore);
- OzoneManager.setTestSecureOmFlag(true);
- om = OzoneManager.createOm(conf);
+ scm = HddsTestUtils.getScmSimple(conf);
+ scm.start();
- assertNotNull(om.getCertificateClient());
- assertNotNull(om.getCertificateClient().getPublicKey());
- assertNotNull(om.getCertificateClient().getPrivateKey());
- assertNotNull(om.getCertificateClient().getCertificate());
- assertEquals(3, om.getCertificateClient().getTrustChain().size());
- assertThat(omLogs.getOutput())
- .contains("Init response: GETCERT")
- .contains("Successfully stored OM signed certificate");
- X509Certificate certificate = om.getCertificateClient().getCertificate();
- validateCertificate(certificate);
- String pemEncodedCACert =
- scm.getSecurityProtocolServer().getCACertificate();
- X509Certificate caCert =
- CertificateCodec.getX509Certificate(pemEncodedCACert);
- X509Certificate caCertStored = om.getCertificateClient()
- .getCertificate(caCert.getSerialNumber().toString());
- assertEquals(caCert, caCertStored);
- } finally {
- if (scm != null) {
- scm.stop();
- }
- if (om != null) {
- om.stop();
- }
- IOUtils.closeQuietly(om);
- }
+ OMStorage omStore = new OMStorage(conf);
+ initializeOmStorage(omStore);
+ OzoneManager.setTestSecureOmFlag(true);
+ om = OzoneManager.createOm(conf);
+
+ assertNotNull(om.getCertificateClient());
+ assertNotNull(om.getCertificateClient().getPublicKey());
+ assertNotNull(om.getCertificateClient().getPrivateKey());
+ assertNotNull(om.getCertificateClient().getCertificate());
+ assertEquals(3, om.getCertificateClient().getTrustChain().size());
+ assertThat(omLogs.getOutput())
+ .contains("Init response: GETCERT")
+ .contains("Successfully stored OM signed certificate");
+ X509Certificate certificate = om.getCertificateClient().getCertificate();
+ validateCertificate(certificate);
+ String pemEncodedCACert =
+ scm.getSecurityProtocolServer().getCACertificate();
+ X509Certificate caCert =
+ CertificateCodec.getX509Certificate(pemEncodedCACert);
+ X509Certificate caCertStored = om.getCertificateClient()
+ .getCertificate(caCert.getSerialNumber().toString());
+ assertEquals(caCert, caCertStored);
}
/**
@@ -1112,7 +953,6 @@ void testCertificateRotationUnRecoverableFailure() throws
Exception {
LogCapturer certClientLogs =
LogCapturer.captureLogs(OMCertificateClient.class);
LogCapturer exitUtilLog = LogCapturer.captureLogs(ExitUtil.class);
-
OMStorage omStorage = new OMStorage(conf);
omStorage.setClusterId(clusterId);
omStorage.setOmId(omId);
@@ -1172,77 +1012,70 @@ public String renewAndStoreKeyAndCertificate(boolean
force) throws CertificateEx
@Test
void testDelegationTokenRenewCrossSecretKeyRotation() throws Exception {
initSCM();
- try {
- scm = HddsTestUtils.getScmSimple(conf);
- scm.start();
-
- // Setup secure OM for start.
- final int certLifetime = 40 * 1000; // 40s
- OzoneConfiguration newConf = new OzoneConfiguration(conf);
- newConf.set(HDDS_X509_DEFAULT_DURATION,
- Duration.ofMillis(certLifetime).toString());
- newConf.set(HDDS_X509_RENEW_GRACE_DURATION,
- Duration.ofMillis(certLifetime - 15 * 1000).toString());
- newConf.setLong(OMConfigKeys.DELEGATION_TOKEN_MAX_LIFETIME_KEY,
- certLifetime - 20 * 1000);
-
- setupOm(newConf);
- OzoneManager.setTestSecureOmFlag(true);
-
- CertificateClientTestImpl certClient =
- new CertificateClientTestImpl(newConf, true);
- // Start OM
- om.setCertClient(certClient);
- om.setScmTopologyClient(new ScmTopologyClient(scmBlockClient));
- SecretKeyTestClient secretKeyClient = new SecretKeyTestClient();
- ManagedSecretKey secretKey1 = secretKeyClient.getCurrentSecretKey();
- om.setSecretKeyClient(secretKeyClient);
- om.start();
- GenericTestUtils.waitFor(() -> om.isLeaderReady(), 100, 10000);
-
- UserGroupInformation ugi = UserGroupInformation.getCurrentUser();
+ scm = HddsTestUtils.getScmSimple(conf);
+ scm.start();
+
+ // Setup secure OM for start.
+ final int certLifetime = 40 * 1000; // 40s
+ OzoneConfiguration newConf = new OzoneConfiguration(conf);
+ newConf.set(HDDS_X509_DEFAULT_DURATION,
+ Duration.ofMillis(certLifetime).toString());
+ newConf.set(HDDS_X509_RENEW_GRACE_DURATION,
+ Duration.ofMillis(certLifetime - 15 * 1000).toString());
+ newConf.setLong(OMConfigKeys.DELEGATION_TOKEN_MAX_LIFETIME_KEY,
+ certLifetime - 20 * 1000);
+
+ setupOm(newConf);
+ OzoneManager.setTestSecureOmFlag(true);
- // Get first OM client which will authenticate via Kerberos
- omClient = new OzoneManagerProtocolClientSideTranslatorPB(
- OmTransportFactory.create(newConf, ugi, null),
- RandomStringUtils.secure().nextAscii(5));
-
- // Since client is already connected get a delegation token
- Token<OzoneTokenIdentifier> token1 = omClient.getDelegationToken(
- new Text("om"));
-
- // Check if token is of right kind and renewer is running om instance
- assertNotNull(token1);
- assertEquals("OzoneToken", token1.getKind().toString());
- assertEquals(SecurityUtil.buildTokenService(
- om.getNodeDetails().getRpcAddress()).toString(),
- token1.getService().toString());
- assertEquals(secretKey1.getId().toString(),
token1.decodeIdentifier().getSecretKeyId());
-
- // Renew delegation token
- long expiryTime = omClient.renewDelegationToken(token1);
- assertThat(expiryTime).isGreaterThan(0);
-
- // Rotate secret key
- secretKeyClient.rotate();
- ManagedSecretKey secretKey2 = secretKeyClient.getCurrentSecretKey();
- assertNotEquals(secretKey1.getId(), secretKey2.getId());
- // Get a new delegation token
- Token<OzoneTokenIdentifier> token2 = omClient.getDelegationToken(
- new Text("om"));
- assertEquals(secretKey2.getId().toString(),
token2.decodeIdentifier().getSecretKeyId());
-
- // Because old secret key is still valid, so renew old token will succeed
- expiryTime = omClient.renewDelegationToken(token1);
- assertThat(expiryTime)
- .isGreaterThan(0)
- .isLessThan(secretKey2.getExpiryTime().toEpochMilli());
- } finally {
- if (scm != null) {
- scm.stop();
- }
- IOUtils.closeQuietly(om);
- }
+ CertificateClientTestImpl certClient =
+ new CertificateClientTestImpl(newConf, true);
+ // Start OM
+ om.setCertClient(certClient);
+ om.setScmTopologyClient(new ScmTopologyClient(scmBlockClient));
+ SecretKeyTestClient secretKeyClient = new SecretKeyTestClient();
+ ManagedSecretKey secretKey1 = secretKeyClient.getCurrentSecretKey();
+ om.setSecretKeyClient(secretKeyClient);
+ om.start();
+ GenericTestUtils.waitFor(() -> om.isLeaderReady(), 100, 10000);
+
+ UserGroupInformation ugi = UserGroupInformation.getCurrentUser();
+
+ // Get first OM client which will authenticate via Kerberos
+ omClient = new OzoneManagerProtocolClientSideTranslatorPB(
+ OmTransportFactory.create(newConf, ugi, null),
+ RandomStringUtils.secure().nextAscii(5));
+
+ // Since client is already connected get a delegation token
+ Token<OzoneTokenIdentifier> token1 = omClient.getDelegationToken(
+ new Text("om"));
+
+ // Check if token is of right kind and renewer is running om instance
+ assertNotNull(token1);
+ assertEquals("OzoneToken", token1.getKind().toString());
+ assertEquals(SecurityUtil.buildTokenService(
+ om.getNodeDetails().getRpcAddress()).toString(),
+ token1.getService().toString());
+ assertEquals(secretKey1.getId().toString(),
token1.decodeIdentifier().getSecretKeyId());
+
+ // Renew delegation token
+ long expiryTime = omClient.renewDelegationToken(token1);
+ assertThat(expiryTime).isGreaterThan(0);
+
+ // Rotate secret key
+ secretKeyClient.rotate();
+ ManagedSecretKey secretKey2 = secretKeyClient.getCurrentSecretKey();
+ assertNotEquals(secretKey1.getId(), secretKey2.getId());
+ // Get a new delegation token
+ Token<OzoneTokenIdentifier> token2 = omClient.getDelegationToken(
+ new Text("om"));
+ assertEquals(secretKey2.getId().toString(),
token2.decodeIdentifier().getSecretKeyId());
+
+ // Because old secret key is still valid, so renew old token will succeed
+ expiryTime = omClient.renewDelegationToken(token1);
+ assertThat(expiryTime)
+ .isGreaterThan(0)
+ .isLessThan(secretKey2.getExpiryTime().toEpochMilli());
}
/**
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]